diff options
| -rw-r--r-- | core/java/android/speech/tts/FileSynthesisRequest.java | 17 | ||||
| -rw-r--r-- | core/java/android/speech/tts/PlaybackSynthesisRequest.java | 16 | ||||
| -rw-r--r-- | core/java/android/speech/tts/SynthesisRequest.java | 13 | ||||
| -rw-r--r-- | core/java/android/speech/tts/TextToSpeechService.java | 11 | ||||
| -rw-r--r-- | core/jni/android/graphics/RtlProperties.h | 2 | ||||
| -rw-r--r-- | core/jni/android/graphics/TextLayoutCache.cpp | 26 | ||||
| -rw-r--r-- | include/gui/SurfaceTexture.h | 25 | ||||
| -rw-r--r-- | include/gui/SurfaceTextureClient.h | 22 | ||||
| -rw-r--r-- | libs/gui/SurfaceTexture.cpp | 72 | ||||
| -rw-r--r-- | libs/gui/SurfaceTextureClient.cpp | 65 | ||||
| -rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 91 | ||||
| -rw-r--r-- | media/libstagefright/SampleTable.cpp | 159 | ||||
| -rw-r--r-- | media/libstagefright/include/MPEG4Extractor.h | 2 | ||||
| -rw-r--r-- | media/libstagefright/include/SampleTable.h | 12 |
14 files changed, 362 insertions, 171 deletions
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisRequest.java index 6a9b2dc14de3..7efc26466c8e 100644 --- a/core/java/android/speech/tts/FileSynthesisRequest.java +++ b/core/java/android/speech/tts/FileSynthesisRequest.java @@ -45,6 +45,7 @@ class FileSynthesisRequest extends SynthesisRequest { private int mChannelCount; private RandomAccessFile mFile; private boolean mStopped = false; + private boolean mDone = false; FileSynthesisRequest(String text, File fileName) { super(text); @@ -89,6 +90,11 @@ class FileSynthesisRequest extends SynthesisRequest { } @Override + boolean isDone() { + return mDone; + } + + @Override public int start(int sampleRateInHz, int audioFormat, int channelCount) { if (DBG) { Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat @@ -164,6 +170,7 @@ class FileSynthesisRequest extends SynthesisRequest { mFile.write( makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength)); closeFile(); + mDone = true; return TextToSpeech.SUCCESS; } catch (IOException ex) { Log.e(TAG, "Failed to write to " + mFileName + ": " + ex); @@ -174,6 +181,14 @@ class FileSynthesisRequest extends SynthesisRequest { } @Override + public void error() { + if (DBG) Log.d(TAG, "FileSynthesisRequest.error()"); + synchronized (mStateLock) { + cleanUp(); + } + } + + @Override public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount, byte[] buffer, int offset, int length) { synchronized (mStateLock) { @@ -187,9 +202,11 @@ class FileSynthesisRequest extends SynthesisRequest { out = new FileOutputStream(mFileName); out.write(makeWavHeader(sampleRateInHz, audioFormat, channelCount, length)); out.write(buffer, offset, length); + mDone = true; return TextToSpeech.SUCCESS; } catch (IOException ex) { Log.e(TAG, "Failed to write to " + mFileName + ": " + ex); + mFileName.delete(); return TextToSpeech.ERROR; } finally { try { diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java index 226701518805..227b0428ed03 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java +++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java @@ -50,6 +50,7 @@ class PlaybackSynthesisRequest extends SynthesisRequest { private final Object mStateLock = new Object(); private AudioTrack mAudioTrack = null; private boolean mStopped = false; + private boolean mDone = false; PlaybackSynthesisRequest(String text, int streamType, float volume, float pan) { super(text); @@ -85,6 +86,11 @@ class PlaybackSynthesisRequest extends SynthesisRequest { return MIN_AUDIO_BUFFER_SIZE; } + @Override + boolean isDone() { + return mDone; + } + // TODO: add a thread that writes to the AudioTrack? @Override public int start(int sampleRateInHz, int audioFormat, int channelCount) { @@ -183,12 +189,21 @@ class PlaybackSynthesisRequest extends SynthesisRequest { Log.e(TAG, "done(): Not started"); return TextToSpeech.ERROR; } + mDone = true; cleanUp(); } return TextToSpeech.SUCCESS; } @Override + public void error() { + if (DBG) Log.d(TAG, "error()"); + synchronized (mStateLock) { + cleanUp(); + } + } + + @Override public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount, byte[] buffer, int offset, int length) { if (DBG) { @@ -217,6 +232,7 @@ class PlaybackSynthesisRequest extends SynthesisRequest { try { mAudioTrack.write(buffer, offset, length); mAudioTrack.play(); + mDone = true; } catch (IllegalStateException ex) { Log.e(TAG, "Playback error", ex); return TextToSpeech.ERROR; diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java index f4bb85228901..515218b2a38e 100644 --- a/core/java/android/speech/tts/SynthesisRequest.java +++ b/core/java/android/speech/tts/SynthesisRequest.java @@ -114,6 +114,11 @@ public abstract class SynthesisRequest { public abstract int getMaxBufferSize(); /** + * Checks whether the synthesis request completed successfully. + */ + abstract boolean isDone(); + + /** * Aborts the speech request. * * Can be called from multiple threads. @@ -162,6 +167,14 @@ public abstract class SynthesisRequest { public abstract int done(); /** + * The service should call this method if the speech synthesis fails. + * + * This method should only be called on the synthesis thread, + * while in {@link TextToSpeechService#onSynthesizeText}. + */ + public abstract void error(); + + /** * The service can call this method instead of using {@link #start}, {@link #audioAvailable} * and {@link #done} if all the audio data is available in a single buffer. * diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index a408ea2e9665..da97fb4bdf80 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -150,12 +150,10 @@ public abstract class TextToSpeechService extends Service { * * Called on the synthesis thread. * - * @param request The synthesis request. The method should - * call {@link SynthesisRequest#start}, {@link SynthesisRequest#audioAvailable}, - * and {@link SynthesisRequest#done} on this request. - * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. + * @param request The synthesis request. The method should use the methods in the request + * object to communicate the results of the synthesis. */ - protected abstract int onSynthesizeText(SynthesisRequest request); + protected abstract void onSynthesizeText(SynthesisRequest request); private boolean areDefaultsEnforced() { return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS, @@ -442,7 +440,8 @@ public abstract class TextToSpeechService extends Service { synthesisRequest = mSynthesisRequest; } setRequestParams(synthesisRequest); - return TextToSpeechService.this.onSynthesizeText(synthesisRequest); + TextToSpeechService.this.onSynthesizeText(synthesisRequest); + return synthesisRequest.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR; } protected SynthesisRequest createSynthesisRequest() { diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h index 4fac89a6fabd..a41c91b0812a 100644 --- a/core/jni/android/graphics/RtlProperties.h +++ b/core/jni/android/graphics/RtlProperties.h @@ -52,7 +52,7 @@ static RtlDebugLevel readRtlDebugLevel() { #define DEBUG_ADVANCES 0 // Define if we want (1) to have Glyphs debug values or not (0) -#define DEBUG_GLYPHS 1 +#define DEBUG_GLYPHS 0 } // namespace android #endif // ANDROID_RTL_PROPERTIES_H diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index 088202e277e7..77a731a3e948 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -420,7 +420,9 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar UBiDi* bidi = ubidi_open(); if (bidi) { UErrorCode status = U_ZERO_ERROR; +#if DEBUG_GLYPHS LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq); +#endif ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status); if (U_SUCCESS(status)) { int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl @@ -430,7 +432,6 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar #endif if (rc == 1 || !U_SUCCESS(status)) { - LOGD("HERE !!!"); computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); ubidi_close(bidi); @@ -517,16 +518,25 @@ void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UC #endif // Get Advances and their total - jfloat totalAdvance = 0; - for (size_t i = 0; i < count; i++) { - totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]); -#if DEBUG_ADVANCES - LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i], - totalAdvance); -#endif + jfloat totalAdvance = outAdvances[0] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]); + for (size_t i = 1; i < count; i++) { + size_t clusterPrevious = shaperItem.log_clusters[i - 1]; + size_t cluster = shaperItem.log_clusters[i]; + if (cluster == clusterPrevious) { + outAdvances[i] = 0; + } else { + totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]); + } } *outTotalAdvance = totalAdvance; +#if DEBUG_ADVANCES + for (size_t i = 0; i < count; i++) { + LOGD("hb-adv[%d] = %f - log_clusters = %d - total = %f", i, + outAdvances[i], shaperItem.log_clusters[i], totalAdvance); + } +#endif + // Get Glyphs if (outGlyphs) { *outGlyphsCount = shaperItem.num_glyphs; diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 340daaf14efd..96828c640c27 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -127,11 +127,28 @@ public: // be called from the client. status_t setDefaultBufferSize(uint32_t w, uint32_t h); -private: + // getCurrentBuffer returns the buffer associated with the current image. + sp<GraphicBuffer> getCurrentBuffer() const; + + // getCurrentTextureTarget returns the texture target of the current + // texture as returned by updateTexImage(). + GLenum getCurrentTextureTarget() const; + + // getCurrentCrop returns the cropping rectangle of the current buffer + Rect getCurrentCrop() const; + + // getCurrentTransform returns the transform of the current buffer + uint32_t getCurrentTransform() const; + +protected: // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for // all slots. void freeAllBuffers(); + static bool isExternalFormat(uint32_t format); + static GLenum getTextureTarget(uint32_t format); + +private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, @@ -194,6 +211,10 @@ private: // reset mCurrentTexture to INVALID_BUFFER_SLOT. int mCurrentTexture; + // mCurrentTextureTarget is the GLES texture target to be used with the + // current texture. + GLenum mCurrentTextureTarget; + // mCurrentTextureBuf is the graphic buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to properly use @@ -256,7 +277,7 @@ private: // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. - Mutex mMutex; + mutable Mutex mMutex; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index df82bf217f72..fe9b04917ed8 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -27,6 +27,8 @@ namespace android { +class Surface; + class SurfaceTextureClient : public EGLNativeBase<ANativeWindow, SurfaceTextureClient, RefBase> { @@ -36,6 +38,7 @@ public: sp<ISurfaceTexture> getISurfaceTexture() const; private: + friend class Surface; // can't be copied SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs); @@ -78,6 +81,8 @@ private: void freeAllBuffers(); + int getConnectedApi() const; + enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS }; enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS }; enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS }; @@ -121,10 +126,25 @@ private: // a timestamp is auto-generated when queueBuffer is called. int64_t mTimestamp; + // mConnectedApi holds the currently connected API to this surface + int mConnectedApi; + + // mQueryWidth is the width returned by query(). It is set to width + // of the last dequeued buffer or to mReqWidth if no buffer was dequeued. + uint32_t mQueryWidth; + + // mQueryHeight is the height returned by query(). It is set to height + // of the last dequeued buffer or to mReqHeight if no buffer was dequeued. + uint32_t mQueryHeight; + + // mQueryFormat is the format returned by query(). It is set to the last + // dequeued format or to mReqFormat if no buffer was dequeued. + uint32_t mQueryFormat; + // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. - Mutex mMutex; + mutable Mutex mMutex; }; }; // namespace android diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index e2346f060ecf..39418f03adae 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -27,6 +27,8 @@ #include <gui/SurfaceTexture.h> +#include <hardware/hardware.h> + #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <surfaceflinger/IGraphicBufferAlloc.h> @@ -82,6 +84,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex) : mUseDefaultSize(true), mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT), + mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES), mCurrentTransform(0), mCurrentTimestamp(0), mLastQueued(INVALID_BUFFER_SLOT), @@ -197,6 +200,7 @@ status_t SurfaceTexture::dequeueBuffer(int *buf) { if (buffer == NULL) { return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; } + if ((mUseDefaultSize) && ((uint32_t(buffer->width) != mDefaultWidth) || (uint32_t(buffer->height) != mDefaultHeight))) { @@ -263,9 +267,6 @@ status_t SurfaceTexture::updateTexImage() { LOGV("SurfaceTexture::updateTexImage"); Mutex::Autolock lock(mMutex); - // We always bind the texture even if we don't update its contents. - glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName); - // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT, // so this check will fail until a buffer gets queued. if (mCurrentTexture != mLastQueued) { @@ -283,7 +284,15 @@ status_t SurfaceTexture::updateTexImage() { while ((error = glGetError()) != GL_NO_ERROR) { LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); } - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image); + + GLenum target = getTextureTarget( + mSlots[mLastQueued].mGraphicBuffer->format); + if (target != mCurrentTextureTarget) { + glDeleteTextures(1, &mTexName); + } + glBindTexture(target, mTexName); + glEGLImageTargetTexture2DOES(target, (GLeglImageOES)image); + bool failed = false; while ((error = glGetError()) != GL_NO_ERROR) { LOGE("error binding external texture image %p (slot %d): %#04x", @@ -296,14 +305,53 @@ status_t SurfaceTexture::updateTexImage() { // Update the SurfaceTexture state. mCurrentTexture = mLastQueued; + mCurrentTextureTarget = target; mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer; mCurrentCrop = mLastQueuedCrop; mCurrentTransform = mLastQueuedTransform; mCurrentTimestamp = mLastQueuedTimestamp; + } else { + // We always bind the texture even if we don't update its contents. + glBindTexture(mCurrentTextureTarget, mTexName); } return OK; } +bool SurfaceTexture::isExternalFormat(uint32_t format) +{ + switch (format) { + // supported YUV formats + case HAL_PIXEL_FORMAT_YV12: + // Legacy/deprecated YUV formats + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return true; + } + + // Any OEM format needs to be considered + if (format>=0x100 && format<=0x1FF) + return true; + + return false; +} + +GLenum SurfaceTexture::getTextureTarget(uint32_t format) +{ + GLenum target = GL_TEXTURE_2D; +#if defined(GL_OES_EGL_image_external) + if (isExternalFormat(format)) { + target = GL_TEXTURE_EXTERNAL_OES; + } +#endif + return target; +} + +GLenum SurfaceTexture::getCurrentTextureTarget() const { + Mutex::Autolock lock(mMutex); + return mCurrentTextureTarget; +} + void SurfaceTexture::getTransformMatrix(float mtx[16]) { LOGV("SurfaceTexture::getTransformMatrix"); Mutex::Autolock lock(mMutex); @@ -445,6 +493,22 @@ EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, return image; } +sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const { + Mutex::Autolock lock(mMutex); + return mCurrentTextureBuf; +} + +Rect SurfaceTexture::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + return mCurrentCrop; +} + +uint32_t SurfaceTexture::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + + static void mtxMul(float out[16], const float a[16], const float b[16]) { out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 29fc4d3edc1e..f4b24162f0bf 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -26,8 +26,10 @@ namespace android { SurfaceTextureClient::SurfaceTextureClient( const sp<ISurfaceTexture>& surfaceTexture): mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0), - mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0), - mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() { + mReqHeight(0), mReqFormat(0), mReqUsage(0), + mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mConnectedApi(0), + mQueryWidth(0), mQueryHeight(0), mQueryFormat(0), + mMutex() { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = setSwapInterval; ANativeWindow::dequeueBuffer = dequeueBuffer; @@ -101,9 +103,10 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { } sp<GraphicBuffer>& gbuf(mSlots[buf]); if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION || - gbuf == 0 || gbuf->getWidth() != mReqWidth || - gbuf->getHeight() != mReqHeight || - uint32_t(gbuf->getPixelFormat()) != mReqFormat || + gbuf == 0 || + (mReqWidth && gbuf->getWidth() != mReqWidth) || + (mReqHeight && gbuf->getHeight() != mReqHeight) || + (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) || (gbuf->getUsage() & mReqUsage) != mReqUsage) { gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight, mReqFormat, mReqUsage); @@ -111,6 +114,9 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed"); return NO_MEMORY; } + mQueryWidth = gbuf->width; + mQueryHeight = gbuf->height; + mQueryFormat = gbuf->format; } *buffer = gbuf.get(); return OK; @@ -159,13 +165,13 @@ int SurfaceTextureClient::query(int what, int* value) { Mutex::Autolock lock(mMutex); switch (what) { case NATIVE_WINDOW_WIDTH: + *value = mQueryWidth ? mQueryWidth : mReqWidth; + return NO_ERROR; case NATIVE_WINDOW_HEIGHT: - // XXX: How should SurfaceTexture behave if setBuffersGeometry didn't - // override the size? - *value = 0; + *value = mQueryHeight ? mQueryHeight : mReqHeight; return NO_ERROR; case NATIVE_WINDOW_FORMAT: - *value = DEFAULT_FORMAT; + *value = mQueryFormat ? mQueryFormat : mReqFormat; return NO_ERROR; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: *value = MIN_UNDEQUEUED_BUFFERS; @@ -260,16 +266,49 @@ int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) { int SurfaceTextureClient::connect(int api) { LOGV("SurfaceTextureClient::connect"); - // XXX: Implement this! - return INVALID_OPERATION; + Mutex::Autolock lock(mMutex); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + if (mConnectedApi) { + err = -EINVAL; + } else { + mConnectedApi = api; + } + break; + default: + err = -EINVAL; + break; + } + return err; } int SurfaceTextureClient::disconnect(int api) { LOGV("SurfaceTextureClient::disconnect"); - // XXX: Implement this! - return INVALID_OPERATION; + Mutex::Autolock lock(mMutex); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + if (mConnectedApi == api) { + mConnectedApi = 0; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + break; + } + return err; } +int SurfaceTextureClient::getConnectedApi() const +{ + Mutex::Autolock lock(mMutex); + return mConnectedApi; +} + + int SurfaceTextureClient::setUsage(uint32_t reqUsage) { LOGV("SurfaceTextureClient::setUsage"); diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 1ca2d6d73355..f9db1a1bdec6 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -377,7 +377,7 @@ status_t MPEG4Extractor::readMetaData() { mFileMetaData->setCString(kKeyMIMEType, "audio/mp4"); } - mInitCheck = verifyIfStreamable(); + mInitCheck = OK; } else { mInitCheck = err; } @@ -1904,7 +1904,7 @@ status_t MPEG4Source::read( off64_t offset; size_t size; - uint32_t dts; + uint32_t cts; bool isSyncSample; bool newBuffer = false; if (mBuffer == NULL) { @@ -1912,7 +1912,7 @@ status_t MPEG4Source::read( status_t err = mSampleTable->getMetaDataForSample( - mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample); + mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample); if (err != OK) { return err; @@ -1942,7 +1942,7 @@ status_t MPEG4Source::read( mBuffer->set_range(0, size); mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( - kKeyTime, ((int64_t)dts * 1000000) / mTimescale); + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { mBuffer->meta_data()->setInt64( @@ -2060,7 +2060,7 @@ status_t MPEG4Source::read( mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( - kKeyTime, ((int64_t)dts * 1000000) / mTimescale); + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { mBuffer->meta_data()->setInt64( @@ -2094,87 +2094,6 @@ MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix( return NULL; } -status_t MPEG4Extractor::verifyIfStreamable() { - if (!(mDataSource->flags() & DataSource::kIsCachingDataSource)) { - return OK; - } - - Track *audio = findTrackByMimePrefix("audio/"); - Track *video = findTrackByMimePrefix("video/"); - - if (audio == NULL || video == NULL) { - return OK; - } - - sp<SampleTable> audioSamples = audio->sampleTable; - sp<SampleTable> videoSamples = video->sampleTable; - - off64_t maxOffsetDiff = 0; - int64_t maxOffsetTimeUs = -1; - - for (uint32_t i = 0; i < videoSamples->countSamples(); ++i) { - off64_t videoOffset; - uint32_t videoTime; - bool isSync; - CHECK_EQ((status_t)OK, videoSamples->getMetaDataForSample( - i, &videoOffset, NULL, &videoTime, &isSync)); - - int64_t videoTimeUs = (int64_t)(videoTime * 1E6 / video->timescale); - - uint32_t reqAudioTime = (videoTimeUs * audio->timescale) / 1000000; - uint32_t j; - if (audioSamples->findSampleAtTime( - reqAudioTime, &j, SampleTable::kFlagClosest) != OK) { - continue; - } - - off64_t audioOffset; - uint32_t audioTime; - CHECK_EQ((status_t)OK, audioSamples->getMetaDataForSample( - j, &audioOffset, NULL, &audioTime)); - - int64_t audioTimeUs = (int64_t)(audioTime * 1E6 / audio->timescale); - - off64_t offsetDiff = videoOffset - audioOffset; - if (offsetDiff < 0) { - offsetDiff = -offsetDiff; - } - -#if 0 - printf("%s%d/%d videoTime %.2f secs audioTime %.2f secs " - "videoOffset %lld audioOffset %lld offsetDiff %lld\n", - isSync ? "*" : " ", - i, - j, - videoTimeUs / 1E6, - audioTimeUs / 1E6, - videoOffset, - audioOffset, - offsetDiff); -#endif - - if (offsetDiff > maxOffsetDiff) { - maxOffsetDiff = offsetDiff; - maxOffsetTimeUs = videoTimeUs; - } - } - -#if 0 - printf("max offset diff: %lld at video time: %.2f secs\n", - maxOffsetDiff, maxOffsetTimeUs / 1E6); -#endif - - if (maxOffsetDiff < 1024 * 1024) { - return OK; - } - - LOGE("This content is not streamable, " - "max offset diff: %lld at video time: %.2f secs", - maxOffsetDiff, maxOffsetTimeUs / 1E6); - - return ERROR_UNSUPPORTED; -} - static bool LegacySniffMPEG4( const sp<DataSource> &source, String8 *mimeType, float *confidence) { uint8_t header[8]; diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index 423df705a592..08db9021314e 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -53,6 +53,7 @@ SampleTable::SampleTable(const sp<DataSource> &source) mNumSampleSizes(0), mTimeToSampleCount(0), mTimeToSample(NULL), + mSampleTimeEntries(NULL), mCompositionTimeDeltaEntries(NULL), mNumCompositionTimeDeltaEntries(0), mSyncSampleOffset(-1), @@ -73,6 +74,9 @@ SampleTable::~SampleTable() { delete[] mCompositionTimeDeltaEntries; mCompositionTimeDeltaEntries = NULL; + delete[] mSampleTimeEntries; + mSampleTimeEntries = NULL; + delete[] mTimeToSample; mTimeToSample = NULL; @@ -381,67 +385,128 @@ uint32_t abs_difference(uint32_t time1, uint32_t time2) { return time1 > time2 ? time1 - time2 : time2 - time1; } -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. +// static +int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) { + const SampleTimeEntry *a = (const SampleTimeEntry *)_a; + const SampleTimeEntry *b = (const SampleTimeEntry *)_b; - *sample_index = 0; + if (a->mCompositionTime < b->mCompositionTime) { + return -1; + } else if (a->mCompositionTime > b->mCompositionTime) { + return 1; + } + + return 0; +} +void SampleTable::buildSampleEntriesTable() { Mutex::Autolock autoLock(mLock); - uint32_t cur_sample = 0; - uint32_t time = 0; + if (mSampleTimeEntries != NULL) { + return; + } + + mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes]; + + uint32_t sampleIndex = 0; + uint32_t sampleTime = 0; + for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { uint32_t n = mTimeToSample[2 * i]; uint32_t delta = mTimeToSample[2 * i + 1]; - if (req_time < time + n * delta) { - int j = (req_time - time) / delta; - - uint32_t time1 = time + j * delta; - uint32_t time2 = time1 + delta; - - uint32_t sampleTime; - if (i+1 == mTimeToSampleCount - || (abs_difference(req_time, time1) - < abs_difference(req_time, time2))) { - *sample_index = cur_sample + j; - sampleTime = time1; - } else { - *sample_index = cur_sample + j + 1; - sampleTime = time2; - } + for (uint32_t j = 0; j < n; ++j) { + CHECK(sampleIndex < mNumSampleSizes); - switch (flags) { - case kFlagBefore: - { - if (sampleTime > req_time && *sample_index > 0) { - --*sample_index; - } - break; - } + mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex; - case kFlagAfter: - { - if (sampleTime < req_time - && *sample_index + 1 < mNumSampleSizes) { - ++*sample_index; - } - break; - } + mSampleTimeEntries[sampleIndex].mCompositionTime = + sampleTime + getCompositionTimeOffset(sampleIndex); + + ++sampleIndex; + sampleTime += delta; + } + } + + qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry), + CompareIncreasingTime); +} - default: - break; +status_t SampleTable::findSampleAtTime( + uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + buildSampleEntriesTable(); + + uint32_t left = 0; + uint32_t right = mNumSampleSizes; + while (left < right) { + uint32_t center = (left + right) / 2; + uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime; + + if (req_time < centerTime) { + right = center; + } else if (req_time > centerTime) { + left = center + 1; + } else { + left = center; + break; + } + } + + if (left == mNumSampleSizes) { + --left; + } + + uint32_t closestIndex = left; + + switch (flags) { + case kFlagBefore: + { + while (closestIndex > 0 + && mSampleTimeEntries[closestIndex].mCompositionTime + > req_time) { + --closestIndex; } + break; + } - return OK; + case kFlagAfter: + { + while (closestIndex + 1 < mNumSampleSizes + && mSampleTimeEntries[closestIndex].mCompositionTime + < req_time) { + ++closestIndex; + } + break; } - time += delta * n; - cur_sample += n; + default: + { + CHECK(flags == kFlagClosest); + + if (closestIndex > 0) { + // Check left neighbour and pick closest. + uint32_t absdiff1 = + abs_difference( + mSampleTimeEntries[closestIndex].mCompositionTime, + req_time); + + uint32_t absdiff2 = + abs_difference( + mSampleTimeEntries[closestIndex - 1].mCompositionTime, + req_time); + + if (absdiff1 > absdiff2) { + closestIndex = closestIndex - 1; + } + } + + break; + } } - return ERROR_OUT_OF_RANGE; + *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex; + + return OK; } status_t SampleTable::findSyncSampleNear( @@ -613,7 +678,7 @@ status_t SampleTable::getMetaDataForSample( uint32_t sampleIndex, off64_t *offset, size_t *size, - uint32_t *decodingTime, + uint32_t *compositionTime, bool *isSyncSample) { Mutex::Autolock autoLock(mLock); @@ -630,8 +695,8 @@ status_t SampleTable::getMetaDataForSample( *size = mSampleIterator->getSampleSize(); } - if (decodingTime) { - *decodingTime = mSampleIterator->getSampleTime(); + if (compositionTime) { + *compositionTime = mSampleIterator->getSampleTime(); } if (isSyncSample) { diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index d9ef208bda1c..3bd4c7eb5807 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -92,8 +92,6 @@ private: Track *findTrackByMimePrefix(const char *mimePrefix); - status_t verifyIfStreamable(); - MPEG4Extractor(const MPEG4Extractor &); MPEG4Extractor &operator=(const MPEG4Extractor &); }; diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h index 2f95de9ce5ed..f44e0a2495f7 100644 --- a/media/libstagefright/include/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -63,7 +63,7 @@ public: uint32_t sampleIndex, off64_t *offset, size_t *size, - uint32_t *decodingTime, + uint32_t *compositionTime, bool *isSyncSample = NULL); enum { @@ -107,6 +107,12 @@ private: uint32_t mTimeToSampleCount; uint32_t *mTimeToSample; + struct SampleTimeEntry { + uint32_t mSampleIndex; + uint32_t mCompositionTime; + }; + SampleTimeEntry *mSampleTimeEntries; + uint32_t *mCompositionTimeDeltaEntries; size_t mNumCompositionTimeDeltaEntries; @@ -130,6 +136,10 @@ private: uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const; + static int CompareIncreasingTime(const void *, const void *); + + void buildSampleEntriesTable(); + SampleTable(const SampleTable &); SampleTable &operator=(const SampleTable &); }; |