From 879d503a5d4460a4265279985c63af954afe4a2c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 17 Oct 2012 12:16:50 -0700 Subject: Ignore SIGPIPE during write() to broken pipe This happens occasionally when taking a bugreport. Bug: 6447319 Change-Id: Ia6531a4a3658461f8fd3f7106e7996da7cc5933a --- media/mediaserver/main_mediaserver.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'media') diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index 6b1abb1..ddd5b84 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -33,6 +33,7 @@ using namespace android; int main(int argc, char** argv) { + signal(SIGPIPE, SIG_IGN); sp proc(ProcessState::self()); sp sm = defaultServiceManager(); ALOGI("ServiceManager: %p", sm.get()); -- cgit v1.1 From a15ed9529e70caaf42aae78f9fe530abe38bcc1b Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 24 Oct 2012 13:43:32 -0700 Subject: Make ThrottledSource more usable Add reconnectAtOffset(), DrmInitialization() and getDrmInfo(). Also rearrange the code a bit so all the methods that just call through to the wrapped DataSource are in the header. Change-Id: If25b674df317b0f6da5d36241c694e32abb0a01c --- media/libstagefright/ThrottledSource.cpp | 12 --------- media/libstagefright/include/ThrottledSource.h | 36 ++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 17 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ThrottledSource.cpp b/media/libstagefright/ThrottledSource.cpp index 348a9d3..7496752 100644 --- a/media/libstagefright/ThrottledSource.cpp +++ b/media/libstagefright/ThrottledSource.cpp @@ -31,10 +31,6 @@ ThrottledSource::ThrottledSource( CHECK(mBandwidthLimitBytesPerSecond > 0); } -status_t ThrottledSource::initCheck() const { - return mSource->initCheck(); -} - ssize_t ThrottledSource::readAt(off64_t offset, void *data, size_t size) { Mutex::Autolock autoLock(mLock); @@ -62,17 +58,9 @@ ssize_t ThrottledSource::readAt(off64_t offset, void *data, size_t size) { if (whenUs > nowUs) { usleep(whenUs - nowUs); } - return n; } -status_t ThrottledSource::getSize(off64_t *size) { - return mSource->getSize(size); -} - -uint32_t ThrottledSource::flags() { - return mSource->flags(); -} } // namespace android diff --git a/media/libstagefright/include/ThrottledSource.h b/media/libstagefright/include/ThrottledSource.h index 7fe7c06..673268b 100644 --- a/media/libstagefright/include/ThrottledSource.h +++ b/media/libstagefright/include/ThrottledSource.h @@ -28,18 +28,44 @@ struct ThrottledSource : public DataSource { const sp &source, int32_t bandwidthLimitBytesPerSecond); - virtual status_t initCheck() const; - + // implementation of readAt() that sleeps to achieve the desired max throughput virtual ssize_t readAt(off64_t offset, void *data, size_t size); - virtual status_t getSize(off64_t *size); - virtual uint32_t flags(); + // returns an empty string to prevent callers from using the Uri to construct a new datasource + virtual String8 getUri() { + return String8(); + } + + // following methods all call through to the wrapped DataSource's methods + + status_t initCheck() const { + return mSource->initCheck(); + } + + virtual status_t getSize(off64_t *size) { + return mSource->getSize(size); + } + + virtual uint32_t flags() { + return mSource->flags(); + } + + virtual status_t reconnectAtOffset(off64_t offset) { + return mSource->reconnectAtOffset(offset); + } + + virtual sp DrmInitialization(const char *mime = NULL) { + return mSource->DrmInitialization(mime); + } + + virtual void getDrmInfo(sp &handle, DrmManagerClient **client) { + mSource->getDrmInfo(handle, client); + }; virtual String8 getMIMEType() const { return mSource->getMIMEType(); } - private: Mutex mLock; -- cgit v1.1 From 1bb85d27f09cb01b7e43e08600229258edf16e60 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 25 Oct 2012 11:02:50 -0700 Subject: Switch to new fx library API Change-Id: I6603aef5e3821a8f911e3f33ef8565d04bd1e2e5 --- media/libeffects/downmix/EffectDownmix.c | 21 ------------ media/libeffects/downmix/EffectDownmix.h | 3 -- .../libeffects/lvm/wrapper/Bundle/EffectBundle.cpp | 38 ---------------------- .../libeffects/lvm/wrapper/Reverb/EffectReverb.cpp | 26 --------------- media/libeffects/preprocessing/PreProcessing.cpp | 26 --------------- media/libeffects/testlibs/EffectEqualizer.cpp | 19 ----------- media/libeffects/testlibs/EffectReverb.c | 19 ----------- media/libeffects/testlibs/EffectReverb.h | 3 -- media/libeffects/visualizer/EffectVisualizer.cpp | 19 ----------- 9 files changed, 174 deletions(-) (limited to 'media') diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c index 5bf052a..aa2134c 100644 --- a/media/libeffects/downmix/EffectDownmix.c +++ b/media/libeffects/downmix/EffectDownmix.c @@ -63,8 +63,6 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { version : EFFECT_LIBRARY_API_VERSION, name : "Downmix Library", implementor : "The Android Open Source Project", - query_num_effects : DownmixLib_QueryNumberEffects, - query_effect : DownmixLib_QueryEffect, create_effect : DownmixLib_Create, release_effect : DownmixLib_Release, get_descriptor : DownmixLib_GetDescriptor, @@ -159,25 +157,6 @@ void Downmix_testIndexComputation(uint32_t mask) { /*--- Effect Library Interface Implementation ---*/ -int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects) { - ALOGV("DownmixLib_QueryNumberEffects()"); - *pNumEffects = kNbEffects; - return 0; -} - -int32_t DownmixLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) { - ALOGV("DownmixLib_QueryEffect() index=%d", index); - if (pDescriptor == NULL) { - return -EINVAL; - } - if (index >= (uint32_t)kNbEffects) { - return -EINVAL; - } - memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t)); - return 0; -} - - int32_t DownmixLib_Create(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, diff --git a/media/libeffects/downmix/EffectDownmix.h b/media/libeffects/downmix/EffectDownmix.h index be3ca3f..cb6b957 100644 --- a/media/libeffects/downmix/EffectDownmix.h +++ b/media/libeffects/downmix/EffectDownmix.h @@ -65,9 +65,6 @@ const uint32_t kUnsupported = * Effect API *------------------------------------ */ -int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects); -int32_t DownmixLib_QueryEffect(uint32_t index, - effect_descriptor_t *pDescriptor); int32_t DownmixLib_Create(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index d706c2d..3ea3e18 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -158,42 +158,6 @@ int Volume_getParameter (EffectContext *pContext, int Effect_setEnabled(EffectContext *pContext, bool enabled); /* Effect Library Interface Implementation */ -extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects){ - ALOGV("\n\tEffectQueryNumberEffects start"); - *pNumEffects = 4; - ALOGV("\tEffectQueryNumberEffects creating %d effects", *pNumEffects); - ALOGV("\tEffectQueryNumberEffects end\n"); - return 0; -} /* end EffectQueryNumberEffects */ - -extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor){ - ALOGV("\n\tEffectQueryEffect start"); - ALOGV("\tEffectQueryEffect processing index %d", index); - - if (pDescriptor == NULL){ - ALOGV("\tLVM_ERROR : EffectQueryEffect was passed NULL pointer"); - return -EINVAL; - } - if (index > 3){ - ALOGV("\tLVM_ERROR : EffectQueryEffect index out of range %d", index); - return -ENOENT; - } - if(index == LVM_BASS_BOOST){ - ALOGV("\tEffectQueryEffect processing LVM_BASS_BOOST"); - *pDescriptor = gBassBoostDescriptor; - }else if(index == LVM_VIRTUALIZER){ - ALOGV("\tEffectQueryEffect processing LVM_VIRTUALIZER"); - *pDescriptor = gVirtualizerDescriptor; - } else if(index == LVM_EQUALIZER){ - ALOGV("\tEffectQueryEffect processing LVM_EQUALIZER"); - *pDescriptor = gEqualizerDescriptor; - } else if(index == LVM_VOLUME){ - ALOGV("\tEffectQueryEffect processing LVM_VOLUME"); - *pDescriptor = gVolumeDescriptor; - } - ALOGV("\tEffectQueryEffect end\n"); - return 0; -} /* end EffectQueryEffect */ extern "C" int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, @@ -3304,8 +3268,6 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { version : EFFECT_LIBRARY_API_VERSION, name : "Effect Bundle Library", implementor : "NXP Software Ltd.", - query_num_effects : android::EffectQueryNumberEffects, - query_effect : android::EffectQueryEffect, create_effect : android::EffectCreate, release_effect : android::EffectRelease, get_descriptor : android::EffectGetDescriptor, diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index 941d651..1a2f9dc 100755 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -186,30 +186,6 @@ int Reverb_getParameter (ReverbContext *pContext, int Reverb_LoadPreset (ReverbContext *pContext); /* Effect Library Interface Implementation */ -extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects){ - ALOGV("\n\tEffectQueryNumberEffects start"); - *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); - ALOGV("\tEffectQueryNumberEffects creating %d effects", *pNumEffects); - ALOGV("\tEffectQueryNumberEffects end\n"); - return 0; -} /* end EffectQueryNumberEffects */ - -extern "C" int EffectQueryEffect(uint32_t index, - effect_descriptor_t *pDescriptor){ - ALOGV("\n\tEffectQueryEffect start"); - ALOGV("\tEffectQueryEffect processing index %d", index); - if (pDescriptor == NULL){ - ALOGV("\tLVM_ERROR : EffectQueryEffect was passed NULL pointer"); - return -EINVAL; - } - if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) { - ALOGV("\tLVM_ERROR : EffectQueryEffect index out of range %d", index); - return -ENOENT; - } - *pDescriptor = *gDescriptors[index]; - ALOGV("\tEffectQueryEffect end\n"); - return 0; -} /* end EffectQueryEffect */ extern "C" int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, @@ -2175,8 +2151,6 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { version : EFFECT_LIBRARY_API_VERSION, name : "Reverb Library", implementor : "NXP Software Ltd.", - query_num_effects : android::EffectQueryNumberEffects, - query_effect : android::EffectQueryEffect, create_effect : android::EffectCreate, release_effect : android::EffectRelease, get_descriptor : android::EffectGetDescriptor, diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp index 597866a..58dc413 100755 --- a/media/libeffects/preprocessing/PreProcessing.cpp +++ b/media/libeffects/preprocessing/PreProcessing.cpp @@ -1818,30 +1818,6 @@ const struct effect_interface_s sEffectInterfaceReverse = { // Effect Library Interface Implementation //------------------------------------------------------------------------------ -int PreProcessingLib_QueryNumberEffects(uint32_t *pNumEffects) -{ - if (PreProc_Init() != 0) { - return sInitStatus; - } - if (pNumEffects == NULL) { - return -EINVAL; - } - *pNumEffects = PREPROC_NUM_EFFECTS; - return sInitStatus; -} - -int PreProcessingLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) -{ - if (PreProc_Init() != 0) { - return sInitStatus; - } - if (index >= PREPROC_NUM_EFFECTS) { - return -EINVAL; - } - *pDescriptor = *sDescriptors[index]; - return 0; -} - int PreProcessingLib_Create(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, @@ -1918,8 +1894,6 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { version : EFFECT_LIBRARY_API_VERSION, name : "Audio Preprocessing Library", implementor : "The Android Open Source Project", - query_num_effects : PreProcessingLib_QueryNumberEffects, - query_effect : PreProcessingLib_QueryEffect, create_effect : PreProcessingLib_Create, release_effect : PreProcessingLib_Release, get_descriptor : PreProcessingLib_GetDescriptor diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp index 90ebe1f..c35453b 100644 --- a/media/libeffects/testlibs/EffectEqualizer.cpp +++ b/media/libeffects/testlibs/EffectEqualizer.cpp @@ -123,23 +123,6 @@ int Equalizer_setParameter(AudioEqualizer * pEqualizer, int32_t *pParam, void *p //--- Effect Library Interface Implementation // -extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) { - *pNumEffects = 1; - return 0; -} /* end EffectQueryNumberEffects */ - -extern "C" int EffectQueryEffect(uint32_t index, - effect_descriptor_t *pDescriptor) { - if (pDescriptor == NULL) { - return -EINVAL; - } - if (index > 0) { - return -EINVAL; - } - *pDescriptor = gEqualizerDescriptor; - return 0; -} /* end EffectQueryNext */ - extern "C" int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, @@ -771,8 +754,6 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { version : EFFECT_LIBRARY_API_VERSION, name : "Test Equalizer Library", implementor : "The Android Open Source Project", - query_num_effects : android::EffectQueryNumberEffects, - query_effect : android::EffectQueryEffect, create_effect : android::EffectCreate, release_effect : android::EffectRelease, get_descriptor : android::EffectGetDescriptor, diff --git a/media/libeffects/testlibs/EffectReverb.c b/media/libeffects/testlibs/EffectReverb.c index a87a834..c37f392 100644 --- a/media/libeffects/testlibs/EffectReverb.c +++ b/media/libeffects/testlibs/EffectReverb.c @@ -94,23 +94,6 @@ static const effect_descriptor_t * const gDescriptors[] = { /*--- Effect Library Interface Implementation ---*/ -int EffectQueryNumberEffects(uint32_t *pNumEffects) { - *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); - return 0; -} - -int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) { - if (pDescriptor == NULL) { - return -EINVAL; - } - if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) { - return -EINVAL; - } - memcpy(pDescriptor, gDescriptors[index], - sizeof(effect_descriptor_t)); - return 0; -} - int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, @@ -2222,8 +2205,6 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { .version = EFFECT_LIBRARY_API_VERSION, .name = "Test Equalizer Library", .implementor = "The Android Open Source Project", - .query_num_effects = EffectQueryNumberEffects, - .query_effect = EffectQueryEffect, .create_effect = EffectCreate, .release_effect = EffectRelease, .get_descriptor = EffectGetDescriptor, diff --git a/media/libeffects/testlibs/EffectReverb.h b/media/libeffects/testlibs/EffectReverb.h index 1fb14a7..e5248fe 100644 --- a/media/libeffects/testlibs/EffectReverb.h +++ b/media/libeffects/testlibs/EffectReverb.h @@ -300,9 +300,6 @@ typedef struct reverb_module_s { * Effect API *------------------------------------ */ -int EffectQueryNumberEffects(uint32_t *pNumEffects); -int EffectQueryEffect(uint32_t index, - effect_descriptor_t *pDescriptor); int EffectCreate(const effect_uuid_t *effectUID, int32_t sessionId, int32_t ioId, diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 44baf93..dc1937e 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -177,23 +177,6 @@ int Visualizer_init(VisualizerContext *pContext) //--- Effect Library Interface Implementation // -int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects) { - *pNumEffects = 1; - return 0; -} - -int VisualizerLib_QueryEffect(uint32_t index, - effect_descriptor_t *pDescriptor) { - if (pDescriptor == NULL) { - return -EINVAL; - } - if (index > 0) { - return -EINVAL; - } - *pDescriptor = gVisualizerDescriptor; - return 0; -} - int VisualizerLib_Create(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, @@ -580,8 +563,6 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { version : EFFECT_LIBRARY_API_VERSION, name : "Visualizer Library", implementor : "The Android Open Source Project", - query_num_effects : VisualizerLib_QueryNumberEffects, - query_effect : VisualizerLib_QueryEffect, create_effect : VisualizerLib_Create, release_effect : VisualizerLib_Release, get_descriptor : VisualizerLib_GetDescriptor, -- cgit v1.1 From 655604a7c1ffadc04ec479e4f45345918f44b460 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 25 Oct 2012 16:05:57 -0700 Subject: Only export the symbols that need to be The effects libraries were exporting many more symbols than needed. This reduces the exported symbols to just the needed ones (basically just "AELI"), which happens to also save about 28KB. Change-Id: I115077e52e8dc845282e6f62a522908d26dd72d6 --- media/libeffects/downmix/Android.mk | 2 ++ media/libeffects/downmix/EffectDownmix.c | 2 ++ media/libeffects/lvm/lib/Android.mk | 9 +++++---- media/libeffects/lvm/wrapper/Android.mk | 11 +++++------ media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp | 2 ++ media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp | 2 ++ media/libeffects/preprocessing/Android.mk | 2 ++ media/libeffects/preprocessing/PreProcessing.cpp | 2 ++ media/libeffects/visualizer/Android.mk | 2 +- media/libeffects/visualizer/EffectVisualizer.cpp | 3 ++- 10 files changed, 25 insertions(+), 12 deletions(-) (limited to 'media') diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk index 95ca6fd..3052ad9 100644 --- a/media/libeffects/downmix/Android.mk +++ b/media/libeffects/downmix/Android.mk @@ -25,4 +25,6 @@ LOCAL_C_INCLUDES := \ LOCAL_PRELINK_MODULE := false +LOCAL_CFLAGS += -fvisibility=hidden + include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c index aa2134c..f17a6e8 100644 --- a/media/libeffects/downmix/EffectDownmix.c +++ b/media/libeffects/downmix/EffectDownmix.c @@ -58,6 +58,8 @@ const struct effect_interface_s gDownmixInterface = { NULL /* no process_reverse function, no reference stream needed */ }; +// This is the only symbol that needs to be exported +__attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { tag : AUDIO_EFFECT_LIBRARY_TAG, version : EFFECT_LIBRARY_API_VERSION, diff --git a/media/libeffects/lvm/lib/Android.mk b/media/libeffects/lvm/lib/Android.mk index f49267e..bb56c75 100644 --- a/media/libeffects/lvm/lib/Android.mk +++ b/media/libeffects/lvm/lib/Android.mk @@ -105,8 +105,6 @@ LOCAL_SRC_FILES:= \ LOCAL_MODULE:= libmusicbundle - - LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/Eq/lib \ $(LOCAL_PATH)/Eq/src \ @@ -121,8 +119,12 @@ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/StereoWidening/src \ $(LOCAL_PATH)/StereoWidening/lib +LOCAL_CFLAGS += -fvisibility=hidden + include $(BUILD_STATIC_LIBRARY) + + # Reverb library include $(CLEAR_VARS) @@ -168,12 +170,11 @@ LOCAL_SRC_FILES:= \ LOCAL_MODULE:= libreverb - - LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/Reverb/lib \ $(LOCAL_PATH)/Reverb/src \ $(LOCAL_PATH)/Common/lib \ $(LOCAL_PATH)/Common/src +LOCAL_CFLAGS += -fvisibility=hidden include $(BUILD_STATIC_LIBRARY) diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk index 4313424..f1af389 100644 --- a/media/libeffects/lvm/wrapper/Android.mk +++ b/media/libeffects/lvm/wrapper/Android.mk @@ -9,28 +9,27 @@ LOCAL_ARM_MODE := arm LOCAL_SRC_FILES:= \ Bundle/EffectBundle.cpp +LOCAL_CFLAGS += -fvisibility=hidden + LOCAL_MODULE:= libbundlewrapper LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx - - LOCAL_STATIC_LIBRARIES += libmusicbundle LOCAL_SHARED_LIBRARIES := \ libcutils \ libdl - LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/Bundle \ $(LOCAL_PATH)/../lib/Common/lib/ \ $(LOCAL_PATH)/../lib/Bundle/lib/ \ $(call include-path-for, audio-effects) - include $(BUILD_SHARED_LIBRARY) + # reverb wrapper include $(CLEAR_VARS) @@ -39,12 +38,12 @@ LOCAL_ARM_MODE := arm LOCAL_SRC_FILES:= \ Reverb/EffectReverb.cpp +LOCAL_CFLAGS += -fvisibility=hidden + LOCAL_MODULE:= libreverbwrapper LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx - - LOCAL_STATIC_LIBRARIES += libreverb LOCAL_SHARED_LIBRARIES := \ diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 3ea3e18..94b9acf 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -3263,6 +3263,8 @@ const struct effect_interface_s gLvmEffectInterface = { NULL, }; /* end gLvmEffectInterface */ +// This is the only symbol that needs to be exported +__attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { tag : AUDIO_EFFECT_LIBRARY_TAG, version : EFFECT_LIBRARY_API_VERSION, diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index 1a2f9dc..87e2c85 100755 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -2146,6 +2146,8 @@ const struct effect_interface_s gReverbInterface = { NULL, }; /* end gReverbInterface */ +// This is the only symbol that needs to be exported +__attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { tag : AUDIO_EFFECT_LIBRARY_TAG, version : EFFECT_LIBRARY_API_VERSION, diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk index c13b9d4..dfa1711 100755 --- a/media/libeffects/preprocessing/Android.mk +++ b/media/libeffects/preprocessing/Android.mk @@ -29,4 +29,6 @@ else LOCAL_SHARED_LIBRARIES += libdl endif +LOCAL_CFLAGS += -fvisibility=hidden + include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp index 58dc413..25586e8 100755 --- a/media/libeffects/preprocessing/PreProcessing.cpp +++ b/media/libeffects/preprocessing/PreProcessing.cpp @@ -1889,6 +1889,8 @@ int PreProcessingLib_GetDescriptor(const effect_uuid_t *uuid, return 0; } +// This is the only symbol that needs to be exported +__attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { tag : AUDIO_EFFECT_LIBRARY_TAG, version : EFFECT_LIBRARY_API_VERSION, diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk index 76b5110..49cf4fa 100644 --- a/media/libeffects/visualizer/Android.mk +++ b/media/libeffects/visualizer/Android.mk @@ -6,7 +6,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ EffectVisualizer.cpp -LOCAL_CFLAGS+= -O2 +LOCAL_CFLAGS+= -O2 -fvisibility=hidden LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index dc1937e..e7eccf1 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -557,7 +557,8 @@ const struct effect_interface_s gVisualizerInterface = { NULL, }; - +// This is the only symbol that needs to be exported +__attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { tag : AUDIO_EFFECT_LIBRARY_TAG, version : EFFECT_LIBRARY_API_VERSION, -- cgit v1.1 From 18a6d9029e18a93748d3d9c33f04c1b360aeb7ae Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 24 Sep 2012 11:27:56 -0700 Subject: Fix typo Change-Id: I8cc2969eb329a830ee866622a8633adcb4e967cc --- media/libmedia/AudioSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 207f96f..0e5c149 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -510,7 +510,7 @@ sp AudioSystem::gAudioPolicyService; sp AudioSystem::gAudioPolicyServiceClient; -// establish binder interface to AudioFlinger service +// establish binder interface to AudioPolicy service const sp& AudioSystem::get_audio_policy_service() { gLock.lock(); -- cgit v1.1 From fd88f86ec6788170fb4d903c1b0932a18ce1197c Mon Sep 17 00:00:00 2001 From: Johann Date: Mon, 29 Oct 2012 16:48:23 -0700 Subject: Match new paths and organization in external/libvpx See I739f99d48b8d7e6354c416ef2ca79c954826307f Change-Id: I42b51e2845a696a6e211dde00951afc8f571336f --- media/libstagefright/Android.mk | 1 + media/libstagefright/codecs/on2/dec/Android.mk | 7 +++---- media/libstagefright/matroska/Android.mk | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index faa0f31..cc0581e 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -95,6 +95,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_matroska \ libstagefright_timedtext \ libvpx \ + libwebm \ libstagefright_mpeg2ts \ libstagefright_httplive \ libstagefright_id3 \ diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk index 3223871..0082d7c 100644 --- a/media/libstagefright/codecs/on2/dec/Android.mk +++ b/media/libstagefright/codecs/on2/dec/Android.mk @@ -5,9 +5,9 @@ LOCAL_SRC_FILES := \ SoftVPX.cpp LOCAL_C_INCLUDES := \ - $(TOP)/external/libvpx \ - $(TOP)/external/libvpx/vpx_codec \ - $(TOP)/external/libvpx/vpx_ports \ + $(TOP)/external/libvpx/libvpx \ + $(TOP)/external/libvpx/libvpx/vpx_codec \ + $(TOP)/external/libvpx/libvpx/vpx_ports \ frameworks/av/media/libstagefright/include \ frameworks/native/include/media/openmax \ @@ -21,4 +21,3 @@ LOCAL_MODULE := libstagefright_soft_vpxdec LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) - diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk index 2cccb4f..2d8c1e1 100644 --- a/media/libstagefright/matroska/Android.mk +++ b/media/libstagefright/matroska/Android.mk @@ -5,7 +5,7 @@ LOCAL_SRC_FILES:= \ MatroskaExtractor.cpp LOCAL_C_INCLUDES:= \ - $(TOP)/external/libvpx/mkvparser \ + $(TOP)/external/libvpx/libwebm \ $(TOP)/frameworks/native/include/media/openmax \ LOCAL_CFLAGS += -Wno-multichar -- cgit v1.1 From c41590251aa84c078c942d258e838aad814b73a5 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 30 Oct 2012 10:51:39 -0700 Subject: Remove obsolete references to libmedia_native Bug: 6654403 Change-Id: I3993d62987cf0dd85db10bf002a5cce53d4f01bd --- media/libmedia/Android.mk | 2 +- media/libmediaplayerservice/Android.mk | 1 - media/libstagefright/Android.mk | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 54666fb..f2b6441 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -54,7 +54,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat \ libcamera_client libstagefright_foundation \ - libgui libdl libaudioutils libmedia_native + libgui libdl libaudioutils LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 5b5ed71..48f48e4 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -28,7 +28,6 @@ LOCAL_SHARED_LIBRARIES := \ libdl \ libgui \ libmedia \ - libmedia_native \ libsonivox \ libstagefright \ libstagefright_foundation \ diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index faa0f31..a056706 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -78,7 +78,6 @@ LOCAL_SHARED_LIBRARIES := \ libicuuc \ liblog \ libmedia \ - libmedia_native \ libsonivox \ libssl \ libstagefright_omx \ -- cgit v1.1 From 77536f9f8fc030379102c9e36ad21ce5b2ab234c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 30 Oct 2012 10:55:15 -0700 Subject: Remove obsolete libmedia_native Bug: 6654403 Change-Id: Ic979a7890e2f4ef3f5409af14372eb52196e6dea --- media/libmedia_native/Android.mk | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 media/libmedia_native/Android.mk (limited to 'media') diff --git a/media/libmedia_native/Android.mk b/media/libmedia_native/Android.mk deleted file mode 100644 index 065a90f..0000000 --- a/media/libmedia_native/Android.mk +++ /dev/null @@ -1,11 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := - -LOCAL_MODULE:= libmedia_native - -LOCAL_MODULE_TAGS := optional - -include $(BUILD_SHARED_LIBRARY) -- cgit v1.1 From b64497eb8724c4c372fffdbf3ee30543432953c5 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 1 Oct 2012 09:47:30 -0700 Subject: Add NBAIO support for more sample rates This will be used for audio capture but it is not scalable, and we need a better approach in the long term. Change-Id: I8b12f6b64a3fd8e8a8c425c82574260fe8ffbed6 --- media/libnbaio/NBAIO.cpp | 124 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 39 deletions(-) (limited to 'media') diff --git a/media/libnbaio/NBAIO.cpp b/media/libnbaio/NBAIO.cpp index 00d2017..e0d2c21 100644 --- a/media/libnbaio/NBAIO.cpp +++ b/media/libnbaio/NBAIO.cpp @@ -24,44 +24,55 @@ namespace android { size_t Format_frameSize(NBAIO_Format format) { - switch (format) { - case Format_SR44_1_C2_I16: - case Format_SR48_C2_I16: - return 2 * sizeof(short); - case Format_SR44_1_C1_I16: - case Format_SR48_C1_I16: - return 1 * sizeof(short); - case Format_Invalid: - default: - return 0; - } + return Format_channelCount(format) * sizeof(short); } size_t Format_frameBitShift(NBAIO_Format format) { - switch (format) { - case Format_SR44_1_C2_I16: - case Format_SR48_C2_I16: - return 2; // 1 << 2 == 2 * sizeof(short) - case Format_SR44_1_C1_I16: - case Format_SR48_C1_I16: - return 1; // 1 << 1 == 1 * sizeof(short) - case Format_Invalid: - default: - return 0; - } + // sizeof(short) == 2, so frame size == 1 << channels + return Format_channelCount(format); } +enum { + Format_SR_8000, + Format_SR_11025, + Format_SR_16000, + Format_SR_22050, + Format_SR_24000, + Format_SR_32000, + Format_SR_44100, + Format_SR_48000, + Format_SR_Mask = 7 +}; + +enum { + Format_C_1 = 0x08, + Format_C_2 = 0x10, + Format_C_Mask = 0x18 +}; + unsigned Format_sampleRate(NBAIO_Format format) { - switch (format) { - case Format_SR44_1_C1_I16: - case Format_SR44_1_C2_I16: + if (format == Format_Invalid) { + return 0; + } + switch (format & Format_SR_Mask) { + case Format_SR_8000: + return 8000; + case Format_SR_11025: + return 11025; + case Format_SR_16000: + return 16000; + case Format_SR_22050: + return 22050; + case Format_SR_24000: + return 24000; + case Format_SR_32000: + return 32000; + case Format_SR_44100: return 44100; - case Format_SR48_C1_I16: - case Format_SR48_C2_I16: + case Format_SR_48000: return 48000; - case Format_Invalid: default: return 0; } @@ -69,14 +80,14 @@ unsigned Format_sampleRate(NBAIO_Format format) unsigned Format_channelCount(NBAIO_Format format) { - switch (format) { - case Format_SR44_1_C1_I16: - case Format_SR48_C1_I16: + if (format == Format_Invalid) { + return 0; + } + switch (format & Format_C_Mask) { + case Format_C_1: return 1; - case Format_SR44_1_C2_I16: - case Format_SR48_C2_I16: + case Format_C_2: return 2; - case Format_Invalid: default: return 0; } @@ -84,11 +95,46 @@ unsigned Format_channelCount(NBAIO_Format format) NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount) { - if (sampleRate == 44100 && channelCount == 2) return Format_SR44_1_C2_I16; - if (sampleRate == 48000 && channelCount == 2) return Format_SR48_C2_I16; - if (sampleRate == 44100 && channelCount == 1) return Format_SR44_1_C1_I16; - if (sampleRate == 48000 && channelCount == 1) return Format_SR48_C1_I16; - return Format_Invalid; + NBAIO_Format format; + switch (sampleRate) { + case 8000: + format = Format_SR_8000; + break; + case 11025: + format = Format_SR_11025; + break; + case 16000: + format = Format_SR_16000; + break; + case 22050: + format = Format_SR_22050; + break; + case 24000: + format = Format_SR_24000; + break; + case 32000: + format = Format_SR_32000; + break; + case 44100: + format = Format_SR_44100; + break; + case 48000: + format = Format_SR_48000; + break; + default: + return Format_Invalid; + } + switch (channelCount) { + case 1: + format |= Format_C_1; + break; + case 2: + format |= Format_C_2; + break; + default: + return Format_Invalid; + } + return format; } // This is a default implementation; it is expected that subclasses will optimize this. -- cgit v1.1 From 85ab62c4b433df3f1a9826bed1c9bec07a86c750 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 1 Nov 2012 11:11:38 -0700 Subject: Line length 100 Change-Id: Ib28fd7b9ce951a6933f006e7f8812ba617625530 --- media/libmedia/AudioEffect.cpp | 21 ++++++++++++++------- media/libmedia/AudioRecord.cpp | 6 ++++-- media/libmedia/AudioSystem.cpp | 18 ++++++++++++------ media/libmedia/AudioTrack.cpp | 28 ++++++++++++++++++---------- media/libmedia/IAudioFlinger.cpp | 6 ++++-- media/libmedia/IAudioFlingerClient.cpp | 3 ++- media/libmedia/IAudioPolicyService.cpp | 9 ++++++--- media/libmedia/Visualizer.cpp | 6 ++++-- 8 files changed, 64 insertions(+), 33 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index 680604b..3317d57 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -152,7 +152,8 @@ status_t AudioEffect::set(const effect_uuid_t *type, mCblk->buffer = (uint8_t *)mCblk + bufOffset; iEffect->asBinder()->linkToDeath(mIEffectClient); - ALOGV("set() %p OK effect: %s id: %d status %d enabled %d", this, mDescriptor.name, mId, mStatus, mEnabled); + ALOGV("set() %p OK effect: %s id: %d status %d enabled %d", this, mDescriptor.name, mId, + mStatus, mEnabled); return mStatus; } @@ -266,9 +267,11 @@ status_t AudioEffect::setParameter(effect_param_t *param) uint32_t size = sizeof(int); uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; - ALOGV("setParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1); + ALOGV("setParameter: param: %d, param2: %d", *(int *)param->data, + (param->psize == 8) ? *((int *)param->data + 1): -1); - return mIEffect->command(EFFECT_CMD_SET_PARAM, sizeof (effect_param_t) + psize, param, &size, ¶m->status); + return mIEffect->command(EFFECT_CMD_SET_PARAM, sizeof (effect_param_t) + psize, param, &size, + ¶m->status); } status_t AudioEffect::setParameterDeferred(effect_param_t *param) @@ -321,11 +324,14 @@ status_t AudioEffect::getParameter(effect_param_t *param) return BAD_VALUE; } - ALOGV("getParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1); + ALOGV("getParameter: param: %d, param2: %d", *(int *)param->data, + (param->psize == 8) ? *((int *)param->data + 1): -1); - uint32_t psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; + uint32_t psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + + param->vsize; - return mIEffect->command(EFFECT_CMD_GET_PARAM, sizeof(effect_param_t) + param->psize, param, &psize, param); + return mIEffect->command(EFFECT_CMD_GET_PARAM, sizeof(effect_param_t) + param->psize, param, + &psize, param); } @@ -346,7 +352,8 @@ void AudioEffect::binderDied() void AudioEffect::controlStatusChanged(bool controlGranted) { - ALOGV("controlStatusChanged %p control %d callback %p mUserData %p", this, controlGranted, mCbf, mUserData); + ALOGV("controlStatusChanged %p control %d callback %p mUserData %p", this, controlGranted, mCbf, + mUserData); if (controlGranted) { if (mStatus == ALREADY_EXISTS) { mStatus = NO_ERROR; diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 8ea6306..bdbee0d 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -127,7 +127,8 @@ status_t AudioRecord::set( int sessionId) { - ALOGV("set(): sampleRate %d, channelMask %#x, frameCount %d",sampleRate, channelMask, frameCount); + ALOGV("set(): sampleRate %d, channelMask %#x, frameCount %d",sampleRate, channelMask, + frameCount); AutoMutex lock(mLock); @@ -701,7 +702,8 @@ bool AudioRecord::processAudioBuffer(const sp& thread) status_t err = obtainBuffer(&audioBuffer, 1); if (err < NO_ERROR) { if (err != TIMED_OUT) { - ALOGE_IF(err != status_t(NO_MORE_BUFFERS), "Error obtaining an audio buffer, giving up."); + ALOGE_IF(err != status_t(NO_MORE_BUFFERS), + "Error obtaining an audio buffer, giving up."); return false; } break; diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 0e5c149..767c452 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -246,7 +246,8 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t output, gLock.unlock(); } - ALOGV("getSamplingRate() streamType %d, output %d, sampling rate %d", streamType, output, *samplingRate); + ALOGV("getSamplingRate() streamType %d, output %d, sampling rate %d", streamType, output, + *samplingRate); return NO_ERROR; } @@ -290,7 +291,8 @@ status_t AudioSystem::getFrameCount(audio_io_handle_t output, gLock.unlock(); } - ALOGV("getFrameCount() streamType %d, output %d, frameCount %d", streamType, output, *frameCount); + ALOGV("getFrameCount() streamType %d, output %d, frameCount %d", streamType, output, + *frameCount); return NO_ERROR; } @@ -369,7 +371,8 @@ status_t AudioSystem::setVoiceVolume(float value) return af->setVoiceVolume(value); } -status_t AudioSystem::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, audio_stream_type_t stream) +status_t AudioSystem::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, + audio_stream_type_t stream) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; @@ -449,8 +452,10 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle OutputDescriptor *outputDesc = new OutputDescriptor(*desc); gOutputs.add(ioHandle, outputDesc); - ALOGV("ioConfigChanged() new output samplingRate %d, format %d channels %#x frameCount %d latency %d", - outputDesc->samplingRate, outputDesc->format, outputDesc->channels, outputDesc->frameCount, outputDesc->latency); + ALOGV("ioConfigChanged() new output samplingRate %d, format %d channels %#x frameCount %d " + "latency %d", + outputDesc->samplingRate, outputDesc->format, outputDesc->channels, + outputDesc->frameCount, outputDesc->latency); } break; case OUTPUT_CLOSED: { if (gOutputs.indexOfKey(ioHandle) < 0) { @@ -471,7 +476,8 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle if (param2 == NULL) break; desc = (const OutputDescriptor *)param2; - ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %#x frameCount %d latency %d", + ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %#x " + "frameCount %d latency %d", ioHandle, desc->samplingRate, desc->format, desc->channels, desc->frameCount, desc->latency); OutputDescriptor *outputDesc = gOutputs.valueAt(index); diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 362d022..5fc9b07 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -198,7 +198,8 @@ status_t AudioTrack::set( int sessionId) { - ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), + sharedBuffer->size()); ALOGV("set() streamType %d frameCount %d flags %04x", streamType, frameCount, flags); @@ -617,12 +618,14 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou if (loopStart >= loopEnd || loopEnd - loopStart > cblk->frameCount || cblk->server > loopStart) { - ALOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, cblk->frameCount, cblk->user); + ALOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, " + "user %d", loopStart, loopEnd, loopCount, cblk->frameCount, cblk->user); return BAD_VALUE; } if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) { - ALOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d", + ALOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, " + "framecount %d", loopStart, loopEnd, cblk->frameCount); return BAD_VALUE; } @@ -924,7 +927,8 @@ status_t AudioTrack::createTrack_l( mCblk->stepUser(mCblk->frameCount); } - mCblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000)); + mCblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | + uint16_t(mVolume[LEFT] * 0x1000)); mCblk->setSendLevel(mSendLevel); mAudioTrack->attachAuxEffect(mAuxEffectId); mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; @@ -994,8 +998,8 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) // timing out when a loop has been set and we have already written upto loop end // is a normal condition: no need to wake AudioFlinger up. if (cblk->user < cblk->loopEnd) { - ALOGW( "obtainBuffer timed out (is the CPU pegged?) %p name=%#x" - "user=%08x, server=%08x", this, cblk->mName, cblk->user, cblk->server); + ALOGW("obtainBuffer timed out (is the CPU pegged?) %p name=%#x user=%08x, " + "server=%08x", this, cblk->mName, cblk->user, cblk->server); //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140) cblk->lock.unlock(); result = mAudioTrack->start(); @@ -1265,7 +1269,8 @@ bool AudioTrack::processAudioBuffer(const sp& thread) status_t err = obtainBuffer(&audioBuffer, waitCount); if (err < NO_ERROR) { if (err != TIMED_OUT) { - ALOGE_IF(err != status_t(NO_MORE_BUFFERS), "Error obtaining an audio buffer, giving up."); + ALOGE_IF(err != status_t(NO_MORE_BUFFERS), + "Error obtaining an audio buffer, giving up."); return false; } break; @@ -1439,11 +1444,14 @@ status_t AudioTrack::dump(int fd, const Vector& args) const String8 result; result.append(" AudioTrack::dump\n"); - snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mVolume[0], mVolume[1]); + snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, + mVolume[0], mVolume[1]); result.append(buffer); - snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mCblk->frameCount); + snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, + mChannelCount, mCblk->frameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", (mCblk == 0) ? 0 : mCblk->sampleRate, mStatus, mMuted); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", + (mCblk == 0) ? 0 : mCblk->sampleRate, mStatus, mMuted); result.append(buffer); snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index ce8ffc4..f412591 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -865,7 +865,8 @@ status_t BnAudioFlinger::onTransact( case REGISTER_CLIENT: { CHECK_INTERFACE(IAudioFlinger, data, reply); - sp client = interface_cast(data.readStrongBinder()); + sp client = interface_cast( + data.readStrongBinder()); registerClient(client); return NO_ERROR; } break; @@ -1043,7 +1044,8 @@ status_t BnAudioFlinger::onTransact( int id; int enabled; - sp effect = createEffect(pid, &desc, client, priority, output, sessionId, &status, &id, &enabled); + sp effect = createEffect(pid, &desc, client, priority, output, sessionId, + &status, &id, &enabled); reply->writeInt32(status); reply->writeInt32(id); reply->writeInt32(enabled); diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index 4178b29..2d1e0f8 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -50,7 +50,8 @@ public: ALOGV("ioConfigChanged stream %d", stream); data.writeInt32(stream); } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) { - const AudioSystem::OutputDescriptor *desc = (const AudioSystem::OutputDescriptor *)param2; + const AudioSystem::OutputDescriptor *desc = + (const AudioSystem::OutputDescriptor *)param2; data.writeInt32(desc->samplingRate); data.writeInt32(desc->format); data.writeInt32(desc->channels); diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 401437c..769deae 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -399,13 +399,15 @@ status_t BnAudioPolicyService::onTransact( case SET_PHONE_STATE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - reply->writeInt32(static_cast (setPhoneState((audio_mode_t) data.readInt32()))); + reply->writeInt32(static_cast (setPhoneState( + (audio_mode_t) data.readInt32()))); return NO_ERROR; } break; case SET_FORCE_USE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_policy_force_use_t usage = static_cast (data.readInt32()); + audio_policy_force_use_t usage = static_cast ( + data.readInt32()); audio_policy_forced_cfg_t config = static_cast (data.readInt32()); reply->writeInt32(static_cast (setForceUse(usage, config))); @@ -414,7 +416,8 @@ status_t BnAudioPolicyService::onTransact( case GET_FORCE_USE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_policy_force_use_t usage = static_cast (data.readInt32()); + audio_policy_force_use_t usage = static_cast ( + data.readInt32()); reply->writeInt32(static_cast (getForceUse(usage))); return NO_ERROR; } break; diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index 8196e10..5b4071b 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -88,7 +88,8 @@ status_t Visualizer::setEnabled(bool enabled) return status; } -status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate) +status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, + uint32_t rate) { if (rate > CAPTURE_RATE_MAX) { return BAD_VALUE; @@ -334,7 +335,8 @@ void Visualizer::controlStatusChanged(bool controlGranted) { //------------------------------------------------------------------------- -Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava) +Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, + bool bCanCallJava) : Thread(bCanCallJava), mReceiver(receiver) { mSleepTimeUs = 1000000000 / captureRate; -- cgit v1.1 From 2e136686cd60556b681480079142142ab4a7e07c Mon Sep 17 00:00:00 2001 From: James Dong Date: Thu, 1 Nov 2012 18:54:43 -0700 Subject: Remove unused default parameter for copyBuffer() Change-Id: I8002986ccb926165af393ca80cece60dee011204 --- media/libstagefright/include/FragmentedMP4Parser.h | 2 +- media/libstagefright/mp4/FragmentedMP4Parser.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/include/FragmentedMP4Parser.h b/media/libstagefright/include/FragmentedMP4Parser.h index 0edafb9..dbe02b8 100644 --- a/media/libstagefright/include/FragmentedMP4Parser.h +++ b/media/libstagefright/include/FragmentedMP4Parser.h @@ -263,7 +263,7 @@ private: void copyBuffer( sp *dst, - size_t offset, uint64_t size, size_t extra = 0) const; + size_t offset, uint64_t size) const; DISALLOW_EVIL_CONSTRUCTORS(FragmentedMP4Parser); }; diff --git a/media/libstagefright/mp4/FragmentedMP4Parser.cpp b/media/libstagefright/mp4/FragmentedMP4Parser.cpp index 7fe4e63..54c3d63 100644 --- a/media/libstagefright/mp4/FragmentedMP4Parser.cpp +++ b/media/libstagefright/mp4/FragmentedMP4Parser.cpp @@ -1971,8 +1971,8 @@ status_t FragmentedMP4Parser::parseTrackFragmentRun( } void FragmentedMP4Parser::copyBuffer( - sp *dst, size_t offset, uint64_t size, size_t extra) const { - sp buf = new ABuffer(size + extra); + sp *dst, size_t offset, uint64_t size) const { + sp buf = new ABuffer(size); memcpy(buf->data(), mBuffer->data() + offset, size); *dst = buf; -- cgit v1.1 From 9a08ebc8de71e260efb86cd1a04559b075b38ebc Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 2 Nov 2012 09:59:51 -0700 Subject: Fix spurious wakeup waiting for new IAudioTrack If there was a spurious wakeup while waiting for another thread to create a new IAudioTrack, we assumed that the track has been created when it might not have been. Change-Id: I5f3999b4f7a06a00aabd65a746cc7222fff396ab --- media/libmedia/AudioTrack.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 5fc9b07..ffed161 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1407,19 +1407,27 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) android_atomic_or(CBLK_RESTORED_ON, &cblk->flags); cblk->cv.broadcast(); } else { - if (!(cblk->flags & CBLK_RESTORED_MSK)) { - ALOGW("dead IAudioTrack, waiting for a new one TID %d", gettid()); - mLock.unlock(); - result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS)); - if (result == NO_ERROR) { + bool haveLogged = false; + for (;;) { + if (cblk->flags & CBLK_RESTORED_MSK) { + ALOGW("dead IAudioTrack restored"); result = mRestoreStatus; + cblk->lock.unlock(); + break; + } + if (!haveLogged) { + ALOGW("dead IAudioTrack, waiting for a new one"); + haveLogged = true; } + mLock.unlock(); + result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS)); cblk->lock.unlock(); mLock.lock(); - } else { - ALOGW("dead IAudioTrack, already restored TID %d", gettid()); - result = mRestoreStatus; - cblk->lock.unlock(); + if (result != NO_ERROR) { + ALOGW("timed out"); + break; + } + cblk->lock.lock(); } } ALOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", -- cgit v1.1 From ad4e408b8ea397caadbfee85e1e39515e7e08104 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 26 Oct 2012 14:28:05 -0700 Subject: Turn off executable bit on ordinary files Change-Id: I0abea25b58fb1d03975bed9cca40f826fcd4c5e4 --- media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp | 0 media/libeffects/preprocessing/Android.mk | 0 media/libeffects/preprocessing/PreProcessing.cpp | 0 media/libstagefright/CameraSource.cpp | 0 media/libstagefright/MPEG4Writer.cpp | 0 media/libstagefright/OMXCodec.cpp | 0 media/libstagefright/SkipCutBuffer.cpp | 0 7 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp mode change 100755 => 100644 media/libeffects/preprocessing/Android.mk mode change 100755 => 100644 media/libeffects/preprocessing/PreProcessing.cpp mode change 100755 => 100644 media/libstagefright/CameraSource.cpp mode change 100755 => 100644 media/libstagefright/MPEG4Writer.cpp mode change 100755 => 100644 media/libstagefright/OMXCodec.cpp mode change 100755 => 100644 media/libstagefright/SkipCutBuffer.cpp (limited to 'media') diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp old mode 100755 new mode 100644 diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk old mode 100755 new mode 100644 diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp old mode 100755 new mode 100644 diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp old mode 100755 new mode 100644 diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp old mode 100755 new mode 100644 diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp old mode 100755 new mode 100644 diff --git a/media/libstagefright/SkipCutBuffer.cpp b/media/libstagefright/SkipCutBuffer.cpp old mode 100755 new mode 100644 -- cgit v1.1 From 9c5fdd83f9b9f49be35107971feb33528d60b945 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 5 Nov 2012 13:38:15 -0800 Subject: Simplify control block flag names Use only one symbol per flag Change-Id: Ia3582e2134abd60c896d11337face65383e79c7c --- media/libmedia/AudioRecord.cpp | 22 +++++++-------- media/libmedia/AudioTrack.cpp | 62 +++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 42 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index bdbee0d..bd558fa 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -292,16 +292,16 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) mActive = true; cblk->lock.lock(); - if (!(cblk->flags & CBLK_INVALID_MSK)) { + if (!(cblk->flags & CBLK_INVALID)) { cblk->lock.unlock(); ALOGV("mAudioRecord->start()"); ret = mAudioRecord->start(event, triggerSession); cblk->lock.lock(); if (ret == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID_ON, &cblk->flags); + android_atomic_or(CBLK_INVALID, &cblk->flags); } } - if (cblk->flags & CBLK_INVALID_MSK) { + if (cblk->flags & CBLK_INVALID) { ret = restoreRecord_l(cblk); } cblk->lock.unlock(); @@ -466,7 +466,7 @@ status_t AudioRecord::openRecord_l( mCblkMemory = cblk; mCblk = static_cast(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); - android_atomic_and(~CBLK_DIRECTION_MSK, &mCblk->flags); + android_atomic_and(~CBLK_DIRECTION, &mCblk->flags); mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; mCblk->waitTimeMs = 0; return NO_ERROR; @@ -499,7 +499,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) cblk->lock.unlock(); return WOULD_BLOCK; } - if (!(cblk->flags & CBLK_INVALID_MSK)) { + if (!(cblk->flags & CBLK_INVALID)) { mLock.unlock(); result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); cblk->lock.unlock(); @@ -509,7 +509,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) } cblk->lock.lock(); } - if (cblk->flags & CBLK_INVALID_MSK) { + if (cblk->flags & CBLK_INVALID) { goto create_new_record; } if (CC_UNLIKELY(result != NO_ERROR)) { @@ -522,7 +522,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0); cblk->lock.lock(); if (result == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID_ON, &cblk->flags); + android_atomic_or(CBLK_INVALID, &cblk->flags); create_new_record: result = AudioRecord::restoreRecord_l(cblk); } @@ -739,7 +739,7 @@ bool AudioRecord::processAudioBuffer(const sp& thread) // The value of active is stale, but we are almost sure to be active here because // otherwise we would have exited when obtainBuffer returned STOPPED earlier. ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); - if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) { + if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) { mCbf(EVENT_OVERRUN, mUserData, NULL); } } @@ -759,7 +759,7 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) { status_t result; - if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) { + if (!(android_atomic_or(CBLK_RESTORING, &cblk->flags) & CBLK_RESTORING)) { ALOGW("dead IAudioRecord, creating a new one"); // signal old cblk condition so that other threads waiting for available buffers stop // waiting now @@ -780,10 +780,10 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) } // signal old cblk condition for other threads waiting for restore completion - android_atomic_or(CBLK_RESTORED_ON, &cblk->flags); + android_atomic_or(CBLK_RESTORED, &cblk->flags); cblk->cv.broadcast(); } else { - if (!(cblk->flags & CBLK_RESTORED_MSK)) { + if (!(cblk->flags & CBLK_RESTORED)) { ALOGW("dead IAudioRecord, waiting for a new one to be created"); mLock.unlock(); result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS)); diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ffed161..5bd1aa7 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -391,7 +391,7 @@ void AudioTrack::start() cblk->lock.lock(); cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; cblk->waitTimeMs = 0; - android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags); + android_atomic_and(~CBLK_DISABLED, &cblk->flags); if (t != 0) { t->resume(); } else { @@ -402,16 +402,16 @@ void AudioTrack::start() ALOGV("start %p before lock cblk %p", this, mCblk); status_t status = NO_ERROR; - if (!(cblk->flags & CBLK_INVALID_MSK)) { + if (!(cblk->flags & CBLK_INVALID)) { cblk->lock.unlock(); ALOGV("mAudioTrack->start()"); status = mAudioTrack->start(); cblk->lock.lock(); if (status == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID_ON, &cblk->flags); + android_atomic_or(CBLK_INVALID, &cblk->flags); } } - if (cblk->flags & CBLK_INVALID_MSK) { + if (cblk->flags & CBLK_INVALID) { status = restoreTrack_l(cblk, true); } cblk->lock.unlock(); @@ -691,7 +691,7 @@ status_t AudioTrack::setPosition(uint32_t position) if (position > mCblk->user) return BAD_VALUE; mCblk->server = position; - android_atomic_or(CBLK_FORCEREADY_ON, &mCblk->flags); + android_atomic_or(CBLK_FORCEREADY, &mCblk->flags); return NO_ERROR; } @@ -905,7 +905,7 @@ status_t AudioTrack::createTrack_l( mCblkMemory = cblk; mCblk = static_cast(cblk->pointer()); // old has the previous value of mCblk->flags before the "or" operation - int32_t old = android_atomic_or(CBLK_DIRECTION_OUT, &mCblk->flags); + int32_t old = android_atomic_or(CBLK_DIRECTION, &mCblk->flags); if (flags & AUDIO_OUTPUT_FLAG_FAST) { if (old & CBLK_FAST) { ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", mCblk->frameCount); @@ -959,7 +959,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) uint32_t framesAvail = cblk->framesAvailable(); cblk->lock.lock(); - if (cblk->flags & CBLK_INVALID_MSK) { + if (cblk->flags & CBLK_INVALID) { goto create_new_track; } cblk->lock.unlock(); @@ -978,7 +978,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) cblk->lock.unlock(); return WOULD_BLOCK; } - if (!(cblk->flags & CBLK_INVALID_MSK)) { + if (!(cblk->flags & CBLK_INVALID)) { mLock.unlock(); result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); cblk->lock.unlock(); @@ -989,7 +989,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) cblk->lock.lock(); } - if (cblk->flags & CBLK_INVALID_MSK) { + if (cblk->flags & CBLK_INVALID) { goto create_new_track; } if (CC_UNLIKELY(result != NO_ERROR)) { @@ -1005,7 +1005,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) result = mAudioTrack->start(); cblk->lock.lock(); if (result == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID_ON, &cblk->flags); + android_atomic_or(CBLK_INVALID, &cblk->flags); create_new_track: result = restoreTrack_l(cblk, false); } @@ -1063,8 +1063,8 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) mCblk->stepUser(audioBuffer->frameCount); if (audioBuffer->frameCount > 0) { // restart track if it was disabled by audioflinger due to previous underrun - if (mActive && (mCblk->flags & CBLK_DISABLED_MSK)) { - android_atomic_and(~CBLK_DISABLED_ON, &mCblk->flags); + if (mActive && (mCblk->flags & CBLK_DISABLED)) { + android_atomic_and(~CBLK_DISABLED, &mCblk->flags); ALOGW("releaseBuffer() track %p name=%#x disabled, restarting", this, mCblk->mName); mAudioTrack->start(); } @@ -1149,16 +1149,16 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) // If the track is not invalid already, try to allocate a buffer. alloc // fails indicating that the server is dead, flag the track as invalid so // we can attempt to restore in just a bit. - if (!(mCblk->flags & CBLK_INVALID_MSK)) { + if (!(mCblk->flags & CBLK_INVALID)) { result = mAudioTrack->allocateTimedBuffer(size, buffer); if (result == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID_ON, &mCblk->flags); + android_atomic_or(CBLK_INVALID, &mCblk->flags); } } // If the track is invalid at this point, attempt to restore it. and try the // allocation one more time. - if (mCblk->flags & CBLK_INVALID_MSK) { + if (mCblk->flags & CBLK_INVALID) { mCblk->lock.lock(); result = restoreTrack_l(mCblk, false); mCblk->lock.unlock(); @@ -1178,8 +1178,8 @@ status_t TimedAudioTrack::queueTimedBuffer(const sp& buffer, AutoMutex lock(mLock); // restart track if it was disabled by audioflinger due to previous underrun if (buffer->size() != 0 && status == NO_ERROR && - mActive && (mCblk->flags & CBLK_DISABLED_MSK)) { - android_atomic_and(~CBLK_DISABLED_ON, &mCblk->flags); + mActive && (mCblk->flags & CBLK_DISABLED)) { + android_atomic_and(~CBLK_DISABLED, &mCblk->flags); ALOGW("queueTimedBuffer() track %p disabled, restarting", this); mAudioTrack->start(); } @@ -1213,7 +1213,7 @@ bool AudioTrack::processAudioBuffer(const sp& thread) // Manage underrun callback if (active && (cblk->framesAvailable() == cblk->frameCount)) { ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); - if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) { + if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) { mCbf(EVENT_UNDERRUN, mUserData, 0); if (cblk->server == cblk->frameCount) { mCbf(EVENT_BUFFER_END, mUserData, 0); @@ -1333,7 +1333,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) { status_t result; - if (!(android_atomic_or(CBLK_RESTORING_ON, &cblk->flags) & CBLK_RESTORING_MSK)) { + if (!(android_atomic_or(CBLK_RESTORING, &cblk->flags) & CBLK_RESTORING)) { ALOGW("dead IAudioTrack, creating a new one from %s TID %d", fromStart ? "start()" : "obtainBuffer()", gettid()); @@ -1381,8 +1381,8 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) memset(mCblk->buffers, 0, frames * mCblk->frameSize); } // restart playback even if buffer is not completely filled. - android_atomic_or(CBLK_FORCEREADY_ON, &mCblk->flags); - // stepUser() clears CBLK_UNDERRUN_ON flag enabling underrun callbacks to + android_atomic_or(CBLK_FORCEREADY, &mCblk->flags); + // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to // the client mCblk->stepUser(frames); } @@ -1399,17 +1399,17 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) } } if (result != NO_ERROR) { - android_atomic_and(~CBLK_RESTORING_ON, &cblk->flags); + android_atomic_and(~CBLK_RESTORING, &cblk->flags); ALOGW_IF(result != NO_ERROR, "restoreTrack_l() failed status %d", result); } mRestoreStatus = result; // signal old cblk condition for other threads waiting for restore completion - android_atomic_or(CBLK_RESTORED_ON, &cblk->flags); + android_atomic_or(CBLK_RESTORED, &cblk->flags); cblk->cv.broadcast(); } else { bool haveLogged = false; for (;;) { - if (cblk->flags & CBLK_RESTORED_MSK) { + if (cblk->flags & CBLK_RESTORED) { ALOGW("dead IAudioTrack restored"); result = mRestoreStatus; cblk->lock.unlock(); @@ -1534,7 +1534,7 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) uint32_t u = user; u += frameCount; // Ensure that user is never ahead of server for AudioRecord - if (flags & CBLK_DIRECTION_MSK) { + if (flags & CBLK_DIRECTION) { // If stepServer() has been called once, switch to normal obtainBuffer() timeout period if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; @@ -1558,8 +1558,8 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) user = u; // Clear flow control error condition as new data has been written/read to/from buffer. - if (flags & CBLK_UNDERRUN_MSK) { - android_atomic_and(~CBLK_UNDERRUN_MSK, &flags); + if (flags & CBLK_UNDERRUN) { + android_atomic_and(~CBLK_UNDERRUN, &flags); } return u; @@ -1578,7 +1578,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount) bool flushed = (s == user); s += frameCount; - if (flags & CBLK_DIRECTION_MSK) { + if (flags & CBLK_DIRECTION) { // Mark that we have read the first buffer so that next time stepUser() is called // we switch to normal obtainBuffer() timeout period if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) { @@ -1616,7 +1616,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount) server = s; - if (!(flags & CBLK_INVALID_MSK)) { + if (!(flags & CBLK_INVALID)) { cv.signal(); } lock.unlock(); @@ -1639,7 +1639,7 @@ uint32_t audio_track_cblk_t::framesAvailable_l() uint32_t u = user; uint32_t s = server; - if (flags & CBLK_DIRECTION_MSK) { + if (flags & CBLK_DIRECTION) { uint32_t limit = (s < loopStart) ? s : loopStart; return limit + frameCount - u; } else { @@ -1652,7 +1652,7 @@ uint32_t audio_track_cblk_t::framesReady() uint32_t u = user; uint32_t s = server; - if (flags & CBLK_DIRECTION_MSK) { + if (flags & CBLK_DIRECTION) { if (u < loopEnd) { return u - s; } else { -- cgit v1.1 From 847d05dc8fa144dcf8f4f435d6a6ac1727f00937 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 27 Feb 2012 16:05:09 -0800 Subject: Remove deprecated AudioTrack APIs Change-Id: I88be6525f3e33df529c0c3cb701d12a484809477 --- media/libmedia/AudioTrack.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ffed161..24db1bd 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -120,28 +120,6 @@ AudioTrack::AudioTrack( 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId); } -// DEPRECATED -AudioTrack::AudioTrack( - int streamType, - uint32_t sampleRate, - int format, - int channelMask, - int frameCount, - uint32_t flags, - callback_t cbf, - void* user, - int notificationFrames, - int sessionId) - : mStatus(NO_INIT), - mIsTimed(false), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) -{ - mStatus = set((audio_stream_type_t)streamType, sampleRate, (audio_format_t)format, - (audio_channel_mask_t) channelMask, - frameCount, (audio_output_flags_t)flags, cbf, user, notificationFrames, - 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId); -} - AudioTrack::AudioTrack( audio_stream_type_t streamType, uint32_t sampleRate, -- cgit v1.1 From b1c0993b215c5c3eebd1c6bafc22bba23d57a70b Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 27 Feb 2012 16:21:04 -0800 Subject: Add all-channel AudioTrack::setVolume() API Add combined channel APIs setVolume to AudioTrack, and remove obsolete getVolume. Change-Id: I0c87bfdbff4f4292259fa33e65f67badbafd270b --- media/libmedia/AudioTrack.cpp | 9 ++------- media/libmedia/ToneGenerator.cpp | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ffed161..596523d 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -529,14 +529,9 @@ status_t AudioTrack::setVolume(float left, float right) return NO_ERROR; } -void AudioTrack::getVolume(float* left, float* right) const +status_t AudioTrack::setVolume(float volume) { - if (left != NULL) { - *left = mVolume[LEFT]; - } - if (right != NULL) { - *right = mVolume[RIGHT]; - } + return setVolume(volume, volume); } status_t AudioTrack::setAuxEffectSendLevel(float level) diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 253602d..42584fe 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -1036,7 +1036,7 @@ bool ToneGenerator::initAudioTrack() { goto initAudioTrack_exit; } - mpAudioTrack->setVolume(mVolume, mVolume); + mpAudioTrack->setVolume(mVolume); mState = TONE_INIT; -- cgit v1.1 From d2c38fc4d5dc742d7441444316849510dd2b7363 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 1 Nov 2012 14:58:02 -0700 Subject: Cache mCblk in local variable cblk Use "iMem" for sp Change-Id: I2f1fbbc517fbd77cfc92f6c3b1f253c26bae93b0 --- media/libmedia/AudioTrack.cpp | 141 +++++++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 62 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 9f087c2..523d844 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -378,7 +378,7 @@ void AudioTrack::start() androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); } - ALOGV("start %p before lock cblk %p", this, mCblk); + ALOGV("start %p before lock cblk %p", this, cblk); status_t status = NO_ERROR; if (!(cblk->flags & CBLK_INVALID)) { cblk->lock.unlock(); @@ -390,7 +390,9 @@ void AudioTrack::start() } } if (cblk->flags & CBLK_INVALID) { - status = restoreTrack_l(cblk, true); + audio_track_cblk_t* temp = cblk; + status = restoreTrack_l(temp, true); + cblk = temp; } cblk->lock.unlock(); if (status != NO_ERROR) { @@ -664,12 +666,13 @@ status_t AudioTrack::setPosition(uint32_t position) if (!stopped_l()) return INVALID_OPERATION; - Mutex::Autolock _l(mCblk->lock); + audio_track_cblk_t* cblk = mCblk; + Mutex::Autolock _l(cblk->lock); - if (position > mCblk->user) return BAD_VALUE; + if (position > cblk->user) return BAD_VALUE; - mCblk->server = position; - android_atomic_or(CBLK_FORCEREADY, &mCblk->flags); + cblk->server = position; + android_atomic_or(CBLK_FORCEREADY, &cblk->flags); return NO_ERROR; } @@ -691,7 +694,8 @@ status_t AudioTrack::reload() flush_l(); - mCblk->stepUser(mCblk->frameCount); + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(cblk->frameCount); return NO_ERROR; } @@ -874,50 +878,51 @@ status_t AudioTrack::createTrack_l( ALOGE("AudioFlinger could not create track, status: %d", status); return status; } - sp cblk = track->getCblk(); - if (cblk == 0) { + sp iMem = track->getCblk(); + if (iMem == 0) { ALOGE("Could not get control block"); return NO_INIT; } mAudioTrack = track; - mCblkMemory = cblk; - mCblk = static_cast(cblk->pointer()); - // old has the previous value of mCblk->flags before the "or" operation - int32_t old = android_atomic_or(CBLK_DIRECTION, &mCblk->flags); + mCblkMemory = iMem; + audio_track_cblk_t* cblk = static_cast(iMem->pointer()); + mCblk = cblk; + // old has the previous value of cblk->flags before the "or" operation + int32_t old = android_atomic_or(CBLK_DIRECTION, &cblk->flags); if (flags & AUDIO_OUTPUT_FLAG_FAST) { if (old & CBLK_FAST) { - ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", mCblk->frameCount); + ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", cblk->frameCount); } else { - ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", mCblk->frameCount); + ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", cblk->frameCount); // once denied, do not request again if IAudioTrack is re-created flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST); mFlags = flags; } if (sharedBuffer == 0) { - mNotificationFramesAct = mCblk->frameCount/2; + mNotificationFramesAct = cblk->frameCount/2; } } if (sharedBuffer == 0) { - mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + cblk->buffers = (char*)cblk + sizeof(audio_track_cblk_t); } else { - mCblk->buffers = sharedBuffer->pointer(); + cblk->buffers = sharedBuffer->pointer(); // Force buffer full condition as data is already present in shared memory - mCblk->stepUser(mCblk->frameCount); + cblk->stepUser(cblk->frameCount); } - mCblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | + cblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000)); - mCblk->setSendLevel(mSendLevel); + cblk->setSendLevel(mSendLevel); mAudioTrack->attachAuxEffect(mAuxEffectId); - mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; - mCblk->waitTimeMs = 0; + cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; + cblk->waitTimeMs = 0; mRemainingFrames = mNotificationFramesAct; // FIXME don't believe this lie - mLatency = afLatency + (1000*mCblk->frameCount) / sampleRate; + mLatency = afLatency + (1000*cblk->frameCount) / sampleRate; // If IAudioTrack is re-created, don't let the requested frameCount // decrease. This can confuse clients that cache frameCount(). - if (mCblk->frameCount > mFrameCount) { - mFrameCount = mCblk->frameCount; + if (cblk->frameCount > mFrameCount) { + mFrameCount = cblk->frameCount; } return NO_ERROR; } @@ -985,7 +990,9 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) if (result == DEAD_OBJECT) { android_atomic_or(CBLK_INVALID, &cblk->flags); create_new_track: - result = restoreTrack_l(cblk, false); + audio_track_cblk_t* temp = cblk; + result = restoreTrack_l(temp, false); + cblk = temp; } if (result != NO_ERROR) { ALOGW("obtainBuffer create Track error %d", result); @@ -1038,12 +1045,13 @@ create_new_track: void AudioTrack::releaseBuffer(Buffer* audioBuffer) { AutoMutex lock(mLock); - mCblk->stepUser(audioBuffer->frameCount); + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(audioBuffer->frameCount); if (audioBuffer->frameCount > 0) { // restart track if it was disabled by audioflinger due to previous underrun - if (mActive && (mCblk->flags & CBLK_DISABLED)) { - android_atomic_and(~CBLK_DISABLED, &mCblk->flags); - ALOGW("releaseBuffer() track %p name=%#x disabled, restarting", this, mCblk->mName); + if (mActive && (cblk->flags & CBLK_DISABLED)) { + android_atomic_and(~CBLK_DISABLED, &cblk->flags); + ALOGW("releaseBuffer() track %p name=%#x disabled, restarting", this, cblk->mName); mAudioTrack->start(); } } @@ -1127,19 +1135,22 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) // If the track is not invalid already, try to allocate a buffer. alloc // fails indicating that the server is dead, flag the track as invalid so // we can attempt to restore in just a bit. - if (!(mCblk->flags & CBLK_INVALID)) { + audio_track_cblk_t* cblk = mCblk; + if (!(cblk->flags & CBLK_INVALID)) { result = mAudioTrack->allocateTimedBuffer(size, buffer); if (result == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID, &mCblk->flags); + android_atomic_or(CBLK_INVALID, &cblk->flags); } } // If the track is invalid at this point, attempt to restore it. and try the // allocation one more time. - if (mCblk->flags & CBLK_INVALID) { - mCblk->lock.lock(); - result = restoreTrack_l(mCblk, false); - mCblk->lock.unlock(); + if (cblk->flags & CBLK_INVALID) { + cblk->lock.lock(); + audio_track_cblk_t* temp = cblk; + result = restoreTrack_l(temp, false); + cblk = temp; + cblk->lock.unlock(); if (result == OK) result = mAudioTrack->allocateTimedBuffer(size, buffer); @@ -1154,10 +1165,11 @@ status_t TimedAudioTrack::queueTimedBuffer(const sp& buffer, status_t status = mAudioTrack->queueTimedBuffer(buffer, pts); { AutoMutex lock(mLock); + audio_track_cblk_t* cblk = mCblk; // restart track if it was disabled by audioflinger due to previous underrun if (buffer->size() != 0 && status == NO_ERROR && - mActive && (mCblk->flags & CBLK_DISABLED)) { - android_atomic_and(~CBLK_DISABLED, &mCblk->flags); + mActive && (cblk->flags & CBLK_DISABLED)) { + android_atomic_and(~CBLK_DISABLED, &cblk->flags); ALOGW("queueTimedBuffer() track %p disabled, restarting", this); mAudioTrack->start(); } @@ -1285,10 +1297,10 @@ bool AudioTrack::processAudioBuffer(const sp& thread) } audioBuffer.size = writtenSize; - // NOTE: mCblk->frameSize is not equal to AudioTrack::frameSize() for - // 8 bit PCM data: in this case, mCblk->frameSize is based on a sample size of + // NOTE: cblk->frameSize is not equal to AudioTrack::frameSize() for + // 8 bit PCM data: in this case, cblk->frameSize is based on a sample size of // 16 bit. - audioBuffer.frameCount = writtenSize/mCblk->frameSize; + audioBuffer.frameCount = writtenSize/cblk->frameSize; frames -= audioBuffer.frameCount; @@ -1304,13 +1316,16 @@ bool AudioTrack::processAudioBuffer(const sp& thread) return true; } -// must be called with mLock and cblk.lock held. Callers must also hold strong references on +// must be called with mLock and refCblk.lock held. Callers must also hold strong references on // the IAudioTrack and IMemory in case they are recreated here. -// If the IAudioTrack is successfully restored, the cblk pointer is updated -status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) +// If the IAudioTrack is successfully restored, the refCblk pointer is updated +// FIXME Don't depend on caller to hold strong references. +status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart) { status_t result; + audio_track_cblk_t* cblk = refCblk; + audio_track_cblk_t* newCblk = cblk; if (!(android_atomic_or(CBLK_RESTORING, &cblk->flags) & CBLK_RESTORING)) { ALOGW("dead IAudioTrack, creating a new one from %s TID %d", fromStart ? "start()" : "obtainBuffer()", gettid()); @@ -1340,40 +1355,41 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) uint32_t user = cblk->user; uint32_t server = cblk->server; // restore write index and set other indexes to reflect empty buffer status - mCblk->user = user; - mCblk->server = user; - mCblk->userBase = user; - mCblk->serverBase = user; + newCblk = mCblk; + newCblk->user = user; + newCblk->server = user; + newCblk->userBase = user; + newCblk->serverBase = user; // restore loop: this is not guaranteed to succeed if new frame count is not // compatible with loop length setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount); if (!fromStart) { - mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; // Make sure that a client relying on callback events indicating underrun or // the actual amount of audio frames played (e.g SoundPool) receives them. if (mSharedBuffer == 0) { uint32_t frames = 0; if (user > server) { - frames = ((user - server) > mCblk->frameCount) ? - mCblk->frameCount : (user - server); - memset(mCblk->buffers, 0, frames * mCblk->frameSize); + frames = ((user - server) > newCblk->frameCount) ? + newCblk->frameCount : (user - server); + memset(newCblk->buffers, 0, frames * newCblk->frameSize); } // restart playback even if buffer is not completely filled. - android_atomic_or(CBLK_FORCEREADY, &mCblk->flags); + android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to // the client - mCblk->stepUser(frames); + newCblk->stepUser(frames); } } if (mSharedBuffer != 0) { - mCblk->stepUser(mCblk->frameCount); + newCblk->stepUser(newCblk->frameCount); } if (mActive) { result = mAudioTrack->start(); ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result); } if (fromStart && result == NO_ERROR) { - mNewPosition = mCblk->server + mUpdatePeriod; + mNewPosition = newCblk->server + mUpdatePeriod; } } if (result != NO_ERROR) { @@ -1409,13 +1425,13 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) } } ALOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", - result, mActive, mCblk, cblk, mCblk->flags, cblk->flags); + result, mActive, newCblk, cblk, newCblk->flags, cblk->flags); if (result == NO_ERROR) { // from now on we switch to the newly created cblk - cblk = mCblk; + refCblk = newCblk; } - cblk->lock.lock(); + newCblk->lock.lock(); ALOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d TID %d", result, gettid()); @@ -1429,15 +1445,16 @@ status_t AudioTrack::dump(int fd, const Vector& args) const char buffer[SIZE]; String8 result; + audio_track_cblk_t* cblk = mCblk; result.append(" AudioTrack::dump\n"); snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mVolume[0], mVolume[1]); result.append(buffer); snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, - mChannelCount, mCblk->frameCount); + mChannelCount, cblk->frameCount); result.append(buffer); snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", - (mCblk == 0) ? 0 : mCblk->sampleRate, mStatus, mMuted); + (cblk == 0) ? 0 : cblk->sampleRate, mStatus, mMuted); result.append(buffer); snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); -- cgit v1.1 From 26ba972eafde73a26271ecf027a1d5988ce50eb8 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 21 Jun 2012 16:24:32 -0700 Subject: Removed unused fields in AudioRecord::Buffer Change-Id: I89fc6d8f695b48516d956b0a9a4a43d408f369f9 --- media/libmedia/AudioRecord.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index bd558fa..3c28ca7 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -560,9 +560,6 @@ create_new_record: framesReq = bufferEnd - u; } - audioBuffer->flags = 0; - audioBuffer->channelCount= mChannelCount; - audioBuffer->format = mFormat; audioBuffer->frameCount = framesReq; audioBuffer->size = framesReq*cblk->frameSize; audioBuffer->raw = (int8_t*)cblk->buffer(u); -- cgit v1.1 From 05d499958e4030938ed77a924ebdd9899f36752e Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 6 Nov 2012 14:25:20 -0800 Subject: Remove unused fields in AudioTrack::Buffer Change-Id: Iab75f6e2348d8b6d1f3cec95aeb3fcd5135dfb50 --- media/libmedia/AudioTrack.cpp | 7 ------- 1 file changed, 7 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 523d844..da467ba 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1028,15 +1028,8 @@ create_new_track: framesReq = bufferEnd - u; } - audioBuffer->flags = mMuted ? Buffer::MUTE : 0; - audioBuffer->channelCount = mChannelCount; audioBuffer->frameCount = framesReq; audioBuffer->size = framesReq * cblk->frameSize; - if (audio_is_linear_pcm(mFormat)) { - audioBuffer->format = AUDIO_FORMAT_PCM_16_BIT; - } else { - audioBuffer->format = mFormat; - } audioBuffer->raw = (int8_t *)cblk->buffer(u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); -- cgit v1.1 From e0b07179a48ee50fda931d2aa1b3c751d167e4d7 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 6 Nov 2012 15:03:34 -0800 Subject: Remove CBLK_FAST from control block flags This is part of a series to clean up the control block. Change-Id: Ic881a3560d9547cb63fcc0cefec87aa3da480e0d --- media/libmedia/AudioTrack.cpp | 7 +++---- media/libmedia/IAudioFlinger.cpp | 12 +++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 523d844..38eaa65 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -867,7 +867,7 @@ status_t AudioTrack::createTrack_l( format, channelMask, frameCount, - trackFlags, + &trackFlags, sharedBuffer, output, tid, @@ -887,10 +887,9 @@ status_t AudioTrack::createTrack_l( mCblkMemory = iMem; audio_track_cblk_t* cblk = static_cast(iMem->pointer()); mCblk = cblk; - // old has the previous value of cblk->flags before the "or" operation - int32_t old = android_atomic_or(CBLK_DIRECTION, &cblk->flags); + android_atomic_or(CBLK_DIRECTION, &cblk->flags); if (flags & AUDIO_OUTPUT_FLAG_FAST) { - if (old & CBLK_FAST) { + if (trackFlags & IAudioFlinger::TRACK_FAST) { ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", cblk->frameCount); } else { ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", cblk->frameCount); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index f412591..bb936ec 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -90,7 +90,7 @@ public: audio_format_t format, audio_channel_mask_t channelMask, int frameCount, - track_flags_t flags, + track_flags_t *flags, const sp& sharedBuffer, audio_io_handle_t output, pid_t tid, @@ -106,7 +106,8 @@ public: data.writeInt32(format); data.writeInt32(channelMask); data.writeInt32(frameCount); - data.writeInt32((int32_t) flags); + track_flags_t lFlags = flags != NULL ? *flags : TRACK_DEFAULT; + data.writeInt32(lFlags); data.writeStrongBinder(sharedBuffer->asBinder()); data.writeInt32((int32_t) output); data.writeInt32((int32_t) tid); @@ -119,6 +120,10 @@ public: if (lStatus != NO_ERROR) { ALOGE("createTrack error: %s", strerror(-lStatus)); } else { + lFlags = reply.readInt32(); + if (flags != NULL) { + *flags = lFlags; + } lSessionId = reply.readInt32(); if (sessionId != NULL) { *sessionId = lSessionId; @@ -732,7 +737,8 @@ status_t BnAudioFlinger::onTransact( status_t status; sp track = createTrack(pid, (audio_stream_type_t) streamType, sampleRate, format, - channelMask, bufferCount, flags, buffer, output, tid, &sessionId, &status); + channelMask, bufferCount, &flags, buffer, output, tid, &sessionId, &status); + reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); -- cgit v1.1 From 864585df53eb97c31e77b3ad7c0d89e4f9b42588 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 6 Nov 2012 16:15:41 -0800 Subject: Remove CBLK_DIRECTION from control block flags This is part of a series to clean up the control block. Change-Id: I0265fece3247356b585d4d48fbda6f37aea8a851 --- media/libmedia/AudioRecord.cpp | 9 ++++----- media/libmedia/AudioTrack.cpp | 37 ++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 24 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 3c28ca7..ae1842e 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -466,7 +466,6 @@ status_t AudioRecord::openRecord_l( mCblkMemory = cblk; mCblk = static_cast(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); - android_atomic_and(~CBLK_DIRECTION, &mCblk->flags); mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; mCblk->waitTimeMs = 0; return NO_ERROR; @@ -484,7 +483,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) audioBuffer->frameCount = 0; audioBuffer->size = 0; - uint32_t framesReady = cblk->framesReady(); + uint32_t framesReady = cblk->framesReadyIn(); if (framesReady == 0) { cblk->lock.lock(); @@ -540,7 +539,7 @@ create_new_record: } // read the server count again start_loop_here: - framesReady = cblk->framesReady(); + framesReady = cblk->framesReadyIn(); } cblk->lock.unlock(); } @@ -570,7 +569,7 @@ create_new_record: void AudioRecord::releaseBuffer(Buffer* audioBuffer) { AutoMutex lock(mLock); - mCblk->stepUser(audioBuffer->frameCount); + mCblk->stepUserIn(audioBuffer->frameCount); } audio_io_handle_t AudioRecord::getInput() const @@ -732,7 +731,7 @@ bool AudioRecord::processAudioBuffer(const sp& thread) // Manage overrun callback - if (active && (cblk->framesAvailable() == 0)) { + if (active && (cblk->framesAvailableIn() == 0)) { // The value of active is stale, but we are almost sure to be active here because // otherwise we would have exited when obtainBuffer returned STOPPED earlier. ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 5348646..f55ec9a 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -695,7 +695,7 @@ status_t AudioTrack::reload() flush_l(); audio_track_cblk_t* cblk = mCblk; - cblk->stepUser(cblk->frameCount); + cblk->stepUserOut(cblk->frameCount); return NO_ERROR; } @@ -887,7 +887,6 @@ status_t AudioTrack::createTrack_l( mCblkMemory = iMem; audio_track_cblk_t* cblk = static_cast(iMem->pointer()); mCblk = cblk; - android_atomic_or(CBLK_DIRECTION, &cblk->flags); if (flags & AUDIO_OUTPUT_FLAG_FAST) { if (trackFlags & IAudioFlinger::TRACK_FAST) { ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", cblk->frameCount); @@ -906,7 +905,7 @@ status_t AudioTrack::createTrack_l( } else { cblk->buffers = sharedBuffer->pointer(); // Force buffer full condition as data is already present in shared memory - cblk->stepUser(cblk->frameCount); + cblk->stepUserOut(cblk->frameCount); } cblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | @@ -938,7 +937,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) audioBuffer->frameCount = 0; audioBuffer->size = 0; - uint32_t framesAvail = cblk->framesAvailable(); + uint32_t framesAvail = cblk->framesAvailableOut(); cblk->lock.lock(); if (cblk->flags & CBLK_INVALID) { @@ -1009,7 +1008,7 @@ create_new_track: } // read the server count again start_loop_here: - framesAvail = cblk->framesAvailable_l(); + framesAvail = cblk->framesAvailableOut_l(); } cblk->lock.unlock(); } @@ -1038,7 +1037,7 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) { AutoMutex lock(mLock); audio_track_cblk_t* cblk = mCblk; - cblk->stepUser(audioBuffer->frameCount); + cblk->stepUserOut(audioBuffer->frameCount); if (audioBuffer->frameCount > 0) { // restart track if it was disabled by audioflinger due to previous underrun if (mActive && (cblk->flags & CBLK_DISABLED)) { @@ -1193,7 +1192,7 @@ bool AudioTrack::processAudioBuffer(const sp& thread) mLock.unlock(); // Manage underrun callback - if (active && (cblk->framesAvailable() == cblk->frameCount)) { + if (active && (cblk->framesAvailableOut() == cblk->frameCount)) { ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) { mCbf(EVENT_UNDERRUN, mUserData, 0); @@ -1370,11 +1369,11 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to // the client - newCblk->stepUser(frames); + newCblk->stepUserOut(frames); } } if (mSharedBuffer != 0) { - newCblk->stepUser(newCblk->frameCount); + newCblk->stepUserOut(newCblk->frameCount); } if (mActive) { result = mAudioTrack->start(); @@ -1514,14 +1513,14 @@ audio_track_cblk_t::audio_track_cblk_t() { } -uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) +uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount, bool isOut) { ALOGV("stepuser %08x %08x %d", user, server, frameCount); uint32_t u = user; u += frameCount; // Ensure that user is never ahead of server for AudioRecord - if (flags & CBLK_DIRECTION) { + if (isOut) { // If stepServer() has been called once, switch to normal obtainBuffer() timeout period if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; @@ -1552,7 +1551,7 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) return u; } -bool audio_track_cblk_t::stepServer(uint32_t frameCount) +bool audio_track_cblk_t::stepServer(uint32_t frameCount, bool isOut) { ALOGV("stepserver %08x %08x %d", user, server, frameCount); @@ -1565,7 +1564,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount) bool flushed = (s == user); s += frameCount; - if (flags & CBLK_DIRECTION) { + if (isOut) { // Mark that we have read the first buffer so that next time stepUser() is called // we switch to normal obtainBuffer() timeout period if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) { @@ -1615,18 +1614,18 @@ void* audio_track_cblk_t::buffer(uint32_t offset) const return (int8_t *)buffers + (offset - userBase) * frameSize; } -uint32_t audio_track_cblk_t::framesAvailable() +uint32_t audio_track_cblk_t::framesAvailable(bool isOut) { Mutex::Autolock _l(lock); - return framesAvailable_l(); + return framesAvailable_l(isOut); } -uint32_t audio_track_cblk_t::framesAvailable_l() +uint32_t audio_track_cblk_t::framesAvailable_l(bool isOut) { uint32_t u = user; uint32_t s = server; - if (flags & CBLK_DIRECTION) { + if (isOut) { uint32_t limit = (s < loopStart) ? s : loopStart; return limit + frameCount - u; } else { @@ -1634,12 +1633,12 @@ uint32_t audio_track_cblk_t::framesAvailable_l() } } -uint32_t audio_track_cblk_t::framesReady() +uint32_t audio_track_cblk_t::framesReady(bool isOut) { uint32_t u = user; uint32_t s = server; - if (flags & CBLK_DIRECTION) { + if (isOut) { if (u < loopEnd) { return u - s; } else { -- cgit v1.1 From d5ed6e88a9bea1879e41d7defaf1edea7c09f554 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 2 Nov 2012 13:05:14 -0700 Subject: Fix call to restoreTrack_l() without lock held Also document lock order Change-Id: I2c1f273a0a51fa79ee3dd766de8d23083e270051 --- media/libmedia/AudioTrack.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 5348646..1f4f3d0 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1122,8 +1122,14 @@ TimedAudioTrack::TimedAudioTrack() { status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) { + AutoMutex lock(mLock); status_t result = UNKNOWN_ERROR; + // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed + // while we are accessing the cblk + sp audioTrack = mAudioTrack; + sp iMem = mCblkMemory; + // If the track is not invalid already, try to allocate a buffer. alloc // fails indicating that the server is dead, flag the track as invalid so // we can attempt to restore in just a bit. -- cgit v1.1 From a47f3165f53c8e8fb8907a94de7417e2c3047eeb Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 7 Nov 2012 10:13:08 -0800 Subject: Simplify AudioTrack::restoreTrack_l() Remove CBLK_RESTORING and CBLK_RESTORED from control block flags, for AudioTrack only. They are still used by AudioRecord. This is part of a series to clean up the control block. Change-Id: Iae4798f5b527c492bdaf789987ff3a1dadd0cb37 --- media/libmedia/AudioTrack.cpp | 160 ++++++++++++++++++------------------------ 1 file changed, 69 insertions(+), 91 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 324fd6d..7ce9879 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -297,7 +297,6 @@ status_t AudioTrack::set( mUpdatePeriod = 0; mFlushed = false; AudioSystem::acquireAudioSessionId(mSessionId); - mRestoreStatus = NO_ERROR; return NO_ERROR; } @@ -956,12 +955,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) } if (!(cblk->flags & CBLK_INVALID)) { mLock.unlock(); + // this condition is in shared memory, so if IAudioTrack and control block + // are replaced due to mediaserver death or IAudioTrack invalidation then + // cv won't be signalled, but fortunately the timeout will limit the wait result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); cblk->lock.unlock(); mLock.lock(); if (!mActive) { return status_t(STOPPED); } + // IAudioTrack may have been re-created while mLock was unlocked + cblk = mCblk; cblk->lock.lock(); } @@ -1072,6 +1076,9 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) sp iMem = mCblkMemory; mLock.unlock(); + // since mLock is unlocked the IAudioTrack and shared memory may be re-created, + // so all cblk references might still refer to old shared memory, but that should be benign + ssize_t written = 0; const int8_t *src = (const int8_t *)buffer; Buffer audioBuffer; @@ -1192,6 +1199,9 @@ bool AudioTrack::processAudioBuffer(const sp& thread) bool active = mActive; mLock.unlock(); + // since mLock is unlocked the IAudioTrack and shared memory may be re-created, + // so all cblk references might still refer to old shared memory, but that should be benign + // Manage underrun callback if (active && (cblk->framesAvailableOut() == cblk->frameCount)) { ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); @@ -1318,104 +1328,72 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart audio_track_cblk_t* cblk = refCblk; audio_track_cblk_t* newCblk = cblk; - if (!(android_atomic_or(CBLK_RESTORING, &cblk->flags) & CBLK_RESTORING)) { - ALOGW("dead IAudioTrack, creating a new one from %s TID %d", - fromStart ? "start()" : "obtainBuffer()", gettid()); + ALOGW("dead IAudioTrack, creating a new one from %s TID %d", + fromStart ? "start()" : "obtainBuffer()", gettid()); - // signal old cblk condition so that other threads waiting for available buffers stop - // waiting now - cblk->cv.broadcast(); - cblk->lock.unlock(); + // signal old cblk condition so that other threads waiting for available buffers stop + // waiting now + cblk->cv.broadcast(); + cblk->lock.unlock(); - // refresh the audio configuration cache in this process to make sure we get new - // output parameters in getOutput_l() and createTrack_l() - AudioSystem::clearAudioConfigCache(); - - // if the new IAudioTrack is created, createTrack_l() will modify the - // following member variables: mAudioTrack, mCblkMemory and mCblk. - // It will also delete the strong references on previous IAudioTrack and IMemory - result = createTrack_l(mStreamType, - cblk->sampleRate, - mFormat, - mChannelMask, - mFrameCount, - mFlags, - mSharedBuffer, - getOutput_l()); - - if (result == NO_ERROR) { - uint32_t user = cblk->user; - uint32_t server = cblk->server; - // restore write index and set other indexes to reflect empty buffer status - newCblk = mCblk; - newCblk->user = user; - newCblk->server = user; - newCblk->userBase = user; - newCblk->serverBase = user; - // restore loop: this is not guaranteed to succeed if new frame count is not - // compatible with loop length - setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount); - if (!fromStart) { - newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - // Make sure that a client relying on callback events indicating underrun or - // the actual amount of audio frames played (e.g SoundPool) receives them. - if (mSharedBuffer == 0) { - uint32_t frames = 0; - if (user > server) { - frames = ((user - server) > newCblk->frameCount) ? - newCblk->frameCount : (user - server); - memset(newCblk->buffers, 0, frames * newCblk->frameSize); - } - // restart playback even if buffer is not completely filled. - android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); - // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to - // the client - newCblk->stepUserOut(frames); + // refresh the audio configuration cache in this process to make sure we get new + // output parameters in getOutput_l() and createTrack_l() + AudioSystem::clearAudioConfigCache(); + + // if the new IAudioTrack is created, createTrack_l() will modify the + // following member variables: mAudioTrack, mCblkMemory and mCblk. + // It will also delete the strong references on previous IAudioTrack and IMemory + result = createTrack_l(mStreamType, + cblk->sampleRate, + mFormat, + mChannelMask, + mFrameCount, + mFlags, + mSharedBuffer, + getOutput_l()); + + if (result == NO_ERROR) { + uint32_t user = cblk->user; + uint32_t server = cblk->server; + // restore write index and set other indexes to reflect empty buffer status + newCblk = mCblk; + newCblk->user = user; + newCblk->server = user; + newCblk->userBase = user; + newCblk->serverBase = user; + // restore loop: this is not guaranteed to succeed if new frame count is not + // compatible with loop length + setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount); + if (!fromStart) { + newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + // Make sure that a client relying on callback events indicating underrun or + // the actual amount of audio frames played (e.g SoundPool) receives them. + if (mSharedBuffer == 0) { + uint32_t frames = 0; + if (user > server) { + frames = ((user - server) > newCblk->frameCount) ? + newCblk->frameCount : (user - server); + memset(newCblk->buffers, 0, frames * newCblk->frameSize); } - } - if (mSharedBuffer != 0) { - newCblk->stepUserOut(newCblk->frameCount); - } - if (mActive) { - result = mAudioTrack->start(); - ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result); - } - if (fromStart && result == NO_ERROR) { - mNewPosition = newCblk->server + mUpdatePeriod; + // restart playback even if buffer is not completely filled. + android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); + // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to + // the client + newCblk->stepUserOut(frames); } } - if (result != NO_ERROR) { - android_atomic_and(~CBLK_RESTORING, &cblk->flags); - ALOGW_IF(result != NO_ERROR, "restoreTrack_l() failed status %d", result); + if (mSharedBuffer != 0) { + newCblk->stepUserOut(newCblk->frameCount); } - mRestoreStatus = result; - // signal old cblk condition for other threads waiting for restore completion - android_atomic_or(CBLK_RESTORED, &cblk->flags); - cblk->cv.broadcast(); - } else { - bool haveLogged = false; - for (;;) { - if (cblk->flags & CBLK_RESTORED) { - ALOGW("dead IAudioTrack restored"); - result = mRestoreStatus; - cblk->lock.unlock(); - break; - } - if (!haveLogged) { - ALOGW("dead IAudioTrack, waiting for a new one"); - haveLogged = true; - } - mLock.unlock(); - result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS)); - cblk->lock.unlock(); - mLock.lock(); - if (result != NO_ERROR) { - ALOGW("timed out"); - break; - } - cblk->lock.lock(); + if (mActive) { + result = mAudioTrack->start(); + ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result); + } + if (fromStart && result == NO_ERROR) { + mNewPosition = newCblk->server + mUpdatePeriod; } } + ALOGW_IF(result != NO_ERROR, "restoreTrack_l() failed status %d", result); ALOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", result, mActive, newCblk, cblk, newCblk->flags, cblk->flags); -- cgit v1.1 From b929e417853694e37aba1ef4399f188987b709d9 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 8 Nov 2012 12:13:58 -0800 Subject: Move buffers pointer out of the control block This is part of a series to clean up the control block. Change-Id: Ie474557db7cb360f2d9a0f11600a68f5a3d46f07 --- media/libmedia/AudioRecord.cpp | 4 ++-- media/libmedia/AudioTrack.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index ae1842e..263a7c7 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -465,7 +465,7 @@ status_t AudioRecord::openRecord_l( mCblkMemory.clear(); mCblkMemory = cblk; mCblk = static_cast(cblk->pointer()); - mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mBuffers = (char*)mCblk + sizeof(audio_track_cblk_t); mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; mCblk->waitTimeMs = 0; return NO_ERROR; @@ -561,7 +561,7 @@ create_new_record: audioBuffer->frameCount = framesReq; audioBuffer->size = framesReq*cblk->frameSize; - audioBuffer->raw = (int8_t*)cblk->buffer(u); + audioBuffer->raw = cblk->buffer(mBuffers, u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 7ce9879..468bd29 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -895,9 +895,9 @@ status_t AudioTrack::createTrack_l( } } if (sharedBuffer == 0) { - cblk->buffers = (char*)cblk + sizeof(audio_track_cblk_t); + mBuffers = (char*)cblk + sizeof(audio_track_cblk_t); } else { - cblk->buffers = sharedBuffer->pointer(); + mBuffers = sharedBuffer->pointer(); // Force buffer full condition as data is already present in shared memory cblk->stepUserOut(cblk->frameCount); } @@ -1027,7 +1027,7 @@ create_new_track: audioBuffer->frameCount = framesReq; audioBuffer->size = framesReq * cblk->frameSize; - audioBuffer->raw = (int8_t *)cblk->buffer(u); + audioBuffer->raw = cblk->buffer(mBuffers, u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } @@ -1373,7 +1373,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart if (user > server) { frames = ((user - server) > newCblk->frameCount) ? newCblk->frameCount : (user - server); - memset(newCblk->buffers, 0, frames * newCblk->frameSize); + memset(mBuffers, 0, frames * newCblk->frameSize); } // restart playback even if buffer is not completely filled. android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); @@ -1486,7 +1486,7 @@ void AudioTrack::AudioTrackThread::resume() audio_track_cblk_t::audio_track_cblk_t() : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), - userBase(0), serverBase(0), buffers(NULL), frameCount(0), + userBase(0), serverBase(0), frameCount(0), loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000), mSendLevel(0), flags(0) { @@ -1588,7 +1588,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount, bool isOut) return true; } -void* audio_track_cblk_t::buffer(uint32_t offset) const +void* audio_track_cblk_t::buffer(void *buffers, uint32_t offset) const { return (int8_t *)buffers + (offset - userBase) * frameSize; } -- cgit v1.1 From bc0f6b92bba33ca9c2e76f2a520d290f055da6b2 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 12 Nov 2012 14:32:06 -0800 Subject: Fix regression for AudioTrack::write() 8-bit PCM Bug: 7526532 Change-Id: I8ddd1f0e9d035b54401788dcc422591281dcd97a --- media/libmedia/AudioTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 468bd29..26cf877 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1104,8 +1104,8 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) } else { toWrite = audioBuffer.size; memcpy(audioBuffer.i8, src, toWrite); - src += toWrite; } + src += toWrite; userSize -= toWrite; written += toWrite; -- cgit v1.1 From 83a0382dc17364567667a4e6135db43f5bd92efc Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 12 Nov 2012 07:58:20 -0800 Subject: Move frame size out of the control block This is part of a series to clean up the control block. Change-Id: Ifab1c42ac0f8be704e571b292713cd2250d12a3f --- media/libmedia/AudioRecord.cpp | 20 +++++++++----------- media/libmedia/AudioTrack.cpp | 28 ++++++++++++++-------------- 2 files changed, 23 insertions(+), 25 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 263a7c7..b40aaf5 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -213,6 +213,13 @@ status_t AudioRecord::set( mFrameCount = mCblk->frameCount; mChannelCount = (uint8_t)channelCount; mChannelMask = channelMask; + + if (audio_is_linear_pcm(mFormat)) { + mFrameSize = channelCount * audio_bytes_per_sample(format); + } else { + mFrameSize = sizeof(uint8_t); + } + mActive = false; mCbf = cbf; mNotificationFrames = notificationFrames; @@ -258,15 +265,6 @@ uint32_t AudioRecord::frameCount() const return mFrameCount; } -size_t AudioRecord::frameSize() const -{ - if (audio_is_linear_pcm(mFormat)) { - return channelCount()*audio_bytes_per_sample(mFormat); - } else { - return sizeof(uint8_t); - } -} - audio_source_t AudioRecord::inputSource() const { return mInputSource; @@ -560,8 +558,8 @@ create_new_record: } audioBuffer->frameCount = framesReq; - audioBuffer->size = framesReq*cblk->frameSize; - audioBuffer->raw = cblk->buffer(mBuffers, u); + audioBuffer->size = framesReq * mFrameSize; + audioBuffer->raw = cblk->buffer(mBuffers, mFrameSize, u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 26cf877..4a4759e 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -286,6 +286,15 @@ status_t AudioTrack::set( mFormat = format; mChannelMask = channelMask; mChannelCount = channelCount; + + if (audio_is_linear_pcm(format)) { + mFrameSize = channelCount * audio_bytes_per_sample(format); + mFrameSizeAF = channelCount * sizeof(int16_t); + } else { + mFrameSize = sizeof(uint8_t); + mFrameSizeAF = sizeof(uint8_t); + } + mSharedBuffer = sharedBuffer; mMuted = false; mActive = false; @@ -332,15 +341,6 @@ uint32_t AudioTrack::frameCount() const return mCblk->frameCount; } -size_t AudioTrack::frameSize() const -{ - if (audio_is_linear_pcm(mFormat)) { - return channelCount()*audio_bytes_per_sample(mFormat); - } else { - return sizeof(uint8_t); - } -} - sp& AudioTrack::sharedBuffer() { return mSharedBuffer; @@ -1026,8 +1026,8 @@ create_new_track: } audioBuffer->frameCount = framesReq; - audioBuffer->size = framesReq * cblk->frameSize; - audioBuffer->raw = cblk->buffer(mBuffers, u); + audioBuffer->size = framesReq * mFrameSizeAF; + audioBuffer->raw = cblk->buffer(mBuffers, mFrameSizeAF, u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } @@ -1302,7 +1302,7 @@ bool AudioTrack::processAudioBuffer(const sp& thread) // NOTE: cblk->frameSize is not equal to AudioTrack::frameSize() for // 8 bit PCM data: in this case, cblk->frameSize is based on a sample size of // 16 bit. - audioBuffer.frameCount = writtenSize/cblk->frameSize; + audioBuffer.frameCount = writtenSize / mFrameSizeAF; frames -= audioBuffer.frameCount; @@ -1373,7 +1373,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart if (user > server) { frames = ((user - server) > newCblk->frameCount) ? newCblk->frameCount : (user - server); - memset(mBuffers, 0, frames * newCblk->frameSize); + memset(mBuffers, 0, frames * mFrameSizeAF); } // restart playback even if buffer is not completely filled. android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); @@ -1588,7 +1588,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount, bool isOut) return true; } -void* audio_track_cblk_t::buffer(void *buffers, uint32_t offset) const +void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offset) const { return (int8_t *)buffers + (offset - userBase) * frameSize; } -- cgit v1.1 From f4fca226d2cb08862d0faa4918e181b3e73f6a0c Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 7 Nov 2012 15:36:59 -0800 Subject: Scan .awb files too b/6122599 Change-Id: Ied3e0392939231447f1fc5685ca1fade1e55ce08 --- media/libstagefright/StagefrightMediaScanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index b7cf96e..359f2be 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -42,7 +42,7 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf", - ".avi", ".mpeg", ".mpg" + ".avi", ".mpeg", ".mpg", ".awb" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); -- cgit v1.1 From b36a7a68af073b1e7fd5cad6aa2c52223fd30efd Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 12 Nov 2012 15:46:10 -0800 Subject: Simplify AudioRecord::restoreTrack_l() Finish removing CBLK_RESTORING and CBLK_RESTORED from control block flags, and remove constant RESTORE_TIMEOUT_MS. Also minor cleanup: - Cache mCblk in local variable cblk and make cblk allocatable in a register. - Use "iMem" for sp. - Add missing error log to AudioRecord; it was already in AudioTrack. This is part of a series to clean up the control block. Change-Id: Ia5f5ab4763c392bc06a45851b167ddaee29e3455 --- media/libmedia/AudioRecord.cpp | 93 ++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 49 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index b40aaf5..062f546 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -300,7 +300,9 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) } } if (cblk->flags & CBLK_INVALID) { - ret = restoreRecord_l(cblk); + audio_track_cblk_t* temp = cblk; + ret = restoreRecord_l(temp); + cblk = temp; } cblk->lock.unlock(); if (ret == NO_ERROR) { @@ -431,6 +433,7 @@ status_t AudioRecord::openRecord_l( status_t status; const sp& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { + ALOGE("Could not get audioflinger"); return NO_INIT; } @@ -453,19 +456,20 @@ status_t AudioRecord::openRecord_l( ALOGE("AudioFlinger could not create record track, status: %d", status); return status; } - sp cblk = record->getCblk(); - if (cblk == 0) { + sp iMem = record->getCblk(); + if (iMem == 0) { ALOGE("Could not get control block"); return NO_INIT; } mAudioRecord.clear(); mAudioRecord = record; mCblkMemory.clear(); - mCblkMemory = cblk; - mCblk = static_cast(cblk->pointer()); - mBuffers = (char*)mCblk + sizeof(audio_track_cblk_t); - mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - mCblk->waitTimeMs = 0; + mCblkMemory = iMem; + audio_track_cblk_t* cblk = static_cast(iMem->pointer()); + mCblk = cblk; + mBuffers = (char*)cblk + sizeof(audio_track_cblk_t); + cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + cblk->waitTimeMs = 0; return NO_ERROR; } @@ -498,12 +502,17 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) } if (!(cblk->flags & CBLK_INVALID)) { mLock.unlock(); + // this condition is in shared memory, so if IAudioRecord and control block + // are replaced due to mediaserver death or IAudioRecord invalidation then + // cv won't be signalled, but fortunately the timeout will limit the wait result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); cblk->lock.unlock(); mLock.lock(); if (!mActive) { return status_t(STOPPED); } + // IAudioRecord may have been re-created while mLock was unlocked + cblk = mCblk; cblk->lock.lock(); } if (cblk->flags & CBLK_INVALID) { @@ -521,7 +530,9 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) if (result == DEAD_OBJECT) { android_atomic_or(CBLK_INVALID, &cblk->flags); create_new_record: - result = AudioRecord::restoreRecord_l(cblk); + audio_track_cblk_t* temp = cblk; + result = AudioRecord::restoreRecord_l(temp); + cblk = temp; } if (result != NO_ERROR) { ALOGW("obtainBuffer create Track error %d", result); @@ -749,57 +760,41 @@ bool AudioRecord::processAudioBuffer(const sp& thread) // must be called with mLock and cblk.lock held. Callers must also hold strong references on // the IAudioRecord and IMemory in case they are recreated here. // If the IAudioRecord is successfully restored, the cblk pointer is updated -status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) +status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk) { status_t result; - if (!(android_atomic_or(CBLK_RESTORING, &cblk->flags) & CBLK_RESTORING)) { - ALOGW("dead IAudioRecord, creating a new one"); - // signal old cblk condition so that other threads waiting for available buffers stop - // waiting now - cblk->cv.broadcast(); - cblk->lock.unlock(); + audio_track_cblk_t* cblk = refCblk; + audio_track_cblk_t* newCblk = cblk; + ALOGW("dead IAudioRecord, creating a new one"); - // if the new IAudioRecord is created, openRecord_l() will modify the - // following member variables: mAudioRecord, mCblkMemory and mCblk. - // It will also delete the strong references on previous IAudioRecord and IMemory - result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask, - mFrameCount, getInput_l()); - if (result == NO_ERROR) { - // callback thread or sync event hasn't changed - result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0); - } - if (result != NO_ERROR) { - mActive = false; - } + // signal old cblk condition so that other threads waiting for available buffers stop + // waiting now + cblk->cv.broadcast(); + cblk->lock.unlock(); - // signal old cblk condition for other threads waiting for restore completion - android_atomic_or(CBLK_RESTORED, &cblk->flags); - cblk->cv.broadcast(); - } else { - if (!(cblk->flags & CBLK_RESTORED)) { - ALOGW("dead IAudioRecord, waiting for a new one to be created"); - mLock.unlock(); - result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS)); - cblk->lock.unlock(); - mLock.lock(); - } else { - ALOGW("dead IAudioRecord, already restored"); - result = NO_ERROR; - cblk->lock.unlock(); - } - if (result != NO_ERROR || !mActive) { - result = status_t(STOPPED); - } + // if the new IAudioRecord is created, openRecord_l() will modify the + // following member variables: mAudioRecord, mCblkMemory and mCblk. + // It will also delete the strong references on previous IAudioRecord and IMemory + result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask, + mFrameCount, getInput_l()); + if (result == NO_ERROR) { + newCblk = mCblk; + // callback thread or sync event hasn't changed + result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0); + } + if (result != NO_ERROR) { + mActive = false; } + ALOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", - result, mActive, mCblk, cblk, mCblk->flags, cblk->flags); + result, mActive, newCblk, cblk, newCblk->flags, cblk->flags); if (result == NO_ERROR) { // from now on we switch to the newly created cblk - cblk = mCblk; + refCblk = newCblk; } - cblk->lock.lock(); + newCblk->lock.lock(); ALOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result); -- cgit v1.1 From a552d6049ccf674b083d011ce7b8a443a9cd68a4 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 13 Nov 2012 15:01:05 -0800 Subject: Remove deprecated AudioSystem methods Change-Id: I952d504e03af9a1d3e1e0aa379c82dfb00197d9f --- media/libmedia/AudioSystem.cpp | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 767c452..488edac 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -205,11 +205,6 @@ int AudioSystem::logToLinear(float volume) return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; } -// DEPRECATED -status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType) { - return getOutputSamplingRate(samplingRate, (audio_stream_type_t)streamType); -} - status_t AudioSystem::getOutputSamplingRate(int* samplingRate, audio_stream_type_t streamType) { audio_io_handle_t output; @@ -252,11 +247,6 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t output, return NO_ERROR; } -// DEPRECATED -status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) { - return getOutputFrameCount(frameCount, (audio_stream_type_t)streamType); -} - status_t AudioSystem::getOutputFrameCount(int* frameCount, audio_stream_type_t streamType) { audio_io_handle_t output; -- cgit v1.1 From b26e3e9f2ab0334bff21a4fa4851dbf6e57fba5d Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 14 Nov 2012 08:32:08 -0800 Subject: Fix build warnings Change-Id: Ic43bcca166a529a6431711b05a7fa21849b6a38b --- media/libmedia/IAudioFlinger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index bb936ec..55658db 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -106,7 +106,7 @@ public: data.writeInt32(format); data.writeInt32(channelMask); data.writeInt32(frameCount); - track_flags_t lFlags = flags != NULL ? *flags : TRACK_DEFAULT; + track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT; data.writeInt32(lFlags); data.writeStrongBinder(sharedBuffer->asBinder()); data.writeInt32((int32_t) output); -- cgit v1.1 From 22eb4e239fbe9103568147d566d7482e480350b8 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 7 Nov 2012 14:03:00 -0800 Subject: Update audio comments Change-Id: I85d7d2f6381b251db5695202fec75128883a8662 --- media/libmedia/AudioTrack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 4a4759e..daf6d07 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -390,7 +390,7 @@ void AudioTrack::start() } if (cblk->flags & CBLK_INVALID) { audio_track_cblk_t* temp = cblk; - status = restoreTrack_l(temp, true); + status = restoreTrack_l(temp, true /*fromStart*/); cblk = temp; } cblk->lock.unlock(); @@ -988,7 +988,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) android_atomic_or(CBLK_INVALID, &cblk->flags); create_new_track: audio_track_cblk_t* temp = cblk; - result = restoreTrack_l(temp, false); + result = restoreTrack_l(temp, false /*fromStart*/); cblk = temp; } if (result != NO_ERROR) { @@ -1147,7 +1147,7 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) if (cblk->flags & CBLK_INVALID) { cblk->lock.lock(); audio_track_cblk_t* temp = cblk; - result = restoreTrack_l(temp, false); + result = restoreTrack_l(temp, false /*fromStart*/); cblk = temp; cblk->lock.unlock(); -- cgit v1.1 From 3b16c766d1ae2cfd8487e8ffb2b23936fc0a8e17 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 14 Nov 2012 08:44:39 -0800 Subject: Use uint32_t for sample rate Change-Id: Ie240b48fb54b08359f69ecd4e5f8bda3d15cbe80 --- media/libmedia/AudioRecord.cpp | 4 ++-- media/libmedia/AudioSystem.cpp | 12 ++++++------ media/libmedia/AudioTrack.cpp | 18 +++++++++--------- media/libmedia/IAudioFlinger.cpp | 2 +- media/libmedia/SoundPool.cpp | 2 +- media/libmediaplayerservice/MediaPlayerService.cpp | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 062f546..8f45a57 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -54,7 +54,7 @@ status_t AudioRecord::getMinFrameCount( } if (size == 0) { - ALOGE("Unsupported configuration: sampleRate %d, format %d, channelMask %#x", + ALOGE("Unsupported configuration: sampleRate %u, format %d, channelMask %#x", sampleRate, format, channelMask); return BAD_VALUE; } @@ -127,7 +127,7 @@ status_t AudioRecord::set( int sessionId) { - ALOGV("set(): sampleRate %d, channelMask %#x, frameCount %d",sampleRate, channelMask, + ALOGV("set(): sampleRate %u, channelMask %#x, frameCount %d", sampleRate, channelMask, frameCount); AutoMutex lock(mLock); diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 488edac..f3b74a2 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -205,7 +205,7 @@ int AudioSystem::logToLinear(float volume) return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; } -status_t AudioSystem::getOutputSamplingRate(int* samplingRate, audio_stream_type_t streamType) +status_t AudioSystem::getOutputSamplingRate(uint32_t* samplingRate, audio_stream_type_t streamType) { audio_io_handle_t output; @@ -223,7 +223,7 @@ status_t AudioSystem::getOutputSamplingRate(int* samplingRate, audio_stream_type status_t AudioSystem::getSamplingRate(audio_io_handle_t output, audio_stream_type_t streamType, - int* samplingRate) + uint32_t* samplingRate) { OutputDescriptor *outputDesc; @@ -241,7 +241,7 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t output, gLock.unlock(); } - ALOGV("getSamplingRate() streamType %d, output %d, sampling rate %d", streamType, output, + ALOGV("getSamplingRate() streamType %d, output %d, sampling rate %u", streamType, output, *samplingRate); return NO_ERROR; @@ -442,7 +442,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle OutputDescriptor *outputDesc = new OutputDescriptor(*desc); gOutputs.add(ioHandle, outputDesc); - ALOGV("ioConfigChanged() new output samplingRate %d, format %d channels %#x frameCount %d " + ALOGV("ioConfigChanged() new output samplingRate %u, format %d channels %#x frameCount %d " "latency %d", outputDesc->samplingRate, outputDesc->format, outputDesc->channels, outputDesc->frameCount, outputDesc->latency); @@ -466,7 +466,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle if (param2 == NULL) break; desc = (const OutputDescriptor *)param2; - ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %#x " + ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %d channels %#x " "frameCount %d latency %d", ioHandle, desc->samplingRate, desc->format, desc->channels, desc->frameCount, desc->latency); @@ -740,7 +740,7 @@ status_t AudioSystem::isSourceActive(audio_source_t stream, bool* state) return NO_ERROR; } -int32_t AudioSystem::getPrimaryOutputSamplingRate() +uint32_t AudioSystem::getPrimaryOutputSamplingRate() { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return 0; diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index daf6d07..7480807 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -65,7 +65,7 @@ status_t AudioTrack::getMinFrameCount( // audio_format_t format // audio_channel_mask_t channelMask // audio_output_flags_t flags - int afSampleRate; + uint32_t afSampleRate; if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { return NO_INIT; } @@ -193,7 +193,7 @@ status_t AudioTrack::set( } if (sampleRate == 0) { - int afSampleRate; + uint32_t afSampleRate; if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { return NO_INIT; } @@ -535,9 +535,9 @@ void AudioTrack::getAuxEffectSendLevel(float* level) const } } -status_t AudioTrack::setSampleRate(int rate) +status_t AudioTrack::setSampleRate(uint32_t rate) { - int afSamplingRate; + uint32_t afSamplingRate; if (mIsTimed) { return INVALID_OPERATION; @@ -547,7 +547,7 @@ status_t AudioTrack::setSampleRate(int rate) return NO_INIT; } // Resampler implementation limits input sampling rate to 2 x output sampling rate. - if (rate <= 0 || rate > afSamplingRate*2 ) return BAD_VALUE; + if (rate == 0 || rate > afSamplingRate*2 ) return BAD_VALUE; AutoMutex lock(mLock); mCblk->sampleRate = rate; @@ -557,7 +557,7 @@ status_t AudioTrack::setSampleRate(int rate) uint32_t AudioTrack::getSampleRate() const { if (mIsTimed) { - return INVALID_OPERATION; + return 0; } AutoMutex lock(mLock); @@ -802,7 +802,7 @@ status_t AudioTrack::createTrack_l( } else if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) { // FIXME move these calculations and associated checks to server - int afSampleRate; + uint32_t afSampleRate; if (AudioSystem::getSamplingRate(output, streamType, &afSampleRate) != NO_ERROR) { return NO_INIT; } @@ -816,7 +816,7 @@ status_t AudioTrack::createTrack_l( if (minBufCount < 2) minBufCount = 2; int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; - ALOGV("minFrameCount: %d, afFrameCount=%d, minBufCount=%d, sampleRate=%d, afSampleRate=%d" + ALOGV("minFrameCount: %d, afFrameCount=%d, minBufCount=%d, sampleRate=%u, afSampleRate=%u" ", afLatency=%d", minFrameCount, afFrameCount, minBufCount, sampleRate, afSampleRate, afLatency); @@ -1423,7 +1423,7 @@ status_t AudioTrack::dump(int fd, const Vector& args) const snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, cblk->frameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", + snprintf(buffer, 255, " sample rate(%u), status(%d), muted(%d)\n", (cblk == 0) ? 0 : cblk->sampleRate, mStatus, mMuted); result.append(buffer); snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 55658db..0eeb6d9 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -695,7 +695,7 @@ public: return (audio_module_handle_t) reply.readInt32(); } - virtual int32_t getPrimaryOutputSamplingRate() + virtual uint32_t getPrimaryOutputSamplingRate() { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index abc8899..b321e92 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -569,7 +569,7 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV // initialize track int afFrameCount; - int afSampleRate; + uint32_t afSampleRate; audio_stream_type_t streamType = mSoundPool->streamType(); if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { afFrameCount = kDefaultFrameCount; diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 9bedff1..769b322 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1387,7 +1387,7 @@ status_t MediaPlayerService::AudioOutput::open( } ALOGV("open(%u, %d, 0x%x, %d, %d, %d)", sampleRate, channelCount, channelMask, format, bufferCount, mSessionId); - int afSampleRate; + uint32_t afSampleRate; int afFrameCount; uint32_t frameCount; -- cgit v1.1 From 60a839204713e0f8258d082af83262b1eb33a6c3 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 21 Jun 2012 12:56:37 -0700 Subject: Clean up frame size in AudioTrack and AudioFlinger TrackBase::mFrameSize, mChannelMask, and mChannelCount are now const. Use TrackBase::mFrameSize instead of re-calculating frame size. AudioFlinger only sees 16-bit PCM format, conversion from 8-bit is now entirely on the client side. Previously a small part of the responsibility was on server side also. size_t is unsigned, so use %u in logs. Fix theoretical bug where TrackBase constructor was over-allocating space for non-linear AudioTrack or 8-bit PCM AudioRecord (probably benign). Change-Id: I7cbbba0bf4dba29ea751d8af341ab8e5cbbdc206 --- media/libmedia/AudioTrack.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 7480807..5fb36ee 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -858,7 +858,9 @@ status_t AudioTrack::createTrack_l( sp track = audioFlinger->createTrack(getpid(), streamType, sampleRate, - format, + // AudioFlinger only sees 16-bit PCM + format == AUDIO_FORMAT_PCM_8_BIT ? + AUDIO_FORMAT_PCM_16_BIT : format, channelMask, frameCount, &trackFlags, -- cgit v1.1 From 5ce181568da90c78ba7fad3e084c8479041545df Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 14 Nov 2012 15:24:53 -0800 Subject: The length information of the chunks making up vorbis codec specific info are "Xiph-style-lacing encoded" instead of individual bytes. Change-Id: Ic1274a5bd8f082197bae6831da04002762a920c5 related-to-bug: 7401329 --- media/libstagefright/codecs/on2/dec/SoftVPX.cpp | 2 +- .../libstagefright/matroska/MatroskaExtractor.cpp | 74 ++++++++++++++++++---- 2 files changed, 61 insertions(+), 15 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index bf9ab3a..a400b4c 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -66,7 +66,7 @@ void SoftVPX::initPorts() { def.eDir = OMX_DirInput; def.nBufferCountMin = kNumBuffers; def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = 256 * 1024; + def.nBufferSize = 768 * 1024; def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainVideo; diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 8f7d12b..7fc7037 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -758,31 +758,69 @@ static void addESDSFromCodecPrivate( esds = NULL; } -void addVorbisCodecInfo( +status_t addVorbisCodecInfo( const sp &meta, const void *_codecPrivate, size_t codecPrivateSize) { - // printf("vorbis private data follows:\n"); // hexdump(_codecPrivate, codecPrivateSize); - CHECK(codecPrivateSize >= 3); + if (codecPrivateSize < 1) { + return ERROR_MALFORMED; + } const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate; - CHECK(codecPrivate[0] == 0x02); - size_t len1 = codecPrivate[1]; - size_t len2 = codecPrivate[2]; + if (codecPrivate[0] != 0x02) { + return ERROR_MALFORMED; + } - CHECK(codecPrivateSize > 3 + len1 + len2); + // codecInfo starts with two lengths, len1 and len2, that are + // "Xiph-style-lacing encoded"... - CHECK(codecPrivate[3] == 0x01); - meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1); + size_t offset = 1; + size_t len1 = 0; + while (offset < codecPrivateSize && codecPrivate[offset] == 0xff) { + len1 += 0xff; + ++offset; + } + if (offset >= codecPrivateSize) { + return ERROR_MALFORMED; + } + len1 += codecPrivate[offset++]; - CHECK(codecPrivate[len1 + 3] == 0x03); + size_t len2 = 0; + while (offset < codecPrivateSize && codecPrivate[offset] == 0xff) { + len2 += 0xff; + ++offset; + } + if (offset >= codecPrivateSize) { + return ERROR_MALFORMED; + } + len2 += codecPrivate[offset++]; + + if (codecPrivateSize < offset + len1 + len2) { + return ERROR_MALFORMED; + } + + if (codecPrivate[offset] != 0x01) { + return ERROR_MALFORMED; + } + meta->setData(kKeyVorbisInfo, 0, &codecPrivate[offset], len1); + + offset += len1; + if (codecPrivate[offset] != 0x03) { + return ERROR_MALFORMED; + } + + offset += len2; + if (codecPrivate[offset] != 0x05) { + return ERROR_MALFORMED; + } - CHECK(codecPrivate[len1 + len2 + 3] == 0x05); meta->setData( - kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3], - codecPrivateSize - len1 - len2 - 3); + kKeyVorbisBooks, 0, &codecPrivate[offset], + codecPrivateSize - offset); + + return OK; } void MatroskaExtractor::addTracks() { @@ -809,6 +847,8 @@ void MatroskaExtractor::addTracks() { sp meta = new MetaData; + status_t err = OK; + switch (track->GetType()) { case VIDEO_TRACK: { @@ -855,7 +895,8 @@ void MatroskaExtractor::addTracks() { } else if (!strcmp("A_VORBIS", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); - addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize); + err = addVorbisCodecInfo( + meta, codecPrivate, codecPrivateSize); } else if (!strcmp("A_MPEG/L3", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); } else { @@ -872,6 +913,11 @@ void MatroskaExtractor::addTracks() { continue; } + if (err != OK) { + ALOGE("skipping track, codec specific data was malformed."); + continue; + } + long long durationNs = mSegment->GetDuration(); meta->setInt64(kKeyDuration, (durationNs + 500) / 1000); -- cgit v1.1 From 22d00b70516f108c3351a29c95d8ba639a8ed520 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 15 Nov 2012 11:16:30 -0800 Subject: wfd sink update. Change-Id: Ib4e41ec1524d045699543536acdddc9a243db741 --- media/libstagefright/wifi-display/sink/TunnelRenderer.cpp | 5 ++++- media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index bc35aef..b913124 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -271,6 +271,7 @@ sp TunnelRenderer::dequeueBuffer() { if (mFirstFailedAttemptUs + 50000ll > ALooper::GetNowUs()) { // We're willing to wait a little while to get the right packet. +#if 0 if (!mRequestedRetransmission) { ALOGI("requesting retransmission of seqNo %d", (mLastDequeuedExtSeqNo + 1) & 0xffff); @@ -280,7 +281,9 @@ sp TunnelRenderer::dequeueBuffer() { notify->post(); mRequestedRetransmission = true; - } else { + } else +#endif + { ALOGI("still waiting for the correct packet to arrive."); } diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index fcd20d4..c3e0470 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -475,9 +475,10 @@ void WifiDisplaySink::onGetParameterRequest( int32_t cseq, const sp &data) { AString body = - "wfd_video_formats: xxx\r\n" - "wfd_audio_codecs: xxx\r\n" - "wfd_client_rtp_ports: RTP/AVP/UDP;unicast xxx 0 mode=play\r\n"; + "wfd_video_formats: " + "28 00 02 02 FFFFFFFF 0000000 00000000 00 0000 0000 00 none none\r\n" + "wfd_audio_codecs: AAC 0000000F 00\r\n" + "wfd_client_rtp_ports: RTP/AVP/UDP;unicast 19000 0 mode=play\r\n"; AString response = "RTSP/1.0 200 OK\r\n"; AppendCommonResponse(&response, cseq); -- cgit v1.1 From e33054eb968cbf8ccaee1b0ff0301403902deed6 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 14 Nov 2012 12:54:39 -0800 Subject: Use size_t for frame counts Also fix typo: bufferCount should be frameCount. Change-Id: Ibed539504db75ef99dc21c8ff1bf2987122063a5 --- media/libmedia/AudioRecord.cpp | 18 +++++++++----- media/libmedia/AudioSystem.cpp | 12 +++++----- media/libmedia/AudioTrack.cpp | 28 +++++++++++++--------- media/libmedia/IAudioFlinger.cpp | 20 ++++++++-------- media/libmedia/SoundPool.cpp | 2 +- media/libmediaplayerservice/MediaPlayerService.cpp | 2 +- media/libstagefright/AudioSource.cpp | 2 +- 7 files changed, 48 insertions(+), 36 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 8f45a57..0587651 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -36,7 +36,7 @@ namespace android { // static status_t AudioRecord::getMinFrameCount( - int* frameCount, + size_t* frameCount, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask) @@ -119,15 +119,21 @@ status_t AudioRecord::set( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - int frameCount, + int frameCountInt, callback_t cbf, void* user, int notificationFrames, bool threadCanCallJava, int sessionId) { + // FIXME "int" here is legacy and will be replaced by size_t later + if (frameCountInt < 0) { + ALOGE("Invalid frame count %d", frameCountInt); + return BAD_VALUE; + } + size_t frameCount = frameCountInt; - ALOGV("set(): sampleRate %u, channelMask %#x, frameCount %d", sampleRate, channelMask, + ALOGV("set(): sampleRate %u, channelMask %#x, frameCount %u", sampleRate, channelMask, frameCount); AutoMutex lock(mLock); @@ -177,7 +183,7 @@ status_t AudioRecord::set( } // validate framecount - int minFrameCount = 0; + size_t minFrameCount = 0; status_t status = getMinFrameCount(&minFrameCount, sampleRate, format, channelMask); if (status != NO_ERROR) { return status; @@ -260,7 +266,7 @@ int AudioRecord::channelCount() const return mChannelCount; } -uint32_t AudioRecord::frameCount() const +size_t AudioRecord::frameCount() const { return mFrameCount; } @@ -427,7 +433,7 @@ status_t AudioRecord::openRecord_l( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - int frameCount, + size_t frameCount, audio_io_handle_t input) { status_t status; diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index f3b74a2..028e4a3 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -247,7 +247,7 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t output, return NO_ERROR; } -status_t AudioSystem::getOutputFrameCount(int* frameCount, audio_stream_type_t streamType) +status_t AudioSystem::getOutputFrameCount(size_t* frameCount, audio_stream_type_t streamType) { audio_io_handle_t output; @@ -265,7 +265,7 @@ status_t AudioSystem::getOutputFrameCount(int* frameCount, audio_stream_type_t s status_t AudioSystem::getFrameCount(audio_io_handle_t output, audio_stream_type_t streamType, - int* frameCount) + size_t* frameCount) { OutputDescriptor *outputDesc; @@ -361,7 +361,7 @@ status_t AudioSystem::setVoiceVolume(float value) return af->setVoiceVolume(value); } -status_t AudioSystem::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, +status_t AudioSystem::getRenderPosition(size_t *halFrames, size_t *dspFrames, audio_stream_type_t stream) { const sp& af = AudioSystem::get_audio_flinger(); @@ -374,7 +374,7 @@ status_t AudioSystem::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames return af->getRenderPosition(halFrames, dspFrames, getOutput(stream)); } -unsigned int AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) { +size_t AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) { const sp& af = AudioSystem::get_audio_flinger(); unsigned int result = 0; if (af == 0) return result; @@ -442,7 +442,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle OutputDescriptor *outputDesc = new OutputDescriptor(*desc); gOutputs.add(ioHandle, outputDesc); - ALOGV("ioConfigChanged() new output samplingRate %u, format %d channels %#x frameCount %d " + ALOGV("ioConfigChanged() new output samplingRate %u, format %d channels %#x frameCount %u " "latency %d", outputDesc->samplingRate, outputDesc->format, outputDesc->channels, outputDesc->frameCount, outputDesc->latency); @@ -747,7 +747,7 @@ uint32_t AudioSystem::getPrimaryOutputSamplingRate() return af->getPrimaryOutputSamplingRate(); } -int32_t AudioSystem::getPrimaryOutputFrameCount() +size_t AudioSystem::getPrimaryOutputFrameCount() { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return 0; diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 5fb36ee..979ee37 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -50,7 +50,7 @@ namespace android { // static status_t AudioTrack::getMinFrameCount( - int* frameCount, + size_t* frameCount, audio_stream_type_t streamType, uint32_t sampleRate) { @@ -69,7 +69,7 @@ status_t AudioTrack::getMinFrameCount( if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { return NO_INIT; } - int afFrameCount; + size_t afFrameCount; if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { return NO_INIT; } @@ -166,7 +166,7 @@ status_t AudioTrack::set( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - int frameCount, + int frameCountInt, audio_output_flags_t flags, callback_t cbf, void* user, @@ -175,11 +175,17 @@ status_t AudioTrack::set( bool threadCanCallJava, int sessionId) { + // FIXME "int" here is legacy and will be replaced by size_t later + if (frameCountInt < 0) { + ALOGE("Invalid frame count %d", frameCountInt); + return BAD_VALUE; + } + size_t frameCount = frameCountInt; ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); - ALOGV("set() streamType %d frameCount %d flags %04x", streamType, frameCount, flags); + ALOGV("set() streamType %d frameCount %u flags %04x", streamType, frameCount, flags); AutoMutex lock(mLock); if (mAudioTrack != 0) { @@ -336,7 +342,7 @@ int AudioTrack::channelCount() const return mChannelCount; } -uint32_t AudioTrack::frameCount() const +size_t AudioTrack::frameCount() const { return mCblk->frameCount; } @@ -730,7 +736,7 @@ status_t AudioTrack::createTrack_l( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - int frameCount, + size_t frameCount, audio_output_flags_t flags, const sp& sharedBuffer, audio_io_handle_t output) @@ -770,7 +776,7 @@ status_t AudioTrack::createTrack_l( // Same comment as below about ignoring frameCount parameter for set() frameCount = sharedBuffer->size(); } else if (frameCount == 0) { - int afFrameCount; + size_t afFrameCount; if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) { return NO_INIT; } @@ -806,7 +812,7 @@ status_t AudioTrack::createTrack_l( if (AudioSystem::getSamplingRate(output, streamType, &afSampleRate) != NO_ERROR) { return NO_INIT; } - int afFrameCount; + size_t afFrameCount; if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) { return NO_INIT; } @@ -815,8 +821,8 @@ status_t AudioTrack::createTrack_l( uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); if (minBufCount < 2) minBufCount = 2; - int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; - ALOGV("minFrameCount: %d, afFrameCount=%d, minBufCount=%d, sampleRate=%u, afSampleRate=%u" + size_t minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; + ALOGV("minFrameCount: %u, afFrameCount=%d, minBufCount=%d, sampleRate=%u, afSampleRate=%u" ", afLatency=%d", minFrameCount, afFrameCount, minBufCount, sampleRate, afSampleRate, afLatency); @@ -828,7 +834,7 @@ status_t AudioTrack::createTrack_l( } // Make sure that application is notified with sufficient margin // before underrun - if (mNotificationFramesAct > (uint32_t)frameCount/2) { + if (mNotificationFramesAct > frameCount/2) { mNotificationFramesAct = frameCount/2; } if (frameCount < minFrameCount) { diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 0eeb6d9..79c3361 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -89,7 +89,7 @@ public: uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - int frameCount, + size_t frameCount, track_flags_t *flags, const sp& sharedBuffer, audio_io_handle_t output, @@ -143,7 +143,7 @@ public: uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - int frameCount, + size_t frameCount, track_flags_t flags, pid_t tid, int *sessionId, @@ -527,7 +527,7 @@ public: return status; } - virtual unsigned int getInputFramesLost(audio_io_handle_t ioHandle) const + virtual size_t getInputFramesLost(audio_io_handle_t ioHandle) const { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); @@ -703,7 +703,7 @@ public: return reply.readInt32(); } - virtual int32_t getPrimaryOutputFrameCount() + virtual size_t getPrimaryOutputFrameCount() { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); @@ -728,7 +728,7 @@ status_t BnAudioFlinger::onTransact( uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); - size_t bufferCount = data.readInt32(); + size_t frameCount = data.readInt32(); track_flags_t flags = (track_flags_t) data.readInt32(); sp buffer = interface_cast(data.readStrongBinder()); audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); @@ -737,7 +737,7 @@ status_t BnAudioFlinger::onTransact( status_t status; sp track = createTrack(pid, (audio_stream_type_t) streamType, sampleRate, format, - channelMask, bufferCount, &flags, buffer, output, tid, &sessionId, &status); + channelMask, frameCount, &flags, buffer, output, tid, &sessionId, &status); reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeInt32(status); @@ -751,13 +751,13 @@ status_t BnAudioFlinger::onTransact( uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); - size_t bufferCount = data.readInt32(); + size_t frameCount = data.readInt32(); track_flags_t flags = (track_flags_t) data.readInt32(); pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); status_t status; sp record = openRecord(pid, input, - sampleRate, format, channelMask, bufferCount, flags, tid, &sessionId, &status); + sampleRate, format, channelMask, frameCount, flags, tid, &sessionId, &status); reply->writeInt32(sessionId); reply->writeInt32(status); reply->writeStrongBinder(record->asBinder()); @@ -972,8 +972,8 @@ status_t BnAudioFlinger::onTransact( case GET_RENDER_POSITION: { CHECK_INTERFACE(IAudioFlinger, data, reply); audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); - uint32_t halFrames; - uint32_t dspFrames; + size_t halFrames; + size_t dspFrames; status_t status = getRenderPosition(&halFrames, &dspFrames, output); reply->writeInt32(status); if (status == NO_ERROR) { diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index b321e92..204e0ce 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -568,7 +568,7 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV } // initialize track - int afFrameCount; + size_t afFrameCount; uint32_t afSampleRate; audio_stream_type_t streamType = mSoundPool->streamType(); if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 769b322..c3e5c40 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1388,7 +1388,7 @@ status_t MediaPlayerService::AudioOutput::open( ALOGV("open(%u, %d, 0x%x, %d, %d, %d)", sampleRate, channelCount, channelMask, format, bufferCount, mSessionId); uint32_t afSampleRate; - int afFrameCount; + size_t afFrameCount; uint32_t frameCount; if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) { diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 861aebe..3cf4d5c 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -58,7 +58,7 @@ AudioSource::AudioSource( ALOGV("sampleRate: %d, channelCount: %d", sampleRate, channelCount); CHECK(channelCount == 1 || channelCount == 2); - int minFrameCount; + size_t minFrameCount; status_t status = AudioRecord::getMinFrameCount(&minFrameCount, sampleRate, AUDIO_FORMAT_PCM_16_BIT, -- cgit v1.1 From ba933df89521d63f75ca66af12ce9d7ae9496b9e Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 15 Nov 2012 14:31:56 -0800 Subject: Add GSM 6.10 decoder Supports Microsoft frame packing only, since that's what the sample file used. b/6620569 Change-Id: Ia89d95bcbf0f8dcbaad42148a7401728f60e079d --- media/libstagefright/ACodec.cpp | 2 + media/libstagefright/MediaDefs.cpp | 1 + media/libstagefright/OMXCodec.cpp | 2 + media/libstagefright/WAVExtractor.cpp | 61 ++++- media/libstagefright/codecs/gsm/Android.mk | 4 + media/libstagefright/codecs/gsm/dec/Android.mk | 21 ++ .../codecs/gsm/dec/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/gsm/dec/NOTICE | 190 +++++++++++++++ media/libstagefright/codecs/gsm/dec/SoftGSM.cpp | 269 +++++++++++++++++++++ media/libstagefright/codecs/gsm/dec/SoftGSM.h | 65 +++++ media/libstagefright/omx/SoftOMXPlugin.cpp | 1 + 11 files changed, 605 insertions(+), 11 deletions(-) create mode 100644 media/libstagefright/codecs/gsm/Android.mk create mode 100644 media/libstagefright/codecs/gsm/dec/Android.mk create mode 100644 media/libstagefright/codecs/gsm/dec/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/gsm/dec/NOTICE create mode 100644 media/libstagefright/codecs/gsm/dec/SoftGSM.cpp create mode 100644 media/libstagefright/codecs/gsm/dec/SoftGSM.h (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 84b4962..a135222 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -803,6 +803,8 @@ status_t ACodec::setComponentRole( "audio_decoder.raw", "audio_encoder.raw" }, { MEDIA_MIMETYPE_AUDIO_FLAC, "audio_decoder.flac", "audio_encoder.flac" }, + { MEDIA_MIMETYPE_AUDIO_MSGSM, + "audio_decoder.gsm", "audio_encoder.gsm" }, }; static const size_t kNumMimeToRole = diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index e7b5903..5d8029c 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -40,6 +40,7 @@ const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw"; const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw"; const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac"; const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts"; +const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav"; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 70de174..22aefcc 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1390,6 +1390,8 @@ void OMXCodec::setComponentRole( "audio_decoder.raw", "audio_encoder.raw" }, { MEDIA_MIMETYPE_AUDIO_FLAC, "audio_decoder.flac", "audio_encoder.flac" }, + { MEDIA_MIMETYPE_AUDIO_MSGSM, + "audio_decoder.gsm", "audio_encoder.gsm" }, }; static const size_t kNumMimeToRole = diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index a38400b..d32f4fb 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -38,6 +38,7 @@ enum { WAVE_FORMAT_PCM = 0x0001, WAVE_FORMAT_ALAW = 0x0006, WAVE_FORMAT_MULAW = 0x0007, + WAVE_FORMAT_MSGSM = 0x0031, WAVE_FORMAT_EXTENSIBLE = 0xFFFE }; @@ -178,6 +179,7 @@ status_t WAVExtractor::init() { if (mWaveFormat != WAVE_FORMAT_PCM && mWaveFormat != WAVE_FORMAT_ALAW && mWaveFormat != WAVE_FORMAT_MULAW + && mWaveFormat != WAVE_FORMAT_MSGSM && mWaveFormat != WAVE_FORMAT_EXTENSIBLE) { return ERROR_UNSUPPORTED; } @@ -216,6 +218,10 @@ status_t WAVExtractor::init() { && mBitsPerSample != 24) { return ERROR_UNSUPPORTED; } + } else if (mWaveFormat == WAVE_FORMAT_MSGSM) { + if (mBitsPerSample != 0) { + return ERROR_UNSUPPORTED; + } } else { CHECK(mWaveFormat == WAVE_FORMAT_MULAW || mWaveFormat == WAVE_FORMAT_ALAW); @@ -283,6 +289,10 @@ status_t WAVExtractor::init() { mTrackMeta->setCString( kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW); break; + case WAVE_FORMAT_MSGSM: + mTrackMeta->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MSGSM); + break; default: CHECK_EQ(mWaveFormat, (uint16_t)WAVE_FORMAT_MULAW); mTrackMeta->setCString( @@ -294,11 +304,17 @@ status_t WAVExtractor::init() { mTrackMeta->setInt32(kKeyChannelMask, mChannelMask); mTrackMeta->setInt32(kKeySampleRate, mSampleRate); - size_t bytesPerSample = mBitsPerSample >> 3; - - int64_t durationUs = - 1000000LL * (mDataSize / (mNumChannels * bytesPerSample)) - / mSampleRate; + int64_t durationUs = 0; + if (mWaveFormat == WAVE_FORMAT_MSGSM) { + // 65 bytes decode to 320 8kHz samples + durationUs = + 1000000LL * (mDataSize / 65 * 320) / 8000; + } else { + size_t bytesPerSample = mBitsPerSample >> 3; + durationUs = + 1000000LL * (mDataSize / (mNumChannels * bytesPerSample)) + / mSampleRate; + } mTrackMeta->setInt64(kKeyDuration, durationUs); @@ -388,7 +404,16 @@ status_t WAVSource::read( int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { - int64_t pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * (mBitsPerSample >> 3); + int64_t pos = 0; + + if (mWaveFormat == WAVE_FORMAT_MSGSM) { + // 65 bytes decode to 320 8kHz samples + int64_t samplenumber = (seekTimeUs * mSampleRate) / 1000000; + int64_t framenumber = samplenumber / 320; + pos = framenumber * 65; + } else { + pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * (mBitsPerSample >> 3); + } if (pos > mSize) { pos = mSize; } @@ -412,6 +437,15 @@ status_t WAVSource::read( maxBytesToRead = maxBytesAvailable; } + if (mWaveFormat == WAVE_FORMAT_MSGSM) { + // Microsoft packs 2 frames into 65 bytes, rather than using separate 33-byte frames, + // so read multiples of 65, and use smaller buffers to account for ~10:1 expansion ratio + if (maxBytesToRead > 1024) { + maxBytesToRead = 1024; + } + maxBytesToRead = (maxBytesToRead / 65) * 65; + } + ssize_t n = mDataSource->readAt( mCurrentPos, buffer->data(), maxBytesToRead); @@ -468,12 +502,17 @@ status_t WAVSource::read( } } - size_t bytesPerSample = mBitsPerSample >> 3; + int64_t timeStampUs = 0; + + if (mWaveFormat == WAVE_FORMAT_MSGSM) { + timeStampUs = 1000000LL * (mCurrentPos - mOffset) * 320 / 65 / mSampleRate; + } else { + size_t bytesPerSample = mBitsPerSample >> 3; + timeStampUs = 1000000LL * (mCurrentPos - mOffset) + / (mNumChannels * bytesPerSample) / mSampleRate; + } - buffer->meta_data()->setInt64( - kKeyTime, - 1000000LL * (mCurrentPos - mOffset) - / (mNumChannels * bytesPerSample) / mSampleRate); + buffer->meta_data()->setInt64(kKeyTime, timeStampUs); buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); mCurrentPos += n; diff --git a/media/libstagefright/codecs/gsm/Android.mk b/media/libstagefright/codecs/gsm/Android.mk new file mode 100644 index 0000000..2e43120 --- /dev/null +++ b/media/libstagefright/codecs/gsm/Android.mk @@ -0,0 +1,4 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/codecs/gsm/dec/Android.mk b/media/libstagefright/codecs/gsm/dec/Android.mk new file mode 100644 index 0000000..9c0c6ae --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftGSM.cpp + +LOCAL_C_INCLUDES := \ + frameworks/av/media/libstagefright/include \ + frameworks/native/include/media/openmax \ + external/libgsm/inc + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_STATIC_LIBRARIES := \ + libgsm + +LOCAL_MODULE := libstagefright_soft_gsmdec +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/gsm/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/gsm/dec/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/gsm/dec/NOTICE b/media/libstagefright/codecs/gsm/dec/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp new file mode 100644 index 0000000..00e0c85 --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp @@ -0,0 +1,269 @@ +/* + * 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 "SoftGSM" +#include + +#include "SoftGSM.h" + +#include +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftGSM::SoftGSM( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mSignalledError(false) { + + CHECK(!strcmp(name, "OMX.google.gsm.decoder")); + + mGsm = gsm_create(); + CHECK(mGsm); + int msopt = 1; + gsm_option(mGsm, GSM_OPT_WAV49, &msopt); + + initPorts(); +} + +SoftGSM::~SoftGSM() { + gsm_destroy(mGsm); +} + +void SoftGSM::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = sizeof(gsm_frame); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast(MEDIA_MIMETYPE_AUDIO_MSGSM); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingGSMFR; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +OMX_ERRORTYPE SoftGSM::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 8000; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftGSM::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels != 1) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nSamplingRate != 8000) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.gsm", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftGSM::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) { + ALOGE("input buffer too large (%ld).", inHeader->nFilledLen); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + } + + if(((inHeader->nFilledLen / 65) * 65) != inHeader->nFilledLen) { + ALOGE("input buffer not multiple of 65 (%ld).", inHeader->nFilledLen); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + } + + uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset; + + int n = mSignalledError ? 0 : DecodeGSM(mGsm, + reinterpret_cast(outHeader->pBuffer), inputptr, inHeader->nFilledLen); + + outHeader->nTimeStamp = inHeader->nTimeStamp; + outHeader->nOffset = 0; + outHeader->nFilledLen = n * sizeof(int16_t); + outHeader->nFlags = 0; + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } +} + + +// static +int SoftGSM::DecodeGSM(gsm handle, + int16_t *out, uint8_t *in, size_t inSize) { + + int ret = 0; + while (inSize > 0) { + gsm_decode(handle, in, out); + in += 33; + inSize -= 33; + out += 160; + ret += 160; + gsm_decode(handle, in, out); + in += 32; + inSize -= 32; + out += 160; + ret += 160; + } + return ret; +} + + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftGSM(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.h b/media/libstagefright/codecs/gsm/dec/SoftGSM.h new file mode 100644 index 0000000..8ab6116 --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.h @@ -0,0 +1,65 @@ +/* + * 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 SOFT_GSM_H_ + +#define SOFT_GSM_H_ + +#include "SimpleSoftOMXComponent.h" + +extern "C" { +#include "gsm.h" +} + +namespace android { + +struct SoftGSM : public SimpleSoftOMXComponent { + SoftGSM(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftGSM(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerFrame = 16384, + }; + + bool mSignalledError; + gsm mGsm; + + void initPorts(); + + static int DecodeGSM(gsm handle, int16_t *out, uint8_t *in, size_t inSize); + + DISALLOW_EVIL_CONSTRUCTORS(SoftGSM); +}; + +} // namespace android + +#endif // SOFT_GSM_H_ + diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 3747b3b..6e1c04d 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -53,6 +53,7 @@ static const struct { { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" }, { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" }, { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" }, + { "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" }, }; static const size_t kNumComponents = -- cgit v1.1 From 26c77556efc30800466b60b3975bc35a70c8c28b Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 16 Nov 2012 12:01:44 -0800 Subject: Fix time vs. bytes units bug in getRenderPosition Rename correctLatency since it requires thread to be locked. Use size_t for byte and frame counts. Change-Id: I178fdd18bdb823813b9563927bdff8c0d28ca5a5 --- media/libmedia/IAudioFlinger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 79c3361..a010bb6 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -506,7 +506,7 @@ public: return reply.readInt32(); } - virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, + virtual status_t getRenderPosition(size_t *halFrames, size_t *dspFrames, audio_io_handle_t output) const { Parcel data, reply; -- cgit v1.1 From 827e5f1237757aee78b677efcf0f7c44fd0dd3d8 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 2 Nov 2012 10:00:06 -0700 Subject: Don't explicitly log tid If needed, it can be obtained with adb logcat -v threadtime Change-Id: I91b3911d20f7bcfc3361db4052db21ff9181f1cf --- media/libmedia/AudioTrack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 979ee37..907f7e6 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1336,8 +1336,8 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart audio_track_cblk_t* cblk = refCblk; audio_track_cblk_t* newCblk = cblk; - ALOGW("dead IAudioTrack, creating a new one from %s TID %d", - fromStart ? "start()" : "obtainBuffer()", gettid()); + ALOGW("dead IAudioTrack, creating a new one from %s", + fromStart ? "start()" : "obtainBuffer()"); // signal old cblk condition so that other threads waiting for available buffers stop // waiting now @@ -1411,7 +1411,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart } newCblk->lock.lock(); - ALOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d TID %d", result, gettid()); + ALOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result); return result; } -- cgit v1.1 From d65d73c4ae74d084751b417615a78cbe7a51372a Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 22 Jun 2012 17:21:07 -0700 Subject: "if" statements use curly braces per media style Change-Id: I130e7849fd1da7a0b7fe56c3c53919d26e3843b8 --- media/libmedia/AudioRecord.cpp | 6 ++-- media/libmedia/AudioTrack.cpp | 70 +++++++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 20 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0587651..dd0a145 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -643,10 +643,12 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS)); if (err < 0) { // out of buffers, return #bytes written - if (err == status_t(NO_MORE_BUFFERS)) + if (err == status_t(NO_MORE_BUFFERS)) { break; - if (err == status_t(TIMED_OUT)) + } + if (err == status_t(TIMED_OUT)) { err = 0; + } return ssize_t(err); } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 979ee37..3056b4c 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -54,7 +54,9 @@ status_t AudioTrack::getMinFrameCount( audio_stream_type_t streamType, uint32_t sampleRate) { - if (frameCount == NULL) return BAD_VALUE; + if (frameCount == NULL) { + return BAD_VALUE; + } // default to 0 in case of error *frameCount = 0; @@ -553,7 +555,9 @@ status_t AudioTrack::setSampleRate(uint32_t rate) return NO_INIT; } // Resampler implementation limits input sampling rate to 2 x output sampling rate. - if (rate == 0 || rate > afSamplingRate*2 ) return BAD_VALUE; + if (rate == 0 || rate > afSamplingRate*2 ) { + return BAD_VALUE; + } AutoMutex lock(mLock); mCblk->sampleRate = rate; @@ -620,7 +624,9 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou status_t AudioTrack::setMarkerPosition(uint32_t marker) { - if (mCbf == NULL) return INVALID_OPERATION; + if (mCbf == NULL) { + return INVALID_OPERATION; + } mMarkerPosition = marker; mMarkerReached = false; @@ -630,7 +636,9 @@ status_t AudioTrack::setMarkerPosition(uint32_t marker) status_t AudioTrack::getMarkerPosition(uint32_t *marker) const { - if (marker == NULL) return BAD_VALUE; + if (marker == NULL) { + return BAD_VALUE; + } *marker = mMarkerPosition; @@ -639,7 +647,9 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) { - if (mCbf == NULL) return INVALID_OPERATION; + if (mCbf == NULL) { + return INVALID_OPERATION; + } uint32_t curPosition; getPosition(&curPosition); @@ -651,7 +661,9 @@ status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const { - if (updatePeriod == NULL) return BAD_VALUE; + if (updatePeriod == NULL) { + return BAD_VALUE; + } *updatePeriod = mUpdatePeriod; @@ -660,16 +672,22 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const status_t AudioTrack::setPosition(uint32_t position) { - if (mIsTimed) return INVALID_OPERATION; + if (mIsTimed) { + return INVALID_OPERATION; + } AutoMutex lock(mLock); - if (!stopped_l()) return INVALID_OPERATION; + if (!stopped_l()) { + return INVALID_OPERATION; + } audio_track_cblk_t* cblk = mCblk; Mutex::Autolock _l(cblk->lock); - if (position > cblk->user) return BAD_VALUE; + if (position > cblk->user) { + return BAD_VALUE; + } cblk->server = position; android_atomic_or(CBLK_FORCEREADY, &cblk->flags); @@ -679,7 +697,9 @@ status_t AudioTrack::setPosition(uint32_t position) status_t AudioTrack::getPosition(uint32_t *position) { - if (position == NULL) return BAD_VALUE; + if (position == NULL) { + return BAD_VALUE; + } AutoMutex lock(mLock); *position = mFlushed ? 0 : mCblk->server; @@ -690,7 +710,9 @@ status_t AudioTrack::reload() { AutoMutex lock(mLock); - if (!stopped_l()) return INVALID_OPERATION; + if (!stopped_l()) { + return INVALID_OPERATION; + } flush_l(); @@ -1060,8 +1082,12 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) ssize_t AudioTrack::write(const void* buffer, size_t userSize) { - if (mSharedBuffer != 0) return INVALID_OPERATION; - if (mIsTimed) return INVALID_OPERATION; + if (mSharedBuffer != 0) { + return INVALID_OPERATION; + } + if (mIsTimed) { + return INVALID_OPERATION; + } if (ssize_t(userSize) < 0) { // Sanity-check: user is most-likely passing an error code, and it would @@ -1098,8 +1124,9 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) status_t err = obtainBuffer(&audioBuffer, -1); if (err < 0) { // out of buffers, return #bytes written - if (err == status_t(NO_MORE_BUFFERS)) + if (err == status_t(NO_MORE_BUFFERS)) { break; + } return ssize_t(err); } @@ -1159,8 +1186,9 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) cblk = temp; cblk->lock.unlock(); - if (result == OK) + if (result == OK) { result = mAudioTrack->allocateTimedBuffer(size, buffer); + } } return result; @@ -1218,7 +1246,9 @@ bool AudioTrack::processAudioBuffer(const sp& thread) if (cblk->server == cblk->frameCount) { mCbf(EVENT_BUFFER_END, mUserData, 0); } - if (mSharedBuffer != 0) return false; + if (mSharedBuffer != 0) { + return false; + } } } @@ -1275,7 +1305,9 @@ bool AudioTrack::processAudioBuffer(const sp& thread) } break; } - if (err == status_t(STOPPED)) return false; + if (err == status_t(STOPPED)) { + return false; + } // Divide buffer size by 2 to take into account the expansion // due to 8 to 16 bit conversion: the callback must fill only half @@ -1298,7 +1330,9 @@ bool AudioTrack::processAudioBuffer(const sp& thread) break; } - if (writtenSize > reqSize) writtenSize = reqSize; + if (writtenSize > reqSize) { + writtenSize = reqSize; + } if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { // 8 to 16 bit conversion, note that source and destination are the same address -- cgit v1.1 From f6f0f0e313f4d4dc7035e842270cd31303bd91e7 Mon Sep 17 00:00:00 2001 From: James Dong Date: Fri, 16 Nov 2012 14:31:15 -0800 Subject: Fix a crash when the stop might be called due to some error before start in RTSPSource o related-to-bug: 7507224 Change-Id: Ic8bfec13097b824ba337a01c9b00c98af2a33f43 --- media/libmediaplayerservice/nuplayer/RTSPSource.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index 5a7a785..6df2ddd 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -57,9 +57,7 @@ NuPlayer::RTSPSource::RTSPSource( } NuPlayer::RTSPSource::~RTSPSource() { - if (mLooper != NULL) { - mLooper->stop(); - } + mLooper->stop(); } void NuPlayer::RTSPSource::start() { @@ -86,6 +84,9 @@ void NuPlayer::RTSPSource::start() { } void NuPlayer::RTSPSource::stop() { + if (mLooper == NULL) { + return; + } sp msg = new AMessage(kWhatDisconnect, mReflector->id()); sp dummy; -- cgit v1.1 From b603744e96b07b1d5bf745bde593fb2c025cefcf Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 14 Nov 2012 13:42:25 -0800 Subject: Don't use control block frame count after create This is part of a series to clean up the control block. Change-Id: I7f4cb05aef63053f8e2ab05b286d302260ef4758 --- media/libmedia/AudioRecord.cpp | 8 ++-- media/libmedia/AudioTrack.cpp | 102 ++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 51 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0587651..0731f00 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -216,7 +216,7 @@ status_t AudioRecord::set( mFormat = format; // Update buffer size in case it has been limited by AudioFlinger during track creation - mFrameCount = mCblk->frameCount; + mFrameCount = mCblk->frameCount_; mChannelCount = (uint8_t)channelCount; mChannelMask = channelMask; @@ -568,7 +568,7 @@ create_new_record: } uint32_t u = cblk->user; - uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + uint32_t bufferEnd = cblk->userBase + mFrameCount; if (framesReq > bufferEnd - u) { framesReq = bufferEnd - u; @@ -584,7 +584,7 @@ create_new_record: void AudioRecord::releaseBuffer(Buffer* audioBuffer) { AutoMutex lock(mLock); - mCblk->stepUserIn(audioBuffer->frameCount); + mCblk->stepUserIn(audioBuffer->frameCount, mFrameCount); } audio_io_handle_t AudioRecord::getInput() const @@ -746,7 +746,7 @@ bool AudioRecord::processAudioBuffer(const sp& thread) // Manage overrun callback - if (active && (cblk->framesAvailableIn() == 0)) { + if (active && (cblk->framesAvailableIn(mFrameCount) == 0)) { // The value of active is stale, but we are almost sure to be active here because // otherwise we would have exited when obtainBuffer returned STOPPED earlier. ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 979ee37..0be5534 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -257,6 +257,7 @@ status_t AudioTrack::set( mVolume[RIGHT] = 1.0f; mSendLevel = 0.0f; mFrameCount = frameCount; + mReqFrameCount = frameCount; mNotificationFramesReq = notificationFrames; mSessionId = sessionId; mAuxEffectId = 0; @@ -344,7 +345,7 @@ int AudioTrack::channelCount() const size_t AudioTrack::frameCount() const { - return mCblk->frameCount; + return mFrameCount; } sp& AudioTrack::sharedBuffer() @@ -596,17 +597,17 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou } if (loopStart >= loopEnd || - loopEnd - loopStart > cblk->frameCount || + loopEnd - loopStart > mFrameCount || cblk->server > loopStart) { ALOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, " - "user %d", loopStart, loopEnd, loopCount, cblk->frameCount, cblk->user); + "user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user); return BAD_VALUE; } - if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) { + if ((mSharedBuffer != 0) && (loopEnd > mFrameCount)) { ALOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, " "framecount %d", - loopStart, loopEnd, cblk->frameCount); + loopStart, loopEnd, mFrameCount); return BAD_VALUE; } @@ -695,7 +696,7 @@ status_t AudioTrack::reload() flush_l(); audio_track_cblk_t* cblk = mCblk; - cblk->stepUserOut(cblk->frameCount); + cblk->stepUserOut(mFrameCount, mFrameCount); return NO_ERROR; } @@ -889,17 +890,25 @@ status_t AudioTrack::createTrack_l( mCblkMemory = iMem; audio_track_cblk_t* cblk = static_cast(iMem->pointer()); mCblk = cblk; + size_t temp = cblk->frameCount_; + if (temp < frameCount || (frameCount == 0 && temp == 0)) { + // In current design, AudioTrack client checks and ensures frame count validity before + // passing it to AudioFlinger so AudioFlinger should not return a different value except + // for fast track as it uses a special method of assigning frame count. + ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp); + } + frameCount = temp; if (flags & AUDIO_OUTPUT_FLAG_FAST) { if (trackFlags & IAudioFlinger::TRACK_FAST) { - ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", cblk->frameCount); + ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount); } else { - ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", cblk->frameCount); + ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", frameCount); // once denied, do not request again if IAudioTrack is re-created flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST); mFlags = flags; } if (sharedBuffer == 0) { - mNotificationFramesAct = cblk->frameCount/2; + mNotificationFramesAct = frameCount/2; } } if (sharedBuffer == 0) { @@ -907,7 +916,7 @@ status_t AudioTrack::createTrack_l( } else { mBuffers = sharedBuffer->pointer(); // Force buffer full condition as data is already present in shared memory - cblk->stepUserOut(cblk->frameCount); + cblk->stepUserOut(frameCount, frameCount); } cblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | @@ -918,11 +927,12 @@ status_t AudioTrack::createTrack_l( cblk->waitTimeMs = 0; mRemainingFrames = mNotificationFramesAct; // FIXME don't believe this lie - mLatency = afLatency + (1000*cblk->frameCount) / sampleRate; + mLatency = afLatency + (1000*frameCount) / sampleRate; + mFrameCount = frameCount; // If IAudioTrack is re-created, don't let the requested frameCount // decrease. This can confuse clients that cache frameCount(). - if (cblk->frameCount > mFrameCount) { - mFrameCount = cblk->frameCount; + if (frameCount > mReqFrameCount) { + mReqFrameCount = frameCount; } return NO_ERROR; } @@ -939,7 +949,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) audioBuffer->frameCount = 0; audioBuffer->size = 0; - uint32_t framesAvail = cblk->framesAvailableOut(); + uint32_t framesAvail = cblk->framesAvailableOut(mFrameCount); cblk->lock.lock(); if (cblk->flags & CBLK_INVALID) { @@ -1015,7 +1025,7 @@ create_new_track: } // read the server count again start_loop_here: - framesAvail = cblk->framesAvailableOut_l(); + framesAvail = cblk->framesAvailableOut_l(mFrameCount); } cblk->lock.unlock(); } @@ -1027,7 +1037,7 @@ create_new_track: } uint32_t u = cblk->user; - uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + uint32_t bufferEnd = cblk->userBase + mFrameCount; if (framesReq > bufferEnd - u) { framesReq = bufferEnd - u; @@ -1044,7 +1054,7 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) { AutoMutex lock(mLock); audio_track_cblk_t* cblk = mCblk; - cblk->stepUserOut(audioBuffer->frameCount); + cblk->stepUserOut(audioBuffer->frameCount, mFrameCount); if (audioBuffer->frameCount > 0) { // restart track if it was disabled by audioflinger due to previous underrun if (mActive && (cblk->flags & CBLK_DISABLED)) { @@ -1211,11 +1221,11 @@ bool AudioTrack::processAudioBuffer(const sp& thread) // so all cblk references might still refer to old shared memory, but that should be benign // Manage underrun callback - if (active && (cblk->framesAvailableOut() == cblk->frameCount)) { + if (active && (cblk->framesAvailableOut(mFrameCount) == mFrameCount)) { ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) { mCbf(EVENT_UNDERRUN, mUserData, 0); - if (cblk->server == cblk->frameCount) { + if (cblk->server == mFrameCount) { mCbf(EVENT_BUFFER_END, mUserData, 0); } if (mSharedBuffer != 0) return false; @@ -1355,7 +1365,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart cblk->sampleRate, mFormat, mChannelMask, - mFrameCount, + mReqFrameCount, // so that frame count never goes down mFlags, mSharedBuffer, getOutput_l()); @@ -1379,19 +1389,19 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart if (mSharedBuffer == 0) { uint32_t frames = 0; if (user > server) { - frames = ((user - server) > newCblk->frameCount) ? - newCblk->frameCount : (user - server); + frames = ((user - server) > mFrameCount) ? + mFrameCount : (user - server); memset(mBuffers, 0, frames * mFrameSizeAF); } // restart playback even if buffer is not completely filled. android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to // the client - newCblk->stepUserOut(frames); + newCblk->stepUserOut(frames, mFrameCount); } } if (mSharedBuffer != 0) { - newCblk->stepUserOut(newCblk->frameCount); + newCblk->stepUserOut(mFrameCount, mFrameCount); } if (mActive) { result = mAudioTrack->start(); @@ -1429,7 +1439,7 @@ status_t AudioTrack::dump(int fd, const Vector& args) const mVolume[0], mVolume[1]); result.append(buffer); snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, - mChannelCount, cblk->frameCount); + mChannelCount, mFrameCount); result.append(buffer); snprintf(buffer, 255, " sample rate(%u), status(%d), muted(%d)\n", (cblk == 0) ? 0 : cblk->sampleRate, mStatus, mMuted); @@ -1494,18 +1504,18 @@ void AudioTrack::AudioTrackThread::resume() audio_track_cblk_t::audio_track_cblk_t() : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), - userBase(0), serverBase(0), frameCount(0), + userBase(0), serverBase(0), frameCount_(0), loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000), mSendLevel(0), flags(0) { } -uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount, bool isOut) +uint32_t audio_track_cblk_t::stepUser(size_t stepCount, size_t frameCount, bool isOut) { - ALOGV("stepuser %08x %08x %d", user, server, frameCount); + ALOGV("stepuser %08x %08x %d", user, server, stepCount); uint32_t u = user; - u += frameCount; + u += stepCount; // Ensure that user is never ahead of server for AudioRecord if (isOut) { // If stepServer() has been called once, switch to normal obtainBuffer() timeout period @@ -1517,15 +1527,14 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount, bool isOut) u = server; } - uint32_t fc = this->frameCount; - if (u >= fc) { + if (u >= frameCount) { // common case, user didn't just wrap - if (u - fc >= userBase ) { - userBase += fc; + if (u - frameCount >= userBase ) { + userBase += frameCount; } - } else if (u >= userBase + fc) { + } else if (u >= userBase + frameCount) { // user just wrapped - userBase += fc; + userBase += frameCount; } user = u; @@ -1538,9 +1547,9 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount, bool isOut) return u; } -bool audio_track_cblk_t::stepServer(uint32_t frameCount, bool isOut) +bool audio_track_cblk_t::stepServer(size_t stepCount, size_t frameCount, bool isOut) { - ALOGV("stepserver %08x %08x %d", user, server, frameCount); + ALOGV("stepserver %08x %08x %d", user, server, stepCount); if (!tryLock()) { ALOGW("stepServer() could not lock cblk"); @@ -1550,7 +1559,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount, bool isOut) uint32_t s = server; bool flushed = (s == user); - s += frameCount; + s += stepCount; if (isOut) { // Mark that we have read the first buffer so that next time stepUser() is called // we switch to normal obtainBuffer() timeout period @@ -1576,15 +1585,14 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount, bool isOut) } } - uint32_t fc = this->frameCount; - if (s >= fc) { + if (s >= frameCount) { // common case, server didn't just wrap - if (s - fc >= serverBase ) { - serverBase += fc; + if (s - frameCount >= serverBase ) { + serverBase += frameCount; } - } else if (s >= serverBase + fc) { + } else if (s >= serverBase + frameCount) { // server just wrapped - serverBase += fc; + serverBase += frameCount; } server = s; @@ -1601,13 +1609,13 @@ void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offse return (int8_t *)buffers + (offset - userBase) * frameSize; } -uint32_t audio_track_cblk_t::framesAvailable(bool isOut) +uint32_t audio_track_cblk_t::framesAvailable(size_t frameCount, bool isOut) { Mutex::Autolock _l(lock); - return framesAvailable_l(isOut); + return framesAvailable_l(frameCount, isOut); } -uint32_t audio_track_cblk_t::framesAvailable_l(bool isOut) +uint32_t audio_track_cblk_t::framesAvailable_l(size_t frameCount, bool isOut) { uint32_t u = user; uint32_t s = server; -- cgit v1.1 From a6b47a17fb1288936b491f30cd751172a572df5c Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 19 Nov 2012 09:49:18 -0800 Subject: delete -> free Strings duplicated with strdup() should be free()d, not deleted. Change-Id: I42bb3df9625bb8d35f80b02d15364b94c36496f8 --- media/libmedia/SoundPool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 204e0ce..ee70ef7 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -489,7 +489,7 @@ Sample::~Sample() ::close(mFd); } mData.clear(); - delete mUrl; + free(mUrl); } status_t Sample::doLoad() -- cgit v1.1 From a42ff007a17d63df22c60dd5e5fd811ee45ca1b3 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 14 Nov 2012 12:47:55 -0800 Subject: Clean up channel count and channel mask Channel count is uint32_t. Remove redundant mask parameter to AudioTrack::createTrack_l() and AudioRecord::openRecord_l(). Change-Id: I5dc2b18eb609b2c0dc3091994cbaa4628062c17f --- media/libmedia/AudioRecord.cpp | 20 ++++++++------------ media/libmedia/AudioTrack.cpp | 24 ++++++++++-------------- 2 files changed, 18 insertions(+), 26 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 2a5a996..c2ef68c 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -63,7 +63,7 @@ status_t AudioRecord::getMinFrameCount( size <<= 1; if (audio_is_linear_pcm(format)) { - int channelCount = popcount(channelMask); + uint32_t channelCount = popcount(channelMask); size /= channelCount * audio_bytes_per_sample(format); } @@ -162,8 +162,9 @@ status_t AudioRecord::set( if (!audio_is_input_channel(channelMask)) { return BAD_VALUE; } - - int channelCount = popcount(channelMask); + mChannelMask = channelMask; + uint32_t channelCount = popcount(channelMask); + mChannelCount = channelCount; if (sessionId == 0 ) { mSessionId = AudioSystem::newAudioSessionId(); @@ -201,8 +202,7 @@ status_t AudioRecord::set( } // create the IAudioRecord - status = openRecord_l(sampleRate, format, channelMask, - frameCount, input); + status = openRecord_l(sampleRate, format, frameCount, input); if (status != NO_ERROR) { return status; } @@ -217,8 +217,6 @@ status_t AudioRecord::set( mFormat = format; // Update buffer size in case it has been limited by AudioFlinger during track creation mFrameCount = mCblk->frameCount_; - mChannelCount = (uint8_t)channelCount; - mChannelMask = channelMask; if (audio_is_linear_pcm(mFormat)) { mFrameSize = channelCount * audio_bytes_per_sample(format); @@ -261,7 +259,7 @@ audio_format_t AudioRecord::format() const return mFormat; } -int AudioRecord::channelCount() const +uint32_t AudioRecord::channelCount() const { return mChannelCount; } @@ -432,7 +430,6 @@ unsigned int AudioRecord::getInputFramesLost() const status_t AudioRecord::openRecord_l( uint32_t sampleRate, audio_format_t format, - audio_channel_mask_t channelMask, size_t frameCount, audio_io_handle_t input) { @@ -449,7 +446,7 @@ status_t AudioRecord::openRecord_l( int originalSessionId = mSessionId; sp record = audioFlinger->openRecord(getpid(), input, sampleRate, format, - channelMask, + mChannelMask, frameCount, IAudioFlinger::TRACK_DEFAULT, tid, @@ -784,8 +781,7 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk) // if the new IAudioRecord is created, openRecord_l() will modify the // following member variables: mAudioRecord, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioRecord and IMemory - result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask, - mFrameCount, getInput_l()); + result = openRecord_l(cblk->sampleRate, mFormat, mFrameCount, getInput_l()); if (result == NO_ERROR) { newCblk = mCblk; // callback thread or sync event hasn't changed diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ff1b21b..e40895a 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -243,7 +243,9 @@ status_t AudioTrack::set( ALOGE("Invalid channel mask %#x", channelMask); return BAD_VALUE; } + mChannelMask = channelMask; uint32_t channelCount = popcount(channelMask); + mChannelCount = channelCount; audio_io_handle_t output = AudioSystem::getOutput( streamType, @@ -275,7 +277,6 @@ status_t AudioTrack::set( status_t status = createTrack_l(streamType, sampleRate, format, - channelMask, frameCount, flags, sharedBuffer, @@ -293,8 +294,6 @@ status_t AudioTrack::set( mStreamType = streamType; mFormat = format; - mChannelMask = channelMask; - mChannelCount = channelCount; if (audio_is_linear_pcm(format)) { mFrameSize = channelCount * audio_bytes_per_sample(format); @@ -340,7 +339,7 @@ audio_format_t AudioTrack::format() const return mFormat; } -int AudioTrack::channelCount() const +uint32_t AudioTrack::channelCount() const { return mChannelCount; } @@ -758,7 +757,6 @@ status_t AudioTrack::createTrack_l( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - audio_channel_mask_t channelMask, size_t frameCount, audio_output_flags_t flags, const sp& sharedBuffer, @@ -808,17 +806,16 @@ status_t AudioTrack::createTrack_l( } else if (sharedBuffer != 0) { - // Ensure that buffer alignment matches channelCount - int channelCount = popcount(channelMask); + // Ensure that buffer alignment matches channel count // 8-bit data in shared memory is not currently supported by AudioFlinger size_t alignment = /* format == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2; - if (channelCount > 1) { + if (mChannelCount > 1) { // More than 2 channels does not require stronger alignment than stereo alignment <<= 1; } - if (((uint32_t)sharedBuffer->pointer() & (alignment - 1)) != 0) { - ALOGE("Invalid buffer alignment: address %p, channelCount %d", - sharedBuffer->pointer(), channelCount); + if (((size_t)sharedBuffer->pointer() & (alignment - 1)) != 0) { + ALOGE("Invalid buffer alignment: address %p, channel count %u", + sharedBuffer->pointer(), mChannelCount); return BAD_VALUE; } @@ -826,7 +823,7 @@ status_t AudioTrack::createTrack_l( // there's no frameCount parameter. // But when initializing a shared buffer AudioTrack via set(), // there _is_ a frameCount parameter. We silently ignore it. - frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t); + frameCount = sharedBuffer->size()/mChannelCount/sizeof(int16_t); } else if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) { @@ -890,7 +887,7 @@ status_t AudioTrack::createTrack_l( // AudioFlinger only sees 16-bit PCM format == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : format, - channelMask, + mChannelMask, frameCount, &trackFlags, sharedBuffer, @@ -1398,7 +1395,6 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart result = createTrack_l(mStreamType, cblk->sampleRate, mFormat, - mChannelMask, mReqFrameCount, // so that frame count never goes down mFlags, mSharedBuffer, -- cgit v1.1 From f6f38287b97ec69b169387add6458f859b770e65 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 29 Nov 2012 13:49:07 -0800 Subject: Clear the sticky EOS flags when transitioning to LOADED state instead of transitioning _from_ UNINITIALIZED state. This makes codec instances reusable. Change-Id: I8f0c11923978ffee58b553a5ac59c740b0223c54 --- media/libstagefright/ACodec.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index a135222..8f3dc6c 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3106,11 +3106,6 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { mCodec->mOMX = omx; mCodec->mNode = node; - mCodec->mPortEOS[kPortIndexInput] = - mCodec->mPortEOS[kPortIndexOutput] = false; - - mCodec->mInputEOSResult = OK; - { sp notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatComponentAllocated); @@ -3132,6 +3127,11 @@ ACodec::LoadedState::LoadedState(ACodec *codec) void ACodec::LoadedState::stateEntered() { ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str()); + mCodec->mPortEOS[kPortIndexInput] = + mCodec->mPortEOS[kPortIndexOutput] = false; + + mCodec->mInputEOSResult = OK; + if (mCodec->mShutdownInProgress) { bool keepComponentAllocated = mCodec->mKeepComponentAllocated; -- cgit v1.1 From 5876f2f28f31c1bd99864ba3bb1590e3d6765018 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 30 Nov 2012 10:52:16 -0800 Subject: Remove last bits of IAudioFlinger::channel_count Change-Id: I9e13678e0aa32a86eb27367a4aff4b32b8aec8cc --- media/libmedia/IAudioFlinger.cpp | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'media') diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index a010bb6..c5fbbf0 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -32,7 +32,7 @@ enum { CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION, OPEN_RECORD, SAMPLE_RATE, - CHANNEL_COUNT, // obsolete + RESERVED, // obsolete, was CHANNEL_COUNT FORMAT, FRAME_COUNT, LATENCY, @@ -191,17 +191,6 @@ public: return reply.readInt32(); } -#if 0 - virtual int channelCount(audio_io_handle_t output) const - { - Parcel data, reply; - data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); - data.writeInt32((int32_t) output); - remote()->transact(CHANNEL_COUNT, data, &reply); - return reply.readInt32(); - } -#endif - virtual audio_format_t format(audio_io_handle_t output) const { Parcel data, reply; @@ -768,13 +757,6 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32( sampleRate((audio_io_handle_t) data.readInt32()) ); return NO_ERROR; } break; -#if 0 - case CHANNEL_COUNT: { - CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32( channelCount((audio_io_handle_t) data.readInt32()) ); - return NO_ERROR; - } break; -#endif case FORMAT: { CHECK_INTERFACE(IAudioFlinger, data, reply); reply->writeInt32( format((audio_io_handle_t) data.readInt32()) ); -- cgit v1.1 From a1f8ab0ad670c30e57f3f072df13df66fe4f4910 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 30 Nov 2012 10:53:22 -0800 Subject: Fix nuplayer seek jankiness by properly flushing decoders before initiating the seek. Also refactor the nuplayer state machine to make this a little more maintainable and extensible in the future. Change-Id: I36a673bdecff732bca7094c8f72bac24f37c01e9 related-to-bug: 7120373 --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 279 +++++++++++++++------- media/libmediaplayerservice/nuplayer/NuPlayer.h | 16 +- 2 files changed, 207 insertions(+), 88 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index d3ec122..f363568 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -50,6 +50,49 @@ namespace android { +struct NuPlayer::Action : public RefBase { + Action() {} + + virtual void execute(NuPlayer *player) = 0; + +private: + DISALLOW_EVIL_CONSTRUCTORS(Action); +}; + +struct NuPlayer::SeekAction : public Action { + SeekAction(int64_t seekTimeUs) + : mSeekTimeUs(seekTimeUs) { + } + + virtual void execute(NuPlayer *player) { + player->performSeek(mSeekTimeUs); + } + +private: + int64_t mSeekTimeUs; + + DISALLOW_EVIL_CONSTRUCTORS(SeekAction); +}; + +// Use this if there's no state necessary to save in order to execute +// the action. +struct NuPlayer::SimpleAction : public Action { + typedef void (NuPlayer::*ActionFunc)(); + + SimpleAction(ActionFunc func) + : mFunc(func) { + } + + virtual void execute(NuPlayer *player) { + (player->*mFunc)(); + } + +private: + ActionFunc mFunc; + + DISALLOW_EVIL_CONSTRUCTORS(SimpleAction); +}; + //////////////////////////////////////////////////////////////////////////////// NuPlayer::NuPlayer() @@ -63,8 +106,6 @@ NuPlayer::NuPlayer() mTimeDiscontinuityPending(false), mFlushingAudio(NONE), mFlushingVideo(NONE), - mResetInProgress(false), - mResetPostponed(false), mSkipRenderingAudioUntilMediaTimeUs(-1ll), mSkipRenderingVideoUntilMediaTimeUs(-1ll), mVideoLateByUs(0ll), @@ -495,8 +536,15 @@ void NuPlayer::onMessageReceived(const sp &msg) { mRenderer->queueEOS(audio, UNKNOWN_ERROR); } else if (what == ACodec::kWhatDrainThisBuffer) { renderBuffer(audio, codecRequest); - } else { - ALOGV("Unhandled codec notification %d.", what); + } else if (what != ACodec::kWhatComponentAllocated + && what != ACodec::kWhatComponentConfigured + && what != ACodec::kWhatBuffersAllocated) { + ALOGV("Unhandled codec notification %d '%c%c%c%c'.", + what, + what >> 24, + (what >> 16) & 0xff, + (what >> 8) & 0xff, + what & 0xff); } break; @@ -569,47 +617,13 @@ void NuPlayer::onMessageReceived(const sp &msg) { { ALOGV("kWhatReset"); - cancelPollDuration(); - - if (mRenderer != NULL) { - // There's an edge case where the renderer owns all output - // buffers and is paused, therefore the decoder will not read - // more input data and will never encounter the matching - // discontinuity. To avoid this, we resume the renderer. + mDeferredActions.push_back( + new SimpleAction(&NuPlayer::performDecoderShutdown)); - if (mFlushingAudio == AWAITING_DISCONTINUITY - || mFlushingVideo == AWAITING_DISCONTINUITY) { - mRenderer->resume(); - } - } - - if (mFlushingAudio != NONE || mFlushingVideo != NONE) { - // We're currently flushing, postpone the reset until that's - // completed. + mDeferredActions.push_back( + new SimpleAction(&NuPlayer::performReset)); - ALOGV("postponing reset mFlushingAudio=%d, mFlushingVideo=%d", - mFlushingAudio, mFlushingVideo); - - mResetPostponed = true; - break; - } - - if (mAudioDecoder == NULL && mVideoDecoder == NULL) { - finishReset(); - break; - } - - mTimeDiscontinuityPending = true; - - if (mAudioDecoder != NULL) { - flushDecoder(true /* audio */, true /* needShutdown */); - } - - if (mVideoDecoder != NULL) { - flushDecoder(false /* audio */, true /* needShutdown */); - } - - mResetInProgress = true; + processDeferredActions(); break; } @@ -618,18 +632,14 @@ void NuPlayer::onMessageReceived(const sp &msg) { int64_t seekTimeUs; CHECK(msg->findInt64("seekTimeUs", &seekTimeUs)); - ALOGV("kWhatSeek seekTimeUs=%lld us (%.2f secs)", - seekTimeUs, seekTimeUs / 1E6); + ALOGV("kWhatSeek seekTimeUs=%lld us", seekTimeUs); - mSource->seekTo(seekTimeUs); + mDeferredActions.push_back( + new SimpleAction(&NuPlayer::performDecoderFlush)); - if (mDriver != NULL) { - sp driver = mDriver.promote(); - if (driver != NULL) { - driver->notifySeekComplete(); - } - } + mDeferredActions.push_back(new SeekAction(seekTimeUs)); + processDeferredActions(); break; } @@ -680,39 +690,7 @@ void NuPlayer::finishFlushIfPossible() { mFlushingAudio = NONE; mFlushingVideo = NONE; - if (mResetInProgress) { - ALOGV("reset completed"); - - mResetInProgress = false; - finishReset(); - } else if (mResetPostponed) { - (new AMessage(kWhatReset, id()))->post(); - mResetPostponed = false; - } else if (mAudioDecoder == NULL || mVideoDecoder == NULL) { - postScanSources(); - } -} - -void NuPlayer::finishReset() { - CHECK(mAudioDecoder == NULL); - CHECK(mVideoDecoder == NULL); - - ++mScanSourcesGeneration; - mScanSourcesPending = false; - - mRenderer.clear(); - - if (mSource != NULL) { - mSource->stop(); - mSource.clear(); - } - - if (mDriver != NULL) { - sp driver = mDriver.promote(); - if (driver != NULL) { - driver->notifyResetComplete(); - } - } + processDeferredActions(); } void NuPlayer::postScanSources() { @@ -831,6 +809,14 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp &msg) { mTimeDiscontinuityPending || timeChange; if (formatChange || timeChange) { + if (mFlushingAudio == NONE && mFlushingVideo == NONE) { + // And we'll resume scanning sources once we're done + // flushing. + mDeferredActions.push_front( + new SimpleAction( + &NuPlayer::performScanSources)); + } + flushDecoder(audio, formatChange); } else { // This stream is unaffected by the discontinuity @@ -1023,4 +1009,127 @@ void NuPlayer::cancelPollDuration() { ++mPollDurationGeneration; } +void NuPlayer::processDeferredActions() { + while (!mDeferredActions.empty()) { + // We won't execute any deferred actions until we're no longer in + // an intermediate state, i.e. one more more decoders are currently + // flushing or shutting down. + + if (mRenderer != NULL) { + // There's an edge case where the renderer owns all output + // buffers and is paused, therefore the decoder will not read + // more input data and will never encounter the matching + // discontinuity. To avoid this, we resume the renderer. + + if (mFlushingAudio == AWAITING_DISCONTINUITY + || mFlushingVideo == AWAITING_DISCONTINUITY) { + mRenderer->resume(); + } + } + + if (mFlushingAudio != NONE || mFlushingVideo != NONE) { + // We're currently flushing, postpone the reset until that's + // completed. + + ALOGV("postponing action mFlushingAudio=%d, mFlushingVideo=%d", + mFlushingAudio, mFlushingVideo); + + break; + } + + sp action = *mDeferredActions.begin(); + mDeferredActions.erase(mDeferredActions.begin()); + + action->execute(this); + } +} + +void NuPlayer::performSeek(int64_t seekTimeUs) { + ALOGV("performSeek seekTimeUs=%lld us (%.2f secs)", + seekTimeUs, + seekTimeUs / 1E6); + + mSource->seekTo(seekTimeUs); + + if (mDriver != NULL) { + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifyPosition(seekTimeUs); + driver->notifySeekComplete(); + } + } + + // everything's flushed, continue playback. +} + +void NuPlayer::performDecoderFlush() { + ALOGV("performDecoderFlush"); + + if (mAudioDecoder != NULL && mVideoDecoder == NULL) { + return; + } + + mTimeDiscontinuityPending = true; + + if (mAudioDecoder != NULL) { + flushDecoder(true /* audio */, false /* needShutdown */); + } + + if (mVideoDecoder != NULL) { + flushDecoder(false /* audio */, false /* needShutdown */); + } +} + +void NuPlayer::performDecoderShutdown() { + ALOGV("performDecoderShutdown"); + + if (mAudioDecoder != NULL && mVideoDecoder == NULL) { + return; + } + + mTimeDiscontinuityPending = true; + + if (mAudioDecoder != NULL) { + flushDecoder(true /* audio */, true /* needShutdown */); + } + + if (mVideoDecoder != NULL) { + flushDecoder(false /* audio */, true /* needShutdown */); + } +} + +void NuPlayer::performReset() { + ALOGV("performReset"); + + CHECK(mAudioDecoder == NULL); + CHECK(mVideoDecoder == NULL); + + cancelPollDuration(); + + ++mScanSourcesGeneration; + mScanSourcesPending = false; + + mRenderer.clear(); + + if (mSource != NULL) { + mSource->stop(); + mSource.clear(); + } + + if (mDriver != NULL) { + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifyResetComplete(); + } + } +} + +void NuPlayer::performScanSources() { + ALOGV("performScanSources"); + + if (mAudioDecoder == NULL || mVideoDecoder == NULL) { + postScanSources(); + } +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 31efb2e..6e174e0 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -73,6 +73,9 @@ private: struct Renderer; struct RTSPSource; struct StreamingSource; + struct Action; + struct SeekAction; + struct SimpleAction; enum { kWhatSetDataSource = '=DaS', @@ -102,6 +105,8 @@ private: sp mAudioDecoder; sp mRenderer; + List > mDeferredActions; + bool mAudioEOS; bool mVideoEOS; @@ -126,8 +131,6 @@ private: FlushStatus mFlushingAudio; FlushStatus mFlushingVideo; - bool mResetInProgress; - bool mResetPostponed; int64_t mSkipRenderingAudioUntilMediaTimeUs; int64_t mSkipRenderingVideoUntilMediaTimeUs; @@ -150,12 +153,19 @@ private: static bool IsFlushingState(FlushStatus state, bool *needShutdown = NULL); - void finishReset(); void postScanSources(); void schedulePollDuration(); void cancelPollDuration(); + void processDeferredActions(); + + void performSeek(int64_t seekTimeUs); + void performDecoderFlush(); + void performDecoderShutdown(); + void performReset(); + void performScanSources(); + DISALLOW_EVIL_CONSTRUCTORS(NuPlayer); }; -- cgit v1.1 From 01437b7cdaecf53acb46b50ff8b5d86b9d36eb20 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 29 Nov 2012 07:32:49 -0800 Subject: AudioTrack inline short const methods Change-Id: I142917edb454d510bbe545e94e6eaea30b650fae --- media/libmedia/AudioTrack.cpp | 47 ------------------------------------------- 1 file changed, 47 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index e40895a..0463433 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -317,43 +317,6 @@ status_t AudioTrack::set( return NO_ERROR; } -status_t AudioTrack::initCheck() const -{ - return mStatus; -} - -// ------------------------------------------------------------------------- - -uint32_t AudioTrack::latency() const -{ - return mLatency; -} - -audio_stream_type_t AudioTrack::streamType() const -{ - return mStreamType; -} - -audio_format_t AudioTrack::format() const -{ - return mFormat; -} - -uint32_t AudioTrack::channelCount() const -{ - return mChannelCount; -} - -size_t AudioTrack::frameCount() const -{ - return mFrameCount; -} - -sp& AudioTrack::sharedBuffer() -{ - return mSharedBuffer; -} - // ------------------------------------------------------------------------- void AudioTrack::start() @@ -496,11 +459,6 @@ void AudioTrack::mute(bool e) mMuted = e; } -bool AudioTrack::muted() const -{ - return mMuted; -} - status_t AudioTrack::setVolume(float left, float right) { if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) { @@ -735,11 +693,6 @@ audio_io_handle_t AudioTrack::getOutput_l() mCblk->sampleRate, mFormat, mChannelMask, mFlags); } -int AudioTrack::getSessionId() const -{ - return mSessionId; -} - status_t AudioTrack::attachAuxEffect(int effectId) { ALOGV("attachAuxEffect(%d)", effectId); -- cgit v1.1 From e4756fe3a387615acb63c6a05788c8db9b5786cb Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 29 Nov 2012 13:38:14 -0800 Subject: AudioTrack::mute() is unused so remove it If ever needed again, it could be implemented on client side by forcing a track volume of 0. Change-Id: I88a9b4f675b6dca2948549414f9ec2c192d29269 --- media/libmedia/AudioTrack.cpp | 11 ++--------- media/libmedia/IAudioTrack.cpp | 15 +-------------- 2 files changed, 3 insertions(+), 23 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 0463433..f5641e0 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -304,7 +304,6 @@ status_t AudioTrack::set( } mSharedBuffer = sharedBuffer; - mMuted = false; mActive = false; mUserData = user; mLoopCount = 0; @@ -453,12 +452,6 @@ void AudioTrack::pause() } } -void AudioTrack::mute(bool e) -{ - mAudioTrack->mute(e); - mMuted = e; -} - status_t AudioTrack::setVolume(float left, float right) { if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) { @@ -1424,8 +1417,8 @@ status_t AudioTrack::dump(int fd, const Vector& args) const snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%u), status(%d), muted(%d)\n", - (cblk == 0) ? 0 : cblk->sampleRate, mStatus, mMuted); + snprintf(buffer, 255, " sample rate(%u), status(%d)\n", + (cblk == 0) ? 0 : cblk->sampleRate, mStatus); result.append(buffer); snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index 867d1a5..e92f8aa 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -33,7 +33,7 @@ enum { START, STOP, FLUSH, - MUTE, + RESERVED, // was MUTE PAUSE, ATTACH_AUX_EFFECT, ALLOCATE_TIMED_BUFFER, @@ -88,14 +88,6 @@ public: remote()->transact(FLUSH, data, &reply); } - virtual void mute(bool e) - { - Parcel data, reply; - data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); - data.writeInt32(e); - remote()->transact(MUTE, data, &reply); - } - virtual void pause() { Parcel data, reply; @@ -192,11 +184,6 @@ status_t BnAudioTrack::onTransact( flush(); return NO_ERROR; } break; - case MUTE: { - CHECK_INTERFACE(IAudioTrack, data, reply); - mute( data.readInt32() ); - return NO_ERROR; - } break; case PAUSE: { CHECK_INTERFACE(IAudioTrack, data, reply); pause(); -- cgit v1.1 From 4bae3649d504d590a546717a8e49f96a30d9a745 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 30 Nov 2012 13:41:12 -0800 Subject: flush() comments and checks flush() is only useful for streaming mode. It is a no-op if track is active or uses a static buffer. Change-Id: I918ac181ffae3d16a0d67d8a7208f4aec61b5bd6 --- media/libmedia/AudioTrack.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index f5641e0..597d057 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -397,6 +397,7 @@ void AudioTrack::stop() mMarkerReached = false; // Force flush if a shared buffer is used otherwise audioflinger // will not stop before end of buffer is reached. + // It may be needed to make sure that we stop playback, likely in case looping is on. if (mSharedBuffer != 0) { flush_l(); } @@ -419,26 +420,26 @@ bool AudioTrack::stopped() const void AudioTrack::flush() { AutoMutex lock(mLock); - flush_l(); + if (!mActive && mSharedBuffer == 0) { + flush_l(); + } } -// must be called with mLock held void AudioTrack::flush_l() { ALOGV("flush"); + ALOG_ASSERT(!mActive); // clear playback marker and periodic update counter mMarkerPosition = 0; mMarkerReached = false; mUpdatePeriod = 0; - if (!mActive) { - mFlushed = true; - mAudioTrack->flush(); - // Release AudioTrack callback thread in case it was waiting for new buffers - // in AudioTrack::obtainBuffer() - mCblk->cv.signal(); - } + mFlushed = true; + mAudioTrack->flush(); + // Release AudioTrack callback thread in case it was waiting for new buffers + // in AudioTrack::obtainBuffer() + mCblk->cv.signal(); } void AudioTrack::pause() -- cgit v1.1 From 083d1c1492d496960d5b28f4664ff02101736677 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 30 Nov 2012 15:00:36 -0800 Subject: Emphasize distinction between streaming and static Update comments and improve error checks to match Change-Id: I7370d6e59a7ef26dfb284a8b058d5ab2e0a42ccf --- media/libmedia/AudioTrack.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 597d057..ac672a7 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -138,6 +138,11 @@ AudioTrack::AudioTrack( mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) { + if (sharedBuffer == 0) { + ALOGE("sharedBuffer must be non-0"); + mStatus = BAD_VALUE; + return; + } mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, sharedBuffer, false /*threadCanCallJava*/, sessionId); @@ -535,6 +540,10 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount // must be called with mLock held status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) { + if (mSharedBuffer == 0 || mIsTimed) { + return INVALID_OPERATION; + } + audio_track_cblk_t* cblk = mCblk; Mutex::Autolock _l(cblk->lock); @@ -547,10 +556,6 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou return NO_ERROR; } - if (mIsTimed) { - return INVALID_OPERATION; - } - if (loopStart >= loopEnd || loopEnd - loopStart > mFrameCount || cblk->server > loopStart) { @@ -624,7 +629,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const status_t AudioTrack::setPosition(uint32_t position) { - if (mIsTimed) { + if (mSharedBuffer == 0 || mIsTimed) { return INVALID_OPERATION; } @@ -660,6 +665,10 @@ status_t AudioTrack::getPosition(uint32_t *position) status_t AudioTrack::reload() { + if (mSharedBuffer == 0 || mIsTimed) { + return INVALID_OPERATION; + } + AutoMutex lock(mLock); if (!stopped_l()) { @@ -1036,10 +1045,7 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) ssize_t AudioTrack::write(const void* buffer, size_t userSize) { - if (mSharedBuffer != 0) { - return INVALID_OPERATION; - } - if (mIsTimed) { + if (mSharedBuffer != 0 || mIsTimed) { return INVALID_OPERATION; } -- cgit v1.1 From 57a339cdb7524f883de3ceb364c0b5606df0c610 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 3 Dec 2012 11:18:00 -0800 Subject: setVideoSurfaceTexture is now synchronous and applied dynamically while playing. Change-Id: If9f08659a01bdc7dac0999730368e9dfa5e58d36 related-to-bug: 5666482 --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 77 +++++++++++++++++++--- media/libmediaplayerservice/nuplayer/NuPlayer.h | 8 ++- .../nuplayer/NuPlayerDriver.cpp | 22 ++++++- .../nuplayer/NuPlayerDriver.h | 2 + 4 files changed, 97 insertions(+), 12 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index f363568..f9c3283 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -74,6 +74,21 @@ private: DISALLOW_EVIL_CONSTRUCTORS(SeekAction); }; +struct NuPlayer::SetSurfaceAction : public Action { + SetSurfaceAction(const sp &wrapper) + : mWrapper(wrapper) { + } + + virtual void execute(NuPlayer *player) { + player->performSetSurface(mWrapper); + } + +private: + sp mWrapper; + + DISALLOW_EVIL_CONSTRUCTORS(SetSurfaceAction); +}; + // Use this if there's no state necessary to save in order to execute // the action. struct NuPlayer::SimpleAction : public Action { @@ -111,7 +126,8 @@ NuPlayer::NuPlayer() mVideoLateByUs(0ll), mNumFramesTotal(0ll), mNumFramesDropped(0ll), - mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) { + mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW), + mStarted(false) { } NuPlayer::~NuPlayer() { @@ -181,11 +197,19 @@ void NuPlayer::setDataSource(int fd, int64_t offset, int64_t length) { msg->post(); } -void NuPlayer::setVideoSurfaceTexture(const sp &surfaceTexture) { +void NuPlayer::setVideoSurfaceTextureAsync( + const sp &surfaceTexture) { sp msg = new AMessage(kWhatSetVideoNativeWindow, id()); - sp surfaceTextureClient(surfaceTexture != NULL ? - new SurfaceTextureClient(surfaceTexture) : NULL); - msg->setObject("native-window", new NativeWindowWrapper(surfaceTextureClient)); + + if (surfaceTexture == NULL) { + msg->setObject("native-window", NULL); + } else { + msg->setObject( + "native-window", + new NativeWindowWrapper( + new SurfaceTextureClient(surfaceTexture))); + } + msg->post(); } @@ -278,13 +302,24 @@ void NuPlayer::onMessageReceived(const sp &msg) { { ALOGV("kWhatSetVideoNativeWindow"); + mDeferredActions.push_back( + new SimpleAction(&NuPlayer::performDecoderShutdown)); + sp obj; CHECK(msg->findObject("native-window", &obj)); - mNativeWindow = static_cast(obj.get()); + mDeferredActions.push_back( + new SetSurfaceAction( + static_cast(obj.get()))); + + if (obj != NULL) { + // If there is a new surface texture, instantiate decoders + // again if possible. + mDeferredActions.push_back( + new SimpleAction(&NuPlayer::performScanSources)); + } - // XXX - ignore error from setVideoScalingMode for now - setVideoScalingMode(mVideoScalingMode); + processDeferredActions(); break; } @@ -311,6 +346,7 @@ void NuPlayer::onMessageReceived(const sp &msg) { mVideoLateByUs = 0; mNumFramesTotal = 0; mNumFramesDropped = 0; + mStarted = true; mSource->start(); @@ -986,8 +1022,7 @@ sp NuPlayer::Source::getFormat(bool audio) { status_t NuPlayer::setVideoScalingMode(int32_t mode) { mVideoScalingMode = mode; - if (mNativeWindow != NULL - && mNativeWindow->getNativeWindow() != NULL) { + if (mNativeWindow != NULL) { status_t ret = native_window_set_scaling_mode( mNativeWindow->getNativeWindow().get(), mVideoScalingMode); if (ret != OK) { @@ -1122,14 +1157,36 @@ void NuPlayer::performReset() { driver->notifyResetComplete(); } } + + mStarted = false; } void NuPlayer::performScanSources() { ALOGV("performScanSources"); + if (!mStarted) { + return; + } + if (mAudioDecoder == NULL || mVideoDecoder == NULL) { postScanSources(); } } +void NuPlayer::performSetSurface(const sp &wrapper) { + ALOGV("performSetSurface"); + + mNativeWindow = wrapper; + + // XXX - ignore error from setVideoScalingMode for now + setVideoScalingMode(mVideoScalingMode); + + if (mDriver != NULL) { + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifySetSurfaceComplete(); + } + } +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 6e174e0..ca87be9 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -42,7 +42,9 @@ struct NuPlayer : public AHandler { void setDataSource(int fd, int64_t offset, int64_t length); - void setVideoSurfaceTexture(const sp &surfaceTexture); + void setVideoSurfaceTextureAsync( + const sp &surfaceTexture); + void setAudioSink(const sp &sink); void start(); @@ -75,6 +77,7 @@ private: struct StreamingSource; struct Action; struct SeekAction; + struct SetSurfaceAction; struct SimpleAction; enum { @@ -140,6 +143,8 @@ private: int32_t mVideoScalingMode; + bool mStarted; + status_t instantiateDecoder(bool audio, sp *decoder); status_t feedDecoderInputData(bool audio, const sp &msg); @@ -165,6 +170,7 @@ private: void performDecoderShutdown(); void performReset(); void performScanSources(); + void performSetSurface(const sp &wrapper); DISALLOW_EVIL_CONSTRUCTORS(NuPlayer); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index d03601f..a485dda 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -29,6 +29,7 @@ namespace android { NuPlayerDriver::NuPlayerDriver() : mResetInProgress(false), + mSetSurfaceInProgress(false), mDurationUs(-1), mPositionUs(-1), mNumFramesTotal(0), @@ -97,7 +98,19 @@ status_t NuPlayerDriver::setDataSource(const sp &source) { status_t NuPlayerDriver::setVideoSurfaceTexture( const sp &surfaceTexture) { - mPlayer->setVideoSurfaceTexture(surfaceTexture); + Mutex::Autolock autoLock(mLock); + + if (mResetInProgress) { + return INVALID_OPERATION; + } + + mSetSurfaceInProgress = true; + + mPlayer->setVideoSurfaceTextureAsync(surfaceTexture); + + while (mSetSurfaceInProgress) { + mCondition.wait(mLock); + } return OK; } @@ -308,6 +321,13 @@ void NuPlayerDriver::notifyResetComplete() { mCondition.broadcast(); } +void NuPlayerDriver::notifySetSurfaceComplete() { + Mutex::Autolock autoLock(mLock); + CHECK(mSetSurfaceInProgress); + mSetSurfaceInProgress = false; + mCondition.broadcast(); +} + void NuPlayerDriver::notifyDuration(int64_t durationUs) { Mutex::Autolock autoLock(mLock); mDurationUs = durationUs; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 4a0026c..d551bf1 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -62,6 +62,7 @@ struct NuPlayerDriver : public MediaPlayerInterface { virtual status_t dump(int fd, const Vector &args) const; void notifyResetComplete(); + void notifySetSurfaceComplete(); void notifyDuration(int64_t durationUs); void notifyPosition(int64_t positionUs); void notifySeekComplete(); @@ -78,6 +79,7 @@ private: // The following are protected through "mLock" // >>> bool mResetInProgress; + bool mSetSurfaceInProgress; int64_t mDurationUs; int64_t mPositionUs; int64_t mNumFramesTotal; -- cgit v1.1 From a8190fc518b6769257896605f3aee091aeb60b50 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 3 Dec 2012 17:06:56 -0800 Subject: Split off the current control block to separate file Prepare for a new implementation of step() etc. Change-Id: I268421976ba577aa1fb5d7015de5441c05861190 --- media/libmedia/Android.mk | 1 + media/libmedia/AudioTrack.cpp | 176 -------------------------------- media/libmedia/AudioTrackShared.cpp | 196 ++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 176 deletions(-) create mode 100644 media/libmedia/AudioTrackShared.cpp (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index f2b6441..a35d562 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -13,6 +13,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ AudioTrack.cpp \ + AudioTrackShared.cpp \ IAudioFlinger.cpp \ IAudioFlingerClient.cpp \ IAudioTrack.cpp \ diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ac672a7..1d87ff8 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1482,180 +1482,4 @@ void AudioTrack::AudioTrackThread::resume() } } -// ========================================================================= - - -audio_track_cblk_t::audio_track_cblk_t() - : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), - userBase(0), serverBase(0), frameCount_(0), - loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000), - mSendLevel(0), flags(0) -{ -} - -uint32_t audio_track_cblk_t::stepUser(size_t stepCount, size_t frameCount, bool isOut) -{ - ALOGV("stepuser %08x %08x %d", user, server, stepCount); - - uint32_t u = user; - u += stepCount; - // Ensure that user is never ahead of server for AudioRecord - if (isOut) { - // If stepServer() has been called once, switch to normal obtainBuffer() timeout period - if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { - bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - } - } else if (u > server) { - ALOGW("stepUser occurred after track reset"); - u = server; - } - - if (u >= frameCount) { - // common case, user didn't just wrap - if (u - frameCount >= userBase ) { - userBase += frameCount; - } - } else if (u >= userBase + frameCount) { - // user just wrapped - userBase += frameCount; - } - - user = u; - - // Clear flow control error condition as new data has been written/read to/from buffer. - if (flags & CBLK_UNDERRUN) { - android_atomic_and(~CBLK_UNDERRUN, &flags); - } - - return u; -} - -bool audio_track_cblk_t::stepServer(size_t stepCount, size_t frameCount, bool isOut) -{ - ALOGV("stepserver %08x %08x %d", user, server, stepCount); - - if (!tryLock()) { - ALOGW("stepServer() could not lock cblk"); - return false; - } - - uint32_t s = server; - bool flushed = (s == user); - - s += stepCount; - if (isOut) { - // Mark that we have read the first buffer so that next time stepUser() is called - // we switch to normal obtainBuffer() timeout period - if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) { - bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1; - } - // It is possible that we receive a flush() - // while the mixer is processing a block: in this case, - // stepServer() is called After the flush() has reset u & s and - // we have s > u - if (flushed) { - ALOGW("stepServer occurred after track reset"); - s = user; - } - } - - if (s >= loopEnd) { - ALOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd); - s = loopStart; - if (--loopCount == 0) { - loopEnd = UINT_MAX; - loopStart = UINT_MAX; - } - } - - if (s >= frameCount) { - // common case, server didn't just wrap - if (s - frameCount >= serverBase ) { - serverBase += frameCount; - } - } else if (s >= serverBase + frameCount) { - // server just wrapped - serverBase += frameCount; - } - - server = s; - - if (!(flags & CBLK_INVALID)) { - cv.signal(); - } - lock.unlock(); - return true; -} - -void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offset) const -{ - return (int8_t *)buffers + (offset - userBase) * frameSize; -} - -uint32_t audio_track_cblk_t::framesAvailable(size_t frameCount, bool isOut) -{ - Mutex::Autolock _l(lock); - return framesAvailable_l(frameCount, isOut); -} - -uint32_t audio_track_cblk_t::framesAvailable_l(size_t frameCount, bool isOut) -{ - uint32_t u = user; - uint32_t s = server; - - if (isOut) { - uint32_t limit = (s < loopStart) ? s : loopStart; - return limit + frameCount - u; - } else { - return frameCount + u - s; - } -} - -uint32_t audio_track_cblk_t::framesReady(bool isOut) -{ - uint32_t u = user; - uint32_t s = server; - - if (isOut) { - if (u < loopEnd) { - return u - s; - } else { - // do not block on mutex shared with client on AudioFlinger side - if (!tryLock()) { - ALOGW("framesReady() could not lock cblk"); - return 0; - } - uint32_t frames = UINT_MAX; - if (loopCount >= 0) { - frames = (loopEnd - loopStart)*loopCount + u - s; - } - lock.unlock(); - return frames; - } - } else { - return s - u; - } -} - -bool audio_track_cblk_t::tryLock() -{ - // the code below simulates lock-with-timeout - // we MUST do this to protect the AudioFlinger server - // as this lock is shared with the client. - status_t err; - - err = lock.tryLock(); - if (err == -EBUSY) { // just wait a bit - usleep(1000); - err = lock.tryLock(); - } - if (err != NO_ERROR) { - // probably, the client just died. - return false; - } - return true; -} - -// ------------------------------------------------------------------------- - }; // namespace android diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp new file mode 100644 index 0000000..bee13c8 --- /dev/null +++ b/media/libmedia/AudioTrackShared.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioTrackShared" +//#define LOG_NDEBUG 0 + +#include +#include + +namespace android { + +audio_track_cblk_t::audio_track_cblk_t() + : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), + userBase(0), serverBase(0), frameCount_(0), + loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000), + mSendLevel(0), flags(0) +{ +} + +uint32_t audio_track_cblk_t::stepUser(size_t stepCount, size_t frameCount, bool isOut) +{ + ALOGV("stepuser %08x %08x %d", user, server, stepCount); + + uint32_t u = user; + u += stepCount; + // Ensure that user is never ahead of server for AudioRecord + if (isOut) { + // If stepServer() has been called once, switch to normal obtainBuffer() timeout period + if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { + bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + } + } else if (u > server) { + ALOGW("stepUser occurred after track reset"); + u = server; + } + + if (u >= frameCount) { + // common case, user didn't just wrap + if (u - frameCount >= userBase ) { + userBase += frameCount; + } + } else if (u >= userBase + frameCount) { + // user just wrapped + userBase += frameCount; + } + + user = u; + + // Clear flow control error condition as new data has been written/read to/from buffer. + if (flags & CBLK_UNDERRUN) { + android_atomic_and(~CBLK_UNDERRUN, &flags); + } + + return u; +} + +bool audio_track_cblk_t::stepServer(size_t stepCount, size_t frameCount, bool isOut) +{ + ALOGV("stepserver %08x %08x %d", user, server, stepCount); + + if (!tryLock()) { + ALOGW("stepServer() could not lock cblk"); + return false; + } + + uint32_t s = server; + bool flushed = (s == user); + + s += stepCount; + if (isOut) { + // Mark that we have read the first buffer so that next time stepUser() is called + // we switch to normal obtainBuffer() timeout period + if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) { + bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1; + } + // It is possible that we receive a flush() + // while the mixer is processing a block: in this case, + // stepServer() is called After the flush() has reset u & s and + // we have s > u + if (flushed) { + ALOGW("stepServer occurred after track reset"); + s = user; + } + } + + if (s >= loopEnd) { + ALOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd); + s = loopStart; + if (--loopCount == 0) { + loopEnd = UINT_MAX; + loopStart = UINT_MAX; + } + } + + if (s >= frameCount) { + // common case, server didn't just wrap + if (s - frameCount >= serverBase ) { + serverBase += frameCount; + } + } else if (s >= serverBase + frameCount) { + // server just wrapped + serverBase += frameCount; + } + + server = s; + + if (!(flags & CBLK_INVALID)) { + cv.signal(); + } + lock.unlock(); + return true; +} + +void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offset) const +{ + return (int8_t *)buffers + (offset - userBase) * frameSize; +} + +uint32_t audio_track_cblk_t::framesAvailable(size_t frameCount, bool isOut) +{ + Mutex::Autolock _l(lock); + return framesAvailable_l(frameCount, isOut); +} + +uint32_t audio_track_cblk_t::framesAvailable_l(size_t frameCount, bool isOut) +{ + uint32_t u = user; + uint32_t s = server; + + if (isOut) { + uint32_t limit = (s < loopStart) ? s : loopStart; + return limit + frameCount - u; + } else { + return frameCount + u - s; + } +} + +uint32_t audio_track_cblk_t::framesReady(bool isOut) +{ + uint32_t u = user; + uint32_t s = server; + + if (isOut) { + if (u < loopEnd) { + return u - s; + } else { + // do not block on mutex shared with client on AudioFlinger side + if (!tryLock()) { + ALOGW("framesReady() could not lock cblk"); + return 0; + } + uint32_t frames = UINT_MAX; + if (loopCount >= 0) { + frames = (loopEnd - loopStart)*loopCount + u - s; + } + lock.unlock(); + return frames; + } + } else { + return s - u; + } +} + +bool audio_track_cblk_t::tryLock() +{ + // the code below simulates lock-with-timeout + // we MUST do this to protect the AudioFlinger server + // as this lock is shared with the client. + status_t err; + + err = lock.tryLock(); + if (err == -EBUSY) { // just wait a bit + usleep(1000); + err = lock.tryLock(); + } + if (err != NO_ERROR) { + // probably, the client just died. + return false; + } + return true; +} + +} // namespace android -- cgit v1.1 From 516dacfb02d0b0eafe21114330c98ce0e7d90da9 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 3 Dec 2012 15:20:40 -0800 Subject: Respect sample aspect ratio in NuPlayer. related-to-bug: 7569402 Change-Id: I302de95d83b180bd2dc72ddd0c69a665dbce2527 --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 41 +++++++++--- media/libstagefright/ACodec.cpp | 14 +++-- media/libstagefright/Utils.cpp | 14 +++++ media/libstagefright/avc_utils.cpp | 76 +++++++++++++++++++++-- media/libstagefright/include/avc_utils.h | 5 +- 5 files changed, 131 insertions(+), 19 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index f363568..746055c 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -448,7 +448,8 @@ void NuPlayer::onMessageReceived(const sp &msg) { } else if (what == ACodec::kWhatOutputFormatChanged) { if (audio) { int32_t numChannels; - CHECK(codecRequest->findInt32("channel-count", &numChannels)); + CHECK(codecRequest->findInt32( + "channel-count", &numChannels)); int32_t sampleRate; CHECK(codecRequest->findInt32("sample-rate", &sampleRate)); @@ -460,13 +461,15 @@ void NuPlayer::onMessageReceived(const sp &msg) { audio_output_flags_t flags; int64_t durationUs; - // FIXME: we should handle the case where the video decoder is created after - // we receive the format change indication. Current code will just make that - // we select deep buffer with video which should not be a problem as it should + // FIXME: we should handle the case where the video decoder + // is created after we receive the format change indication. + // Current code will just make that we select deep buffer + // with video which should not be a problem as it should // not prevent from keeping A/V sync. if (mVideoDecoder == NULL && mSource->getDuration(&durationUs) == OK && - durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { + durationUs + > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; } else { flags = AUDIO_OUTPUT_FLAG_NONE; @@ -502,17 +505,35 @@ void NuPlayer::onMessageReceived(const sp &msg) { "crop", &cropLeft, &cropTop, &cropRight, &cropBottom)); + int32_t displayWidth = cropRight - cropLeft + 1; + int32_t displayHeight = cropBottom - cropTop + 1; + ALOGV("Video output format changed to %d x %d " "(crop: %d x %d @ (%d, %d))", width, height, - (cropRight - cropLeft + 1), - (cropBottom - cropTop + 1), + displayWidth, + displayHeight, cropLeft, cropTop); + sp videoInputFormat = + mSource->getFormat(false /* audio */); + + // Take into account sample aspect ratio if necessary: + int32_t sarWidth, sarHeight; + if (videoInputFormat->findInt32("sar-width", &sarWidth) + && videoInputFormat->findInt32( + "sar-height", &sarHeight)) { + ALOGV("Sample aspect ratio %d : %d", + sarWidth, sarHeight); + + displayWidth = (displayWidth * sarWidth) / sarHeight; + + ALOGV("display dimensions %d x %d", + displayWidth, displayHeight); + } + notifyListener( - MEDIA_SET_VIDEO_SIZE, - cropRight - cropLeft + 1, - cropBottom - cropTop + 1); + MEDIA_SET_VIDEO_SIZE, displayWidth, displayHeight); } } else if (what == ACodec::kWhatShutdownCompleted) { ALOGV("%s shutdown completed", audio ? "audio" : "video"); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 2a7b2ae..2b20ab0 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -966,17 +966,23 @@ status_t ACodec::configureCodec( err = INVALID_OPERATION; } else { if (encoder) { - if (!msg->findInt32("flac-compression-level", &compressionLevel)) { + if (!msg->findInt32( + "flac-compression-level", &compressionLevel)) { compressionLevel = 5;// default FLAC compression level } else if (compressionLevel < 0) { - ALOGW("compression level %d outside [0..8] range, using 0", compressionLevel); + ALOGW("compression level %d outside [0..8] range, " + "using 0", + compressionLevel); compressionLevel = 0; } else if (compressionLevel > 8) { - ALOGW("compression level %d outside [0..8] range, using 8", compressionLevel); + ALOGW("compression level %d outside [0..8] range, " + "using 8", + compressionLevel); compressionLevel = 8; } } - err = setupFlacCodec(encoder, numChannels, sampleRate, compressionLevel); + err = setupFlacCodec( + encoder, numChannels, sampleRate, compressionLevel); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { int32_t numChannels, sampleRate; diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 74e9222..1a6ff4b 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -85,6 +85,13 @@ status_t convertMetaDataToMessage( msg->setInt32("width", width); msg->setInt32("height", height); + + int32_t sarWidth, sarHeight; + if (meta->findInt32(kKeySARWidth, &sarWidth) + && meta->findInt32(kKeySARHeight, &sarHeight)) { + msg->setInt32("sar-width", sarWidth); + msg->setInt32("sar-height", sarHeight); + } } else if (!strncasecmp("audio/", mime, 6)) { int32_t numChannels, sampleRate; CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); @@ -372,6 +379,13 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { } else { ALOGW("did not find width and/or height"); } + + int32_t sarWidth, sarHeight; + if (msg->findInt32("sar-width", &sarWidth) + && msg->findInt32("sar-height", &sarHeight)) { + meta->setInt32(kKeySARWidth, sarWidth); + meta->setInt32(kKeySARHeight, sarHeight); + } } else if (mime.startsWith("audio/")) { int32_t numChannels; if (msg->findInt32("channel-count", &numChannels)) { diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index a141752..b822868 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -41,7 +42,9 @@ unsigned parseUE(ABitReader *br) { // Determine video dimensions from the sequence parameterset. void FindAVCDimensions( - const sp &seqParamSet, int32_t *width, int32_t *height) { + const sp &seqParamSet, + int32_t *width, int32_t *height, + int32_t *sarWidth, int32_t *sarHeight) { ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1); unsigned profile_idc = br.getBits(8); @@ -129,6 +132,48 @@ void FindAVCDimensions( *height -= (frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY; } + + if (sarWidth != NULL) { + *sarWidth = 0; + } + + if (sarHeight != NULL) { + *sarHeight = 0; + } + + if (br.getBits(1)) { // vui_parameters_present_flag + unsigned sar_width = 0, sar_height = 0; + + if (br.getBits(1)) { // aspect_ratio_info_present_flag + unsigned aspect_ratio_idc = br.getBits(8); + + if (aspect_ratio_idc == 255 /* extendedSAR */) { + sar_width = br.getBits(16); + sar_height = br.getBits(16); + } else if (aspect_ratio_idc > 0 && aspect_ratio_idc < 14) { + static const int32_t kFixedSARWidth[] = { + 1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160 + }; + + static const int32_t kFixedSARHeight[] = { + 1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99 + }; + + sar_width = kFixedSARWidth[aspect_ratio_idc - 1]; + sar_height = kFixedSARHeight[aspect_ratio_idc - 1]; + } + } + + ALOGV("sample aspect ratio = %u : %u", sar_width, sar_height); + + if (sarWidth != NULL) { + *sarWidth = sar_width; + } + + if (sarHeight != NULL) { + *sarHeight = sar_height; + } + } } status_t getNextNALUnit( @@ -254,7 +299,9 @@ sp MakeAVCCodecSpecificData(const sp &accessUnit) { } int32_t width, height; - FindAVCDimensions(seqParamSet, &width, &height); + int32_t sarWidth, sarHeight; + FindAVCDimensions( + seqParamSet, &width, &height, &sarWidth, &sarHeight); size_t stopOffset; sp picParamSet = FindNAL(data, size, 8, &stopOffset); @@ -301,8 +348,29 @@ sp MakeAVCCodecSpecificData(const sp &accessUnit) { meta->setInt32(kKeyWidth, width); meta->setInt32(kKeyHeight, height); - ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)", - width, height, AVCProfileToString(profile), level / 10, level % 10); + if (sarWidth > 1 || sarHeight > 1) { + // We treat 0:0 (unspecified) as 1:1. + + meta->setInt32(kKeySARWidth, sarWidth); + meta->setInt32(kKeySARHeight, sarHeight); + + ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d) " + "SAR %d : %d", + width, + height, + AVCProfileToString(profile), + level / 10, + level % 10, + sarWidth, + sarHeight); + } else { + ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)", + width, + height, + AVCProfileToString(profile), + level / 10, + level % 10); + } return meta; } diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h index e418822..d517320 100644 --- a/media/libstagefright/include/avc_utils.h +++ b/media/libstagefright/include/avc_utils.h @@ -36,8 +36,11 @@ enum { kAVCProfileCAVLC444Intra = 0x2c }; +// Optionally returns sample aspect ratio as well. void FindAVCDimensions( - const sp &seqParamSet, int32_t *width, int32_t *height); + const sp &seqParamSet, + int32_t *width, int32_t *height, + int32_t *sarWidth = NULL, int32_t *sarHeight = NULL); unsigned parseUE(ABitReader *br); -- cgit v1.1 From 86355f5b1ef6c6434d8717c71428e3165b0fe7b5 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 11 Dec 2012 15:34:18 -0800 Subject: Fix timestamps after seek Ensure buffers are correctly timestamped after a seek. Change-Id: I7d76689138e4f95c0ceb9fb7a4c4d42c48568212 --- media/libstagefright/mp4/FragmentedMP4Parser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/mp4/FragmentedMP4Parser.cpp b/media/libstagefright/mp4/FragmentedMP4Parser.cpp index 54c3d63..7aa5be9 100644 --- a/media/libstagefright/mp4/FragmentedMP4Parser.cpp +++ b/media/libstagefright/mp4/FragmentedMP4Parser.cpp @@ -319,8 +319,7 @@ status_t FragmentedMP4Parser::onSeekTo(bool wantAudio, int64_t position) { off_t totalOffset = mFirstMoofOffset; for (int i = 0; i < numSidxEntries; i++) { const SidxEntry *se = &info->mSidx[i]; - totalTime += se->mDurationUs; - if (totalTime > position) { + if (totalTime + se->mDurationUs > position) { mBuffer->setRange(0,0); mBufferPos = totalOffset; if (mFinalResult == ERROR_END_OF_STREAM) { @@ -329,9 +328,10 @@ status_t FragmentedMP4Parser::onSeekTo(bool wantAudio, int64_t position) { resumeIfNecessary(); } info->mFragments.clear(); - info->mDecodingTime = position * info->mMediaTimeScale / 1000000ll; + info->mDecodingTime = totalTime * info->mMediaTimeScale / 1000000ll; return OK; } + totalTime += se->mDurationUs; totalOffset += se->mSize; } } -- cgit v1.1 From e3aa659e9cee7df5c12a80d285cc29ab3b2cbb39 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 4 Dec 2012 12:22:46 -0800 Subject: Start isolating control block accesses in a proxy The proxy object will eventually be the only code that understands the details of the control block. This should make it easier to change the control block in the future. Initial set of control block fields that are isolated: - sample rate - send level - volume Prepare for streaming/static separation by adding a union to the control block for the new fields. Fix bug in handling of max sample rate on a track. It was only checking at re-configuration, not at each mix. Simplify OutputTrack::obtainBuffer. Change-Id: I2249f9d04f73a911a922ad1d7f6197292c74cd92 --- media/libmedia/AudioRecord.cpp | 47 ++++++++++------ media/libmedia/AudioTrack.cpp | 109 +++++++++++++++++++++++------------- media/libmedia/AudioTrackShared.cpp | 2 +- 3 files changed, 103 insertions(+), 55 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index c2ef68c..8eb1656 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -75,7 +75,8 @@ status_t AudioRecord::getMinFrameCount( AudioRecord::AudioRecord() : mStatus(NO_INIT), mSessionId(0), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), + mProxy(NULL) { } @@ -90,7 +91,9 @@ AudioRecord::AudioRecord( int notificationFrames, int sessionId) : mStatus(NO_INIT), mSessionId(0), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) + mPreviousPriority(ANDROID_PRIORITY_NORMAL), + mPreviousSchedulingGroup(SP_DEFAULT), + mProxy(NULL) { mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user, notificationFrames, sessionId); @@ -112,6 +115,7 @@ AudioRecord::~AudioRecord() IPCThreadState::self()->flushCommands(); AudioSystem::releaseAudioSessionId(mSessionId); } + delete mProxy; } status_t AudioRecord::set( @@ -149,6 +153,8 @@ status_t AudioRecord::set( if (sampleRate == 0) { sampleRate = DEFAULT_SAMPLE_RATE; } + mSampleRate = sampleRate; + // these below should probably come from the audioFlinger too... if (format == AUDIO_FORMAT_DEFAULT) { format = AUDIO_FORMAT_PCM_16_BIT; @@ -166,6 +172,12 @@ status_t AudioRecord::set( uint32_t channelCount = popcount(channelMask); mChannelCount = channelCount; + if (audio_is_linear_pcm(mFormat)) { + mFrameSize = channelCount * audio_bytes_per_sample(format); + } else { + mFrameSize = sizeof(uint8_t); + } + if (sessionId == 0 ) { mSessionId = AudioSystem::newAudioSessionId(); } else { @@ -218,12 +230,6 @@ status_t AudioRecord::set( // Update buffer size in case it has been limited by AudioFlinger during track creation mFrameCount = mCblk->frameCount_; - if (audio_is_linear_pcm(mFormat)) { - mFrameSize = channelCount * audio_bytes_per_sample(format); - } else { - mFrameSize = sizeof(uint8_t); - } - mActive = false; mCbf = cbf; mNotificationFrames = notificationFrames; @@ -360,7 +366,7 @@ bool AudioRecord::stopped() const uint32_t AudioRecord::getSampleRate() const { - return mCblk->sampleRate; + return mSampleRate; } status_t AudioRecord::setMarkerPosition(uint32_t marker) @@ -473,11 +479,18 @@ status_t AudioRecord::openRecord_l( mBuffers = (char*)cblk + sizeof(audio_track_cblk_t); cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; cblk->waitTimeMs = 0; + + // update proxy + delete mProxy; + mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize); + return NO_ERROR; } status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { + ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + AutoMutex lock(mLock); bool active; status_t result = NO_ERROR; @@ -488,7 +501,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) audioBuffer->frameCount = 0; audioBuffer->size = 0; - uint32_t framesReady = cblk->framesReadyIn(); + size_t framesReady = mProxy->framesReady(); if (framesReady == 0) { cblk->lock.lock(); @@ -551,7 +564,7 @@ create_new_record: } // read the server count again start_loop_here: - framesReady = cblk->framesReadyIn(); + framesReady = mProxy->framesReady(); } cblk->lock.unlock(); } @@ -573,15 +586,17 @@ create_new_record: audioBuffer->frameCount = framesReq; audioBuffer->size = framesReq * mFrameSize; - audioBuffer->raw = cblk->buffer(mBuffers, mFrameSize, u); + audioBuffer->raw = mProxy->buffer(u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } void AudioRecord::releaseBuffer(Buffer* audioBuffer) { + ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + AutoMutex lock(mLock); - mCblk->stepUserIn(audioBuffer->frameCount, mFrameCount); + (void) mProxy->stepUser(audioBuffer->frameCount); } audio_io_handle_t AudioRecord::getInput() const @@ -594,7 +609,7 @@ audio_io_handle_t AudioRecord::getInput() const audio_io_handle_t AudioRecord::getInput_l() { mInput = AudioSystem::getInput(mInputSource, - mCblk->sampleRate, + mSampleRate, mFormat, mChannelMask, mSessionId); @@ -745,7 +760,7 @@ bool AudioRecord::processAudioBuffer(const sp& thread) // Manage overrun callback - if (active && (cblk->framesAvailableIn(mFrameCount) == 0)) { + if (active && (mProxy->framesAvailable() == 0)) { // The value of active is stale, but we are almost sure to be active here because // otherwise we would have exited when obtainBuffer returned STOPPED earlier. ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); @@ -781,7 +796,7 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk) // if the new IAudioRecord is created, openRecord_l() will modify the // following member variables: mAudioRecord, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioRecord and IMemory - result = openRecord_l(cblk->sampleRate, mFormat, mFrameCount, getInput_l()); + result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l()); if (result == NO_ERROR) { newCblk = mCblk; // callback thread or sync event hasn't changed diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 1d87ff8..86a5579 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -97,7 +97,8 @@ AudioTrack::AudioTrack() : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), - mPreviousSchedulingGroup(SP_DEFAULT) + mPreviousSchedulingGroup(SP_DEFAULT), + mProxy(NULL) { } @@ -115,7 +116,8 @@ AudioTrack::AudioTrack( : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), - mPreviousSchedulingGroup(SP_DEFAULT) + mPreviousSchedulingGroup(SP_DEFAULT), + mProxy(NULL) { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, @@ -136,7 +138,8 @@ AudioTrack::AudioTrack( : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), - mPreviousSchedulingGroup(SP_DEFAULT) + mPreviousSchedulingGroup(SP_DEFAULT), + mProxy(NULL) { if (sharedBuffer == 0) { ALOGE("sharedBuffer must be non-0"); @@ -166,6 +169,7 @@ AudioTrack::~AudioTrack() IPCThreadState::self()->flushCommands(); AudioSystem::releaseAudioSessionId(mSessionId); } + delete mProxy; } status_t AudioTrack::set( @@ -212,6 +216,7 @@ status_t AudioTrack::set( } sampleRate = afSampleRate; } + mSampleRate = sampleRate; // these below should probably come from the audioFlinger too... if (format == AUDIO_FORMAT_DEFAULT) { @@ -252,6 +257,14 @@ status_t AudioTrack::set( uint32_t channelCount = popcount(channelMask); mChannelCount = channelCount; + if (audio_is_linear_pcm(format)) { + mFrameSize = channelCount * audio_bytes_per_sample(format); + mFrameSizeAF = channelCount * sizeof(int16_t); + } else { + mFrameSize = sizeof(uint8_t); + mFrameSizeAF = sizeof(uint8_t); + } + audio_io_handle_t output = AudioSystem::getOutput( streamType, sampleRate, format, channelMask, @@ -300,14 +313,6 @@ status_t AudioTrack::set( mStreamType = streamType; mFormat = format; - if (audio_is_linear_pcm(format)) { - mFrameSize = channelCount * audio_bytes_per_sample(format); - mFrameSizeAF = channelCount * sizeof(int16_t); - } else { - mFrameSize = sizeof(uint8_t); - mFrameSizeAF = sizeof(uint8_t); - } - mSharedBuffer = sharedBuffer; mActive = false; mUserData = user; @@ -460,6 +465,11 @@ void AudioTrack::pause() status_t AudioTrack::setVolume(float left, float right) { + if (mStatus != NO_ERROR) { + return mStatus; + } + ALOG_ASSERT(mProxy != NULL); + if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) { return BAD_VALUE; } @@ -468,7 +478,7 @@ status_t AudioTrack::setVolume(float left, float right) mVolume[LEFT] = left; mVolume[RIGHT] = right; - mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000)); + mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000)); return NO_ERROR; } @@ -481,14 +491,19 @@ status_t AudioTrack::setVolume(float volume) status_t AudioTrack::setAuxEffectSendLevel(float level) { ALOGV("setAuxEffectSendLevel(%f)", level); + + if (mStatus != NO_ERROR) { + return mStatus; + } + ALOG_ASSERT(mProxy != NULL); + if (level < 0.0f || level > 1.0f) { return BAD_VALUE; } AutoMutex lock(mLock); mSendLevel = level; - - mCblk->setSendLevel(level); + mProxy->setSendLevel(level); return NO_ERROR; } @@ -517,7 +532,9 @@ status_t AudioTrack::setSampleRate(uint32_t rate) } AutoMutex lock(mLock); - mCblk->sampleRate = rate; + mSampleRate = rate; + mProxy->setSampleRate(rate); + return NO_ERROR; } @@ -528,7 +545,7 @@ uint32_t AudioTrack::getSampleRate() const } AutoMutex lock(mLock); - return mCblk->sampleRate; + return mSampleRate; } status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) @@ -665,6 +682,11 @@ status_t AudioTrack::getPosition(uint32_t *position) status_t AudioTrack::reload() { + if (mStatus != NO_ERROR) { + return mStatus; + } + ALOG_ASSERT(mProxy != NULL); + if (mSharedBuffer == 0 || mIsTimed) { return INVALID_OPERATION; } @@ -677,8 +699,7 @@ status_t AudioTrack::reload() flush_l(); - audio_track_cblk_t* cblk = mCblk; - cblk->stepUserOut(mFrameCount, mFrameCount); + (void) mProxy->stepUser(mFrameCount); return NO_ERROR; } @@ -693,7 +714,7 @@ audio_io_handle_t AudioTrack::getOutput() audio_io_handle_t AudioTrack::getOutput_l() { return AudioSystem::getOutput(mStreamType, - mCblk->sampleRate, mFormat, mChannelMask, mFlags); + mSampleRate, mFormat, mChannelMask, mFlags); } status_t AudioTrack::attachAuxEffect(int effectId) @@ -890,13 +911,8 @@ status_t AudioTrack::createTrack_l( mBuffers = (char*)cblk + sizeof(audio_track_cblk_t); } else { mBuffers = sharedBuffer->pointer(); - // Force buffer full condition as data is already present in shared memory - cblk->stepUserOut(frameCount, frameCount); } - cblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | - uint16_t(mVolume[LEFT] * 0x1000)); - cblk->setSendLevel(mSendLevel); mAudioTrack->attachAuxEffect(mAuxEffectId); cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; cblk->waitTimeMs = 0; @@ -909,11 +925,26 @@ status_t AudioTrack::createTrack_l( if (frameCount > mReqFrameCount) { mReqFrameCount = frameCount; } + + // update proxy + delete mProxy; + mProxy = new AudioTrackClientProxy(cblk, mBuffers, frameCount, mFrameSizeAF); + mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | + uint16_t(mVolume[LEFT] * 0x1000)); + mProxy->setSendLevel(mSendLevel); + mProxy->setSampleRate(mSampleRate); + if (sharedBuffer != 0) { + // Force buffer full condition as data is already present in shared memory + mProxy->stepUser(frameCount); + } + return NO_ERROR; } status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { + ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + AutoMutex lock(mLock); bool active; status_t result = NO_ERROR; @@ -924,7 +955,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) audioBuffer->frameCount = 0; audioBuffer->size = 0; - uint32_t framesAvail = cblk->framesAvailableOut(mFrameCount); + size_t framesAvail = mProxy->framesAvailable(); cblk->lock.lock(); if (cblk->flags & CBLK_INVALID) { @@ -1000,7 +1031,7 @@ create_new_track: } // read the server count again start_loop_here: - framesAvail = cblk->framesAvailableOut_l(mFrameCount); + framesAvail = mProxy->framesAvailable_l(); } cblk->lock.unlock(); } @@ -1020,16 +1051,18 @@ create_new_track: audioBuffer->frameCount = framesReq; audioBuffer->size = framesReq * mFrameSizeAF; - audioBuffer->raw = cblk->buffer(mBuffers, mFrameSizeAF, u); + audioBuffer->raw = mProxy->buffer(u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } void AudioTrack::releaseBuffer(Buffer* audioBuffer) { + ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + AutoMutex lock(mLock); audio_track_cblk_t* cblk = mCblk; - cblk->stepUserOut(audioBuffer->frameCount, mFrameCount); + (void) mProxy->stepUser(audioBuffer->frameCount); if (audioBuffer->frameCount > 0) { // restart track if it was disabled by audioflinger due to previous underrun if (mActive && (cblk->flags & CBLK_DISABLED)) { @@ -1199,7 +1232,7 @@ bool AudioTrack::processAudioBuffer(const sp& thread) // so all cblk references might still refer to old shared memory, but that should be benign // Manage underrun callback - if (active && (cblk->framesAvailableOut(mFrameCount) == mFrameCount)) { + if (active && (mProxy->framesAvailable() == mFrameCount)) { ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) { mCbf(EVENT_UNDERRUN, mUserData, 0); @@ -1346,7 +1379,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart // following member variables: mAudioTrack, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioTrack and IMemory result = createTrack_l(mStreamType, - cblk->sampleRate, + mSampleRate, mFormat, mReqFrameCount, // so that frame count never goes down mFlags, @@ -1365,12 +1398,12 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart // restore loop: this is not guaranteed to succeed if new frame count is not // compatible with loop length setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount); + size_t frames = 0; if (!fromStart) { newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; // Make sure that a client relying on callback events indicating underrun or // the actual amount of audio frames played (e.g SoundPool) receives them. if (mSharedBuffer == 0) { - uint32_t frames = 0; if (user > server) { frames = ((user - server) > mFrameCount) ? mFrameCount : (user - server); @@ -1378,13 +1411,15 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart } // restart playback even if buffer is not completely filled. android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); - // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to - // the client - newCblk->stepUserOut(frames, mFrameCount); } } if (mSharedBuffer != 0) { - newCblk->stepUserOut(mFrameCount, mFrameCount); + frames = mFrameCount; + } + if (frames > 0) { + // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to + // the client + mProxy->stepUser(frames); } if (mActive) { result = mAudioTrack->start(); @@ -1416,7 +1451,6 @@ status_t AudioTrack::dump(int fd, const Vector& args) const char buffer[SIZE]; String8 result; - audio_track_cblk_t* cblk = mCblk; result.append(" AudioTrack::dump\n"); snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mVolume[0], mVolume[1]); @@ -1424,8 +1458,7 @@ status_t AudioTrack::dump(int fd, const Vector& args) const snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%u), status(%d)\n", - (cblk == 0) ? 0 : cblk->sampleRate, mStatus); + snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus); result.append(buffer); snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index bee13c8..13d47c9 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -26,7 +26,7 @@ audio_track_cblk_t::audio_track_cblk_t() : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), userBase(0), serverBase(0), frameCount_(0), loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000), - mSendLevel(0), flags(0) + mSampleRate(0), mSendLevel(0), flags(0) { } -- cgit v1.1 From 308ca621005ab86847b1b1dabaf65a2521844a2a Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 12 Dec 2012 11:49:23 -0800 Subject: Increase buffer size for video Change-Id: I055e1336954387f7b48aa58d893a3a5fae036ece --- media/libstagefright/FragmentedMP4Extractor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/FragmentedMP4Extractor.cpp b/media/libstagefright/FragmentedMP4Extractor.cpp index 82712ef..10655b9 100644 --- a/media/libstagefright/FragmentedMP4Extractor.cpp +++ b/media/libstagefright/FragmentedMP4Extractor.cpp @@ -222,8 +222,8 @@ status_t FragmentedMPEG4Source::start(MetaData *params) { mGroup = new MediaBufferGroup; - int32_t max_size = 65536; - // XXX CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size)); + // for video, make the buffer big enough for an extremely poorly compressed 1080p frame. + int32_t max_size = mIsAudioTrack ? 65536 : 3110400; mGroup->add_buffer(new MediaBuffer(max_size)); -- cgit v1.1 From 8c95fa91fff6e8726df03598d52243f22e5ff8e7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 13 Dec 2012 11:10:05 -0800 Subject: Signal that IDR frames are sync frames Change-Id: Iaf77edc0572cae38935fd9d94367adbfcb370985 --- media/libstagefright/FragmentedMP4Extractor.cpp | 4 ++++ media/libstagefright/Utils.cpp | 10 ++++++++++ media/libstagefright/mp4/FragmentedMP4Parser.cpp | 8 ++++++++ 3 files changed, 22 insertions(+) (limited to 'media') diff --git a/media/libstagefright/FragmentedMP4Extractor.cpp b/media/libstagefright/FragmentedMP4Extractor.cpp index 10655b9..496828d 100644 --- a/media/libstagefright/FragmentedMP4Extractor.cpp +++ b/media/libstagefright/FragmentedMP4Extractor.cpp @@ -278,6 +278,10 @@ status_t FragmentedMPEG4Source::read( sp meta = parseBuffer->meta(); int64_t timeUs; CHECK(meta->findInt64("timeUs", &timeUs)); + int32_t isSync; + if (meta->findInt32("is-sync-frame", &isSync) && isSync != 0) { + buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + } buffer->meta_data()->setInt64(kKeyTime, timeUs); buffer->set_range(0, parseBuffer->size()); memcpy(buffer->data(), parseBuffer->data(), parseBuffer->size()); diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 1a6ff4b..8ed07bf 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -78,6 +78,11 @@ status_t convertMetaDataToMessage( msg->setInt64("durationUs", durationUs); } + int32_t isSync; + if (meta->findInt32(kKeyIsSyncFrame, &isSync) && isSync != 0) { + msg->setInt32("is-sync-frame", 1); + } + if (!strncasecmp("video/", mime, 6)) { int32_t width, height; CHECK(meta->findInt32(kKeyWidth, &width)); @@ -370,6 +375,11 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { meta->setInt64(kKeyDuration, durationUs); } + int32_t isSync; + if (msg->findInt32("is-sync-frame", &isSync) && isSync != 0) { + meta->setInt32(kKeyIsSyncFrame, 1); + } + if (mime.startsWith("video/")) { int32_t width; int32_t height; diff --git a/media/libstagefright/mp4/FragmentedMP4Parser.cpp b/media/libstagefright/mp4/FragmentedMP4Parser.cpp index 7aa5be9..6130d72 100644 --- a/media/libstagefright/mp4/FragmentedMP4Parser.cpp +++ b/media/libstagefright/mp4/FragmentedMP4Parser.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "FragmentedMP4Parser" #include +#include "include/avc_utils.h" #include "include/ESDS.h" #include "include/FragmentedMP4Parser.h" #include "TrackFragment.h" @@ -961,6 +962,10 @@ status_t FragmentedMP4Parser::makeAccessUnit( sample.mSize); (*accessUnit)->meta()->setInt64("timeUs", presentationTimeUs); + if (IsIDR(*accessUnit)) { + (*accessUnit)->meta()->setInt32("is-sync-frame", 1); + } + return OK; } @@ -1003,6 +1008,9 @@ status_t FragmentedMP4Parser::makeAccessUnit( "timeUs", presentationTimeUs); } } + if (IsIDR(*accessUnit)) { + (*accessUnit)->meta()->setInt32("is-sync-frame", 1); + } return OK; } -- cgit v1.1 From ef3d158d102b64513ebb0707b49eb99566b067a6 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Fri, 14 Dec 2012 13:49:48 -0800 Subject: SurfaceMediaSource: wait on fence from acquired buffers Change-Id: I4ab93a4adeec536648258c70a7d943503d9b10f4 Signed-off-by: Greg Hackmann --- media/libstagefright/SurfaceMediaSource.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'media') diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 3c002fc..0345de6 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -298,6 +298,10 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer, // wait for a buffer to be queued mFrameAvailableCondition.wait(mMutex); } else if (err == OK) { + err = item.mFence->waitForever(1000, "SurfaceMediaSource::read"); + if (err) { + ALOGW("read: failed to wait for buffer fence: %d", err); + } // First time seeing the buffer? Added it to the SMS slot if (item.mGraphicBuffer != NULL) { -- cgit v1.1 From 89b629b398e87095cf262692f4e476d605fe87ed Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 17 Dec 2012 11:44:20 -0800 Subject: Fix bug with discarded AudioRecord::read count Formerly, if an AudioRecord::read() got a timeout on obtainBuffer() after already successfully transferring some data, then it returned zero. This had the effect of discarding a partial transfer, which resulted in a gap in the audio data delivered to the app. Now if a timeout occurs after a partial transfer, it returns that partial transfer count so that no data is lost. Change-Id: I0d9c2f4e495a400b56ef916a06613ba26537ca97 --- media/libmedia/AudioRecord.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index c2ef68c..9fda0a5 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -644,7 +644,8 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) break; } if (err == status_t(TIMED_OUT)) { - err = 0; + // return partial transfer count + return read; } return ssize_t(err); } -- cgit v1.1 From 7b670d4a0a4fa560f536f132e0a3fc7247f6724c Mon Sep 17 00:00:00 2001 From: James Dong Date: Thu, 13 Dec 2012 18:58:38 -0800 Subject: Fix memory leakage from MPEG4Writer. o The in-memory cache, mMoovBoxBuffer, holding the content for Moov box may not be freed. o Added comment describing how the in-memory cache works o Moved the memory release to a single place to make the code more robust o Avoided allocating the in-memory cache if the file is not intended to be streamable o related-to-bug: 7664029 Change-Id: If04fc6b12daeaaa86710dfb4b4b9c175da6421df --- media/libstagefright/MPEG4Writer.cpp | 90 ++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 18 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 8b52e15..14986b2 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -575,13 +575,50 @@ status_t MPEG4Writer::start(MetaData *param) { /* * When the requested file size limit is small, the priority * is to meet the file size limit requirement, rather than - * to make the file streamable. + * to make the file streamable. mStreamableFile does not tell + * whether the actual recorded file is streamable or not. */ mStreamableFile = (mMaxFileSizeLimitBytes != 0 && mMaxFileSizeLimitBytes >= kMinStreamableFileSizeInBytes); - mWriteMoovBoxToMemory = mStreamableFile; + /* + * mWriteMoovBoxToMemory is true if the amount of data in moov box is + * smaller than the reserved free space at the beginning of a file, AND + * when the content of moov box is constructed. Note that video/audio + * frame data is always written to the file but not in the memory. + * + * Before stop()/reset() is called, mWriteMoovBoxToMemory is always + * false. When reset() is called at the end of a recording session, + * Moov box needs to be constructed. + * + * 1) Right before a moov box is constructed, mWriteMoovBoxToMemory + * to set to mStreamableFile so that if + * the file is intended to be streamable, it is set to true; + * otherwise, it is set to false. When the value is set to false, + * all the content of the moov box is written immediately to + * the end of the file. When the value is set to true, all the + * content of the moov box is written to an in-memory cache, + * mMoovBoxBuffer, util the following condition happens. Note + * that the size of the in-memory cache is the same as the + * reserved free space at the beginning of the file. + * + * 2) While the data of the moov box is written to an in-memory + * cache, the data size is checked against the reserved space. + * If the data size surpasses the reserved space, subsequent moov + * data could no longer be hold in the in-memory cache. This also + * indicates that the reserved space was too small. At this point, + * _all_ moov data must be written to the end of the file. + * mWriteMoovBoxToMemory must be set to false to direct the write + * to the file. + * + * 3) If the data size in moov box is smaller than the reserved + * space after moov box is completely constructed, the in-memory + * cache copy of the moov box is written to the reserved free + * space. Thus, immediately after the moov is completedly + * constructed, mWriteMoovBoxToMemory is always set to false. + */ + mWriteMoovBoxToMemory = false; mMoovBoxBuffer = NULL; mMoovBoxBufferOffset = 0; @@ -786,15 +823,25 @@ status_t MPEG4Writer::reset() { } lseek64(mFd, mOffset, SEEK_SET); - const off64_t moovOffset = mOffset; - mWriteMoovBoxToMemory = mStreamableFile; - mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); + // Construct moov box now mMoovBoxBufferOffset = 0; - CHECK(mMoovBoxBuffer != NULL); + mWriteMoovBoxToMemory = mStreamableFile; + if (mWriteMoovBoxToMemory) { + // There is no need to allocate in-memory cache + // for moov box if the file is not streamable. + + mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); + CHECK(mMoovBoxBuffer != NULL); + } writeMoovBox(maxDurationUs); - mWriteMoovBoxToMemory = false; - if (mStreamableFile) { + // mWriteMoovBoxToMemory could be set to false in + // MPEG4Writer::write() method + if (mWriteMoovBoxToMemory) { + mWriteMoovBoxToMemory = false; + // Content of the moov box is saved in the cache, and the in-memory + // moov box needs to be written to the file in a single shot. + CHECK_LE(mMoovBoxBufferOffset + 8, mEstimatedMoovBoxSize); // Moov box @@ -806,13 +853,15 @@ status_t MPEG4Writer::reset() { lseek64(mFd, mOffset, SEEK_SET); writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset); write("free", 4); + } else { + ALOGI("The mp4 file will not be streamable."); + } - // Free temp memory + // Free in-memory cache for moov box + if (mMoovBoxBuffer != NULL) { free(mMoovBoxBuffer); mMoovBoxBuffer = NULL; mMoovBoxBufferOffset = 0; - } else { - ALOGI("The mp4 file will not be streamable."); } CHECK(mBoxes.empty()); @@ -994,23 +1043,28 @@ size_t MPEG4Writer::write( const size_t bytes = size * nmemb; if (mWriteMoovBoxToMemory) { - // This happens only when we write the moov box at the end of - // recording, not for each output video/audio frame we receive. + off64_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes; if (moovBoxSize > mEstimatedMoovBoxSize) { + // The reserved moov box at the beginning of the file + // is not big enough. Moov box should be written to + // the end of the file from now on, but not to the + // in-memory cache. + + // We write partial moov box that is in the memory to + // the file first. for (List::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { (*it) += mOffset; } lseek64(mFd, mOffset, SEEK_SET); ::write(mFd, mMoovBoxBuffer, mMoovBoxBufferOffset); - ::write(mFd, ptr, size * nmemb); + ::write(mFd, ptr, bytes); mOffset += (bytes + mMoovBoxBufferOffset); - free(mMoovBoxBuffer); - mMoovBoxBuffer = NULL; - mMoovBoxBufferOffset = 0; + + // All subsequent moov box content will be written + // to the end of the file. mWriteMoovBoxToMemory = false; - mStreamableFile = false; } else { memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes); mMoovBoxBufferOffset += bytes; -- cgit v1.1 From 8ba01021b573889802e67e029225a96f0dfa471a Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 18 Dec 2012 09:46:54 -0800 Subject: Rename ISurfaceTexture and SurfaceTexture The C++ class names don't match what the classes do, so rename ISurfaceTexture to IGraphicBufferProducer, and SurfaceTexture to GLConsumer. Bug 7736700 Change-Id: I64520a55f8c09fe6215382ea361c539a9940cba5 --- media/libmedia/IMediaPlayer.cpp | 14 +++++++------- media/libmedia/IMediaRecorder.cpp | 8 ++++---- media/libmedia/IRemoteDisplayClient.cpp | 10 +++++----- media/libmedia/mediaplayer.cpp | 4 ++-- media/libmedia/mediarecorder.cpp | 8 ++++---- media/libmediaplayerservice/MediaPlayerService.cpp | 16 ++++++++-------- media/libmediaplayerservice/MediaPlayerService.h | 2 +- media/libmediaplayerservice/MediaRecorderClient.cpp | 4 ++-- media/libmediaplayerservice/MediaRecorderClient.h | 4 ++-- media/libmediaplayerservice/MidiFile.h | 2 +- media/libmediaplayerservice/StagefrightPlayer.cpp | 4 ++-- media/libmediaplayerservice/StagefrightPlayer.h | 2 +- media/libmediaplayerservice/StagefrightRecorder.cpp | 2 +- media/libmediaplayerservice/StagefrightRecorder.h | 6 +++--- media/libmediaplayerservice/TestPlayerStub.h | 2 +- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 8 ++++---- media/libmediaplayerservice/nuplayer/NuPlayer.h | 2 +- media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp | 4 ++-- media/libmediaplayerservice/nuplayer/NuPlayerDriver.h | 2 +- media/libstagefright/AwesomePlayer.cpp | 8 ++++---- .../libstagefright/colorconversion/SoftwareRenderer.cpp | 2 +- media/libstagefright/include/AwesomePlayer.h | 4 ++-- media/libstagefright/tests/SurfaceMediaSource_test.cpp | 12 ++++++------ media/libstagefright/wifi-display/sink/RTPSink.cpp | 4 ++-- media/libstagefright/wifi-display/sink/RTPSink.h | 4 ++-- .../libstagefright/wifi-display/sink/TunnelRenderer.cpp | 4 ++-- media/libstagefright/wifi-display/sink/TunnelRenderer.h | 4 ++-- .../libstagefright/wifi-display/sink/WifiDisplaySink.cpp | 4 ++-- media/libstagefright/wifi-display/sink/WifiDisplaySink.h | 4 ++-- .../wifi-display/source/PlaybackSession.cpp | 2 +- .../libstagefright/wifi-display/source/PlaybackSession.h | 4 ++-- .../wifi-display/source/WifiDisplaySource.cpp | 2 +- media/libstagefright/wifi-display/wfd.cpp | 8 ++++---- 33 files changed, 85 insertions(+), 85 deletions(-) (limited to 'media') diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index cb07766..e79bcd2 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include namespace android { @@ -113,12 +113,12 @@ public: return reply.readInt32(); } - // pass the buffered ISurfaceTexture to the media player service - status_t setVideoSurfaceTexture(const sp& surfaceTexture) + // pass the buffered IGraphicBufferProducer to the media player service + status_t setVideoSurfaceTexture(const sp& bufferProducer) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); - sp b(surfaceTexture->asBinder()); + sp b(bufferProducer->asBinder()); data.writeStrongBinder(b); remote()->transact(SET_VIDEO_SURFACETEXTURE, data, &reply); return reply.readInt32(); @@ -383,9 +383,9 @@ status_t BnMediaPlayer::onTransact( } case SET_VIDEO_SURFACETEXTURE: { CHECK_INTERFACE(IMediaPlayer, data, reply); - sp surfaceTexture = - interface_cast(data.readStrongBinder()); - reply->writeInt32(setVideoSurfaceTexture(surfaceTexture)); + sp bufferProducer = + interface_cast(data.readStrongBinder()); + reply->writeInt32(setVideoSurfaceTexture(bufferProducer)); return NO_ERROR; } break; case PREPARE_ASYNC: { diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index a710fd7..fdbc747 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include @@ -73,7 +73,7 @@ public: return reply.readInt32(); } - sp querySurfaceMediaSource() + sp querySurfaceMediaSource() { ALOGV("Query SurfaceMediaSource"); Parcel data, reply; @@ -83,7 +83,7 @@ public: if (returnedNull) { return NULL; } - return interface_cast(reply.readStrongBinder()); + return interface_cast(reply.readStrongBinder()); } status_t setPreviewSurface(const sp& surface) @@ -444,7 +444,7 @@ status_t BnMediaRecorder::onTransact( CHECK_INTERFACE(IMediaRecorder, data, reply); // call the mediaserver side to create // a surfacemediasource - sp surfaceMediaSource = querySurfaceMediaSource(); + sp surfaceMediaSource = querySurfaceMediaSource(); // The mediaserver might have failed to create a source int returnedNull= (surfaceMediaSource == NULL) ? 1 : 0 ; reply->writeInt32(returnedNull); diff --git a/media/libmedia/IRemoteDisplayClient.cpp b/media/libmedia/IRemoteDisplayClient.cpp index 4a1b570..5c494b3 100644 --- a/media/libmedia/IRemoteDisplayClient.cpp +++ b/media/libmedia/IRemoteDisplayClient.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include namespace android { @@ -37,12 +37,12 @@ public: { } - void onDisplayConnected(const sp& surfaceTexture, + void onDisplayConnected(const sp& bufferProducer, uint32_t width, uint32_t height, uint32_t flags) { Parcel data, reply; data.writeInterfaceToken(IRemoteDisplayClient::getInterfaceDescriptor()); - data.writeStrongBinder(surfaceTexture->asBinder()); + data.writeStrongBinder(bufferProducer->asBinder()); data.writeInt32(width); data.writeInt32(height); data.writeInt32(flags); @@ -75,8 +75,8 @@ status_t BnRemoteDisplayClient::onTransact( switch (code) { case ON_DISPLAY_CONNECTED: { CHECK_INTERFACE(IRemoteDisplayClient, data, reply); - sp surfaceTexture( - interface_cast(data.readStrongBinder())); + sp surfaceTexture( + interface_cast(data.readStrongBinder())); uint32_t width = data.readInt32(); uint32_t height = data.readInt32(); uint32_t flags = data.readInt32(); diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index bbbf4b6..ae527e8 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -221,12 +221,12 @@ status_t MediaPlayer::getMetadata(bool update_only, bool apply_filter, Parcel *m } status_t MediaPlayer::setVideoSurfaceTexture( - const sp& surfaceTexture) + const sp& bufferProducer) { ALOGV("setVideoSurfaceTexture"); Mutex::Autolock _l(mLock); if (mPlayer == 0) return NO_INIT; - return mPlayer->setVideoSurfaceTexture(surfaceTexture); + return mPlayer->setVideoSurfaceTexture(bufferProducer); } // must call with lock held diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 9541015..95c7f3e 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -24,7 +24,7 @@ #include #include #include // for MEDIA_ERROR_SERVER_DIED -#include +#include namespace android { @@ -348,9 +348,9 @@ status_t MediaRecorder::setVideoSize(int width, int height) } // Query a SurfaceMediaSurface through the Mediaserver, over the -// binder interface. This is used by the Filter Framework (MeidaEncoder) -// to get an object to hook up to ANativeWindow. -sp MediaRecorder:: +// binder interface. This is used by the Filter Framework (MediaEncoder) +// to get an object to hook up to ANativeWindow. +sp MediaRecorder:: querySurfaceMediaSourceFromMediaServer() { Mutex::Autolock _l(mLock); diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index c3e5c40..4ca0811 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -714,21 +714,21 @@ void MediaPlayerService::Client::disconnectNativeWindow() { } status_t MediaPlayerService::Client::setVideoSurfaceTexture( - const sp& surfaceTexture) + const sp& bufferProducer) { - ALOGV("[%d] setVideoSurfaceTexture(%p)", mConnId, surfaceTexture.get()); + ALOGV("[%d] setVideoSurfaceTexture(%p)", mConnId, bufferProducer.get()); sp p = getPlayer(); if (p == 0) return UNKNOWN_ERROR; - sp binder(surfaceTexture == NULL ? NULL : - surfaceTexture->asBinder()); + sp binder(bufferProducer == NULL ? NULL : + bufferProducer->asBinder()); if (mConnectedWindowBinder == binder) { return OK; } sp anw; - if (surfaceTexture != NULL) { - anw = new SurfaceTextureClient(surfaceTexture); + if (bufferProducer != NULL) { + anw = new SurfaceTextureClient(bufferProducer); status_t err = native_window_api_connect(anw.get(), NATIVE_WINDOW_API_MEDIA); @@ -745,10 +745,10 @@ status_t MediaPlayerService::Client::setVideoSurfaceTexture( } } - // Note that we must set the player's new SurfaceTexture before + // Note that we must set the player's new GraphicBufferProducer before // disconnecting the old one. Otherwise queue/dequeue calls could be made // on the disconnected ANW, which may result in errors. - status_t err = p->setVideoSurfaceTexture(surfaceTexture); + status_t err = p->setVideoSurfaceTexture(bufferProducer); disconnectNativeWindow(); diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index fd648df..afb6780 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -307,7 +307,7 @@ private: // IMediaPlayer interface virtual void disconnect(); virtual status_t setVideoSurfaceTexture( - const sp& surfaceTexture); + const sp& bufferProducer); virtual status_t prepareAsync(); virtual status_t start(); virtual status_t stop(); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index eadc8ee..c6d8b76 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -38,7 +38,7 @@ #include "MediaPlayerService.h" #include "StagefrightRecorder.h" -#include +#include namespace android { @@ -56,7 +56,7 @@ static bool checkPermission(const char* permissionString) { } -sp MediaRecorderClient::querySurfaceMediaSource() +sp MediaRecorderClient::querySurfaceMediaSource() { ALOGV("Query SurfaceMediaSource"); Mutex::Autolock lock(mLock); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index c9ccf22..5623917 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -25,7 +25,7 @@ namespace android { class MediaRecorderBase; class MediaPlayerService; class ICameraRecordingProxy; -class ISurfaceTexture; +class IGraphicBufferProducer; class MediaRecorderClient : public BnMediaRecorder { @@ -55,7 +55,7 @@ public: virtual status_t close(); virtual status_t release(); virtual status_t dump(int fd, const Vector& args) const; - virtual sp querySurfaceMediaSource(); + virtual sp querySurfaceMediaSource(); private: friend class MediaPlayerService; // for accessing private constructor diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h index f6f8f7b..24d59b4 100644 --- a/media/libmediaplayerservice/MidiFile.h +++ b/media/libmediaplayerservice/MidiFile.h @@ -36,7 +36,7 @@ public: virtual status_t setDataSource(int fd, int64_t offset, int64_t length); virtual status_t setVideoSurfaceTexture( - const sp& surfaceTexture) + const sp& bufferProducer) { return UNKNOWN_ERROR; } virtual status_t prepare(); virtual status_t prepareAsync(); diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index 619c149..de61d9b 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -70,10 +70,10 @@ status_t StagefrightPlayer::setDataSource(const sp &source) { } status_t StagefrightPlayer::setVideoSurfaceTexture( - const sp &surfaceTexture) { + const sp &bufferProducer) { ALOGV("setVideoSurfaceTexture"); - return mPlayer->setSurfaceTexture(surfaceTexture); + return mPlayer->setSurfaceTexture(bufferProducer); } status_t StagefrightPlayer::prepare() { diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h index e89e18a..600945e 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.h +++ b/media/libmediaplayerservice/StagefrightPlayer.h @@ -41,7 +41,7 @@ public: virtual status_t setDataSource(const sp &source); virtual status_t setVideoSurfaceTexture( - const sp &surfaceTexture); + const sp &bufferProducer); virtual status_t prepare(); virtual status_t prepareAsync(); virtual status_t start(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 57b0ec2..497dda6 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -89,7 +89,7 @@ status_t StagefrightRecorder::init() { // The client side of mediaserver asks it to creat a SurfaceMediaSource // and return a interface reference. The client side will use that // while encoding GL Frames -sp StagefrightRecorder::querySurfaceMediaSource() const { +sp StagefrightRecorder::querySurfaceMediaSource() const { ALOGV("Get SurfaceMediaSource"); return mSurfaceMediaSource->getBufferQueue(); } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index ec5ce7e..351efd4 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -35,7 +35,7 @@ struct MediaWriter; class MetaData; struct AudioSource; class MediaProfiles; -class ISurfaceTexture; +class IGraphicBufferProducer; class SurfaceMediaSource; struct StagefrightRecorder : public MediaRecorderBase { @@ -65,7 +65,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t getMaxAmplitude(int *max); virtual status_t dump(int fd, const Vector& args) const; // Querying a SurfaceMediaSourcer - virtual sp querySurfaceMediaSource() const; + virtual sp querySurfaceMediaSource() const; private: sp mCamera; @@ -116,7 +116,7 @@ private: bool mStarted; // Needed when GLFrames are encoded. - // An pointer + // An pointer // will be sent to the client side using which the // frame buffers will be queued and dequeued sp mSurfaceMediaSource; diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h index 91ffa7d..a3802eb 100644 --- a/media/libmediaplayerservice/TestPlayerStub.h +++ b/media/libmediaplayerservice/TestPlayerStub.h @@ -76,7 +76,7 @@ class TestPlayerStub : public MediaPlayerInterface { // All the methods below wrap the mPlayer instance. virtual status_t setVideoSurfaceTexture( - const android::sp& st) { + const android::sp& st) { return mPlayer->setVideoSurfaceTexture(st); } virtual status_t prepare() {return mPlayer->prepare();} diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 0f30372..517fb34 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include "avc_utils.h" @@ -198,16 +198,16 @@ void NuPlayer::setDataSource(int fd, int64_t offset, int64_t length) { } void NuPlayer::setVideoSurfaceTextureAsync( - const sp &surfaceTexture) { + const sp &bufferProducer) { sp msg = new AMessage(kWhatSetVideoNativeWindow, id()); - if (surfaceTexture == NULL) { + if (bufferProducer == NULL) { msg->setObject("native-window", NULL); } else { msg->setObject( "native-window", new NativeWindowWrapper( - new SurfaceTextureClient(surfaceTexture))); + new SurfaceTextureClient(bufferProducer))); } msg->post(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index ca87be9..09fc0ba 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -43,7 +43,7 @@ struct NuPlayer : public AHandler { void setDataSource(int fd, int64_t offset, int64_t length); void setVideoSurfaceTextureAsync( - const sp &surfaceTexture); + const sp &bufferProducer); void setAudioSink(const sp &sink); void start(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index a485dda..7043404 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -97,7 +97,7 @@ status_t NuPlayerDriver::setDataSource(const sp &source) { } status_t NuPlayerDriver::setVideoSurfaceTexture( - const sp &surfaceTexture) { + const sp &bufferProducer) { Mutex::Autolock autoLock(mLock); if (mResetInProgress) { @@ -106,7 +106,7 @@ status_t NuPlayerDriver::setVideoSurfaceTexture( mSetSurfaceInProgress = true; - mPlayer->setVideoSurfaceTextureAsync(surfaceTexture); + mPlayer->setVideoSurfaceTextureAsync(bufferProducer); while (mSetSurfaceInProgress) { mCondition.wait(mLock); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index d551bf1..553c406 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -38,7 +38,7 @@ struct NuPlayerDriver : public MediaPlayerInterface { virtual status_t setDataSource(const sp &source); virtual status_t setVideoSurfaceTexture( - const sp &surfaceTexture); + const sp &bufferProducer); virtual status_t prepare(); virtual status_t prepareAsync(); virtual status_t start(); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 1e2625a..23ce088 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -48,7 +48,7 @@ #include #include -#include +#include #include #include @@ -1178,12 +1178,12 @@ bool AwesomePlayer::isPlaying() const { return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN); } -status_t AwesomePlayer::setSurfaceTexture(const sp &surfaceTexture) { +status_t AwesomePlayer::setSurfaceTexture(const sp &bufferProducer) { Mutex::Autolock autoLock(mLock); status_t err; - if (surfaceTexture != NULL) { - err = setNativeWindow_l(new SurfaceTextureClient(surfaceTexture)); + if (bufferProducer != NULL) { + err = setNativeWindow_l(new SurfaceTextureClient(bufferProducer)); } else { err = setNativeWindow_l(NULL); } diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index 2704a37..77f21b7 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include namespace android { diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 1422687..2306f31 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -36,7 +36,7 @@ struct MediaBuffer; struct MediaExtractor; struct MediaSource; struct NuCachedSource2; -struct ISurfaceTexture; +struct IGraphicBufferProducer; class DrmManagerClinet; class DecryptHandle; @@ -81,7 +81,7 @@ struct AwesomePlayer { bool isPlaying() const; - status_t setSurfaceTexture(const sp &surfaceTexture); + status_t setSurfaceTexture(const sp &bufferProducer); void setAudioSink(const sp &audioSink); status_t setLooping(bool shouldLoop); diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp index a61d6a2..6a98509 100644 --- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp +++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp @@ -107,7 +107,7 @@ protected: window.get(), NULL); } else { ALOGV("No actual display. Choosing EGLSurface based on SurfaceMediaSource"); - sp sms = (new SurfaceMediaSource( + sp sms = (new SurfaceMediaSource( getSurfaceWidth(), getSurfaceHeight()))->getBufferQueue(); sp stc = new SurfaceTextureClient(sms); sp window = stc; @@ -361,7 +361,7 @@ protected: mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); // Manual cast is required to avoid constructor ambiguity - mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); + mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); mANW = mSTC; } @@ -396,7 +396,7 @@ protected: ALOGV("SMS-GLTest::SetUp()"); android::ProcessState::self()->startThreadPool(); mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); - mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); + mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); mANW = mSTC; // Doing the setup related to the GL Side @@ -482,7 +482,7 @@ sp SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int video // query the mediarecorder for a surfacemeidasource and create an egl surface with that void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp& mr) { - sp iST = mr->querySurfaceMediaSourceFromMediaServer(); + sp iST = mr->querySurfaceMediaSourceFromMediaServer(); mSTC = new SurfaceTextureClient(iST); mANW = mSTC; @@ -749,7 +749,7 @@ TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaS mYuvTexHeight, 30); // get the reference to the surfacemediasource living in // mediaserver that is created by stagefrightrecorder - sp iST = mr->querySurfaceMediaSourceFromMediaServer(); + sp iST = mr->querySurfaceMediaSourceFromMediaServer(); mSTC = new SurfaceTextureClient(iST); mANW = mSTC; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); @@ -781,7 +781,7 @@ TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWriter) { ALOGV("Verify creating a surface w/ right config + dummy writer*********"); mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); - mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); + mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); mANW = mSTC; DummyRecorder writer(mSMS); diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp index 0918034..640e055 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.cpp +++ b/media/libstagefright/wifi-display/sink/RTPSink.cpp @@ -238,9 +238,9 @@ void RTPSink::Source::addReportBlock( RTPSink::RTPSink( const sp &netSession, - const sp &surfaceTex) + const sp &bufferProducer) : mNetSession(netSession), - mSurfaceTex(surfaceTex), + mSurfaceTex(bufferProducer), mRTPPort(0), mRTPSessionID(0), mRTCPSessionID(0), diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h index a1d127d..2183fd6 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.h +++ b/media/libstagefright/wifi-display/sink/RTPSink.h @@ -35,7 +35,7 @@ struct TunnelRenderer; // the RTCP channel. struct RTPSink : public AHandler { RTPSink(const sp &netSession, - const sp &surfaceTex); + const sp &bufferProducer); // If TCP interleaving is used, no UDP sockets are created, instead // incoming RTP/RTCP packets (arriving on the RTSP control connection) @@ -66,7 +66,7 @@ private: struct StreamSource; sp mNetSession; - sp mSurfaceTex; + sp mSurfaceTex; KeyedVector > mSources; int32_t mRTPPort; diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index b913124..8ffb877 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -159,9 +159,9 @@ void TunnelRenderer::StreamSource::doSomeWork() { TunnelRenderer::TunnelRenderer( const sp ¬ifyLost, - const sp &surfaceTex) + const sp &bufferProducer) : mNotifyLost(notifyLost), - mSurfaceTex(surfaceTex), + mSurfaceTex(bufferProducer), mTotalBytesQueued(0ll), mLastDequeuedExtSeqNo(-1), mFirstFailedAttemptUs(-1ll), diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h index c9597e0..52e6e66 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.h @@ -36,7 +36,7 @@ struct IStreamListener; struct TunnelRenderer : public AHandler { TunnelRenderer( const sp ¬ifyLost, - const sp &surfaceTex); + const sp &bufferProducer); sp dequeueBuffer(); @@ -55,7 +55,7 @@ private: mutable Mutex mLock; sp mNotifyLost; - sp mSurfaceTex; + sp mSurfaceTex; List > mPackets; int64_t mTotalBytesQueued; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index c3e0470..0f0caf1 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -31,10 +31,10 @@ namespace android { WifiDisplaySink::WifiDisplaySink( const sp &netSession, - const sp &surfaceTex) + const sp &bufferProducer) : mState(UNDEFINED), mNetSession(netSession), - mSurfaceTex(surfaceTex), + mSurfaceTex(bufferProducer), mSessionID(0), mNextCSeq(1) { } diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index f886ee5..a508839 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -34,7 +34,7 @@ struct RTPSink; struct WifiDisplaySink : public AHandler { WifiDisplaySink( const sp &netSession, - const sp &surfaceTex = NULL); + const sp &bufferProducer = NULL); void start(const char *sourceHost, int32_t sourcePort); void start(const char *uri); @@ -76,7 +76,7 @@ private: State mState; sp mNetSession; - sp mSurfaceTex; + sp mSurfaceTex; AString mSetupURI; AString mRTSPHost; int32_t mSessionID; diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 916f797..d6b87a7 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -786,7 +786,7 @@ status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) { return OK; } -sp WifiDisplaySource::PlaybackSession::getSurfaceTexture() { +sp WifiDisplaySource::PlaybackSession::getSurfaceTexture() { return mBufferQueue; } diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index b9d193b..281548d 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -26,7 +26,7 @@ namespace android { struct ABuffer; struct BufferQueue; struct IHDCP; -struct ISurfaceTexture; +struct IGraphicBufferProducer; struct MediaPuller; struct MediaSource; struct TSPacketizer; @@ -56,7 +56,7 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { status_t finishPlay(); status_t pause(); - sp getSurfaceTexture(); + sp getSurfaceTexture(); int32_t width() const; int32_t height() const; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 08f67f9..9ec1064 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -25,7 +25,7 @@ #include "Sender.h" #include -#include +#include #include #include #include diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 03a1123..2ec9b4f 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -47,7 +47,7 @@ struct RemoteDisplayClient : public BnRemoteDisplayClient { RemoteDisplayClient(); virtual void onDisplayConnected( - const sp &surfaceTexture, + const sp &bufferProducer, uint32_t width, uint32_t height, uint32_t flags); @@ -67,7 +67,7 @@ private: bool mDone; sp mComposerClient; - sp mSurfaceTexture; + sp mSurfaceTexture; sp mDisplayBinder; DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient); @@ -83,14 +83,14 @@ RemoteDisplayClient::~RemoteDisplayClient() { } void RemoteDisplayClient::onDisplayConnected( - const sp &surfaceTexture, + const sp &bufferProducer, uint32_t width, uint32_t height, uint32_t flags) { ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x", width, height, flags); - mSurfaceTexture = surfaceTexture; + mSurfaceTexture = bufferProducer; mDisplayBinder = mComposerClient->createDisplay( String8("foo"), false /* secure */); -- cgit v1.1 From 21ad778dcfcddb8f8fd9dc3fe4992fbef246c511 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 18 Dec 2012 12:28:27 -0800 Subject: Report buffer size even when using hardware buffers This makes it so that the buffers dequeued from a MediaCodec show a non-zero size when there's actually data in them, which allows the caller to distinguish between a valid frame and an empty buffer. Change-Id: I891b2301501e26f0b4e8cf2e24c169e501a6d026 --- media/libstagefright/ACodec.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 2b20ab0..7920d32 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -612,7 +612,7 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { sp graphicBuffer(new GraphicBuffer(buf, false)); BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; - info.mData = new ABuffer(0); + info.mData = new ABuffer(NULL /* data */, def.nBufferSize /* capacity */); info.mGraphicBuffer = graphicBuffer; mBuffers[kPortIndexOutput].push(info); @@ -2868,15 +2868,14 @@ bool ACodec::BaseState::onOMXFillBufferDone( mCodec->sendFormatChange(); } - if (mCodec->mNativeWindow == NULL) { - info->mData->setRange(rangeOffset, rangeLength); - + info->mData->setRange(rangeOffset, rangeLength); #if 0 + if (mCodec->mNativeWindow == NULL) { if (IsIDR(info->mData)) { ALOGI("IDR frame"); } -#endif } +#endif if (mCodec->mSkipCutBuffer != NULL) { mCodec->mSkipCutBuffer->submit(info->mData); -- cgit v1.1 From 6fc72b01a3b67903b52f1d33b1ad5c960b5365f1 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 17 Dec 2012 16:35:08 -0800 Subject: Make codecs reconfigurable Change-Id: I3dd46cb4401493becbf6152f4dcd5a8f1e9a0b44 --- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 5 +++++ media/libstagefright/codecs/aacdec/SoftAAC2.h | 1 + media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 5 +++++ media/libstagefright/codecs/mp3dec/SoftMP3.h | 1 + media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp | 5 +++++ media/libstagefright/codecs/vorbis/dec/SoftVorbis.h | 1 + media/libstagefright/include/SimpleSoftOMXComponent.h | 1 + media/libstagefright/omx/SimpleSoftOMXComponent.cpp | 8 ++++++++ 8 files changed, 27 insertions(+) (limited to 'media') diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index d88813e..a8ab2ac 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -594,6 +594,11 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { } } +void SoftAAC2::onReset() { + aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); + mIsFirst = true; +} + void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { if (portIndex != 1) { return; diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h index 0353196..6957ade 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -41,6 +41,7 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); private: enum { diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index fb1135c..849be87 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -343,6 +343,11 @@ void SoftMP3::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { } } +void SoftMP3::onReset() { + pvmp3_InitDecoder(mConfig, mDecoderBuf); + mIsFirst = true; +} + } // namespace android android::SoftOMXComponent *createSoftOMXComponent( diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h index 3a05466..4af91ea 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.h +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -42,6 +42,7 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); private: enum { diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index ac88107..13dfc8c 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -410,6 +410,11 @@ void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) { } } +void SoftVorbis::onReset() { + mNumFramesOutput = 0; + vorbis_dsp_restart(mState); +} + void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { if (portIndex != 1) { return; diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h index e252f55..cb628a0 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h @@ -43,6 +43,7 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); private: enum { diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h index 50cd275..f8c61eb 100644 --- a/media/libstagefright/include/SimpleSoftOMXComponent.h +++ b/media/libstagefright/include/SimpleSoftOMXComponent.h @@ -71,6 +71,7 @@ protected: virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); PortInfo *editPortInfo(OMX_U32 portIndex); diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp index c79e01f..4999663 100644 --- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -450,6 +450,10 @@ void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) { checkTransitions(); } +void SimpleSoftOMXComponent::onReset() { + // no-op +} + void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) { CHECK_LT(portIndex, mPorts.size()); @@ -581,6 +585,10 @@ void SimpleSoftOMXComponent::checkTransitions() { if (transitionComplete) { mState = mTargetState; + if (mState == OMX_StateLoaded) { + onReset(); + } + notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL); } } -- cgit v1.1 From 8d6cc842e8d525405c68e57fdf3bc5da0b4d7e87 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 3 Feb 2012 11:06:53 -0800 Subject: Remove unnecessary parameter Just get the parameter on server side Change-Id: I433a63104dbb257e0d862be2ab61847cb36d1c15 --- media/libmedia/AudioEffect.cpp | 2 +- media/libmedia/AudioRecord.cpp | 2 +- media/libmedia/AudioTrack.cpp | 3 +-- media/libmedia/IAudioFlinger.cpp | 16 ++++------------ media/libmedia/IMediaPlayerService.cpp | 18 ++++++------------ media/libmedia/mediametadataretriever.cpp | 2 +- media/libmedia/mediaplayer.cpp | 6 +++--- media/libmedia/mediarecorder.cpp | 2 +- media/libmediaplayerservice/MediaPlayerService.cpp | 9 ++++++--- media/libmediaplayerservice/MediaPlayerService.h | 6 +++--- .../wifi-display/sink/TunnelRenderer.cpp | 2 +- 11 files changed, 28 insertions(+), 40 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index 3317d57..8dfffb3 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -127,7 +127,7 @@ status_t AudioEffect::set(const effect_uuid_t *type, mIEffectClient = new EffectClient(this); - iEffect = audioFlinger->createEffect(getpid(), &mDescriptor, + iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor, mIEffectClient, priority, io, mSessionId, &mStatus, &mId, &enabled); if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) { diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index c2ef68c..3db69a4 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -444,7 +444,7 @@ status_t AudioRecord::openRecord_l( // FIXME see similar logic at AudioTrack int originalSessionId = mSessionId; - sp record = audioFlinger->openRecord(getpid(), input, + sp record = audioFlinger->openRecord(input, sampleRate, format, mChannelMask, frameCount, diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 1d87ff8..d0872f0 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -837,8 +837,7 @@ status_t AudioTrack::createTrack_l( } } - sp track = audioFlinger->createTrack(getpid(), - streamType, + sp track = audioFlinger->createTrack(streamType, sampleRate, // AudioFlinger only sees 16-bit PCM format == AUDIO_FORMAT_PCM_8_BIT ? diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index c5fbbf0..2f18680 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -84,7 +84,6 @@ public: } virtual sp createTrack( - pid_t pid, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, @@ -100,7 +99,6 @@ public: Parcel data, reply; sp track; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); - data.writeInt32(pid); data.writeInt32((int32_t) streamType); data.writeInt32(sampleRate); data.writeInt32(format); @@ -138,7 +136,6 @@ public: } virtual sp openRecord( - pid_t pid, audio_io_handle_t input, uint32_t sampleRate, audio_format_t format, @@ -152,7 +149,6 @@ public: Parcel data, reply; sp record; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); - data.writeInt32(pid); data.writeInt32((int32_t) input); data.writeInt32(sampleRate); data.writeInt32(format); @@ -612,7 +608,7 @@ public: return NO_ERROR; } - virtual sp createEffect(pid_t pid, + virtual sp createEffect( effect_descriptor_t *pDesc, const sp& client, int32_t priority, @@ -633,7 +629,6 @@ public: } data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); - data.writeInt32(pid); data.write(pDesc, sizeof(effect_descriptor_t)); data.writeStrongBinder(client->asBinder()); data.writeInt32(priority); @@ -712,7 +707,6 @@ status_t BnAudioFlinger::onTransact( switch (code) { case CREATE_TRACK: { CHECK_INTERFACE(IAudioFlinger, data, reply); - pid_t pid = data.readInt32(); int streamType = data.readInt32(); uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); @@ -724,7 +718,7 @@ status_t BnAudioFlinger::onTransact( pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); status_t status; - sp track = createTrack(pid, + sp track = createTrack( (audio_stream_type_t) streamType, sampleRate, format, channelMask, frameCount, &flags, buffer, output, tid, &sessionId, &status); reply->writeInt32(flags); @@ -735,7 +729,6 @@ status_t BnAudioFlinger::onTransact( } break; case OPEN_RECORD: { CHECK_INTERFACE(IAudioFlinger, data, reply); - pid_t pid = data.readInt32(); audio_io_handle_t input = (audio_io_handle_t) data.readInt32(); uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); @@ -745,7 +738,7 @@ status_t BnAudioFlinger::onTransact( pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); status_t status; - sp record = openRecord(pid, input, + sp record = openRecord(input, sampleRate, format, channelMask, frameCount, flags, tid, &sessionId, &status); reply->writeInt32(sessionId); reply->writeInt32(status); @@ -1021,7 +1014,6 @@ status_t BnAudioFlinger::onTransact( } case CREATE_EFFECT: { CHECK_INTERFACE(IAudioFlinger, data, reply); - pid_t pid = data.readInt32(); effect_descriptor_t desc; data.read(&desc, sizeof(effect_descriptor_t)); sp client = interface_cast(data.readStrongBinder()); @@ -1032,7 +1024,7 @@ status_t BnAudioFlinger::onTransact( int id; int enabled; - sp effect = createEffect(pid, &desc, client, priority, output, sessionId, + sp effect = createEffect(&desc, client, priority, output, sessionId, &status, &id, &enabled); reply->writeInt32(status); reply->writeInt32(id); diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index c0a0260..ae76c10 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -56,20 +56,18 @@ public: { } - virtual sp createMetadataRetriever(pid_t pid) + virtual sp createMetadataRetriever() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - data.writeInt32(pid); remote()->transact(CREATE_METADATA_RETRIEVER, data, &reply); return interface_cast(reply.readStrongBinder()); } virtual sp create( - pid_t pid, const sp& client, int audioSessionId) { + const sp& client, int audioSessionId) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - data.writeInt32(pid); data.writeStrongBinder(client->asBinder()); data.writeInt32(audioSessionId); @@ -77,11 +75,10 @@ public: return interface_cast(reply.readStrongBinder()); } - virtual sp createMediaRecorder(pid_t pid) + virtual sp createMediaRecorder() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - data.writeInt32(pid); remote()->transact(CREATE_MEDIA_RECORDER, data, &reply); return interface_cast(reply.readStrongBinder()); } @@ -168,11 +165,10 @@ status_t BnMediaPlayerService::onTransact( switch (code) { case CREATE: { CHECK_INTERFACE(IMediaPlayerService, data, reply); - pid_t pid = data.readInt32(); sp client = interface_cast(data.readStrongBinder()); int audioSessionId = data.readInt32(); - sp player = create(pid, client, audioSessionId); + sp player = create(client, audioSessionId); reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; @@ -206,15 +202,13 @@ status_t BnMediaPlayerService::onTransact( } break; case CREATE_MEDIA_RECORDER: { CHECK_INTERFACE(IMediaPlayerService, data, reply); - pid_t pid = data.readInt32(); - sp recorder = createMediaRecorder(pid); + sp recorder = createMediaRecorder(); reply->writeStrongBinder(recorder->asBinder()); return NO_ERROR; } break; case CREATE_METADATA_RETRIEVER: { CHECK_INTERFACE(IMediaPlayerService, data, reply); - pid_t pid = data.readInt32(); - sp retriever = createMetadataRetriever(pid); + sp retriever = createMetadataRetriever(); reply->writeStrongBinder(retriever->asBinder()); return NO_ERROR; } break; diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp index b0241aa..110b94c 100644 --- a/media/libmedia/mediametadataretriever.cpp +++ b/media/libmedia/mediametadataretriever.cpp @@ -64,7 +64,7 @@ MediaMetadataRetriever::MediaMetadataRetriever() ALOGE("failed to obtain MediaMetadataRetrieverService"); return; } - sp retriever(service->createMetadataRetriever(getpid())); + sp retriever(service->createMetadataRetriever()); if (retriever == 0) { ALOGE("failed to create IMediaMetadataRetriever object from server"); } diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index bbbf4b6..dbff8dc 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -143,7 +143,7 @@ status_t MediaPlayer::setDataSource( if (url != NULL) { const sp& service(getMediaPlayerService()); if (service != 0) { - sp player(service->create(getpid(), this, mAudioSessionId)); + sp player(service->create(this, mAudioSessionId)); if ((NO_ERROR != doSetRetransmitEndpoint(player)) || (NO_ERROR != player->setDataSource(url, headers))) { player.clear(); @@ -160,7 +160,7 @@ status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length) status_t err = UNKNOWN_ERROR; const sp& service(getMediaPlayerService()); if (service != 0) { - sp player(service->create(getpid(), this, mAudioSessionId)); + sp player(service->create(this, mAudioSessionId)); if ((NO_ERROR != doSetRetransmitEndpoint(player)) || (NO_ERROR != player->setDataSource(fd, offset, length))) { player.clear(); @@ -176,7 +176,7 @@ status_t MediaPlayer::setDataSource(const sp &source) status_t err = UNKNOWN_ERROR; const sp& service(getMediaPlayerService()); if (service != 0) { - sp player(service->create(getpid(), this, mAudioSessionId)); + sp player(service->create(this, mAudioSessionId)); if ((NO_ERROR != doSetRetransmitEndpoint(player)) || (NO_ERROR != player->setDataSource(source))) { player.clear(); diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 9541015..46a8f54 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -620,7 +620,7 @@ MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL) const sp& service(getMediaPlayerService()); if (service != NULL) { - mMediaRecorder = service->createMediaRecorder(getpid()); + mMediaRecorder = service->createMediaRecorder(); } if (mMediaRecorder != NULL) { mCurrentState = MEDIA_RECORDER_IDLE; diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index c3e5c40..c211072 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -224,8 +224,9 @@ MediaPlayerService::~MediaPlayerService() ALOGV("MediaPlayerService destroyed"); } -sp MediaPlayerService::createMediaRecorder(pid_t pid) +sp MediaPlayerService::createMediaRecorder() { + pid_t pid = IPCThreadState::self()->getCallingPid(); sp recorder = new MediaRecorderClient(this, pid); wp w = recorder; Mutex::Autolock lock(mLock); @@ -241,16 +242,18 @@ void MediaPlayerService::removeMediaRecorderClient(wp clien ALOGV("Delete media recorder client"); } -sp MediaPlayerService::createMetadataRetriever(pid_t pid) +sp MediaPlayerService::createMetadataRetriever() { + pid_t pid = IPCThreadState::self()->getCallingPid(); sp retriever = new MetadataRetrieverClient(pid); ALOGV("Create new media retriever from pid %d", pid); return retriever; } -sp MediaPlayerService::create(pid_t pid, const sp& client, +sp MediaPlayerService::create(const sp& client, int audioSessionId) { + pid_t pid = IPCThreadState::self()->getCallingPid(); int32_t connId = android_atomic_inc(&mNextConnId); sp c = new Client( diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index fd648df..a8af66e 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -239,11 +239,11 @@ public: static void instantiate(); // IMediaPlayerService interface - virtual sp createMediaRecorder(pid_t pid); + virtual sp createMediaRecorder(); void removeMediaRecorderClient(wp client); - virtual sp createMetadataRetriever(pid_t pid); + virtual sp createMetadataRetriever(); - virtual sp create(pid_t pid, const sp& client, int audioSessionId); + virtual sp create(const sp& client, int audioSessionId); virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index b913124..558fd41 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -370,7 +370,7 @@ void TunnelRenderer::initPlayer() { mPlayerClient = new PlayerClient; - mPlayer = service->create(getpid(), mPlayerClient, 0); + mPlayer = service->create(mPlayerClient, 0); CHECK(mPlayer != NULL); CHECK_EQ(mPlayer->setDataSource(mStreamSource), (status_t)OK); -- cgit v1.1 From 4c44e9fed87ff6363393f2559b150291242da247 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 20 Dec 2012 11:19:49 -0800 Subject: Ensure proper EOS behavior If a buffer is tagged with EOS but has data in it, decode that data instead of ignoring it. Change-Id: Ie41c8485c3ad7fe7d9c64f0752c2e7601d91d602 --- media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp | 8 ++++++-- media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp | 10 +++------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index d527fde..020cc0a 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -326,7 +326,7 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); @@ -445,6 +445,11 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { inHeader->nOffset += bufferSize; inHeader->nFilledLen = 0; + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } else { + outHeader->nFlags = 0; + } if (inHeader->nFilledLen == 0) { inInfo->mOwnedByUs = false; @@ -458,7 +463,6 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { outHeader->nOffset = 0; outHeader->nFilledLen = (mWidth * mHeight * 3) / 2; - outHeader->nFlags = 0; List::iterator it = outQueue.begin(); while ((*it)->mHeader != outHeader) { diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp index 6c3f834..6e36651 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp @@ -311,18 +311,14 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; ++mPicId; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inQueue.erase(inQueue.begin()); - inInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inHeader); - mEOSStatus = INPUT_EOS_SEEN; - continue; - } OMX_BUFFERHEADERTYPE *header = new OMX_BUFFERHEADERTYPE; memset(header, 0, sizeof(OMX_BUFFERHEADERTYPE)); header->nTimeStamp = inHeader->nTimeStamp; header->nFlags = inHeader->nFlags; + if (header->nFlags & OMX_BUFFERFLAG_EOS) { + mEOSStatus = INPUT_EOS_SEEN; + } mPicToHeaderMap.add(mPicId, header); inQueue.erase(inQueue.begin()); -- cgit v1.1 From c8e07e483c116ecaca1c9c6991588607f1187b75 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 20 Dec 2012 13:49:34 -0800 Subject: Properly release any MediaBuffer references associated with the encoder input buffers on shutdown. This worked fine before for an orderly shutdown but didn't release all references in case of error. Change-Id: I0ea3eb26da76fbeb33cadf58d237b0c68a86ac4a related-to-bug: 7893090 --- .../wifi-display/source/Converter.cpp | 60 +++++++++++++++------- .../libstagefright/wifi-display/source/Converter.h | 1 + 2 files changed, 42 insertions(+), 19 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 7a87444..5628dec 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -67,13 +67,47 @@ Converter::Converter( mInitCheck = initEncoder(); if (mInitCheck != OK) { - if (mEncoder != NULL) { - mEncoder->release(); - mEncoder.clear(); - } + releaseEncoder(); + } +} + +static void ReleaseMediaBufferReference(const sp &accessUnit) { + void *mbuf; + if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) + && mbuf != NULL) { + ALOGV("releasing mbuf %p", mbuf); + + accessUnit->meta()->setPointer("mediaBuffer", NULL); + + static_cast(mbuf)->release(); + mbuf = NULL; } } +void Converter::releaseEncoder() { + if (mEncoder == NULL) { + return; + } + + mEncoder->release(); + mEncoder.clear(); + + while (!mInputBufferQueue.empty()) { + sp accessUnit = *mInputBufferQueue.begin(); + mInputBufferQueue.erase(mInputBufferQueue.begin()); + + ReleaseMediaBufferReference(accessUnit); + } + + for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) { + sp accessUnit = mEncoderInputBuffers.itemAt(i); + ReleaseMediaBufferReference(accessUnit); + } + + mEncoderInputBuffers.clear(); + mEncoderOutputBuffers.clear(); +} + Converter::~Converter() { CHECK(mEncoder == NULL); } @@ -274,16 +308,7 @@ void Converter::onMessageReceived(const sp &msg) { sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); - void *mbuf; - if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) - && mbuf != NULL) { - ALOGV("releasing mbuf %p", mbuf); - - accessUnit->meta()->setPointer("mediaBuffer", NULL); - - static_cast(mbuf)->release(); - mbuf = NULL; - } + ReleaseMediaBufferReference(accessUnit); } break; } @@ -385,12 +410,9 @@ void Converter::onMessageReceived(const sp &msg) { case kWhatShutdown: { - ALOGI("shutting down encoder"); + ALOGI("shutting down %s encoder", mIsVideo ? "video" : "audio"); - if (mEncoder != NULL) { - mEncoder->release(); - mEncoder.clear(); - } + releaseEncoder(); AString mime; CHECK(mInputFormat->findString("mime", &mime)); diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 0665eea..3357d61 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -101,6 +101,7 @@ private: sp mPartialAudioAU; status_t initEncoder(); + void releaseEncoder(); status_t feedEncoderInputBuffers(); -- cgit v1.1 From a589764c3c0617c7a8996e929ce2d6db1cc01d77 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 4 Jan 2013 16:57:33 -0800 Subject: Fix bug in AudioRecord() constructor It was calling set() with wrong parameter list. This goes back to commit be916aa1267e2e6b1c148f51d11bcbbc79cb864c from 2010. Change-Id: I2f6917765baf58260bf35e89a2cc59c199734ff6 --- media/libmedia/AudioRecord.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index f6e60fc..4f555c1 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -96,7 +96,7 @@ AudioRecord::AudioRecord( mProxy(NULL) { mStatus = set(inputSource, sampleRate, format, channelMask, - frameCount, cbf, user, notificationFrames, sessionId); + frameCount, cbf, user, notificationFrames, false /*threadCanCallJava*/, sessionId); } AudioRecord::~AudioRecord() -- cgit v1.1 From 9c6745f128648f6e0144b74ee593911a9fa10d51 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 30 Nov 2012 13:35:29 -0800 Subject: Propose new interpretation for setPosition and setLoop Add new API getBufferPosition to return position relative to start of fixed buffer. Change-Id: I7aca8e392d45b988545f07b36b5032691057b03e --- media/libmedia/AudioTrack.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 1bd839f..2d77581 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -561,6 +561,26 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou return INVALID_OPERATION; } + if (loopCount < 0 && loopCount != -1) { + return BAD_VALUE; + } + +#if 0 + // This will be for the new interpretation of loopStart and loopEnd + + if (loopCount != 0) { + if (loopStart >= mFrameCount || loopEnd >= mFrameCount || loopStart >= loopEnd) { + return BAD_VALUE; + } + uint32_t periodFrames = loopEnd - loopStart; + if (periodFrames < PERIOD_FRAMES_MIN) { + return BAD_VALUE; + } + } + + // The remainder of this code still uses the old interpretation +#endif + audio_track_cblk_t* cblk = mCblk; Mutex::Autolock _l(cblk->lock); @@ -656,6 +676,16 @@ status_t AudioTrack::setPosition(uint32_t position) return INVALID_OPERATION; } +#if 0 + // This will be for the new interpretation of position + + if (position >= mFrameCount) { + return BAD_VALUE; + } + + // The remainder of this code still uses the old interpretation +#endif + audio_track_cblk_t* cblk = mCblk; Mutex::Autolock _l(cblk->lock); @@ -680,6 +710,21 @@ status_t AudioTrack::getPosition(uint32_t *position) return NO_ERROR; } +#if 0 +status_t AudioTrack::getBufferPosition(uint32_t *position) +{ + if (mSharedBuffer == 0 || mIsTimed) { + return INVALID_OPERATION; + } + if (position == NULL) { + return BAD_VALUE; + } + *position = 0; + + return NO_ERROR; +} +#endif + status_t AudioTrack::reload() { if (mStatus != NO_ERROR) { -- cgit v1.1 From 2e4664677d72ce54201d3fd0beb0e10280add93c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 10 Jan 2013 14:26:24 -0800 Subject: Fix AudioRecord Bug: 7965744 Change-Id: Ic024e7fb32f7459b8093c2cf6cd5752aade21ddb --- media/libmedia/AudioRecord.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 4f555c1..0a2b0b0 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -164,6 +164,7 @@ status_t AudioRecord::set( ALOGE("Invalid format"); return BAD_VALUE; } + mFormat = format; if (!audio_is_input_channel(channelMask)) { return BAD_VALUE; @@ -172,7 +173,7 @@ status_t AudioRecord::set( uint32_t channelCount = popcount(channelMask); mChannelCount = channelCount; - if (audio_is_linear_pcm(mFormat)) { + if (audio_is_linear_pcm(format)) { mFrameSize = channelCount * audio_bytes_per_sample(format); } else { mFrameSize = sizeof(uint8_t); @@ -226,7 +227,6 @@ status_t AudioRecord::set( mStatus = NO_ERROR; - mFormat = format; // Update buffer size in case it has been limited by AudioFlinger during track creation mFrameCount = mCblk->frameCount_; -- cgit v1.1 From 2ba042ff8a8bb5aa0320580119771e11e64ba2cd Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Tue, 25 Sep 2012 20:22:25 +0900 Subject: Bug fix: set 'und' as default metadata language info for srt. o Previously, it leaves language code empty and it is inconsistent with other code which gives 'und'. As a result, selected SRT track returned empty language info. With this fix, it returns 'und'. Bug: 7227230 TESTED=runtest -d cts-media -c android.media.cts.MediaPlayerTest -m testGetTrackInfo Change-Id: I225848f029637dd782c376e0d770dddd5c849550 --- media/libstagefright/timedtext/TimedTextSRTSource.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp index eac23ba..2ac1e72 100644 --- a/media/libstagefright/timedtext/TimedTextSRTSource.cpp +++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp @@ -36,6 +36,9 @@ TimedTextSRTSource::TimedTextSRTSource(const sp& dataSource) : mSource(dataSource), mMetaData(new MetaData), mIndex(0) { + // TODO: Need to detect the language, because SRT doesn't give language + // information explicitly. + mMetaData->setCString(kKeyMediaLanguage, "und"); } TimedTextSRTSource::~TimedTextSRTSource() { @@ -46,14 +49,10 @@ status_t TimedTextSRTSource::start() { if (err != OK) { reset(); } - // TODO: Need to detect the language, because SRT doesn't give language - // information explicitly. - mMetaData->setCString(kKeyMediaLanguage, ""); return err; } void TimedTextSRTSource::reset() { - mMetaData->clear(); mTextVector.clear(); mIndex = 0; } -- cgit v1.1 From 2a330d6cbb25f0cdd6208aeee53b4a3b88dae3b0 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 18 Jan 2013 15:17:05 -0800 Subject: Remove obsolete audioflinger reference Change-Id: I9ae754c908f3b0102c3828c71d6f542851a74341 --- media/mediaserver/Android.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'media') diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 5a73cdd..8c3cc5e 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -11,12 +11,10 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder -# FIXME The duplicate audioflinger is temporary LOCAL_C_INCLUDES := \ frameworks/av/media/libmediaplayerservice \ frameworks/av/services/audioflinger \ - frameworks/av/services/camera/libcameraservice \ - frameworks/native/services/audioflinger + frameworks/av/services/camera/libcameraservice LOCAL_MODULE:= mediaserver -- cgit v1.1 From 11d8dfcc063425ae7d59229f54b6752fd8987c10 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 14 Jan 2013 14:53:13 -0800 Subject: Add non-blocking event logger NBLog Change-Id: I6c136cf3d7f46a8af84c69ecfc199dab394c10dc --- media/libnbaio/Android.mk | 3 + media/libnbaio/NBLog.cpp | 447 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+) create mode 100644 media/libnbaio/NBLog.cpp (limited to 'media') diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk index 757272f..d372d20 100644 --- a/media/libnbaio/Android.mk +++ b/media/libnbaio/Android.mk @@ -14,6 +14,8 @@ LOCAL_SRC_FILES := \ roundup.c \ SourceAudioBufferProvider.cpp +LOCAL_SRC_FILES += NBLog.cpp + # libsndfile license is incompatible; uncomment to use for local debug only #LOCAL_SRC_FILES += LibsndfileSink.cpp LibsndfileSource.cpp #LOCAL_C_INCLUDES += path/to/libsndfile/src @@ -25,6 +27,7 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libnbaio LOCAL_SHARED_LIBRARIES := \ + libbinder \ libcommon_time_client \ libcutils \ libutils diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp new file mode 100644 index 0000000..045bf64 --- /dev/null +++ b/media/libnbaio/NBLog.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "NBLog" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +int NBLog::Entry::readAt(size_t offset) const +{ + // FIXME This is too slow, despite the name it is used during writing + if (offset == 0) + return mEvent; + else if (offset == 1) + return mLength; + else if (offset < (size_t) (mLength + 2)) + return ((char *) mData)[offset - 2]; + else if (offset == (size_t) (mLength + 2)) + return mLength; + else + return 0; +} + +// --------------------------------------------------------------------------- + +#if 0 // FIXME see note in NBLog.h +NBLog::Timeline::Timeline(size_t size, void *shared) + : mSize(roundup(size)), mOwn(shared == NULL), + mShared((Shared *) (mOwn ? new char[sharedSize(size)] : shared)) +{ + new (mShared) Shared; +} + +NBLog::Timeline::~Timeline() +{ + mShared->~Shared(); + if (mOwn) { + delete[] (char *) mShared; + } +} +#endif + +/*static*/ +size_t NBLog::Timeline::sharedSize(size_t size) +{ + return sizeof(Shared) + roundup(size); +} + +// --------------------------------------------------------------------------- + +NBLog::Writer::Writer() + : mSize(0), mShared(NULL), mRear(0), mEnabled(false) +{ +} + +NBLog::Writer::Writer(size_t size, void *shared) + : mSize(roundup(size)), mShared((Shared *) shared), mRear(0), mEnabled(mShared != NULL) +{ +} + +NBLog::Writer::Writer(size_t size, const sp& iMemory) + : mSize(roundup(size)), mShared(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL), + mIMemory(iMemory), mRear(0), mEnabled(mShared != NULL) +{ +} + +void NBLog::Writer::log(const char *string) +{ + if (!mEnabled) { + return; + } + size_t length = strlen(string); + if (length > 255) { + length = 255; + } + log(EVENT_STRING, string, length); +} + +void NBLog::Writer::logf(const char *fmt, ...) +{ + if (!mEnabled) { + return; + } + va_list ap; + va_start(ap, fmt); + Writer::logvf(fmt, ap); // the Writer:: is needed to avoid virtual dispatch for LockedWriter + va_end(ap); +} + +void NBLog::Writer::logvf(const char *fmt, va_list ap) +{ + if (!mEnabled) { + return; + } + char buffer[256]; + int length = vsnprintf(buffer, sizeof(buffer), fmt, ap); + if (length >= (int) sizeof(buffer)) { + length = sizeof(buffer) - 1; + // NUL termination is not required + // buffer[length] = '\0'; + } + if (length >= 0) { + log(EVENT_STRING, buffer, length); + } +} + +void NBLog::Writer::logTimestamp() +{ + if (!mEnabled) { + return; + } + struct timespec ts; + if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { + log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec)); + } +} + +void NBLog::Writer::logTimestamp(const struct timespec& ts) +{ + if (!mEnabled) { + return; + } + log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec)); +} + +void NBLog::Writer::log(Event event, const void *data, size_t length) +{ + if (!mEnabled) { + return; + } + if (data == NULL || length > 255) { + return; + } + switch (event) { + case EVENT_STRING: + case EVENT_TIMESTAMP: + break; + case EVENT_RESERVED: + default: + return; + } + Entry entry(event, data, length); + log(&entry, true /*trusted*/); +} + +void NBLog::Writer::log(const NBLog::Entry *entry, bool trusted) +{ + if (!mEnabled) { + return; + } + if (!trusted) { + log(entry->mEvent, entry->mData, entry->mLength); + return; + } + size_t rear = mRear & (mSize - 1); + size_t written = mSize - rear; // written = number of bytes that have been written so far + size_t need = entry->mLength + 3; // mEvent, mLength, data[length], mLength + // need = number of bytes remaining to write + if (written > need) { + written = need; + } + size_t i; + // FIXME optimize this using memcpy for the data part of the Entry. + // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy. + for (i = 0; i < written; ++i) { + mShared->mBuffer[rear + i] = entry->readAt(i); + } + if (rear + written == mSize && (need -= written) > 0) { + for (i = 0; i < need; ++i) { + mShared->mBuffer[i] = entry->readAt(written + i); + } + written += need; + } + android_atomic_release_store(mRear += written, &mShared->mRear); +} + +bool NBLog::Writer::isEnabled() const +{ + return mEnabled; +} + +bool NBLog::Writer::setEnabled(bool enabled) +{ + bool old = mEnabled; + mEnabled = enabled && mShared != NULL; + return old; +} + +// --------------------------------------------------------------------------- + +NBLog::LockedWriter::LockedWriter() + : Writer() +{ +} + +NBLog::LockedWriter::LockedWriter(size_t size, void *shared) + : Writer(size, shared) +{ +} + +void NBLog::LockedWriter::log(const char *string) +{ + Mutex::Autolock _l(mLock); + Writer::log(string); +} + +void NBLog::LockedWriter::logf(const char *fmt, ...) +{ + // FIXME should not take the lock until after formatting is done + Mutex::Autolock _l(mLock); + va_list ap; + va_start(ap, fmt); + Writer::logvf(fmt, ap); + va_end(ap); +} + +void NBLog::LockedWriter::logvf(const char *fmt, va_list ap) +{ + // FIXME should not take the lock until after formatting is done + Mutex::Autolock _l(mLock); + Writer::logvf(fmt, ap); +} + +void NBLog::LockedWriter::logTimestamp() +{ + // FIXME should not take the lock until after the clock_gettime() syscall + Mutex::Autolock _l(mLock); + Writer::logTimestamp(); +} + +void NBLog::LockedWriter::logTimestamp(const struct timespec& ts) +{ + Mutex::Autolock _l(mLock); + Writer::logTimestamp(ts); +} + +bool NBLog::LockedWriter::isEnabled() const +{ + Mutex::Autolock _l(mLock); + return Writer::isEnabled(); +} + +bool NBLog::LockedWriter::setEnabled(bool enabled) +{ + Mutex::Autolock _l(mLock); + return Writer::setEnabled(enabled); +} + +// --------------------------------------------------------------------------- + +NBLog::Reader::Reader(size_t size, const void *shared) + : mSize(roundup(size)), mShared((const Shared *) shared), mFront(0) +{ +} + +NBLog::Reader::Reader(size_t size, const sp& iMemory) + : mSize(roundup(size)), mShared(iMemory != 0 ? (const Shared *) iMemory->pointer() : NULL), + mIMemory(iMemory), mFront(0) +{ +} + +void NBLog::Reader::dump(int fd, size_t indent) +{ + int32_t rear = android_atomic_acquire_load(&mShared->mRear); + size_t avail = rear - mFront; + if (avail == 0) { + return; + } + size_t lost = 0; + if (avail > mSize) { + lost = avail - mSize; + mFront += lost; + avail = mSize; + } + size_t remaining = avail; // remaining = number of bytes left to read + size_t front = mFront & (mSize - 1); + size_t read = mSize - front; // read = number of bytes that have been read so far + if (read > remaining) { + read = remaining; + } + // make a copy to avoid race condition with writer + uint8_t *copy = new uint8_t[avail]; + // copy first part of circular buffer up until the wraparound point + memcpy(copy, &mShared->mBuffer[front], read); + if (front + read == mSize) { + if ((remaining -= read) > 0) { + // copy second part of circular buffer starting at beginning + memcpy(©[read], mShared->mBuffer, remaining); + read += remaining; + // remaining = 0 but not necessary + } + } + mFront += read; + size_t i = avail; + Event event; + size_t length; + struct timespec ts; + time_t maxSec = -1; + while (i >= 3) { + length = copy[i - 1]; + if (length + 3 > i || copy[i - length - 2] != length) { + break; + } + event = (Event) copy[i - length - 3]; + if (event == EVENT_TIMESTAMP) { + if (length != sizeof(struct timespec)) { + // corrupt + break; + } + memcpy(&ts, ©[i - length - 1], sizeof(struct timespec)); + if (ts.tv_sec > maxSec) { + maxSec = ts.tv_sec; + } + } + i -= length + 3; + } + if (i > 0) { + lost += i; + if (fd >= 0) { + fdprintf(fd, "%*swarning: lost %u bytes worth of events\n", indent, "", lost); + } else { + ALOGI("%*swarning: lost %u bytes worth of events\n", indent, "", lost); + } + } + size_t width = 1; + while (maxSec >= 10) { + ++width; + maxSec /= 10; + } + char prefix[32]; + if (maxSec >= 0) { + snprintf(prefix, sizeof(prefix), "[%*s] ", width + 4, ""); + } else { + prefix[0] = '\0'; + } + while (i < avail) { + event = (Event) copy[i]; + length = copy[i + 1]; + const void *data = ©[i + 2]; + size_t advance = length + 3; + switch (event) { + case EVENT_STRING: + if (fd >= 0) { + fdprintf(fd, "%*s%s%.*s\n", indent, "", prefix, length, (const char *) data); + } else { + ALOGI("%*s%s%.*s", indent, "", prefix, length, (const char *) data); + } break; + case EVENT_TIMESTAMP: { + // already checked that length == sizeof(struct timespec); + memcpy(&ts, data, sizeof(struct timespec)); + long prevNsec = ts.tv_nsec; + long deltaMin = LONG_MAX; + long deltaMax = -1; + long deltaTotal = 0; + size_t j = i; + for (;;) { + j += sizeof(struct timespec) + 3; + if (j >= avail || (Event) copy[j] != EVENT_TIMESTAMP) { + break; + } + struct timespec tsNext; + memcpy(&tsNext, ©[j + 2], sizeof(struct timespec)); + if (tsNext.tv_sec != ts.tv_sec) { + break; + } + long delta = tsNext.tv_nsec - prevNsec; + if (delta < 0) { + break; + } + if (delta < deltaMin) { + deltaMin = delta; + } + if (delta > deltaMax) { + deltaMax = delta; + } + deltaTotal += delta; + prevNsec = tsNext.tv_nsec; + } + size_t n = (j - i) / (sizeof(struct timespec) + 3); + if (n >= kSquashTimestamp) { + if (fd >= 0) { + fdprintf(fd, "%*s[%d.%03d to .%.03d by .%.03d to .%.03d]\n", indent, "", + (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000), + (int) ((ts.tv_nsec + deltaTotal) / 1000000), + (int) (deltaMin / 1000000), (int) (deltaMax / 1000000)); + } else { + ALOGI("%*s[%d.%03d to .%.03d by .%.03d to .%.03d]\n", indent, "", + (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000), + (int) ((ts.tv_nsec + deltaTotal) / 1000000), + (int) (deltaMin / 1000000), (int) (deltaMax / 1000000)); + } + i = j; + advance = 0; + break; + } + if (fd >= 0) { + fdprintf(fd, "%*s[%d.%03d]\n", indent, "", (int) ts.tv_sec, + (int) (ts.tv_nsec / 1000000)); + } else { + ALOGI("%*s[%d.%03d]", indent, "", (int) ts.tv_sec, + (int) (ts.tv_nsec / 1000000)); + } + } break; + case EVENT_RESERVED: + default: + if (fd >= 0) { + fdprintf(fd, "%*s%swarning: unknown event %d\n", indent, "", prefix, event); + } else { + ALOGI("%*s%swarning: unknown event %d", indent, "", prefix, event); + } + break; + } + i += advance; + } + // FIXME it would be more efficient to put a char mCopy[256] as a member variable of the dumper + delete[] copy; +} + +bool NBLog::Reader::isIMemory(const sp& iMemory) const +{ + return iMemory.get() == mIMemory.get(); +} + +} // namespace android -- cgit v1.1 From 6f1c1918d0dfece10f728711b055441e4d135c73 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 18 Jan 2013 15:31:41 -0800 Subject: Add media.log service based on NBLog Change-Id: Ie45093df6ac9a739d05c8d408fab52a9a8a27e7f --- media/libmedia/Android.mk | 1 + media/libmedia/IMediaLogService.cpp | 94 +++++++++++++++++++++++++++++ media/mediaserver/Android.mk | 5 ++ media/mediaserver/main_mediaserver.cpp | 105 ++++++++++++++++++++++++++++++--- 4 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 media/libmedia/IMediaLogService.cpp (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index a35d562..52fa3e1 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -23,6 +23,7 @@ LOCAL_SRC_FILES:= \ AudioRecord.cpp \ AudioSystem.cpp \ mediaplayer.cpp \ + IMediaLogService.cpp \ IMediaPlayerService.cpp \ IMediaPlayerClient.cpp \ IMediaRecorderClient.cpp \ diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp new file mode 100644 index 0000000..33239a7 --- /dev/null +++ b/media/libmedia/IMediaLogService.cpp @@ -0,0 +1,94 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "IMediaLogService" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include + +namespace android { + +enum { + REGISTER_WRITER = IBinder::FIRST_CALL_TRANSACTION, + UNREGISTER_WRITER, +}; + +class BpMediaLogService : public BpInterface +{ +public: + BpMediaLogService(const sp& impl) + : BpInterface(impl) + { + } + + virtual void registerWriter(const sp& shared, size_t size, const char *name) { + Parcel data, reply; + data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor()); + data.writeStrongBinder(shared->asBinder()); + data.writeInt32((int32_t) size); + data.writeCString(name); + status_t status = remote()->transact(REGISTER_WRITER, data, &reply); + // FIXME ignores status + } + + virtual void unregisterWriter(const sp& shared) { + Parcel data, reply; + data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor()); + data.writeStrongBinder(shared->asBinder()); + status_t status = remote()->transact(UNREGISTER_WRITER, data, &reply); + // FIXME ignores status + } + +}; + +IMPLEMENT_META_INTERFACE(MediaLogService, "android.media.IMediaLogService"); + +// ---------------------------------------------------------------------- + +status_t BnMediaLogService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + + case REGISTER_WRITER: { + CHECK_INTERFACE(IMediaLogService, data, reply); + sp shared = interface_cast(data.readStrongBinder()); + size_t size = (size_t) data.readInt32(); + const char *name = data.readCString(); + registerWriter(shared, size, name); + return NO_ERROR; + } + + case UNREGISTER_WRITER: { + CHECK_INTERFACE(IMediaLogService, data, reply); + sp shared = interface_cast(data.readStrongBinder()); + unregisterWriter(shared); + return NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 8c3cc5e..0a0f4db 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -7,12 +7,17 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libaudioflinger \ libcameraservice \ + libmedialogservice \ + libcutils \ + libnbaio \ + libmedia \ libmediaplayerservice \ libutils \ libbinder LOCAL_C_INCLUDES := \ frameworks/av/media/libmediaplayerservice \ + frameworks/av/services/medialog \ frameworks/av/services/audioflinger \ frameworks/av/services/camera/libcameraservice diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index ddd5b84..0862952 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -18,14 +18,19 @@ #define LOG_TAG "mediaserver" //#define LOG_NDEBUG 0 +#include +#include +#include #include #include #include +#include #include // from LOCAL_C_INCLUDES #include "AudioFlinger.h" #include "CameraService.h" +#include "MediaLogService.h" #include "MediaPlayerService.h" #include "AudioPolicyService.h" @@ -34,13 +39,95 @@ using namespace android; int main(int argc, char** argv) { signal(SIGPIPE, SIG_IGN); - sp proc(ProcessState::self()); - sp sm = defaultServiceManager(); - ALOGI("ServiceManager: %p", sm.get()); - AudioFlinger::instantiate(); - MediaPlayerService::instantiate(); - CameraService::instantiate(); - AudioPolicyService::instantiate(); - ProcessState::self()->startThreadPool(); - IPCThreadState::self()->joinThreadPool(); + char value[PROPERTY_VALUE_MAX]; + bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1); + pid_t childPid; + // FIXME The advantage of making the process containing media.log service the parent process of + // the process that contains all the other real services, is that it allows us to collect more + // detailed information such as signal numbers, stop and continue, resource usage, etc. + // But it is also more complex. Consider replacing this by independent processes, and using + // binder on death notification instead. + if (doLog && (childPid = fork()) != 0) { + // media.log service + //prctl(PR_SET_NAME, (unsigned long) "media.log", 0, 0, 0); + // unfortunately ps ignores PR_SET_NAME for the main thread, so use this ugly hack + strcpy(argv[0], "media.log"); + sp proc(ProcessState::self()); + MediaLogService::instantiate(); + ProcessState::self()->startThreadPool(); + for (;;) { + siginfo_t info; + int ret = waitid(P_PID, childPid, &info, WEXITED | WSTOPPED | WCONTINUED); + if (ret == EINTR) { + continue; + } + if (ret < 0) { + break; + } + char buffer[32]; + const char *code; + switch (info.si_code) { + case CLD_EXITED: + code = "CLD_EXITED"; + break; + case CLD_KILLED: + code = "CLD_KILLED"; + break; + case CLD_DUMPED: + code = "CLD_DUMPED"; + break; + case CLD_STOPPED: + code = "CLD_STOPPED"; + break; + case CLD_TRAPPED: + code = "CLD_TRAPPED"; + break; + case CLD_CONTINUED: + code = "CLD_CONTINUED"; + break; + default: + snprintf(buffer, sizeof(buffer), "unknown (%d)", info.si_code); + code = buffer; + break; + } + struct rusage usage; + getrusage(RUSAGE_CHILDREN, &usage); + ALOG(LOG_ERROR, "media.log", "pid %d status %d code %s user %ld.%03lds sys %ld.%03lds", + info.si_pid, info.si_status, code, + usage.ru_utime.tv_sec, usage.ru_utime.tv_usec / 1000, + usage.ru_stime.tv_sec, usage.ru_stime.tv_usec / 1000); + sp sm = defaultServiceManager(); + sp binder = sm->getService(String16("media.log")); + if (binder != 0) { + Vector args; + binder->dump(-1, args); + } + switch (info.si_code) { + case CLD_EXITED: + case CLD_KILLED: + case CLD_DUMPED: { + ALOG(LOG_INFO, "media.log", "exiting"); + _exit(0); + // not reached + } + default: + break; + } + } + } else { + // all other services + if (doLog) { + prctl(PR_SET_PDEATHSIG, SIGKILL); // if parent media.log dies before me, kill me also + setpgid(0, 0); // but if I die first, don't kill my parent + } + sp proc(ProcessState::self()); + sp sm = defaultServiceManager(); + ALOGI("ServiceManager: %p", sm.get()); + AudioFlinger::instantiate(); + MediaPlayerService::instantiate(); + CameraService::instantiate(); + AudioPolicyService::instantiate(); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + } } -- cgit v1.1 From 0f6675d5fdf15d4b8765545fb6a351138acccdf6 Mon Sep 17 00:00:00 2001 From: SeungBeom Kim Date: Wed, 16 Jan 2013 15:34:00 +0900 Subject: ACodec Fix. Bug: 7961269 Change-Id: Ie12530f89dd96dbce82e873de0c2310490390c34 Signed-off-by: SeungBeom Kim --- media/libstagefright/ACodec.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 7920d32..7b27843 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1421,7 +1421,8 @@ status_t ACodec::setSupportedOutputFormat() { || format.eColorFormat == OMX_COLOR_FormatCbYCrY || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar - || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka); + || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka + || format.eColorFormat == OMX_SEC_COLOR_FormatNV12Tiled); return mOMX->setParameter( mNode, OMX_IndexParamVideoPortFormat, -- cgit v1.1 From 94a483bf2bd699275673d9cd57cb125d48572f30 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 29 Jan 2013 09:22:16 -0800 Subject: Squashed commit of the following: commit f2c38e5cf8cee3b597c744c9d6a9c0969ac8599a Author: Andreas Huber Date: Mon Jan 28 16:33:07 2013 -0800 Proper support for video format selection/negotiation. Change-Id: I7db86cef939d63b8064be1c74de9ad78e85d45d9 commit 488023b7bad086692ffe942114fa3cc0e59a16c0 Author: Andreas Huber Date: Mon Jan 28 11:21:23 2013 -0800 Sink now notifies clients once it is disconnected. Change-Id: I2f0a458ef1ec30dda1272ad5a013fee4ee70edc9 commit 783932e40dd904aa531c263ad51280d9ca814dcb Author: Andreas Huber Date: Tue Dec 18 15:03:40 2012 -0800 Alternative DirectRenderer implementation. Change-Id: I307beb913d7a61cb938bcb02696cc2e82d2b8b07 commit 1935cc9a87824aea71fc8ebe2162f62ec634ce5a Author: Andreas Huber Date: Tue Dec 18 10:24:27 2012 -0800 Experimenting with wifi sink timing. Change-Id: I059bae9762cf11777666988a8b4ab2012b5807be commit a859ee1eadd6a1d6a080667917e8b102c3770d61 Author: Andreas Huber Date: Thu Nov 15 11:16:30 2012 -0800 wfd sink update. Change-Id: I026dfc580be92aa40dbbe7c1bc061fadf3b08be8 Change-Id: I191d3d7015869ca99254d813d074328fb5b2f479 --- media/libstagefright/mpeg2ts/ATSParser.cpp | 10 + media/libstagefright/wifi-display/Android.mk | 2 + media/libstagefright/wifi-display/VideoFormats.cpp | 370 ++++++++++++++++++ media/libstagefright/wifi-display/VideoFormats.h | 83 ++++ .../wifi-display/sink/DirectRenderer.cpp | 428 +++++++++++++++++++++ .../wifi-display/sink/DirectRenderer.h | 94 +++++ media/libstagefright/wifi-display/sink/RTPSink.cpp | 17 +- media/libstagefright/wifi-display/sink/RTPSink.h | 15 +- .../wifi-display/sink/TunnelRenderer.cpp | 9 +- .../wifi-display/sink/WifiDisplaySink.cpp | 87 ++++- .../wifi-display/sink/WifiDisplaySink.h | 22 +- .../wifi-display/source/PlaybackSession.cpp | 78 ++-- .../wifi-display/source/PlaybackSession.h | 21 +- .../libstagefright/wifi-display/source/Sender.cpp | 10 +- .../wifi-display/source/WifiDisplaySource.cpp | 217 +++++++---- .../wifi-display/source/WifiDisplaySource.h | 12 +- media/libstagefright/wifi-display/wfd.cpp | 1 + 17 files changed, 1347 insertions(+), 129 deletions(-) create mode 100644 media/libstagefright/wifi-display/VideoFormats.cpp create mode 100644 media/libstagefright/wifi-display/VideoFormats.h create mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.cpp create mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.h (limited to 'media') diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 4f6c4b2..a167b5a 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -534,6 +534,16 @@ status_t ATSParser::Stream::parse( mBuffer->setRange(0, 0); mExpectedContinuityCounter = -1; +#if 0 + // Uncomment this if you'd rather see no corruption whatsoever on + // screen and suspend updates until we come across another IDR frame. + + if (mStreamType == STREAMTYPE_H264) { + ALOGI("clearing video queue"); + mQueue->clear(true /* clearFormat */); + } +#endif + return OK; } diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 75098f1..5095e82 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \ ANetworkSession.cpp \ Parameters.cpp \ ParsedMessage.cpp \ + sink/DirectRenderer.cpp \ sink/LinearRegression.cpp \ sink/RTPSink.cpp \ sink/TunnelRenderer.cpp \ @@ -18,6 +19,7 @@ LOCAL_SRC_FILES:= \ source/TSPacketizer.cpp \ source/WifiDisplaySource.cpp \ TimeSeries.cpp \ + VideoFormats.cpp \ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright \ diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp new file mode 100644 index 0000000..9ad8c3c --- /dev/null +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -0,0 +1,370 @@ +/* + * Copyright 2013, 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 "VideoFormats" +#include + +#include "VideoFormats.h" + +#include + +namespace android { + +VideoFormats::VideoFormats() { + for (size_t i = 0; i < kNumResolutionTypes; ++i) { + mResolutionEnabled[i] = 0; + } + + setNativeResolution(RESOLUTION_CEA, 0); // default to 640x480 p60 +} + +void VideoFormats::setNativeResolution(ResolutionType type, size_t index) { + CHECK_LT(type, kNumResolutionTypes); + CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL)); + + mNativeType = type; + mNativeIndex = index; + + setResolutionEnabled(type, index); +} + +void VideoFormats::getNativeResolution( + ResolutionType *type, size_t *index) const { + *type = mNativeType; + *index = mNativeIndex; +} + +void VideoFormats::disableAll() { + for (size_t i = 0; i < kNumResolutionTypes; ++i) { + mResolutionEnabled[i] = 0; + } +} + +void VideoFormats::enableAll() { + for (size_t i = 0; i < kNumResolutionTypes; ++i) { + mResolutionEnabled[i] = 0xffffffff; + } +} + +void VideoFormats::setResolutionEnabled( + ResolutionType type, size_t index, bool enabled) { + CHECK_LT(type, kNumResolutionTypes); + CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL)); + + if (enabled) { + mResolutionEnabled[type] |= (1ul << index); + } else { + mResolutionEnabled[type] &= ~(1ul << index); + } +} + +bool VideoFormats::isResolutionEnabled( + ResolutionType type, size_t index) const { + CHECK_LT(type, kNumResolutionTypes); + CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL)); + + return mResolutionEnabled[type] & (1ul << index); +} + +// static +bool VideoFormats::GetConfiguration( + ResolutionType type, + size_t index, + size_t *width, size_t *height, size_t *framesPerSecond, + bool *interlaced) { + CHECK_LT(type, kNumResolutionTypes); + + if (index >= 32) { + return false; + } + + static const struct config_t { + size_t width, height, framesPerSecond; + bool interlaced; + } kConfigs[kNumResolutionTypes][32] = { + { + // CEA Resolutions + { 640, 480, 60, false }, + { 720, 480, 60, false }, + { 720, 480, 60, true }, + { 720, 576, 50, false }, + { 720, 576, 50, true }, + { 1280, 720, 30, false }, + { 1280, 720, 60, false }, + { 1920, 1080, 30, false }, + { 1920, 1080, 60, false }, + { 1920, 1080, 60, true }, + { 1280, 720, 25, false }, + { 1280, 720, 50, false }, + { 1920, 1080, 25, false }, + { 1920, 1080, 50, false }, + { 1920, 1080, 50, true }, + { 1280, 720, 24, false }, + { 1920, 1080, 24, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + }, + { + // VESA Resolutions + { 800, 600, 30, false }, + { 800, 600, 60, false }, + { 1024, 768, 30, false }, + { 1024, 768, 60, false }, + { 1152, 864, 30, false }, + { 1152, 864, 60, false }, + { 1280, 768, 30, false }, + { 1280, 768, 60, false }, + { 1280, 800, 30, false }, + { 1280, 800, 60, false }, + { 1360, 768, 30, false }, + { 1360, 768, 60, false }, + { 1366, 768, 30, false }, + { 1366, 768, 60, false }, + { 1280, 1024, 30, false }, + { 1280, 1024, 60, false }, + { 1400, 1050, 30, false }, + { 1400, 1050, 60, false }, + { 1440, 900, 30, false }, + { 1440, 900, 60, false }, + { 1600, 900, 30, false }, + { 1600, 900, 60, false }, + { 1600, 1200, 30, false }, + { 1600, 1200, 60, false }, + { 1680, 1024, 30, false }, + { 1680, 1024, 60, false }, + { 1680, 1050, 30, false }, + { 1680, 1050, 60, false }, + { 1920, 1200, 30, false }, + { 1920, 1200, 60, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + }, + { + // HH Resolutions + { 800, 480, 30, false }, + { 800, 480, 60, false }, + { 854, 480, 30, false }, + { 854, 480, 60, false }, + { 864, 480, 30, false }, + { 864, 480, 60, false }, + { 640, 360, 30, false }, + { 640, 360, 60, false }, + { 960, 540, 30, false }, + { 960, 540, 60, false }, + { 848, 480, 30, false }, + { 848, 480, 60, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + { 0, 0, 0, false }, + } + }; + + const config_t *config = &kConfigs[type][index]; + + if (config->width == 0) { + return false; + } + + if (width) { + *width = config->width; + } + + if (height) { + *height = config->height; + } + + if (framesPerSecond) { + *framesPerSecond = config->framesPerSecond; + } + + if (interlaced) { + *interlaced = config->interlaced; + } + + return true; +} + +bool VideoFormats::parseFormatSpec(const char *spec) { + CHECK_EQ(kNumResolutionTypes, 3); + + unsigned native, dummy; + + if (sscanf( + spec, + "%02x %02x %02x %02x %08X %08X %08X", + &native, + &dummy, + &dummy, + &dummy, + &mResolutionEnabled[0], + &mResolutionEnabled[1], + &mResolutionEnabled[2]) != 7) { + return false; + } + + mNativeIndex = native >> 3; + mNativeType = (ResolutionType)(native & 7); + + if (mNativeType >= kNumResolutionTypes) { + return false; + } + + return GetConfiguration(mNativeType, mNativeIndex, NULL, NULL, NULL, NULL); +} + +AString VideoFormats::getFormatSpec() const { + CHECK_EQ(kNumResolutionTypes, 3); + + // wfd_video_formats: + // 1 byte "native" + // 1 byte "preferred-display-mode-supported" 0 or 1 + // one or more avc codec structures + // 1 byte profile + // 1 byte level + // 4 byte CEA mask + // 4 byte VESA mask + // 4 byte HH mask + // 1 byte latency + // 2 byte min-slice-slice + // 2 byte slice-enc-params + // 1 byte framerate-control-support + // max-hres (none or 2 byte) + // max-vres (none or 2 byte) + + return StringPrintf( + "%02x 00 02 02 %08x %08x %08x 00 0000 0000 00 none none", + (mNativeIndex << 3) | mNativeType, + mResolutionEnabled[0], + mResolutionEnabled[1], + mResolutionEnabled[2]); +} + +// static +bool VideoFormats::PickBestFormat( + const VideoFormats &sinkSupported, + const VideoFormats &sourceSupported, + ResolutionType *chosenType, + size_t *chosenIndex) { + ResolutionType nativeType; + size_t nativeIndex; + sinkSupported.getNativeResolution(&nativeType, &nativeIndex); + if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) { + if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) { + ALOGI("Choosing sink's native resolution"); + *chosenType = nativeType; + *chosenIndex = nativeIndex; + return true; + } + } else { + ALOGW("Sink advertised native resolution that it doesn't " + "actually support... ignoring"); + } + + sourceSupported.getNativeResolution(&nativeType, &nativeIndex); + if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) { + if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) { + ALOGI("Choosing source's native resolution"); + *chosenType = nativeType; + *chosenIndex = nativeIndex; + return true; + } + } else { + ALOGW("Source advertised native resolution that it doesn't " + "actually support... ignoring"); + } + + bool first = true; + uint32_t bestScore = 0; + size_t bestType = 0; + size_t bestIndex = 0; + for (size_t i = 0; i < kNumResolutionTypes; ++i) { + for (size_t j = 0; j < 32; ++j) { + size_t width, height, framesPerSecond; + bool interlaced; + if (!GetConfiguration( + (ResolutionType)i, + j, + &width, &height, &framesPerSecond, &interlaced)) { + break; + } + + if (!sinkSupported.isResolutionEnabled((ResolutionType)i, j) + || !sourceSupported.isResolutionEnabled( + (ResolutionType)i, j)) { + continue; + } + + ALOGV("type %u, index %u, %u x %u %c%u supported", + i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond); + + uint32_t score = width * height * framesPerSecond; + if (!interlaced) { + score *= 2; + } + + if (first || score > bestScore) { + bestScore = score; + bestType = i; + bestIndex = j; + + first = false; + } + } + } + + if (first) { + return false; + } + + *chosenType = (ResolutionType)bestType; + *chosenIndex = bestIndex; + + return true; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h new file mode 100644 index 0000000..a84407a --- /dev/null +++ b/media/libstagefright/wifi-display/VideoFormats.h @@ -0,0 +1,83 @@ +/* + * Copyright 2013, 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 VIDEO_FORMATS_H_ + +#define VIDEO_FORMATS_H_ + +#include + +#include + +namespace android { + +struct AString; + +// This class encapsulates that video resolution capabilities of a wfd source +// or sink as outlined in the wfd specs. Currently three sets of resolutions +// are specified, each of which supports up to 32 resolutions. +// In addition to its capabilities each sink/source also publishes its +// "native" resolution, presumably one that is preferred among all others +// because it wouldn't require any scaling and directly corresponds to the +// display capabilities/pixels. +struct VideoFormats { + VideoFormats(); + + enum ResolutionType { + RESOLUTION_CEA, + RESOLUTION_VESA, + RESOLUTION_HH, + kNumResolutionTypes, + }; + + void setNativeResolution(ResolutionType type, size_t index); + void getNativeResolution(ResolutionType *type, size_t *index) const; + + void disableAll(); + void enableAll(); + + void setResolutionEnabled( + ResolutionType type, size_t index, bool enabled = true); + + bool isResolutionEnabled(ResolutionType type, size_t index) const; + + static bool GetConfiguration( + ResolutionType type, size_t index, + size_t *width, size_t *height, size_t *framesPerSecond, + bool *interlaced); + + bool parseFormatSpec(const char *spec); + AString getFormatSpec() const; + + static bool PickBestFormat( + const VideoFormats &sinkSupported, + const VideoFormats &sourceSupported, + ResolutionType *chosenType, + size_t *chosenIndex); + +private: + ResolutionType mNativeType; + size_t mNativeIndex; + + uint32_t mResolutionEnabled[kNumResolutionTypes]; + + DISALLOW_EVIL_CONSTRUCTORS(VideoFormats); +}; + +} // namespace android + +#endif // VIDEO_FORMATS_H_ + diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp new file mode 100644 index 0000000..8120634 --- /dev/null +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -0,0 +1,428 @@ +/* + * Copyright 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 "DirectRenderer" +#include + +#include "DirectRenderer.h" + +#include "AnotherPacketSource.h" +#include "ATSParser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +// static +const int64_t DirectRenderer::kPacketLostDelayUs = 80000ll; + +// static +const int64_t DirectRenderer::kPacketLateDelayUs = 60000ll; + +DirectRenderer::DirectRenderer( + const sp ¬ifyLost, + const sp &bufferProducer) + : mNotifyLost(notifyLost), + mSurfaceTex(bufferProducer), + mTSParser(new ATSParser(ATSParser::ALIGNED_VIDEO_DATA)), + mVideoDecoderNotificationPending(false), + mAwaitingExtSeqNo(-1), + mRequestedRetransmission(false), + mPacketLostGeneration(0) { +} + +DirectRenderer::~DirectRenderer() { + if (mVideoDecoder != NULL) { + mVideoDecoder->release(); + mVideoDecoder.clear(); + + mVideoDecoderLooper->stop(); + mVideoDecoderLooper.clear(); + } +} + +void DirectRenderer::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatQueueBuffer: + { + sp buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + onQueueBuffer(buffer); + + dequeueMore(); + break; + } + + case kWhatPacketLate: + case kWhatPacketLost: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mPacketLostGeneration) { + // stale. + break; + } + + if (msg->what() == kWhatPacketLate) { + CHECK(!mRequestedRetransmission); + CHECK_GE(mAwaitingExtSeqNo, 0); + + ALOGV("packet extSeqNo %d is late, requesting retransmission.", + mAwaitingExtSeqNo); + + sp notify = mNotifyLost->dup(); + notify->setInt32("seqNo", (mAwaitingExtSeqNo & 0xffff)); + notify->post(); + + mRequestedRetransmission = true; + break; + } + + ALOGW("lost packet extSeqNo %d", mAwaitingExtSeqNo); + + sp extra; + mTSParser->signalDiscontinuity( + ATSParser::DISCONTINUITY_TIME, extra); + + mAwaitingExtSeqNo = -1; + mRequestedRetransmission = false; + dequeueMore(); + break; + } + + case kWhatVideoDecoderNotify: + { + onVideoDecoderNotify(); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::onQueueBuffer(const sp &buffer) { + int32_t newExtendedSeqNo = buffer->int32Data(); + + if (mPackets.empty()) { + mPackets.push_back(buffer); + return; + } + + if (mAwaitingExtSeqNo > 0 && newExtendedSeqNo < mAwaitingExtSeqNo) { + // We're no longer interested in these. They're old. + return; + } + + List >::iterator firstIt = mPackets.begin(); + List >::iterator it = --mPackets.end(); + for (;;) { + int32_t extendedSeqNo = (*it)->int32Data(); + + if (extendedSeqNo == newExtendedSeqNo) { + // Duplicate packet. + return; + } + + if (extendedSeqNo < newExtendedSeqNo) { + // Insert new packet after the one at "it". + mPackets.insert(++it, buffer); + return; + } + + if (it == firstIt) { + // Insert new packet before the first existing one. + mPackets.insert(it, buffer); + return; + } + + --it; + } +} + +void DirectRenderer::dequeueMore() { + if (mAwaitingExtSeqNo >= 0) { + // Remove all packets before the one we're looking for, they had + // their chance. + while (!mPackets.empty() + && (*mPackets.begin())->int32Data() < mAwaitingExtSeqNo) { + ALOGV("dropping late packet extSeqNo %d", + (*mPackets.begin())->int32Data()); + + mPackets.erase(mPackets.begin()); + } + } + + bool packetLostScheduled = (mAwaitingExtSeqNo >= 0); + + while (!mPackets.empty()) { + sp buffer = *mPackets.begin(); + int32_t extSeqNo = buffer->int32Data(); + + if (mAwaitingExtSeqNo >= 0 && extSeqNo != mAwaitingExtSeqNo) { + break; + } + + mPackets.erase(mPackets.begin()); + + if (packetLostScheduled) { + packetLostScheduled = false; + cancelPacketLost(); + } + + if (mRequestedRetransmission) { + ALOGV("recovered after requesting retransmission of extSeqNo %d", + mAwaitingExtSeqNo); + } + + CHECK_EQ(buffer->size() % 188, 0u); + + for (size_t offset = 0; offset < buffer->size(); offset += 188) { + status_t err = mTSParser->feedTSPacket( + buffer->data() + offset, 188); + + CHECK_EQ(err, (status_t)OK); + } + + mAwaitingExtSeqNo = extSeqNo + 1; + mRequestedRetransmission = false; + } + + if (!packetLostScheduled && mAwaitingExtSeqNo >= 0) { + schedulePacketLost(); + } + + dequeueAccessUnits(); +} + +void DirectRenderer::dequeueAccessUnits() { + sp audioSource = + static_cast( + mTSParser->getSource(ATSParser::AUDIO).get()); + + if (audioSource != NULL) { + status_t finalResult; + size_t n = 0; + while (audioSource->hasBufferAvailable(&finalResult)) { + sp accessUnit; + status_t err = audioSource->dequeueAccessUnit(&accessUnit); + if (err == OK) { + ++n; + } + } + + if (n > 0) { + ALOGV("dequeued %d audio access units.", n); + } + } + + sp videoSource = + static_cast( + mTSParser->getSource(ATSParser::VIDEO).get()); + + if (videoSource != NULL) { + if (mVideoDecoder == NULL) { + sp meta = videoSource->getFormat(); + if (meta != NULL) { + sp videoFormat; + status_t err = convertMetaDataToMessage(meta, &videoFormat); + CHECK_EQ(err, (status_t)OK); + + AString mime; + CHECK(videoFormat->findString("mime", &mime)); + + mVideoDecoderLooper = new ALooper; + mVideoDecoderLooper->setName("video codec looper"); + + mVideoDecoderLooper->start( + false /* runOnCallingThread */, + false /* canCallJava */, + PRIORITY_DEFAULT); + + mVideoDecoder = MediaCodec::CreateByType( + mVideoDecoderLooper, mime.c_str(), false /* encoder */); + + CHECK(mVideoDecoder != NULL); + + err = mVideoDecoder->configure( + videoFormat, + new SurfaceTextureClient(mSurfaceTex), + NULL /* crypto */, + 0 /* flags */); + + CHECK_EQ(err, (status_t)OK); + + err = mVideoDecoder->start(); + CHECK_EQ(err, (status_t)OK); + + err = mVideoDecoder->getInputBuffers( + &mVideoDecoderInputBuffers); + CHECK_EQ(err, (status_t)OK); + + scheduleVideoDecoderNotification(); + } + } + + status_t finalResult; + size_t n = 0; + while (videoSource->hasBufferAvailable(&finalResult)) { + sp accessUnit; + status_t err = videoSource->dequeueAccessUnit(&accessUnit); + if (err == OK) { + mVideoAccessUnits.push_back(accessUnit); + ++n; + } + } + + if (n > 0) { + ALOGV("dequeued %d video access units.", n); + queueVideoDecoderInputBuffers(); + } + } +} + +void DirectRenderer::schedulePacketLost() { + sp msg; + +#if 1 + msg = new AMessage(kWhatPacketLate, id()); + msg->setInt32("generation", mPacketLostGeneration); + msg->post(kPacketLateDelayUs); +#endif + + msg = new AMessage(kWhatPacketLost, id()); + msg->setInt32("generation", mPacketLostGeneration); + msg->post(kPacketLostDelayUs); +} + +void DirectRenderer::cancelPacketLost() { + ++mPacketLostGeneration; +} + +void DirectRenderer::queueVideoDecoderInputBuffers() { + if (mVideoDecoder == NULL) { + return; + } + + bool submittedMore = false; + + while (!mVideoAccessUnits.empty() + && !mVideoDecoderInputBuffersAvailable.empty()) { + size_t index = *mVideoDecoderInputBuffersAvailable.begin(); + + mVideoDecoderInputBuffersAvailable.erase( + mVideoDecoderInputBuffersAvailable.begin()); + + sp srcBuffer = *mVideoAccessUnits.begin(); + mVideoAccessUnits.erase(mVideoAccessUnits.begin()); + + const sp &dstBuffer = + mVideoDecoderInputBuffers.itemAt(index); + + memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size()); + + int64_t timeUs; + CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs)); + + status_t err = mVideoDecoder->queueInputBuffer( + index, + 0 /* offset */, + srcBuffer->size(), + timeUs, + 0 /* flags */); + CHECK_EQ(err, (status_t)OK); + + submittedMore = true; + } + + if (submittedMore) { + scheduleVideoDecoderNotification(); + } +} + +void DirectRenderer::onVideoDecoderNotify() { + mVideoDecoderNotificationPending = false; + + for (;;) { + size_t index; + status_t err = mVideoDecoder->dequeueInputBuffer(&index); + + if (err == OK) { + mVideoDecoderInputBuffersAvailable.push_back(index); + } else if (err == -EAGAIN) { + break; + } else { + TRESPASS(); + } + } + + queueVideoDecoderInputBuffers(); + + for (;;) { + size_t index; + size_t offset; + size_t size; + int64_t timeUs; + uint32_t flags; + status_t err = mVideoDecoder->dequeueOutputBuffer( + &index, + &offset, + &size, + &timeUs, + &flags); + + if (err == OK) { + err = mVideoDecoder->renderOutputBufferAndRelease(index); + CHECK_EQ(err, (status_t)OK); + } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { + // We don't care. + } else if (err == INFO_FORMAT_CHANGED) { + // We don't care. + } else if (err == -EAGAIN) { + break; + } else { + TRESPASS(); + } + } + + scheduleVideoDecoderNotification(); +} + +void DirectRenderer::scheduleVideoDecoderNotification() { + if (mVideoDecoderNotificationPending) { + return; + } + + sp notify = + new AMessage(kWhatVideoDecoderNotify, id()); + + mVideoDecoder->requestActivityNotification(notify); + mVideoDecoderNotificationPending = true; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h new file mode 100644 index 0000000..2babcb8 --- /dev/null +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -0,0 +1,94 @@ +/* + * Copyright 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 DIRECT_RENDERER_H_ + +#define DIRECT_RENDERER_H_ + +#include + +namespace android { + +struct ABuffer; +struct ATSParser; +struct IGraphicBufferProducer; +struct MediaCodec; + +// An experimental renderer that only supports video and decodes video data +// as soon as it arrives using a MediaCodec instance, rendering it without +// delay. Primarily meant to finetune packet loss discovery and minimize +// latency. +struct DirectRenderer : public AHandler { + DirectRenderer( + const sp ¬ifyLost, + const sp &bufferProducer); + + enum { + kWhatQueueBuffer = 'queB', + }; + +protected: + virtual void onMessageReceived(const sp &msg); + virtual ~DirectRenderer(); + +private: + enum { + kWhatPacketLate, + kWhatPacketLost, + kWhatVideoDecoderNotify, + }; + + static const int64_t kPacketLateDelayUs; + static const int64_t kPacketLostDelayUs; + + sp mNotifyLost; + sp mSurfaceTex; + + // Ordered by extended seq number. + List > mPackets; + + sp mTSParser; + + sp mVideoDecoderLooper; + sp mVideoDecoder; + Vector > mVideoDecoderInputBuffers; + List mVideoDecoderInputBuffersAvailable; + bool mVideoDecoderNotificationPending; + + List > mVideoAccessUnits; + + int32_t mAwaitingExtSeqNo; + bool mRequestedRetransmission; + int32_t mPacketLostGeneration; + + void onQueueBuffer(const sp &buffer); + void onVideoDecoderNotify(); + + void dequeueMore(); + void dequeueAccessUnits(); + + void schedulePacketLost(); + void cancelPacketLost(); + + void queueVideoDecoderInputBuffers(); + void scheduleVideoDecoderNotification(); + + DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); +}; + +} // namespace android + +#endif // DIRECT_RENDERER_H_ diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp index 640e055..ad75373 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.cpp +++ b/media/libstagefright/wifi-display/sink/RTPSink.cpp @@ -21,7 +21,14 @@ #include "RTPSink.h" #include "ANetworkSession.h" + +#if USE_TUNNEL_RENDERER #include "TunnelRenderer.h" +#define RENDERER_CLASS TunnelRenderer +#else +#include "DirectRenderer.h" +#define RENDERER_CLASS DirectRenderer +#endif #include #include @@ -238,9 +245,11 @@ void RTPSink::Source::addReportBlock( RTPSink::RTPSink( const sp &netSession, - const sp &bufferProducer) + const sp &bufferProducer, + const sp ¬ify) : mNetSession(netSession), mSurfaceTex(bufferProducer), + mNotify(notify), mRTPPort(0), mRTPSessionID(0), mRTCPSessionID(0), @@ -470,6 +479,7 @@ status_t RTPSink::parseRTP(const sp &buffer) { uint32_t rtpTime = U32_AT(&data[4]); uint16_t seqNo = U16_AT(&data[2]); +#if 0 int64_t arrivalTimeUs; CHECK(buffer->meta()->findInt64("arrivalTimeUs", &arrivalTimeUs)); @@ -500,6 +510,7 @@ status_t RTPSink::parseRTP(const sp &buffer) { ALOGI("packet was %.2f ms late", latenessMs); } } +#endif sp meta = buffer->meta(); meta->setInt32("ssrc", srcId); @@ -515,12 +526,12 @@ status_t RTPSink::parseRTP(const sp &buffer) { sp notifyLost = new AMessage(kWhatPacketLost, id()); notifyLost->setInt32("ssrc", srcId); - mRenderer = new TunnelRenderer(notifyLost, mSurfaceTex); + mRenderer = new RENDERER_CLASS(notifyLost, mSurfaceTex); looper()->registerHandler(mRenderer); } sp queueBufferMsg = - new AMessage(TunnelRenderer::kWhatQueueBuffer, mRenderer->id()); + new AMessage(RENDERER_CLASS::kWhatQueueBuffer, mRenderer->id()); sp source = new Source(seqNo, buffer, queueBufferMsg); mSources.add(srcId, source); diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h index 2183fd6..6e40185 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.h +++ b/media/libstagefright/wifi-display/sink/RTPSink.h @@ -24,18 +24,26 @@ #include +#define USE_TUNNEL_RENDERER 0 + namespace android { struct ABuffer; struct ANetworkSession; + +#if USE_TUNNEL_RENDERER struct TunnelRenderer; +#else +struct DirectRenderer; +#endif // Creates a pair of sockets for RTP/RTCP traffic, instantiates a renderer // for incoming transport stream data and occasionally sends statistics over // the RTCP channel. struct RTPSink : public AHandler { RTPSink(const sp &netSession, - const sp &bufferProducer); + const sp &bufferProducer, + const sp ¬ify); // If TCP interleaving is used, no UDP sockets are created, instead // incoming RTP/RTCP packets (arriving on the RTSP control connection) @@ -67,6 +75,7 @@ private: sp mNetSession; sp mSurfaceTex; + sp mNotify; KeyedVector > mSources; int32_t mRTPPort; @@ -78,7 +87,11 @@ private: LinearRegression mRegression; int64_t mMaxDelayMs; +#if USE_TUNNEL_RENDERER sp mRenderer; +#else + sp mRenderer; +#endif status_t parseRTP(const sp &buffer); status_t parseRTCP(const sp &buffer); diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index f3f4536..04dbd7b 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -264,16 +264,17 @@ sp TunnelRenderer::dequeueBuffer() { if (mFirstFailedAttemptUs < 0ll) { mFirstFailedAttemptUs = ALooper::GetNowUs(); - ALOGI("failed to get the correct packet the first time."); + ALOGV("failed to get the correct packet the first time."); return NULL; } if (mFirstFailedAttemptUs + 50000ll > ALooper::GetNowUs()) { // We're willing to wait a little while to get the right packet. -#if 0 +#if 1 if (!mRequestedRetransmission) { - ALOGI("requesting retransmission of seqNo %d", + ALOGI("requesting retransmission of extSeqNo %d (seqNo %d)", + mLastDequeuedExtSeqNo + 1, (mLastDequeuedExtSeqNo + 1) & 0xffff); sp notify = mNotifyLost->dup(); @@ -284,7 +285,7 @@ sp TunnelRenderer::dequeueBuffer() { } else #endif { - ALOGI("still waiting for the correct packet to arrive."); + ALOGV("still waiting for the correct packet to arrive."); } return NULL; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 0f0caf1..46c40c7 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -31,12 +31,27 @@ namespace android { WifiDisplaySink::WifiDisplaySink( const sp &netSession, - const sp &bufferProducer) + const sp &bufferProducer, + const sp ¬ify) : mState(UNDEFINED), mNetSession(netSession), mSurfaceTex(bufferProducer), + mNotify(notify), mSessionID(0), mNextCSeq(1) { +#if 1 + // We support any and all resolutions, but prefer 720p30 + mSinkSupportedVideoFormats.setNativeResolution( + VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 + + mSinkSupportedVideoFormats.enableAll(); +#else + // We only support 800 x 600 p60. + mSinkSupportedVideoFormats.disableAll(); + + mSinkSupportedVideoFormats.setNativeResolution( + VideoFormats::RESOLUTION_VESA, 1); // 800 x 600 p60 +#endif } WifiDisplaySink::~WifiDisplaySink() { @@ -123,6 +138,8 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatStart: { + sleep(2); // XXX + int32_t sourcePort; if (msg->findString("setupURI", &mSetupURI)) { @@ -176,7 +193,13 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { mNetSession->destroySession(mSessionID); mSessionID = 0; - looper()->stop(); + if (mNotify == NULL) { + looper()->stop(); + } else { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatDisconnected); + notify->post(); + } } break; } @@ -227,6 +250,18 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { break; } + case kWhatRequestIDRFrame: + { + ALOGI("requesting IDR frame"); + sendIDRFrameRequest(mSessionID); + break; + } + + case kWhatRTPSinkNotify: + { + break; + } + default: TRESPASS(); } @@ -392,6 +427,11 @@ status_t WifiDisplaySink::onReceivePlayResponse( return OK; } +status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse( + int32_t sessionID, const sp &msg) { + return OK; +} + void WifiDisplaySink::onReceiveClientData(const sp &msg) { int32_t sessionID; CHECK(msg->findInt32("sessionID", &sessionID)); @@ -474,11 +514,11 @@ void WifiDisplaySink::onGetParameterRequest( int32_t sessionID, int32_t cseq, const sp &data) { - AString body = - "wfd_video_formats: " - "28 00 02 02 FFFFFFFF 0000000 00000000 00 0000 0000 00 none none\r\n" - "wfd_audio_codecs: AAC 0000000F 00\r\n" - "wfd_client_rtp_ports: RTP/AVP/UDP;unicast 19000 0 mode=play\r\n"; + AString body = "wfd_video_formats: "; + body.append(mSinkSupportedVideoFormats.getFormatSpec()); + body.append( + "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n" + "wfd_client_rtp_ports: RTP/AVP/UDP;unicast 19000 0 mode=play\r\n"); AString response = "RTSP/1.0 200 OK\r\n"; AppendCommonResponse(&response, cseq); @@ -517,7 +557,9 @@ status_t WifiDisplaySink::sendDescribe(int32_t sessionID, const char *uri) { } status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { - mRTPSink = new RTPSink(mNetSession, mSurfaceTex); + sp notify = new AMessage(kWhatRTPSinkNotify, id()); + + mRTPSink = new RTPSink(mNetSession, mSurfaceTex, notify); looper()->registerHandler(mRTPSink); status_t err = mRTPSink->init(sUseTCPInterleaving); @@ -584,6 +626,35 @@ status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) { return OK; } +status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) { + AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; + + AppendCommonResponse(&request, mNextCSeq); + + AString content = "wfd_idr_request\r\n"; + + request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); + request.append(StringPrintf("Content-Length: %d\r\n", content.size())); + request.append("\r\n"); + request.append(content); + + status_t err = + mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, + mNextCSeq, + &WifiDisplaySink::onReceiveIDRFrameRequestResponse); + + ++mNextCSeq; + + return OK; +} + void WifiDisplaySink::onSetParameterRequest( int32_t sessionID, int32_t cseq, diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index a508839..5f86519 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -20,11 +20,14 @@ #include "ANetworkSession.h" +#include "VideoFormats.h" + #include #include namespace android { +struct AMessage; struct ParsedMessage; struct RTPSink; @@ -32,9 +35,18 @@ struct RTPSink; // Connects to a wifi display source and renders the incoming // transport stream using a MediaPlayer instance. struct WifiDisplaySink : public AHandler { + enum { + kWhatDisconnected, + }; + + // If no notification message is specified (notify == NULL) + // the sink will stop its looper() once the session ends, + // otherwise it will post an appropriate notification but leave + // the looper() running. WifiDisplaySink( const sp &netSession, - const sp &bufferProducer = NULL); + const sp &bufferProducer = NULL, + const sp ¬ify = NULL); void start(const char *sourceHost, int32_t sourcePort); void start(const char *uri); @@ -56,6 +68,8 @@ private: kWhatStart, kWhatRTSPNotify, kWhatStop, + kWhatRequestIDRFrame, + kWhatRTPSinkNotify, }; struct ResponseID { @@ -75,8 +89,10 @@ private: static const bool sUseTCPInterleaving = false; State mState; + VideoFormats mSinkSupportedVideoFormats; sp mNetSession; sp mSurfaceTex; + sp mNotify; AString mSetupURI; AString mRTSPHost; int32_t mSessionID; @@ -93,6 +109,7 @@ private: status_t sendDescribe(int32_t sessionID, const char *uri); status_t sendSetup(int32_t sessionID, const char *uri); status_t sendPlay(int32_t sessionID, const char *uri); + status_t sendIDRFrameRequest(int32_t sessionID); status_t onReceiveM2Response( int32_t sessionID, const sp &msg); @@ -108,6 +125,9 @@ private: status_t onReceivePlayResponse( int32_t sessionID, const sp &msg); + status_t onReceiveIDRFrameRequestResponse( + int32_t sessionID, const sp &msg); + void registerResponseHandler( int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index d6b87a7..91dc1fa 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -346,8 +346,17 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( status_t WifiDisplaySource::PlaybackSession::init( const char *clientIP, int32_t clientRtp, int32_t clientRtcp, Sender::TransportMode transportMode, - bool usePCMAudio) { - status_t err = setupPacketizer(usePCMAudio); + bool enableAudio, + bool usePCMAudio, + bool enableVideo, + VideoFormats::ResolutionType videoResolutionType, + size_t videoResolutionIndex) { + status_t err = setupPacketizer( + enableAudio, + usePCMAudio, + enableVideo, + videoResolutionType, + videoResolutionIndex); if (err != OK) { return err; @@ -639,13 +648,27 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } -status_t WifiDisplaySource::PlaybackSession::setupPacketizer(bool usePCMAudio) { +status_t WifiDisplaySource::PlaybackSession::setupPacketizer( + bool enableAudio, + bool usePCMAudio, + bool enableVideo, + VideoFormats::ResolutionType videoResolutionType, + size_t videoResolutionIndex) { + CHECK(enableAudio || enableVideo); + mPacketizer = new TSPacketizer; - status_t err = addVideoSource(); + if (enableVideo) { + status_t err = addVideoSource( + videoResolutionType, videoResolutionIndex); - if (err != OK) { - return err; + if (err != OK) { + return err; + } + } + + if (!enableAudio) { + return OK; } return addAudioSource(usePCMAudio); @@ -735,27 +758,30 @@ status_t WifiDisplaySource::PlaybackSession::addSource( return OK; } -status_t WifiDisplaySource::PlaybackSession::addVideoSource() { - sp source = new SurfaceMediaSource(width(), height()); +status_t WifiDisplaySource::PlaybackSession::addVideoSource( + VideoFormats::ResolutionType videoResolutionType, + size_t videoResolutionIndex) { + size_t width, height, framesPerSecond; + bool interlaced; + CHECK(VideoFormats::GetConfiguration( + videoResolutionType, + videoResolutionIndex, + &width, + &height, + &framesPerSecond, + &interlaced)); + + sp source = new SurfaceMediaSource(width, height); source->setUseAbsoluteTimestamps(); -#if 1 sp videoSource = - new RepeaterSource(source, 30.0 /* rateHz */); -#endif + new RepeaterSource(source, framesPerSecond); -#if 1 size_t numInputBuffers; status_t err = addSource( true /* isVideo */, videoSource, true /* isRepeaterSource */, false /* usePCMAudio */, &numInputBuffers); -#else - size_t numInputBuffers; - status_t err = addSource( - true /* isVideo */, source, false /* isRepeaterSource */, - false /* usePCMAudio */, &numInputBuffers); -#endif if (err != OK) { return err; @@ -790,22 +816,6 @@ sp WifiDisplaySource::PlaybackSession::getSurfaceTexture return mBufferQueue; } -int32_t WifiDisplaySource::PlaybackSession::width() const { -#if USE_1080P - return 1920; -#else - return 1280; -#endif -} - -int32_t WifiDisplaySource::PlaybackSession::height() const { -#if USE_1080P - return 1080; -#else - return 720; -#endif -} - void WifiDisplaySource::PlaybackSession::requestIDRFrame() { for (size_t i = 0; i < mTracks.size(); ++i) { const sp &track = mTracks.valueAt(i); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index 281548d..7365c78 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -19,6 +19,7 @@ #define PLAYBACK_SESSION_H_ #include "Sender.h" +#include "VideoFormats.h" #include "WifiDisplaySource.h" namespace android { @@ -43,7 +44,11 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { status_t init( const char *clientIP, int32_t clientRtp, int32_t clientRtcp, Sender::TransportMode transportMode, - bool usePCMAudio); + bool enableAudio, + bool usePCMAudio, + bool enableVideo, + VideoFormats::ResolutionType videoResolutionType, + size_t videoResolutionIndex); void destroyAsync(); @@ -57,8 +62,6 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { status_t pause(); sp getSurfaceTexture(); - int32_t width() const; - int32_t height() const; void requestIDRFrame(); @@ -109,7 +112,12 @@ private: bool mAllTracksHavePacketizerIndex; - status_t setupPacketizer(bool usePCMAudio); + status_t setupPacketizer( + bool enableAudio, + bool usePCMAudio, + bool enableVideo, + VideoFormats::ResolutionType videoResolutionType, + size_t videoResolutionIndex); status_t addSource( bool isVideo, @@ -118,7 +126,10 @@ private: bool usePCMAudio, size_t *numInputBuffers); - status_t addVideoSource(); + status_t addVideoSource( + VideoFormats::ResolutionType videoResolutionType, + size_t videoResolutionIndex); + status_t addAudioSource(bool usePCMAudio); ssize_t appendTSData( diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp index 9048691..8b7d93f 100644 --- a/media/libstagefright/wifi-display/source/Sender.cpp +++ b/media/libstagefright/wifi-display/source/Sender.cpp @@ -685,7 +685,15 @@ status_t Sender::parseTSFB( if (!foundSeqNo || blp != 0) { ALOGI("Some sequence numbers were no longer available for " - "retransmission"); + "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)", + seqNo, foundSeqNo, blp); + + if (!mHistory.empty()) { + int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff; + int32_t latest = (*--mHistory.end())->int32Data() & 0xffff; + + ALOGI("have seq numbers from %d - %d", earliest, latest); + } } } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 9ec1064..0fed19b 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -58,8 +58,19 @@ WifiDisplaySource::WifiDisplaySource( mIsHDCP2_0(false), mHDCPPort(0), mHDCPInitializationComplete(false), - mSetupTriggerDeferred(false) -{ + mSetupTriggerDeferred(false) { + mSupportedSourceVideoFormats.enableAll(); + + mSupportedSourceVideoFormats.setNativeResolution( + VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30 + + // Disable resolutions above 1080p since the encoder won't be able to + // handle them. + mSupportedSourceVideoFormats.setResolutionEnabled( + VideoFormats::RESOLUTION_VESA, 28, false); // 1920x1200 p30 + + mSupportedSourceVideoFormats.setResolutionEnabled( + VideoFormats::RESOLUTION_VESA, 29, false); // 1920x1200 p60 } WifiDisplaySource::~WifiDisplaySource() { @@ -375,13 +386,33 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { IRemoteDisplayClient::kDisplayErrorUnknown); } else if (what == PlaybackSession::kWhatSessionEstablished) { if (mClient != NULL) { - mClient->onDisplayConnected( - mClientInfo.mPlaybackSession->getSurfaceTexture(), - mClientInfo.mPlaybackSession->width(), - mClientInfo.mPlaybackSession->height(), - mUsingHDCP - ? IRemoteDisplayClient::kDisplayFlagSecure - : 0); + if (!mSinkSupportsVideo) { + mClient->onDisplayConnected( + NULL, // SurfaceTexture + 0, // width, + 0, // height, + mUsingHDCP + ? IRemoteDisplayClient::kDisplayFlagSecure + : 0); + } else { + size_t width, height; + + CHECK(VideoFormats::GetConfiguration( + mChosenVideoResolutionType, + mChosenVideoResolutionIndex, + &width, + &height, + NULL /* framesPerSecond */, + NULL /* interlaced */)); + + mClient->onDisplayConnected( + mClientInfo.mPlaybackSession->getSurfaceTexture(), + width, + height, + mUsingHDCP + ? IRemoteDisplayClient::kDisplayFlagSecure + : 0); + } } if (mState == ABOUT_TO_PLAY) { @@ -564,22 +595,6 @@ status_t WifiDisplaySource::sendM3(int32_t sessionID) { } status_t WifiDisplaySource::sendM4(int32_t sessionID) { - // wfd_video_formats: - // 1 byte "native" - // 1 byte "preferred-display-mode-supported" 0 or 1 - // one or more avc codec structures - // 1 byte profile - // 1 byte level - // 4 byte CEA mask - // 4 byte VESA mask - // 4 byte HH mask - // 1 byte latency - // 2 byte min-slice-slice - // 2 byte slice-enc-params - // 1 byte framerate-control-support - // max-hres (none or 2 byte) - // max-vres (none or 2 byte) - CHECK_EQ(sessionID, mClientSessionID); AString transportString = "UDP"; @@ -591,28 +606,35 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { transportString = "TCP"; } - // For 720p60: - // use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n" - // For 720p30: - // use "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n" - // For 720p24: - // use "78 00 02 02 00008000 00000000 00000000 00 0000 0000 00 none none\r\n" - // For 1080p30: - // use "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n" - AString body = StringPrintf( - "wfd_video_formats: " -#if USE_1080P - "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n" -#else - "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n" -#endif - "wfd_audio_codecs: %s\r\n" - "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n" - "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n", - (mUsingPCMAudio - ? "LPCM 00000002 00" // 2 ch PCM 48kHz - : "AAC 00000001 00"), // 2 ch AAC 48kHz - mClientInfo.mLocalIP.c_str(), transportString.c_str(), mChosenRTPPort); + AString body; + + if (mSinkSupportsVideo) { + body.append("wfd_video_formats: "); + + VideoFormats chosenVideoFormat; + chosenVideoFormat.disableAll(); + chosenVideoFormat.setNativeResolution( + mChosenVideoResolutionType, mChosenVideoResolutionIndex); + + body.append(chosenVideoFormat.getFormatSpec()); + body.append("\r\n"); + } + + if (mSinkSupportsAudio) { + body.append( + StringPrintf("wfd_audio_codecs: %s\r\n", + (mUsingPCMAudio + ? "LPCM 00000002 00" // 2 ch PCM 48kHz + : "AAC 00000001 00"))); // 2 ch AAC 48kHz + } + + body.append( + StringPrintf( + "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n" + "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n", + mClientInfo.mLocalIP.c_str(), + transportString.c_str(), + mChosenRTPPort)); AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; AppendCommonResponse(&request, mNextCSeq); @@ -789,39 +811,90 @@ status_t WifiDisplaySource::onReceiveM3Response( mChosenRTPPort = port0; + if (!params->findParameter("wfd_video_formats", &value)) { + ALOGE("Sink doesn't report its choice of wfd_video_formats."); + return ERROR_MALFORMED; + } + + mSinkSupportsVideo = false; + + if (!(value == "none")) { + mSinkSupportsVideo = true; + if (!mSupportedSinkVideoFormats.parseFormatSpec(value.c_str())) { + ALOGE("Failed to parse sink provided wfd_video_formats (%s)", + value.c_str()); + + return ERROR_MALFORMED; + } + + if (!VideoFormats::PickBestFormat( + mSupportedSinkVideoFormats, + mSupportedSourceVideoFormats, + &mChosenVideoResolutionType, + &mChosenVideoResolutionIndex)) { + ALOGE("Sink and source share no commonly supported video " + "formats."); + + return ERROR_UNSUPPORTED; + } + + size_t width, height, framesPerSecond; + bool interlaced; + CHECK(VideoFormats::GetConfiguration( + mChosenVideoResolutionType, + mChosenVideoResolutionIndex, + &width, + &height, + &framesPerSecond, + &interlaced)); + + ALOGI("Picked video resolution %u x %u %c%u", + width, height, interlaced ? 'i' : 'p', framesPerSecond); + } else { + ALOGI("Sink doesn't support video at all."); + } + if (!params->findParameter("wfd_audio_codecs", &value)) { ALOGE("Sink doesn't report its choice of wfd_audio_codecs."); return ERROR_MALFORMED; } - if (value == "none") { - ALOGE("Sink doesn't support audio at all."); - return ERROR_UNSUPPORTED; - } + mSinkSupportsAudio = false; - uint32_t modes; - GetAudioModes(value.c_str(), "AAC", &modes); + if (!(value == "none")) { + mSinkSupportsAudio = true; - bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz + uint32_t modes; + GetAudioModes(value.c_str(), "AAC", &modes); - GetAudioModes(value.c_str(), "LPCM", &modes); + bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz - bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz + GetAudioModes(value.c_str(), "LPCM", &modes); - char val[PROPERTY_VALUE_MAX]; - if (supportsPCM - && property_get("media.wfd.use-pcm-audio", val, NULL) - && (!strcasecmp("true", val) || !strcmp("1", val))) { - ALOGI("Using PCM audio."); - mUsingPCMAudio = true; - } else if (supportsAAC) { - ALOGI("Using AAC audio."); - mUsingPCMAudio = false; - } else if (supportsPCM) { - ALOGI("Using PCM audio."); - mUsingPCMAudio = true; + bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz + + char val[PROPERTY_VALUE_MAX]; + if (supportsPCM + && property_get("media.wfd.use-pcm-audio", val, NULL) + && (!strcasecmp("true", val) || !strcmp("1", val))) { + ALOGI("Using PCM audio."); + mUsingPCMAudio = true; + } else if (supportsAAC) { + ALOGI("Using AAC audio."); + mUsingPCMAudio = false; + } else if (supportsPCM) { + ALOGI("Using PCM audio."); + mUsingPCMAudio = true; + } else { + ALOGI("Sink doesn't support an audio format we do."); + return ERROR_UNSUPPORTED; + } } else { - ALOGI("Sink doesn't support an audio format we do."); + ALOGI("Sink doesn't support audio at all."); + } + + if (!mSinkSupportsVideo && !mSinkSupportsAudio) { + ALOGE("Sink supports neither video nor audio..."); return ERROR_UNSUPPORTED; } @@ -1160,7 +1233,11 @@ status_t WifiDisplaySource::onSetupRequest( clientRtp, clientRtcp, transportMode, - mUsingPCMAudio); + mSinkSupportsAudio, + mUsingPCMAudio, + mSinkSupportsVideo, + mChosenVideoResolutionType, + mChosenVideoResolutionIndex); if (err != OK) { looper()->unregisterHandler(playbackSession->id()); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 974e070..fec2c6d 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -19,6 +19,7 @@ #define WIFI_DISPLAY_SOURCE_H_ #include "ANetworkSession.h" +#include "VideoFormats.h" #include @@ -26,8 +27,6 @@ namespace android { -#define USE_1080P 0 - struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; @@ -112,6 +111,7 @@ private: kPlaybackSessionTimeoutSecs * 1000000ll; State mState; + VideoFormats mSupportedSourceVideoFormats; sp mNetSession; sp mClient; struct in_addr mInterfaceAddr; @@ -121,6 +121,14 @@ private: int32_t mChosenRTPPort; // extracted from "wfd_client_rtp_ports" + bool mSinkSupportsVideo; + VideoFormats mSupportedSinkVideoFormats; + + VideoFormats::ResolutionType mChosenVideoResolutionType; + size_t mChosenVideoResolutionIndex; + + bool mSinkSupportsAudio; + bool mUsingPCMAudio; int32_t mClientSessionID; diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 2ec9b4f..be9e35e 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace android { -- cgit v1.1 From a6a88d9c445e261972c2433254e0a996336e78a4 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 30 Jan 2013 10:41:25 -0800 Subject: Plumbing to reflect minor changes in the HDCP module API that allow for support of _decryption_ modules in addition to what we already supported. Change-Id: Ic37b87dc170ba8def3817991d25df798f21e950b --- media/libmedia/IHDCP.cpp | 49 ++++++++++++++++++++++ media/libmedia/IMediaPlayerService.cpp | 6 ++- media/libmediaplayerservice/HDCP.cpp | 26 ++++++++++-- media/libmediaplayerservice/HDCP.h | 8 +++- media/libmediaplayerservice/MediaPlayerService.cpp | 4 +- media/libmediaplayerservice/MediaPlayerService.h | 2 +- .../wifi-display/source/WifiDisplaySource.cpp | 2 +- 7 files changed, 87 insertions(+), 10 deletions(-) (limited to 'media') diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp index 493f5a4..f13addc 100644 --- a/media/libmedia/IHDCP.cpp +++ b/media/libmedia/IHDCP.cpp @@ -31,6 +31,7 @@ enum { HDCP_INIT_ASYNC, HDCP_SHUTDOWN_ASYNC, HDCP_ENCRYPT, + HDCP_DECRYPT, }; struct BpHDCPObserver : public BpInterface { @@ -106,6 +107,29 @@ struct BpHDCP : public BpInterface { return err; } + + virtual status_t decrypt( + const void *inData, size_t size, + uint32_t streamCTR, uint64_t inputCTR, + void *outData) { + Parcel data, reply; + data.writeInterfaceToken(IHDCP::getInterfaceDescriptor()); + data.writeInt32(size); + data.write(inData, size); + data.writeInt32(streamCTR); + data.writeInt64(inputCTR); + remote()->transact(HDCP_DECRYPT, data, &reply); + + status_t err = reply.readInt32(); + + if (err != OK) { + return err; + } + + reply.read(outData, size); + + return err; + } }; IMPLEMENT_META_INTERFACE(HDCP, "android.hardware.IHDCP"); @@ -198,6 +222,31 @@ status_t BnHDCP::onTransact( return OK; } + case HDCP_DECRYPT: + { + size_t size = data.readInt32(); + + void *inData = malloc(2 * size); + void *outData = (uint8_t *)inData + size; + + data.read(inData, size); + + uint32_t streamCTR = data.readInt32(); + uint64_t inputCTR = data.readInt64(); + status_t err = decrypt(inData, size, streamCTR, inputCTR, outData); + + reply->writeInt32(err); + + if (err == OK) { + reply->write(outData, size); + } + + free(inData); + inData = outData = NULL; + + return OK; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index ae76c10..a95f4c9 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -123,9 +123,10 @@ public: return interface_cast(reply.readStrongBinder()); } - virtual sp makeHDCP() { + virtual sp makeHDCP(bool createEncryptionModule) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(createEncryptionModule); remote()->transact(MAKE_HDCP, data, &reply); return interface_cast(reply.readStrongBinder()); } @@ -226,7 +227,8 @@ status_t BnMediaPlayerService::onTransact( } break; case MAKE_HDCP: { CHECK_INTERFACE(IMediaPlayerService, data, reply); - sp hdcp = makeHDCP(); + bool createEncryptionModule = data.readInt32(); + sp hdcp = makeHDCP(createEncryptionModule); reply->writeStrongBinder(hdcp->asBinder()); return NO_ERROR; } break; diff --git a/media/libmediaplayerservice/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp index 09b9719..469a02e 100644 --- a/media/libmediaplayerservice/HDCP.cpp +++ b/media/libmediaplayerservice/HDCP.cpp @@ -26,8 +26,9 @@ namespace android { -HDCP::HDCP() - : mLibHandle(NULL), +HDCP::HDCP(bool createEncryptionModule) + : mIsEncryptionModule(createEncryptionModule), + mLibHandle(NULL), mHDCPModule(NULL) { mLibHandle = dlopen("libstagefright_hdcp.so", RTLD_NOW); @@ -40,7 +41,10 @@ HDCP::HDCP() void *, HDCPModule::ObserverFunc); CreateHDCPModuleFunc createHDCPModule = - (CreateHDCPModuleFunc)dlsym(mLibHandle, "createHDCPModule"); + mIsEncryptionModule + ? (CreateHDCPModuleFunc)dlsym(mLibHandle, "createHDCPModule") + : (CreateHDCPModuleFunc)dlsym( + mLibHandle, "createHDCPModuleForDecryption"); if (createHDCPModule == NULL) { ALOGE("Unable to find symbol 'createHDCPModule'."); @@ -101,6 +105,8 @@ status_t HDCP::encrypt( uint64_t *outInputCTR, void *outData) { Mutex::Autolock autoLock(mLock); + CHECK(mIsEncryptionModule); + if (mHDCPModule == NULL) { *outInputCTR = 0; @@ -110,6 +116,20 @@ status_t HDCP::encrypt( return mHDCPModule->encrypt(inData, size, streamCTR, outInputCTR, outData); } +status_t HDCP::decrypt( + const void *inData, size_t size, + uint32_t streamCTR, uint64_t outInputCTR, void *outData) { + Mutex::Autolock autoLock(mLock); + + CHECK(!mIsEncryptionModule); + + if (mHDCPModule == NULL) { + return NO_INIT; + } + + return mHDCPModule->decrypt(inData, size, streamCTR, outInputCTR, outData); +} + // static void HDCP::ObserveWrapper(void *me, int msg, int ext1, int ext2) { static_cast(me)->observe(msg, ext1, ext2); diff --git a/media/libmediaplayerservice/HDCP.h b/media/libmediaplayerservice/HDCP.h index b2fc457..42e6467 100644 --- a/media/libmediaplayerservice/HDCP.h +++ b/media/libmediaplayerservice/HDCP.h @@ -24,7 +24,7 @@ namespace android { struct HDCP : public BnHDCP { - HDCP(); + HDCP(bool createEncryptionModule); virtual ~HDCP(); virtual status_t setObserver(const sp &observer); @@ -35,9 +35,15 @@ struct HDCP : public BnHDCP { const void *inData, size_t size, uint32_t streamCTR, uint64_t *outInputCTR, void *outData); + virtual status_t decrypt( + const void *inData, size_t size, + uint32_t streamCTR, uint64_t outInputCTR, void *outData); + private: Mutex mLock; + bool mIsEncryptionModule; + void *mLibHandle; HDCPModule *mHDCPModule; sp mObserver; diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 749f48c..f932131 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -285,8 +285,8 @@ sp MediaPlayerService::makeCrypto() { return new Crypto; } -sp MediaPlayerService::makeHDCP() { - return new HDCP; +sp MediaPlayerService::makeHDCP(bool createEncryptionModule) { + return new HDCP(createEncryptionModule); } sp MediaPlayerService::listenForRemoteDisplay( diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index d2d8939..2d2a09d 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -249,7 +249,7 @@ public: virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); virtual sp getOMX(); virtual sp makeCrypto(); - virtual sp makeHDCP(); + virtual sp makeHDCP(bool createEncryptionModule); virtual sp listenForRemoteDisplay(const sp& client, const String8& iface); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 0fed19b..981d5f9 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -1637,7 +1637,7 @@ status_t WifiDisplaySource::makeHDCP() { sp service = interface_cast(binder); CHECK(service != NULL); - mHDCP = service->makeHDCP(); + mHDCP = service->makeHDCP(true /* createEncryptionModule */); if (mHDCP == NULL) { return ERROR_UNSUPPORTED; -- cgit v1.1 From 0fcdb7271e1a25bc501ead6093ab1ae2667fdd47 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 30 Jan 2013 11:35:39 -0800 Subject: Actually display something on screen when instantiating a wfd sink via the wfd commandline tool. Change-Id: I26466efb95a5837a14d77d6581e0777038d31d95 --- .../wifi-display/sink/DirectRenderer.cpp | 3 +- media/libstagefright/wifi-display/wfd.cpp | 38 +++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index 8120634..93430eb 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -270,7 +270,8 @@ void DirectRenderer::dequeueAccessUnits() { err = mVideoDecoder->configure( videoFormat, - new SurfaceTextureClient(mSurfaceTex), + mSurfaceTex == NULL + ? NULL : new SurfaceTextureClient(mSurfaceTex), NULL /* crypto */, 0 /* flags */); diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index be9e35e..21d661e 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include namespace android { @@ -282,12 +284,44 @@ int main(int argc, char **argv) { exit(1); } + sp composerClient = new SurfaceComposerClient; + CHECK_EQ(composerClient->initCheck(), (status_t)OK); + + sp display(SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ssize_t displayWidth = info.w; + ssize_t displayHeight = info.h; + + ALOGV("display is %d x %d\n", displayWidth, displayHeight); + + sp control = + composerClient->createSurface( + String8("A Surface"), + displayWidth, + displayHeight, + PIXEL_FORMAT_RGB_565, + 0); + + CHECK(control != NULL); + CHECK(control->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); + CHECK_EQ(control->show(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); + + sp surface = control->getSurface(); + CHECK(surface != NULL); + sp session = new ANetworkSession; session->start(); sp looper = new ALooper; - sp sink = new WifiDisplaySink(session); + sp sink = new WifiDisplaySink( + session, surface->getSurfaceTexture()); looper->registerHandler(sink); if (connectToPort >= 0) { @@ -298,5 +332,7 @@ int main(int argc, char **argv) { looper->start(true /* runOnCallingThread */); + composerClient->dispose(); + return 0; } -- cgit v1.1 From aa65ddb06862fa542c9ec8d556bd3e01bf4c32b2 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 31 Jan 2013 15:25:07 -0800 Subject: Fix typo and reduce allocation overhead of RTP retransmission requests. Change-Id: I402a195da5dfeceadb4d073888ee7702c5532dc8 --- media/libstagefright/wifi-display/sink/RTPSink.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp index ad75373..7f4b66f 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.cpp +++ b/media/libstagefright/wifi-display/sink/RTPSink.cpp @@ -787,12 +787,12 @@ void RTPSink::onPacketLost(const sp &msg) { int32_t blp = 0; - sp buf = new ABuffer(1500); + sp buf = new ABuffer(16); buf->setRange(0, 0); uint8_t *ptr = buf->data(); ptr[0] = 0x80 | 1; // generic NACK - ptr[1] = 205; // RTPFB + ptr[1] = 205; // TSFB ptr[2] = 0; ptr[3] = 3; ptr[4] = 0xde; // sender SSRC -- cgit v1.1 From 7cc0c29d6a7b76520ec588437ab51d5b8eac9ebc Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 1 Feb 2013 11:43:44 -0800 Subject: Revive the code to support TCP interleaved transport Also support non-interleaved transport now, but the sink gets to decide what mode it wants to run in, _not_ the source. Change-Id: I3b6057f86871084e4decf930bb4a7a2d7517b0f2 --- .../wifi-display/ANetworkSession.cpp | 1 - .../wifi-display/sink/DirectRenderer.cpp | 18 ++- media/libstagefright/wifi-display/sink/RTPSink.cpp | 53 ++++++++- media/libstagefright/wifi-display/sink/RTPSink.h | 10 +- .../wifi-display/sink/WifiDisplaySink.cpp | 124 +++++++++------------ .../wifi-display/sink/WifiDisplaySink.h | 9 +- .../wifi-display/source/PlaybackSession.cpp | 12 ++ .../wifi-display/source/WifiDisplaySource.cpp | 40 +++---- .../wifi-display/source/WifiDisplaySource.h | 1 + 9 files changed, 157 insertions(+), 111 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 62a6e7f..06f71f4 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -1091,7 +1091,6 @@ void ANetworkSession::threadLoop() { clientSocket); sp clientSession = - // using socket sd as sessionID new Session( mNextSessionID++, Session::CONNECTED, diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index 93430eb..23cf6fd 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -35,11 +35,19 @@ namespace android { +#if 0 // static const int64_t DirectRenderer::kPacketLostDelayUs = 80000ll; // static const int64_t DirectRenderer::kPacketLateDelayUs = 60000ll; +#else +// static +const int64_t DirectRenderer::kPacketLostDelayUs = 1000000ll; + +// static +const int64_t DirectRenderer::kPacketLateDelayUs = -1ll; +#endif DirectRenderer::DirectRenderer( const sp ¬ifyLost, @@ -309,11 +317,11 @@ void DirectRenderer::dequeueAccessUnits() { void DirectRenderer::schedulePacketLost() { sp msg; -#if 1 - msg = new AMessage(kWhatPacketLate, id()); - msg->setInt32("generation", mPacketLostGeneration); - msg->post(kPacketLateDelayUs); -#endif + if (kPacketLateDelayUs > 0ll) { + msg = new AMessage(kWhatPacketLate, id()); + msg->setInt32("generation", mPacketLostGeneration); + msg->post(kPacketLateDelayUs); + } msg = new AMessage(kWhatPacketLost, id()); msg->setInt32("generation", mPacketLostGeneration); diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp index 7f4b66f..be54595 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.cpp +++ b/media/libstagefright/wifi-display/sink/RTPSink.cpp @@ -253,6 +253,8 @@ RTPSink::RTPSink( mRTPPort(0), mRTPSessionID(0), mRTCPSessionID(0), + mRTPClientSessionID(0), + mRTCPClientSessionID(0), mFirstArrivalTimeUs(-1ll), mNumPacketsReceived(0ll), mRegression(1000), @@ -260,6 +262,14 @@ RTPSink::RTPSink( } RTPSink::~RTPSink() { + if (mRTCPClientSessionID != 0) { + mNetSession->destroySession(mRTCPClientSessionID); + } + + if (mRTPClientSessionID != 0) { + mNetSession->destroySession(mRTPClientSessionID); + } + if (mRTCPSessionID != 0) { mNetSession->destroySession(mRTCPSessionID); } @@ -269,8 +279,8 @@ RTPSink::~RTPSink() { } } -status_t RTPSink::init(bool useTCPInterleaving) { - if (useTCPInterleaving) { +status_t RTPSink::init(bool usingTCPTransport, bool usingTCPInterleaving) { + if (usingTCPInterleaving) { return OK; } @@ -280,8 +290,16 @@ status_t RTPSink::init(bool useTCPInterleaving) { sp rtcpNotify = new AMessage(kWhatRTCPNotify, id()); for (clientRtp = 15550;; clientRtp += 2) { int32_t rtpSession; - status_t err = mNetSession->createUDPSession( - clientRtp, rtpNotify, &rtpSession); + status_t err; + struct in_addr ifaceAddr; + if (usingTCPTransport) { + ifaceAddr.s_addr = INADDR_ANY; + err = mNetSession->createTCPDatagramSession( + ifaceAddr, clientRtp, rtpNotify, &rtpSession); + } else { + err = mNetSession->createUDPSession( + clientRtp, rtpNotify, &rtpSession); + } if (err != OK) { ALOGI("failed to create RTP socket on port %d", clientRtp); @@ -289,8 +307,13 @@ status_t RTPSink::init(bool useTCPInterleaving) { } int32_t rtcpSession; - err = mNetSession->createUDPSession( - clientRtp + 1, rtcpNotify, &rtcpSession); + if (usingTCPTransport) { + err = mNetSession->createTCPDatagramSession( + ifaceAddr, clientRtp + 1, rtcpNotify, &rtcpSession); + } else { + err = mNetSession->createUDPSession( + clientRtp + 1, rtcpNotify, &rtcpSession); + } if (err == OK) { mRTPPort = clientRtp; @@ -367,6 +390,24 @@ void RTPSink::onMessageReceived(const sp &msg) { break; } + case ANetworkSession::kWhatClientConnected: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + ALOGI("TCP session %d now connected", sessionID); + + int32_t serverPort; + CHECK(msg->findInt32("server-port", &serverPort)); + + if (serverPort == mRTPPort) { + mRTPClientSessionID = sessionID; + } else { + CHECK_EQ(serverPort, mRTPPort + 1); + mRTCPClientSessionID = sessionID; + } + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h index 6e40185..f9cbce9 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.h +++ b/media/libstagefright/wifi-display/sink/RTPSink.h @@ -48,7 +48,7 @@ struct RTPSink : public AHandler { // If TCP interleaving is used, no UDP sockets are created, instead // incoming RTP/RTCP packets (arriving on the RTSP control connection) // are manually injected by WifiDisplaySink. - status_t init(bool useTCPInterleaving); + status_t init(bool usingTCPTransport, bool usingTCPInterleaving); status_t connect( const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort); @@ -79,8 +79,12 @@ private: KeyedVector > mSources; int32_t mRTPPort; - int32_t mRTPSessionID; - int32_t mRTCPSessionID; + + int32_t mRTPSessionID; // in TCP unicast mode these are just server + int32_t mRTCPSessionID; // sockets. No data is transferred through them. + + int32_t mRTPClientSessionID; // in TCP unicast mode + int32_t mRTCPClientSessionID; int64_t mFirstArrivalTimeUs; int64_t mNumPacketsReceived; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 46c40c7..55581a6 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -27,6 +27,8 @@ #include #include +#include + namespace android { WifiDisplaySink::WifiDisplaySink( @@ -37,6 +39,8 @@ WifiDisplaySink::WifiDisplaySink( mNetSession(netSession), mSurfaceTex(bufferProducer), mNotify(notify), + mUsingTCPTransport(false), + mUsingTCPInterleaving(false), mSessionID(0), mNextCSeq(1) { #if 1 @@ -141,17 +145,8 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { sleep(2); // XXX int32_t sourcePort; - - if (msg->findString("setupURI", &mSetupURI)) { - AString path, user, pass; - CHECK(ParseURL( - mSetupURI.c_str(), - &mRTSPHost, &sourcePort, &path, &user, &pass) - && user.empty() && pass.empty()); - } else { - CHECK(msg->findString("sourceHost", &mRTSPHost)); - CHECK(msg->findInt32("sourcePort", &sourcePort)); - } + CHECK(msg->findString("sourceHost", &mRTSPHost)); + CHECK(msg->findInt32("sourcePort", &sourcePort)); sp notify = new AMessage(kWhatRTSPNotify, id()); @@ -208,13 +203,6 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { { ALOGI("We're now connected."); mState = CONNECTED; - - if (!mSetupURI.empty()) { - status_t err = - sendDescribe(mSessionID, mSetupURI.c_str()); - - CHECK_EQ(err, (status_t)OK); - } break; } @@ -226,7 +214,7 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { case ANetworkSession::kWhatBinaryData: { - CHECK(sUseTCPInterleaving); + CHECK(mUsingTCPInterleaving); int32_t channel; CHECK(msg->findInt32("channel", &channel)); @@ -312,20 +300,6 @@ status_t WifiDisplaySink::onReceiveM2Response( return OK; } -status_t WifiDisplaySink::onReceiveDescribeResponse( - int32_t sessionID, const sp &msg) { - int32_t statusCode; - if (!msg->getStatusCode(&statusCode)) { - return ERROR_MALFORMED; - } - - if (statusCode != 200) { - return ERROR_UNSUPPORTED; - } - - return sendSetup(sessionID, mSetupURI.c_str()); -} - status_t WifiDisplaySink::onReceiveSetupResponse( int32_t sessionID, const sp &msg) { int32_t statusCode; @@ -365,12 +339,11 @@ status_t WifiDisplaySink::onReceiveSetupResponse( return sendPlay( sessionID, - !mSetupURI.empty() - ? mSetupURI.c_str() : "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); + "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); } status_t WifiDisplaySink::configureTransport(const sp &msg) { - if (sUseTCPInterleaving) { + if (mUsingTCPTransport) { return OK; } @@ -514,11 +487,45 @@ void WifiDisplaySink::onGetParameterRequest( int32_t sessionID, int32_t cseq, const sp &data) { - AString body = "wfd_video_formats: "; - body.append(mSinkSupportedVideoFormats.getFormatSpec()); - body.append( - "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n" - "wfd_client_rtp_ports: RTP/AVP/UDP;unicast 19000 0 mode=play\r\n"); + AString body; + + if (mState == CONNECTED) { + mUsingTCPTransport = false; + mUsingTCPInterleaving = false; + + char val[PROPERTY_VALUE_MAX]; + if (property_get("media.wfd-sink.tcp-mode", val, NULL)) { + if (!strcasecmp("true", val) || !strcmp("1", val)) { + ALOGI("Using TCP unicast transport."); + mUsingTCPTransport = true; + mUsingTCPInterleaving = false; + } else if (!strcasecmp("interleaved", val)) { + ALOGI("Using TCP interleaved transport."); + mUsingTCPTransport = true; + mUsingTCPInterleaving = true; + } + } + + body = "wfd_video_formats: "; + body.append(mSinkSupportedVideoFormats.getFormatSpec()); + + body.append( + "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n" + "wfd_client_rtp_ports: RTP/AVP/"); + + if (mUsingTCPTransport) { + body.append("TCP;"); + if (mUsingTCPInterleaving) { + body.append("interleaved"); + } else { + body.append("unicast 19000 0"); + } + } else { + body.append("UDP;unicast 19000 0"); + } + + body.append(" mode=play\r\n"); + } AString response = "RTSP/1.0 200 OK\r\n"; AppendCommonResponse(&response, cseq); @@ -531,38 +538,13 @@ void WifiDisplaySink::onGetParameterRequest( CHECK_EQ(err, (status_t)OK); } -status_t WifiDisplaySink::sendDescribe(int32_t sessionID, const char *uri) { - uri = "rtsp://xwgntvx.is.livestream-api.com/livestreamiphone/wgntv"; - uri = "rtsp://v2.cache6.c.youtube.com/video.3gp?cid=e101d4bf280055f9&fmt=18"; - - AString request = StringPrintf("DESCRIBE %s RTSP/1.0\r\n", uri); - AppendCommonResponse(&request, mNextCSeq); - - request.append("Accept: application/sdp\r\n"); - request.append("\r\n"); - - status_t err = mNetSession->sendRequest( - sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, mNextCSeq, &WifiDisplaySink::onReceiveDescribeResponse); - - ++mNextCSeq; - - return OK; -} - status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { sp notify = new AMessage(kWhatRTPSinkNotify, id()); mRTPSink = new RTPSink(mNetSession, mSurfaceTex, notify); looper()->registerHandler(mRTPSink); - status_t err = mRTPSink->init(sUseTCPInterleaving); + status_t err = mRTPSink->init(mUsingTCPTransport, mUsingTCPInterleaving); if (err != OK) { looper()->unregisterHandler(mRTPSink->id()); @@ -574,15 +556,17 @@ status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { AppendCommonResponse(&request, mNextCSeq); - if (sUseTCPInterleaving) { + if (mUsingTCPInterleaving) { request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n"); } else { int32_t rtpPort = mRTPSink->getRTPPort(); request.append( StringPrintf( - "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", - rtpPort, rtpPort + 1)); + "Transport: RTP/AVP/%s;unicast;client_port=%d-%d\r\n", + mUsingTCPTransport ? "TCP" : "UDP", + rtpPort, + rtpPort + 1)); } request.append("\r\n"); diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index 5f86519..8b5ff6b 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -86,14 +86,13 @@ private: typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)( int32_t sessionID, const sp &msg); - static const bool sUseTCPInterleaving = false; - State mState; VideoFormats mSinkSupportedVideoFormats; sp mNetSession; sp mSurfaceTex; sp mNotify; - AString mSetupURI; + bool mUsingTCPTransport; + bool mUsingTCPInterleaving; AString mRTSPHost; int32_t mSessionID; @@ -106,7 +105,6 @@ private: int32_t mPlaybackSessionTimeoutSecs; status_t sendM2(int32_t sessionID); - status_t sendDescribe(int32_t sessionID, const char *uri); status_t sendSetup(int32_t sessionID, const char *uri); status_t sendPlay(int32_t sessionID, const char *uri); status_t sendIDRFrameRequest(int32_t sessionID); @@ -114,9 +112,6 @@ private: status_t onReceiveM2Response( int32_t sessionID, const sp &msg); - status_t onReceiveDescribeResponse( - int32_t sessionID, const sp &msg); - status_t onReceiveSetupResponse( int32_t sessionID, const sp &msg); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 91dc1fa..453cbc5 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -546,6 +546,18 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( onFinishPlay2(); } else if (what == Sender::kWhatSessionDead) { notifySessionDead(); + } else if (what == Sender::kWhatBinaryData) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatBinaryData); + + int32_t channel; + CHECK(msg->findInt32("channel", &channel)); + notify->setInt32("channel", channel); + + sp data; + CHECK(msg->findBuffer("data", &data)); + notify->setBuffer("data", data); + notify->post(); } else { TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 981d5f9..825ebc6 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -597,15 +597,6 @@ status_t WifiDisplaySource::sendM3(int32_t sessionID) { status_t WifiDisplaySource::sendM4(int32_t sessionID) { CHECK_EQ(sessionID, mClientSessionID); - AString transportString = "UDP"; - - char val[PROPERTY_VALUE_MAX]; - if (property_get("media.wfd.enable-tcp", val, NULL) - && (!strcasecmp("true", val) || !strcmp("1", val))) { - ALOGI("Using TCP transport."); - transportString = "TCP"; - } - AString body; if (mSinkSupportsVideo) { @@ -630,11 +621,11 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { body.append( StringPrintf( - "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n" - "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n", - mClientInfo.mLocalIP.c_str(), - transportString.c_str(), - mChosenRTPPort)); + "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n", + mClientInfo.mLocalIP.c_str())); + + body.append(mWfdClientRtpPorts); + body.append("\r\n"); AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; AppendCommonResponse(&request, mNextCSeq); @@ -797,18 +788,29 @@ status_t WifiDisplaySource::onReceiveM3Response( return ERROR_MALFORMED; } - unsigned port0, port1; + unsigned port0 = 0, port1 = 0; if (sscanf(value.c_str(), "RTP/AVP/UDP;unicast %u %u mode=play", &port0, - &port1) != 2 - || port0 == 0 || port0 > 65535 || port1 != 0) { - ALOGE("Sink chose its wfd_client_rtp_ports poorly (%s)", + &port1) == 2 + || sscanf(value.c_str(), + "RTP/AVP/TCP;unicast %u %u mode=play", + &port0, + &port1) == 2) { + if (port0 == 0 || port0 > 65535 || port1 != 0) { + ALOGE("Sink chose its wfd_client_rtp_ports poorly (%s)", + value.c_str()); + + return ERROR_MALFORMED; + } + } else if (strcmp(value.c_str(), "RTP/AVP/TCP;interleaved mode=play")) { + ALOGE("Unsupported value for wfd_client_rtp_ports (%s)", value.c_str()); - return ERROR_MALFORMED; + return ERROR_UNSUPPORTED; } + mWfdClientRtpPorts = value; mChosenRTPPort = port0; if (!params->findParameter("wfd_video_formats", &value)) { diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index fec2c6d..724462c 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -119,6 +119,7 @@ private: uint32_t mStopReplyID; + AString mWfdClientRtpPorts; int32_t mChosenRTPPort; // extracted from "wfd_client_rtp_ports" bool mSinkSupportsVideo; -- cgit v1.1 From 2fa05230219f72118388f3a350b1239db1299647 Mon Sep 17 00:00:00 2001 From: James Dong Date: Wed, 30 Jan 2013 13:31:59 -0800 Subject: Remove a few unused header includes from PlaybackSession.cpp Change-Id: Id3147e2f2d5d75ccc172e67802639cd43ed68870 --- media/libstagefright/wifi-display/source/PlaybackSession.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 91dc1fa..e542908 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -29,8 +29,6 @@ #include "WifiDisplaySource.h" #include -#include -#include #include #include #include @@ -41,10 +39,8 @@ #include #include #include -#include #include #include -#include #include #include -- cgit v1.1 From a77c496d4520f25b7b337d32ebd9681d8ea0f7bb Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 5 Feb 2013 09:03:22 -0800 Subject: Return error code if setting scaling mode fails The return code from native_window_set_scaling_mode() was ignored. Looking at the code review comments that introduced this code, it seems like the intention was to return the error code in that case. Change-Id: I9592cc378f0a0b960d37178aa0525fc17e8734ba --- media/libstagefright/AwesomePlayer.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 23ce088..0f4d866 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -2511,6 +2511,7 @@ status_t AwesomePlayer::setVideoScalingMode_l(int32_t mode) { if (err != OK) { ALOGW("Failed to set scaling mode: %d", err); } + return err; } return OK; } -- cgit v1.1 From b5f25f005bc1d3ae35f45b58c88345e183dc336d Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 5 Feb 2013 10:14:26 -0800 Subject: Groundwork to support bidirectional, asynchronous communication between NuPlayer and its sources. Change-Id: I1989022d806206b926555add3aa5c1fcf37aa78d --- .../nuplayer/GenericSource.cpp | 8 +++-- .../libmediaplayerservice/nuplayer/GenericSource.h | 5 +++- .../nuplayer/HTTPLiveSource.cpp | 4 ++- .../nuplayer/HTTPLiveSource.h | 1 + media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 35 ++++++++++++++++++---- media/libmediaplayerservice/nuplayer/NuPlayer.h | 1 + .../nuplayer/NuPlayerSource.h | 16 ++++++++-- .../libmediaplayerservice/nuplayer/RTSPSource.cpp | 4 ++- media/libmediaplayerservice/nuplayer/RTSPSource.h | 1 + .../nuplayer/StreamingSource.cpp | 7 +++-- .../nuplayer/StreamingSource.h | 4 ++- .../nuplayer/mp4/MP4Source.cpp | 6 ++-- .../libmediaplayerservice/nuplayer/mp4/MP4Source.h | 2 +- 13 files changed, 75 insertions(+), 19 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index f281879..450fae5 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -32,11 +32,13 @@ namespace android { NuPlayer::GenericSource::GenericSource( + const sp ¬ify, const char *url, const KeyedVector *headers, bool uidValid, uid_t uid) - : mDurationUs(0ll), + : Source(notify), + mDurationUs(0ll), mAudioIsVorbis(false) { DataSource::RegisterDefaultSniffers(); @@ -48,8 +50,10 @@ NuPlayer::GenericSource::GenericSource( } NuPlayer::GenericSource::GenericSource( + const sp ¬ify, int fd, int64_t offset, int64_t length) - : mDurationUs(0ll), + : Source(notify), + mDurationUs(0ll), mAudioIsVorbis(false) { DataSource::RegisterDefaultSniffers(); diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index e1ce2c1..e59ea3a 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -32,12 +32,15 @@ struct MediaSource; struct NuPlayer::GenericSource : public NuPlayer::Source { GenericSource( + const sp ¬ify, const char *url, const KeyedVector *headers, bool uidValid = false, uid_t uid = 0); - GenericSource(int fd, int64_t offset, int64_t length); + GenericSource( + const sp ¬ify, + int fd, int64_t offset, int64_t length); virtual void start(); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index 5dcca12..d38ee62 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -34,10 +34,12 @@ namespace android { NuPlayer::HTTPLiveSource::HTTPLiveSource( + const sp ¬ify, const char *url, const KeyedVector *headers, bool uidValid, uid_t uid) - : mURL(url), + : Source(notify), + mURL(url), mUIDValid(uidValid), mUID(uid), mFlags(0), diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 79f4ab8..4a217af 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -28,6 +28,7 @@ struct LiveSession; struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { HTTPLiveSource( + const sp ¬ify, const char *url, const KeyedVector *headers, bool uidValid = false, diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 517fb34..9585aba 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -145,12 +145,14 @@ void NuPlayer::setDriver(const wp &driver) { void NuPlayer::setDataSource(const sp &source) { sp msg = new AMessage(kWhatSetDataSource, id()); + sp notify = new AMessage(kWhatSourceNotify, id()); + char prop[PROPERTY_VALUE_MAX]; if (property_get("media.stagefright.use-mp4source", prop, NULL) && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) { - msg->setObject("source", new MP4Source(source)); + msg->setObject("source", new MP4Source(notify, source)); } else { - msg->setObject("source", new StreamingSource(source)); + msg->setObject("source", new StreamingSource(notify, source)); } msg->post(); @@ -176,13 +178,15 @@ void NuPlayer::setDataSource( const char *url, const KeyedVector *headers) { sp msg = new AMessage(kWhatSetDataSource, id()); + sp notify = new AMessage(kWhatSourceNotify, id()); + sp source; if (IsHTTPLiveURL(url)) { - source = new HTTPLiveSource(url, headers, mUIDValid, mUID); + source = new HTTPLiveSource(notify, url, headers, mUIDValid, mUID); } else if (!strncasecmp(url, "rtsp://", 7)) { - source = new RTSPSource(url, headers, mUIDValid, mUID); + source = new RTSPSource(notify, url, headers, mUIDValid, mUID); } else { - source = new GenericSource(url, headers, mUIDValid, mUID); + source = new GenericSource(notify, url, headers, mUIDValid, mUID); } msg->setObject("source", source); @@ -192,7 +196,9 @@ void NuPlayer::setDataSource( void NuPlayer::setDataSource(int fd, int64_t offset, int64_t length) { sp msg = new AMessage(kWhatSetDataSource, id()); - sp source = new GenericSource(fd, offset, length); + sp notify = new AMessage(kWhatSourceNotify, id()); + + sp source = new GenericSource(notify, fd, offset, length); msg->setObject("source", source); msg->post(); } @@ -273,6 +279,8 @@ void NuPlayer::onMessageReceived(const sp &msg) { CHECK(msg->findObject("source", &obj)); mSource = static_cast(obj.get()); + + looper()->registerHandler(mSource); break; } @@ -714,6 +722,12 @@ void NuPlayer::onMessageReceived(const sp &msg) { break; } + case kWhatSourceNotify: + { + TRESPASS(); // TBD + break; + } + default: TRESPASS(); break; @@ -1169,6 +1183,9 @@ void NuPlayer::performReset() { if (mSource != NULL) { mSource->stop(); + + looper()->unregisterHandler(mSource->id()); + mSource.clear(); } @@ -1210,4 +1227,10 @@ void NuPlayer::performSetSurface(const sp &wrapper) { } } +//////////////////////////////////////////////////////////////////////////////// + +void NuPlayer::Source::onMessageReceived(const sp &msg) { + TRESPASS(); +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 09fc0ba..0ff6089 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -95,6 +95,7 @@ private: kWhatPause = 'paus', kWhatResume = 'rsme', kWhatPollDuration = 'polD', + kWhatSourceNotify = 'srcN', }; wp mDriver; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index a635340..a3201cf 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -20,17 +20,23 @@ #include "NuPlayer.h" +#include + namespace android { struct ABuffer; -struct NuPlayer::Source : public RefBase { +struct NuPlayer::Source : public AHandler { enum Flags { FLAG_SEEKABLE = 1, FLAG_DYNAMIC_DURATION = 2, }; - Source() {} + // The provides message is used to notify the player about various + // events. + Source(const sp ¬ify) + : mNotify(notify) { + } virtual void start() = 0; virtual void stop() {} @@ -57,9 +63,15 @@ struct NuPlayer::Source : public RefBase { protected: virtual ~Source() {} + virtual void onMessageReceived(const sp &msg); + virtual sp getFormatMeta(bool audio) { return NULL; } + sp dupNotify() const { return mNotify->dup(); } + private: + sp mNotify; + DISALLOW_EVIL_CONSTRUCTORS(Source); }; diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index afaa5db..e402115 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -29,11 +29,13 @@ namespace android { NuPlayer::RTSPSource::RTSPSource( + const sp ¬ify, const char *url, const KeyedVector *headers, bool uidValid, uid_t uid) - : mURL(url), + : Source(notify), + mURL(url), mUIDValid(uidValid), mUID(uid), mFlags(0), diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index 779d791..033b3e8 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -32,6 +32,7 @@ struct MyHandler; struct NuPlayer::RTSPSource : public NuPlayer::Source { RTSPSource( + const sp ¬ify, const char *url, const KeyedVector *headers, bool uidValid = false, diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index 7159404..9b04833 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -32,8 +32,11 @@ namespace android { -NuPlayer::StreamingSource::StreamingSource(const sp &source) - : mSource(source), +NuPlayer::StreamingSource::StreamingSource( + const sp ¬ify, + const sp &source) + : Source(notify), + mSource(source), mFinalResult(OK) { } diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h index a27b58a..dc616f7 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.h +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h @@ -27,7 +27,9 @@ struct ABuffer; struct ATSParser; struct NuPlayer::StreamingSource : public NuPlayer::Source { - StreamingSource(const sp &source); + StreamingSource( + const sp ¬ify, + const sp &source); virtual void start(); diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp index a62d5a2..d659b73 100644 --- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp +++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp @@ -104,8 +104,10 @@ private: DISALLOW_EVIL_CONSTRUCTORS(StreamSource); }; -MP4Source::MP4Source(const sp &source) - : mSource(source), +MP4Source::MP4Source( + const sp ¬ify, const sp &source) + : Source(notify), + mSource(source), mLooper(new ALooper), mParser(new FragmentedMP4Parser), mEOS(false) { diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h index abca236..b16a111 100644 --- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h +++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h @@ -24,7 +24,7 @@ namespace android { struct FragmentedMP4Parser; struct MP4Source : public NuPlayer::Source { - MP4Source(const sp &source); + MP4Source(const sp ¬ify, const sp &source); virtual void start(); -- cgit v1.1 From 7a33b7740412accf6a1cc912686c8d0acfb2a883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Rydh=C3=A9?= Date: Mon, 20 Feb 2012 10:15:48 +0100 Subject: Added HTTP support for SDP files. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added support for playing SDP files from http links. Previously, SDP files only worked when started from rtsp links (rtsp://a.b.c/abc.sdp), but they are just as common in http links. patch provided by "Oscar Rydhé " Change-Id: Ic73af3a9a002009dbe8b04c267a4621bf7fe2f46 --- media/libmediaplayerservice/MediaPlayerFactory.cpp | 4 + media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 6 + .../libmediaplayerservice/nuplayer/RTSPSource.cpp | 74 +++++++++- media/libmediaplayerservice/nuplayer/RTSPSource.h | 7 +- media/libstagefright/include/SDPLoader.h | 70 ++++++++++ media/libstagefright/rtsp/Android.mk | 1 + media/libstagefright/rtsp/MyHandler.h | 53 +++++++ media/libstagefright/rtsp/SDPLoader.cpp | 154 +++++++++++++++++++++ 8 files changed, 362 insertions(+), 7 deletions(-) create mode 100644 media/libstagefright/include/SDPLoader.h create mode 100644 media/libstagefright/rtsp/SDPLoader.cpp (limited to 'media') diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index 3f69c11..bb441cc 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -215,6 +215,10 @@ class NuPlayerFactory : public MediaPlayerFactory::IFactory { if (strstr(url,"m3u8")) { return kOurScore; } + + if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) { + return kOurScore; + } } if (!strncasecmp("rtsp://", url, 7)) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 9585aba..0736fbe 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -177,6 +177,7 @@ static bool IsHTTPLiveURL(const char *url) { void NuPlayer::setDataSource( const char *url, const KeyedVector *headers) { sp msg = new AMessage(kWhatSetDataSource, id()); + size_t len = strlen(url); sp notify = new AMessage(kWhatSourceNotify, id()); @@ -185,6 +186,11 @@ void NuPlayer::setDataSource( source = new HTTPLiveSource(notify, url, headers, mUIDValid, mUID); } else if (!strncasecmp(url, "rtsp://", 7)) { source = new RTSPSource(notify, url, headers, mUIDValid, mUID); + } else if ((!strncasecmp(url, "http://", 7) + || !strncasecmp(url, "https://", 8)) + && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) + || strstr(url, ".sdp?"))) { + source = new RTSPSource(notify, url, headers, mUIDValid, mUID, true); } else { source = new GenericSource(notify, url, headers, mUIDValid, mUID); } diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index e402115..3035589 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -22,6 +22,7 @@ #include "AnotherPacketSource.h" #include "MyHandler.h" +#include "SDPLoader.h" #include #include @@ -33,12 +34,14 @@ NuPlayer::RTSPSource::RTSPSource( const char *url, const KeyedVector *headers, bool uidValid, - uid_t uid) + uid_t uid, + bool isSDP) : Source(notify), mURL(url), mUIDValid(uidValid), mUID(uid), mFlags(0), + mIsSDP(isSDP), mState(DISCONNECTED), mFinalResult(OK), mDisconnectReplyID(0), @@ -73,16 +76,25 @@ void NuPlayer::RTSPSource::start() { } CHECK(mHandler == NULL); + CHECK(mSDPLoader == NULL); sp notify = new AMessage(kWhatNotify, mReflector->id()); - mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID); - mLooper->registerHandler(mHandler); - CHECK_EQ(mState, (int)DISCONNECTED); mState = CONNECTING; - mHandler->connect(); + if (mIsSDP) { + mSDPLoader = new SDPLoader(notify, + (mFlags & kFlagIncognito) ? SDPLoader::kFlagIncognito : 0, + mUIDValid, mUID); + + mSDPLoader->load(mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); + } else { + mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID); + mLooper->registerHandler(mHandler); + + mHandler->connect(); + } } void NuPlayer::RTSPSource::stop() { @@ -408,6 +420,12 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp &msg) { break; } + case SDPLoader::kWhatSDPLoaded: + { + onSDPLoaded(msg); + break; + } + default: TRESPASS(); } @@ -461,6 +479,46 @@ void NuPlayer::RTSPSource::onConnected() { mState = CONNECTED; } +void NuPlayer::RTSPSource::onSDPLoaded(const sp &msg) { + status_t err; + CHECK(msg->findInt32("result", &err)); + + mSDPLoader.clear(); + + if (mDisconnectReplyID != 0) { + err = UNKNOWN_ERROR; + } + + if (err == OK) { + sp desc; + sp obj; + CHECK(msg->findObject("description", &obj)); + desc = static_cast(obj.get()); + + AString rtspUri; + if (!desc->findAttribute(0, "a=control", &rtspUri)) { + ALOGE("Unable to find url in SDP"); + err = UNKNOWN_ERROR; + } else { + sp notify = new AMessage(kWhatNotify, mReflector->id()); + + mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID); + mLooper->registerHandler(mHandler); + + mHandler->loadSDP(desc); + } + } + + if (err != OK) { + mState = DISCONNECTED; + mFinalResult = err; + + if (mDisconnectReplyID != 0) { + finishDisconnectIfPossible(); + } + } +} + void NuPlayer::RTSPSource::onDisconnected(const sp &msg) { status_t err; CHECK(msg->findInt32("result", &err)); @@ -479,7 +537,11 @@ void NuPlayer::RTSPSource::onDisconnected(const sp &msg) { void NuPlayer::RTSPSource::finishDisconnectIfPossible() { if (mState != DISCONNECTED) { - mHandler->disconnect(); + if (mHandler != NULL) { + mHandler->disconnect(); + } else if (mSDPLoader != NULL) { + mSDPLoader->cancel(); + } return; } diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index 033b3e8..b2a7dae 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -29,6 +29,7 @@ namespace android { struct ALooper; struct AnotherPacketSource; struct MyHandler; +struct SDPLoader; struct NuPlayer::RTSPSource : public NuPlayer::Source { RTSPSource( @@ -36,7 +37,8 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source { const char *url, const KeyedVector *headers, bool uidValid = false, - uid_t uid = 0); + uid_t uid = 0, + bool isSDP = false); virtual void start(); virtual void stop(); @@ -90,6 +92,7 @@ private: bool mUIDValid; uid_t mUID; uint32_t mFlags; + bool mIsSDP; State mState; status_t mFinalResult; uint32_t mDisconnectReplyID; @@ -98,6 +101,7 @@ private: sp mLooper; sp > mReflector; sp mHandler; + sp mSDPLoader; Vector mTracks; sp mAudioTrack; @@ -110,6 +114,7 @@ private: sp getSource(bool audio); void onConnected(); + void onSDPLoaded(const sp &msg); void onDisconnected(const sp &msg); void finishDisconnectIfPossible(); diff --git a/media/libstagefright/include/SDPLoader.h b/media/libstagefright/include/SDPLoader.h new file mode 100644 index 0000000..ca59dc0 --- /dev/null +++ b/media/libstagefright/include/SDPLoader.h @@ -0,0 +1,70 @@ +/* + * 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 SDP_LOADER_H_ + +#define SDP_LOADER_H_ + +#include +#include +#include + +namespace android { + +struct HTTPBase; + +struct SDPLoader : public AHandler { + enum Flags { + // Don't log any URLs. + kFlagIncognito = 1, + }; + enum { + kWhatSDPLoaded = 'sdpl' + }; + SDPLoader(const sp ¬ify, uint32_t flags = 0, bool uidValid = false, uid_t uid = 0); + + void load(const char* url, const KeyedVector *headers); + + void cancel(); + +protected: + virtual ~SDPLoader() {} + + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatLoad = 'load', + }; + + void onLoad(const sp &msg); + + sp mNotify; + const char* mUrl; + uint32_t mFlags; + bool mUIDValid; + uid_t mUID; + sp mNetLooper; + bool mCancelled; + + sp mHTTPDataSource; + + DISALLOW_EVIL_CONSTRUCTORS(SDPLoader); +}; + +} // namespace android + +#endif // SDP_LOADER_H_ diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index 49e2daf..9e2724d 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \ ARTPWriter.cpp \ ARTSPConnection.cpp \ ASessionDescription.cpp \ + SDPLoader.cpp \ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright/include \ diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 96c7683..b7183b1 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -173,6 +173,18 @@ struct MyHandler : public AHandler { mConn->connect(mOriginalSessionURL.c_str(), reply); } + void loadSDP(const sp& desc) { + looper()->registerHandler(mConn); + (1 ? mNetLooper : looper())->registerHandler(mRTPConn); + + sp notify = new AMessage('biny', id()); + mConn->observeBinaryData(notify); + + sp reply = new AMessage('sdpl', id()); + reply->setObject("description", desc); + mConn->connect(mOriginalSessionURL.c_str(), reply); + } + void disconnect() { (new AMessage('abor', id()))->post(); } @@ -486,6 +498,47 @@ struct MyHandler : public AHandler { break; } + case 'sdpl': + { + int32_t result; + CHECK(msg->findInt32("result", &result)); + + ALOGI("SDP connection request completed with result %d (%s)", + result, strerror(-result)); + + if (result == OK) { + sp obj; + CHECK(msg->findObject("description", &obj)); + mSessionDesc = + static_cast(obj.get()); + + if (!mSessionDesc->isValid()) { + ALOGE("Failed to parse session description."); + result = ERROR_MALFORMED; + } else { + mBaseURL = mSessionURL; + + if (mSessionDesc->countTracks() < 2) { + // There's no actual tracks in this session. + // The first "track" is merely session meta + // data. + + ALOGW("Session doesn't contain any playable " + "tracks. Aborting."); + result = ERROR_UNSUPPORTED; + } else { + setupTrack(1); + } + } + } + + if (result != OK) { + sp reply = new AMessage('disc', id()); + mConn->disconnect(reply); + } + break; + } + case 'setu': { size_t index; diff --git a/media/libstagefright/rtsp/SDPLoader.cpp b/media/libstagefright/rtsp/SDPLoader.cpp new file mode 100644 index 0000000..ed3fa7e --- /dev/null +++ b/media/libstagefright/rtsp/SDPLoader.cpp @@ -0,0 +1,154 @@ +/* + * 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 "SDPLoader" +#include + +#include "SDPLoader.h" + +#include "ASessionDescription.h" +#include "HTTPBase.h" + +#include +#include + +#define DEFAULT_SDP_SIZE 100000 + +namespace android { + +SDPLoader::SDPLoader(const sp ¬ify, uint32_t flags, bool uidValid, uid_t uid) + : mNotify(notify), + mFlags(flags), + mUIDValid(uidValid), + mUID(uid), + mNetLooper(new ALooper), + mCancelled(false), + mHTTPDataSource( + HTTPBase::Create( + (mFlags & kFlagIncognito) + ? HTTPBase::kFlagIncognito + : 0)) { + if (mUIDValid) { + mHTTPDataSource->setUID(mUID); + } + + mNetLooper->setName("sdp net"); + mNetLooper->start(false /* runOnCallingThread */, + false /* canCallJava */, + PRIORITY_HIGHEST); +} + +void SDPLoader::load(const char *url, const KeyedVector *headers) { + mNetLooper->registerHandler(this); + + sp msg = new AMessage(kWhatLoad, id()); + msg->setString("url", url); + + if (headers != NULL) { + msg->setPointer( + "headers", + new KeyedVector(*headers)); + } + + msg->post(); +} + +void SDPLoader::cancel() { + mCancelled = true; + sp HTTPDataSource = mHTTPDataSource; + HTTPDataSource->disconnect(); +} + +void SDPLoader::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatLoad: + onLoad(msg); + break; + + default: + TRESPASS(); + break; + } +} + +void SDPLoader::onLoad(const sp &msg) { + status_t err = OK; + sp desc = NULL; + AString url; + CHECK(msg->findString("url", &url)); + + KeyedVector *headers = NULL; + msg->findPointer("headers", (void **)&headers); + + if (!(mFlags & kFlagIncognito)) { + ALOGI("onLoad '%s'", url.c_str()); + } else { + ALOGI("onLoad "); + } + + if (!mCancelled) { + err = mHTTPDataSource->connect(url.c_str(), headers); + + if (err != OK) { + ALOGE("connect() returned %d", err); + } + } + + if (headers != NULL) { + delete headers; + headers = NULL; + } + + off64_t sdpSize; + if (err == OK && !mCancelled) { + err = mHTTPDataSource->getSize(&sdpSize); + + if (err != OK) { + //We did not get the size of the sdp file, default to a large value + sdpSize = DEFAULT_SDP_SIZE; + err = OK; + } + } + + sp buffer = new ABuffer(sdpSize); + + if (err == OK && !mCancelled) { + ssize_t readSize = mHTTPDataSource->readAt(0, buffer->data(), sdpSize); + + if (readSize < 0) { + ALOGE("Failed to read SDP, error code = %ld", readSize); + err = UNKNOWN_ERROR; + } else { + desc = new ASessionDescription; + + if (desc == NULL || !desc->setTo(buffer->data(), (size_t)readSize)) { + err = UNKNOWN_ERROR; + ALOGE("Failed to parse SDP"); + } + } + } + + mHTTPDataSource.clear(); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatSDPLoaded); + notify->setInt32("result", err); + notify->setObject("description", desc); + notify->post(); +} + +} // namespace android -- cgit v1.1 From 84ca0414fedea2dfe51607b422f6227e1c4f0d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20J=C3=B6nsson?= Date: Thu, 17 Jan 2013 13:22:31 +0100 Subject: Detect live streams The information is used to decide on visibility of pause button and to handle the duration clock correctly. Change-Id: I286ac992fd171c7fc313e429326d38b6fc80e3fb --- media/libstagefright/rtsp/MyHandler.h | 44 +++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index b7183b1..cdd00e4 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -133,7 +133,7 @@ struct MyHandler : public AHandler { mTryFakeRTCP(false), mReceivedFirstRTCPPacket(false), mReceivedFirstRTPPacket(false), - mSeekable(false), + mSeekable(true), mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs), mKeepAliveGeneration(0) { mNetLooper->setName("rtsp net"); @@ -360,6 +360,39 @@ struct MyHandler : public AHandler { return true; } + static bool isLiveStream(const sp &desc) { + AString attrLiveStream; + if (desc->findAttribute(0, "a=LiveStream", &attrLiveStream)) { + ssize_t semicolonPos = attrLiveStream.find(";", 2); + + const char* liveStreamValue; + if (semicolonPos < 0) { + liveStreamValue = attrLiveStream.c_str(); + } else { + AString valString; + valString.setTo(attrLiveStream, + semicolonPos + 1, + attrLiveStream.size() - semicolonPos - 1); + liveStreamValue = valString.c_str(); + } + + uint32_t value = strtoul(liveStreamValue, NULL, 10); + if (value == 1) { + ALOGV("found live stream"); + return true; + } + } else { + // It is a live stream if no duration is returned + int64_t durationUs; + if (!desc->getDurationUs(&durationUs)) { + ALOGV("No duration found, assume live stream"); + return true; + } + } + + return false; + } + virtual void onMessageReceived(const sp &msg) { switch (msg->what()) { case 'conn': @@ -457,6 +490,8 @@ struct MyHandler : public AHandler { } } + mSeekable = !isLiveStream(mSessionDesc); + if (!mBaseURL.startsWith("rtsp://")) { // Some misbehaving servers specify a relative // URL in one of the locations above, combine @@ -518,6 +553,8 @@ struct MyHandler : public AHandler { } else { mBaseURL = mSessionURL; + mSeekable = !isLiveStream(mSessionDesc); + if (mSessionDesc->countTracks() < 2) { // There's no actual tracks in this session. // The first "track" is merely session meta @@ -783,7 +820,7 @@ struct MyHandler : public AHandler { mNumAccessUnitsReceived = 0; mReceivedFirstRTCPPacket = false; mReceivedFirstRTPPacket = false; - mSeekable = false; + mSeekable = true; sp reply = new AMessage('tear', id()); @@ -1143,7 +1180,6 @@ struct MyHandler : public AHandler { } void parsePlayResponse(const sp &response) { - mSeekable = false; if (mTracks.size() == 0) { ALOGV("parsePlayResponse: late packets ignored."); return; @@ -1218,8 +1254,6 @@ struct MyHandler : public AHandler { ++n; } - - mSeekable = true; } sp getTrackFormat(size_t index, int32_t *timeScale) { -- cgit v1.1 From 198a893671ce80d951625fe328a92073306660d0 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 5 Feb 2013 13:16:39 -0800 Subject: Fix a typo in MediaPlayerFactory that would prevent us from opting in to nuplayer for general media playback. Change-Id: I050f5178aadbb0b8bf422861ef885745c0b9006a --- media/libmediaplayerservice/MediaPlayerFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index bb441cc..1fb8b1a 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -100,7 +100,7 @@ void MediaPlayerFactory::unregisterFactory(player_type type) { } \ \ if (0.0 == bestScore) { \ - bestScore = getDefaultPlayerType(); \ + ret = getDefaultPlayerType(); \ } \ \ return ret; -- cgit v1.1 From 9575c96b6e418914e2ffc6741ecc8d71e3968dbe Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 5 Feb 2013 13:59:56 -0800 Subject: Support for a "preparation" state that can take care of lengthy operations in NuPlayer and its sources. Sources also can publish their flags now and the mediaplayer UI will be able to pick up on these. Change-Id: I4f2b7e5d105dcb4b6c9132cd0e8799efa0c6a14b --- .../nuplayer/GenericSource.cpp | 24 +- .../libmediaplayerservice/nuplayer/GenericSource.h | 4 +- .../nuplayer/HTTPLiveSource.cpp | 35 +-- .../nuplayer/HTTPLiveSource.h | 3 +- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 97 +++++++- media/libmediaplayerservice/nuplayer/NuPlayer.h | 12 +- .../nuplayer/NuPlayerDriver.cpp | 247 ++++++++++++++++----- .../nuplayer/NuPlayerDriver.h | 30 ++- .../nuplayer/NuPlayerSource.h | 22 +- .../libmediaplayerservice/nuplayer/RTSPSource.cpp | 22 +- media/libmediaplayerservice/nuplayer/RTSPSource.h | 3 +- .../nuplayer/StreamingSource.cpp | 10 +- .../nuplayer/StreamingSource.h | 3 +- .../nuplayer/mp4/MP4Source.cpp | 10 +- .../libmediaplayerservice/nuplayer/mp4/MP4Source.h | 3 +- 15 files changed, 406 insertions(+), 119 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 450fae5..b04e7a6 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -106,6 +106,26 @@ void NuPlayer::GenericSource::initFromDataSource( NuPlayer::GenericSource::~GenericSource() { } +void NuPlayer::GenericSource::prepareAsync() { + if (mVideoTrack.mSource != NULL) { + sp meta = mVideoTrack.mSource->getFormat(); + + int32_t width, height; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + + notifyVideoSizeChanged(width, height); + } + + notifyFlagsChanged( + FLAG_CAN_PAUSE + | FLAG_CAN_SEEK_BACKWARD + | FLAG_CAN_SEEK_FORWARD + | FLAG_CAN_SEEK); + + notifyPrepared(); +} + void NuPlayer::GenericSource::start() { ALOGI("start"); @@ -262,8 +282,4 @@ void NuPlayer::GenericSource::readBuffer( } } -uint32_t NuPlayer::GenericSource::flags() const { - return FLAG_SEEKABLE; -} - } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index e59ea3a..2da680c 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -42,6 +42,8 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { const sp ¬ify, int fd, int64_t offset, int64_t length); + virtual void prepareAsync(); + virtual void start(); virtual status_t feedMoreTSData(); @@ -51,8 +53,6 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual status_t seekTo(int64_t seekTimeUs); - virtual uint32_t flags() const; - protected: virtual ~GenericSource(); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index d38ee62..ae67906 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -66,7 +66,7 @@ NuPlayer::HTTPLiveSource::~HTTPLiveSource() { } } -void NuPlayer::HTTPLiveSource::start() { +void NuPlayer::HTTPLiveSource::prepareAsync() { mLiveLooper = new ALooper; mLiveLooper->setName("http live"); mLiveLooper->start(); @@ -81,6 +81,26 @@ void NuPlayer::HTTPLiveSource::start() { mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); mTSParser = new ATSParser; + + notifyVideoSizeChanged(0, 0); + + uint32_t flags = FLAG_CAN_PAUSE; + if (mLiveSession->isSeekable()) { + flags |= FLAG_CAN_SEEK; + flags |= FLAG_CAN_SEEK_BACKWARD; + flags |= FLAG_CAN_SEEK_FORWARD; + } + + if (mLiveSession->hasDynamicDuration()) { + flags |= FLAG_DYNAMIC_DURATION; + } + + notifyFlagsChanged(flags); + + notifyPrepared(); +} + +void NuPlayer::HTTPLiveSource::start() { } sp NuPlayer::HTTPLiveSource::getFormatMeta(bool audio) { @@ -194,18 +214,5 @@ status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) { return OK; } -uint32_t NuPlayer::HTTPLiveSource::flags() const { - uint32_t flags = 0; - if (mLiveSession->isSeekable()) { - flags |= FLAG_SEEKABLE; - } - - if (mLiveSession->hasDynamicDuration()) { - flags |= FLAG_DYNAMIC_DURATION; - } - - return flags; -} - } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 4a217af..269f3c0 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -34,6 +34,7 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { bool uidValid = false, uid_t uid = 0); + virtual void prepareAsync(); virtual void start(); virtual status_t feedMoreTSData(); @@ -43,8 +44,6 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual status_t seekTo(int64_t seekTimeUs); - virtual uint32_t flags() const; - protected: virtual ~HTTPLiveSource(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 0736fbe..78b94ba 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -112,6 +112,7 @@ private: NuPlayer::NuPlayer() : mUIDValid(false), + mSourceFlags(0), mVideoIsAVC(false), mAudioEOS(false), mVideoEOS(false), @@ -142,7 +143,7 @@ void NuPlayer::setDriver(const wp &driver) { mDriver = driver; } -void NuPlayer::setDataSource(const sp &source) { +void NuPlayer::setDataSourceAsync(const sp &source) { sp msg = new AMessage(kWhatSetDataSource, id()); sp notify = new AMessage(kWhatSourceNotify, id()); @@ -174,7 +175,7 @@ static bool IsHTTPLiveURL(const char *url) { return false; } -void NuPlayer::setDataSource( +void NuPlayer::setDataSourceAsync( const char *url, const KeyedVector *headers) { sp msg = new AMessage(kWhatSetDataSource, id()); size_t len = strlen(url); @@ -199,7 +200,7 @@ void NuPlayer::setDataSource( msg->post(); } -void NuPlayer::setDataSource(int fd, int64_t offset, int64_t length) { +void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) { sp msg = new AMessage(kWhatSetDataSource, id()); sp notify = new AMessage(kWhatSourceNotify, id()); @@ -209,6 +210,10 @@ void NuPlayer::setDataSource(int fd, int64_t offset, int64_t length) { msg->post(); } +void NuPlayer::prepareAsync() { + (new AMessage(kWhatPrepare, id()))->post(); +} + void NuPlayer::setVideoSurfaceTextureAsync( const sp &bufferProducer) { sp msg = new AMessage(kWhatSetVideoNativeWindow, id()); @@ -287,6 +292,18 @@ void NuPlayer::onMessageReceived(const sp &msg) { mSource = static_cast(obj.get()); looper()->registerHandler(mSource); + + CHECK(mDriver != NULL); + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifySetDataSourceCompleted(OK); + } + break; + } + + case kWhatPrepare: + { + mSource->prepareAsync(); break; } @@ -403,9 +420,7 @@ void NuPlayer::onMessageReceived(const sp &msg) { && (mAudioDecoder != NULL || mVideoDecoder != NULL)) { // This is the first time we've found anything playable. - uint32_t flags = mSource->flags(); - - if (flags & Source::FLAG_DYNAMIC_DURATION) { + if (mSourceFlags & Source::FLAG_DYNAMIC_DURATION) { schedulePollDuration(); } } @@ -730,7 +745,7 @@ void NuPlayer::onMessageReceived(const sp &msg) { case kWhatSourceNotify: { - TRESPASS(); // TBD + onSourceNotify(msg); break; } @@ -1233,8 +1248,76 @@ void NuPlayer::performSetSurface(const sp &wrapper) { } } +void NuPlayer::onSourceNotify(const sp &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case Source::kWhatPrepared: + { + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifyPrepareCompleted(OK); + } + break; + } + + case Source::kWhatFlagsChanged: + { + uint32_t flags; + CHECK(msg->findInt32("flags", (int32_t *)&flags)); + + if ((mSourceFlags & Source::FLAG_DYNAMIC_DURATION) + && (!(flags & Source::FLAG_DYNAMIC_DURATION))) { + cancelPollDuration(); + } else if (!(mSourceFlags & Source::FLAG_DYNAMIC_DURATION) + && (flags & Source::FLAG_DYNAMIC_DURATION) + && (mAudioDecoder != NULL || mVideoDecoder != NULL)) { + schedulePollDuration(); + } + + mSourceFlags = flags; + break; + } + + case Source::kWhatVideoSizeChanged: + { + int32_t width, height; + CHECK(msg->findInt32("width", &width)); + CHECK(msg->findInt32("height", &height)); + + notifyListener(MEDIA_SET_VIDEO_SIZE, width, height); + break; + } + + default: + TRESPASS(); + } +} + //////////////////////////////////////////////////////////////////////////////// +void NuPlayer::Source::notifyFlagsChanged(uint32_t flags) { + sp notify = dupNotify(); + notify->setInt32("what", kWhatFlagsChanged); + notify->setInt32("flags", flags); + notify->post(); +} + +void NuPlayer::Source::notifyVideoSizeChanged(int32_t width, int32_t height) { + sp notify = dupNotify(); + notify->setInt32("what", kWhatVideoSizeChanged); + notify->setInt32("width", width); + notify->setInt32("height", height); + notify->post(); +} + +void NuPlayer::Source::notifyPrepared() { + sp notify = dupNotify(); + notify->setInt32("what", kWhatPrepared); + notify->post(); +} + void NuPlayer::Source::onMessageReceived(const sp &msg) { TRESPASS(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 0ff6089..50d0462 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -35,12 +35,14 @@ struct NuPlayer : public AHandler { void setDriver(const wp &driver); - void setDataSource(const sp &source); + void setDataSourceAsync(const sp &source); - void setDataSource( + void setDataSourceAsync( const char *url, const KeyedVector *headers); - void setDataSource(int fd, int64_t offset, int64_t length); + void setDataSourceAsync(int fd, int64_t offset, int64_t length); + + void prepareAsync(); void setVideoSurfaceTextureAsync( const sp &bufferProducer); @@ -82,6 +84,7 @@ private: enum { kWhatSetDataSource = '=DaS', + kWhatPrepare = 'prep', kWhatSetVideoNativeWindow = '=NaW', kWhatSetAudioSink = '=AuS', kWhatMoreDataQueued = 'more', @@ -102,6 +105,7 @@ private: bool mUIDValid; uid_t mUID; sp mSource; + uint32_t mSourceFlags; sp mNativeWindow; sp mAudioSink; sp mVideoDecoder; @@ -173,6 +177,8 @@ private: void performScanSources(); void performSetSurface(const sp &wrapper); + void onSourceNotify(const sp &msg); + DISALLOW_EVIL_CONSTRUCTORS(NuPlayer); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 7043404..ab7b4e8 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -21,21 +21,24 @@ #include "NuPlayerDriver.h" #include "NuPlayer.h" +#include "NuPlayerSource.h" #include #include +#include namespace android { NuPlayerDriver::NuPlayerDriver() - : mResetInProgress(false), + : mState(STATE_IDLE), + mAsyncResult(UNKNOWN_ERROR), mSetSurfaceInProgress(false), mDurationUs(-1), mPositionUs(-1), mNumFramesTotal(0), mNumFramesDropped(0), mLooper(new ALooper), - mState(UNINITIALIZED), + mPlayerFlags(0), mAtEOS(false), mStartupSeekTimeUs(-1) { mLooper->setName("NuPlayerDriver Looper"); @@ -67,43 +70,76 @@ status_t NuPlayerDriver::setUID(uid_t uid) { status_t NuPlayerDriver::setDataSource( const char *url, const KeyedVector *headers) { - CHECK_EQ((int)mState, (int)UNINITIALIZED); + Mutex::Autolock autoLock(mLock); - mPlayer->setDataSource(url, headers); + if (mState != STATE_IDLE) { + return INVALID_OPERATION; + } - mState = STOPPED; + mState = STATE_SET_DATASOURCE_PENDING; - return OK; + mPlayer->setDataSourceAsync(url, headers); + + while (mState == STATE_SET_DATASOURCE_PENDING) { + mCondition.wait(mLock); + } + + return mAsyncResult; } status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) { - CHECK_EQ((int)mState, (int)UNINITIALIZED); + Mutex::Autolock autoLock(mLock); - mPlayer->setDataSource(fd, offset, length); + if (mState != STATE_IDLE) { + return INVALID_OPERATION; + } - mState = STOPPED; + mState = STATE_SET_DATASOURCE_PENDING; - return OK; + mPlayer->setDataSourceAsync(fd, offset, length); + + while (mState == STATE_SET_DATASOURCE_PENDING) { + mCondition.wait(mLock); + } + + return mAsyncResult; } status_t NuPlayerDriver::setDataSource(const sp &source) { - CHECK_EQ((int)mState, (int)UNINITIALIZED); + Mutex::Autolock autoLock(mLock); - mPlayer->setDataSource(source); + if (mState != STATE_IDLE) { + return INVALID_OPERATION; + } - mState = STOPPED; + mState = STATE_SET_DATASOURCE_PENDING; - return OK; + mPlayer->setDataSourceAsync(source); + + while (mState == STATE_SET_DATASOURCE_PENDING) { + mCondition.wait(mLock); + } + + return mAsyncResult; } status_t NuPlayerDriver::setVideoSurfaceTexture( const sp &bufferProducer) { Mutex::Autolock autoLock(mLock); - if (mResetInProgress) { + if (mSetSurfaceInProgress) { return INVALID_OPERATION; } + switch (mState) { + case STATE_SET_DATASOURCE_PENDING: + case STATE_RESET_IN_PROGRESS: + return INVALID_OPERATION; + + default: + break; + } + mSetSurfaceInProgress = true; mPlayer->setVideoSurfaceTextureAsync(bufferProducer); @@ -116,23 +152,55 @@ status_t NuPlayerDriver::setVideoSurfaceTexture( } status_t NuPlayerDriver::prepare() { - sendEvent(MEDIA_SET_VIDEO_SIZE, 0, 0); - return OK; + Mutex::Autolock autoLock(mLock); + return prepare_l(); } -status_t NuPlayerDriver::prepareAsync() { - status_t err = prepare(); +status_t NuPlayerDriver::prepare_l() { + switch (mState) { + case STATE_UNPREPARED: + mState = STATE_PREPARING; + mPlayer->prepareAsync(); + while (mState == STATE_PREPARING) { + mCondition.wait(mLock); + } + return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR; + default: + return INVALID_OPERATION; + }; +} - notifyListener(MEDIA_PREPARED); +status_t NuPlayerDriver::prepareAsync() { + Mutex::Autolock autoLock(mLock); - return err; + switch (mState) { + case STATE_UNPREPARED: + mState = STATE_PREPARING; + mPlayer->prepareAsync(); + return OK; + default: + return INVALID_OPERATION; + }; } status_t NuPlayerDriver::start() { + Mutex::Autolock autoLock(mLock); + switch (mState) { - case UNINITIALIZED: - return INVALID_OPERATION; - case STOPPED: + case STATE_UNPREPARED: + { + status_t err = prepare_l(); + + if (err != OK) { + return err; + } + + CHECK_EQ(mState, STATE_PREPARED); + + // fall through + } + + case STATE_PREPARED: { mAtEOS = false; mPlayer->start(); @@ -146,21 +214,23 @@ status_t NuPlayerDriver::start() { mStartupSeekTimeUs = -1; } - break; } - case PLAYING: - return OK; - default: - { - CHECK_EQ((int)mState, (int)PAUSED); + case STATE_RUNNING: + break; + + case STATE_PAUSED: + { mPlayer->resume(); break; } + + default: + return INVALID_OPERATION; } - mState = PLAYING; + mState = STATE_RUNNING; return OK; } @@ -170,43 +240,44 @@ status_t NuPlayerDriver::stop() { } status_t NuPlayerDriver::pause() { + Mutex::Autolock autoLock(mLock); + switch (mState) { - case UNINITIALIZED: - return INVALID_OPERATION; - case STOPPED: + case STATE_PAUSED: + case STATE_PREPARED: return OK; - case PLAYING: + + case STATE_RUNNING: mPlayer->pause(); break; + default: - { - CHECK_EQ((int)mState, (int)PAUSED); - return OK; - } + return INVALID_OPERATION; } - mState = PAUSED; + mState = STATE_PAUSED; return OK; } bool NuPlayerDriver::isPlaying() { - return mState == PLAYING && !mAtEOS; + return mState == STATE_RUNNING && !mAtEOS; } status_t NuPlayerDriver::seekTo(int msec) { + Mutex::Autolock autoLock(mLock); + int64_t seekTimeUs = msec * 1000ll; switch (mState) { - case UNINITIALIZED: - return INVALID_OPERATION; - case STOPPED: + case STATE_PREPARED: { mStartupSeekTimeUs = seekTimeUs; break; } - case PLAYING: - case PAUSED: + + case STATE_RUNNING: + case STATE_PAUSED: { mAtEOS = false; mPlayer->seekToAsync(seekTimeUs); @@ -214,8 +285,7 @@ status_t NuPlayerDriver::seekTo(int msec) { } default: - TRESPASS(); - break; + return INVALID_OPERATION; } return OK; @@ -247,17 +317,28 @@ status_t NuPlayerDriver::getDuration(int *msec) { status_t NuPlayerDriver::reset() { Mutex::Autolock autoLock(mLock); - mResetInProgress = true; + switch (mState) { + case STATE_IDLE: + return OK; + + case STATE_SET_DATASOURCE_PENDING: + case STATE_RESET_IN_PROGRESS: + return INVALID_OPERATION; + + default: + break; + } + + mState = STATE_RESET_IN_PROGRESS; mPlayer->resetAsync(); - while (mResetInProgress) { + while (mState == STATE_RESET_IN_PROGRESS) { mCondition.wait(mLock); } mDurationUs = -1; mPositionUs = -1; - mState = UNINITIALIZED; mStartupSeekTimeUs = -1; return OK; @@ -311,20 +392,45 @@ status_t NuPlayerDriver::getParameter(int key, Parcel *reply) { status_t NuPlayerDriver::getMetadata( const media::Metadata::Filter& ids, Parcel *records) { - return INVALID_OPERATION; + Mutex::Autolock autoLock(mLock); + + using media::Metadata; + + Metadata meta(records); + + meta.appendBool( + Metadata::kPauseAvailable, + mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE); + + meta.appendBool( + Metadata::kSeekBackwardAvailable, + mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD); + + meta.appendBool( + Metadata::kSeekForwardAvailable, + mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD); + + meta.appendBool( + Metadata::kSeekAvailable, + mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK); + + return OK; } void NuPlayerDriver::notifyResetComplete() { Mutex::Autolock autoLock(mLock); - CHECK(mResetInProgress); - mResetInProgress = false; + + CHECK_EQ(mState, STATE_RESET_IN_PROGRESS); + mState = STATE_IDLE; mCondition.broadcast(); } void NuPlayerDriver::notifySetSurfaceComplete() { Mutex::Autolock autoLock(mLock); + CHECK(mSetSurfaceInProgress); mSetSurfaceInProgress = false; + mCondition.broadcast(); } @@ -376,4 +482,37 @@ void NuPlayerDriver::notifyListener(int msg, int ext1, int ext2) { sendEvent(msg, ext1, ext2); } +void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) { + Mutex::Autolock autoLock(mLock); + + CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING); + + mAsyncResult = err; + mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE; + mCondition.broadcast(); +} + +void NuPlayerDriver::notifyPrepareCompleted(status_t err) { + Mutex::Autolock autoLock(mLock); + + CHECK_EQ(mState, STATE_PREPARING); + + mAsyncResult = err; + + if (err == OK) { + notifyListener(MEDIA_PREPARED); + mState = STATE_PREPARED; + } else { + mState = STATE_UNPREPARED; + } + + mCondition.broadcast(); +} + +void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) { + Mutex::Autolock autoLock(mLock); + + mPlayerFlags = flags; +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 553c406..49b8ed2 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -61,6 +61,8 @@ struct NuPlayerDriver : public MediaPlayerInterface { virtual status_t dump(int fd, const Vector &args) const; + void notifySetDataSourceCompleted(status_t err); + void notifyPrepareCompleted(status_t err); void notifyResetComplete(); void notifySetSurfaceComplete(); void notifyDuration(int64_t durationUs); @@ -68,17 +70,32 @@ struct NuPlayerDriver : public MediaPlayerInterface { void notifySeekComplete(); void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped); void notifyListener(int msg, int ext1 = 0, int ext2 = 0); + void notifyFlagsChanged(uint32_t flags); protected: virtual ~NuPlayerDriver(); private: + enum State { + STATE_IDLE, + STATE_SET_DATASOURCE_PENDING, + STATE_UNPREPARED, + STATE_PREPARING, + STATE_PREPARED, + STATE_RUNNING, + STATE_PAUSED, + STATE_RESET_IN_PROGRESS, + }; + mutable Mutex mLock; Condition mCondition; + State mState; + + status_t mAsyncResult; + // The following are protected through "mLock" // >>> - bool mResetInProgress; bool mSetSurfaceInProgress; int64_t mDurationUs; int64_t mPositionUs; @@ -88,19 +105,14 @@ private: sp mLooper; sp mPlayer; + uint32_t mPlayerFlags; - enum State { - UNINITIALIZED, - STOPPED, - PLAYING, - PAUSED - }; - - State mState; bool mAtEOS; int64_t mStartupSeekTimeUs; + status_t prepare_l(); + DISALLOW_EVIL_CONSTRUCTORS(NuPlayerDriver); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index a3201cf..53c7c12 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -25,11 +25,21 @@ namespace android { struct ABuffer; +struct MetaData; struct NuPlayer::Source : public AHandler { enum Flags { - FLAG_SEEKABLE = 1, - FLAG_DYNAMIC_DURATION = 2, + FLAG_CAN_PAUSE = 1, + FLAG_CAN_SEEK_BACKWARD = 2, // the "10 sec back button" + FLAG_CAN_SEEK_FORWARD = 4, // the "10 sec forward button" + FLAG_CAN_SEEK = 8, // the "seek bar" + FLAG_DYNAMIC_DURATION = 16, + }; + + enum { + kWhatPrepared, + kWhatFlagsChanged, + kWhatVideoSizeChanged, }; // The provides message is used to notify the player about various @@ -38,6 +48,8 @@ struct NuPlayer::Source : public AHandler { : mNotify(notify) { } + virtual void prepareAsync() = 0; + virtual void start() = 0; virtual void stop() {} @@ -58,8 +70,6 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } - virtual uint32_t flags() const = 0; - protected: virtual ~Source() {} @@ -69,6 +79,10 @@ protected: sp dupNotify() const { return mNotify->dup(); } + void notifyFlagsChanged(uint32_t flags); + void notifyVideoSizeChanged(int32_t width, int32_t height); + void notifyPrepared(); + private: sp mNotify; diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index 3035589..e4d72d9 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -65,7 +65,7 @@ NuPlayer::RTSPSource::~RTSPSource() { mLooper->stop(); } -void NuPlayer::RTSPSource::start() { +void NuPlayer::RTSPSource::prepareAsync() { if (mLooper == NULL) { mLooper = new ALooper; mLooper->setName("rtsp"); @@ -88,13 +88,27 @@ void NuPlayer::RTSPSource::start() { (mFlags & kFlagIncognito) ? SDPLoader::kFlagIncognito : 0, mUIDValid, mUID); - mSDPLoader->load(mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); + mSDPLoader->load( + mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); } else { mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID); mLooper->registerHandler(mHandler); mHandler->connect(); } + + notifyVideoSizeChanged(0, 0); + + notifyFlagsChanged( + FLAG_CAN_PAUSE + | FLAG_CAN_SEEK_BACKWARD + | FLAG_CAN_SEEK_FORWARD + | FLAG_CAN_SEEK); + + notifyPrepared(); +} + +void NuPlayer::RTSPSource::start() { } void NuPlayer::RTSPSource::stop() { @@ -225,10 +239,6 @@ void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { mHandler->seek(seekTimeUs); } -uint32_t NuPlayer::RTSPSource::flags() const { - return FLAG_SEEKABLE; -} - void NuPlayer::RTSPSource::onMessageReceived(const sp &msg) { if (msg->what() == kWhatDisconnect) { uint32_t replyID; diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index b2a7dae..cbb6f90 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -40,6 +40,7 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source { uid_t uid = 0, bool isSDP = false); + virtual void prepareAsync(); virtual void start(); virtual void stop(); @@ -50,8 +51,6 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual status_t seekTo(int64_t seekTimeUs); - virtual uint32_t flags() const; - void onMessageReceived(const sp &msg); protected: diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index 9b04833..df03f86 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -43,6 +43,12 @@ NuPlayer::StreamingSource::StreamingSource( NuPlayer::StreamingSource::~StreamingSource() { } +void NuPlayer::StreamingSource::prepareAsync() { + notifyVideoSizeChanged(0, 0); + notifyFlagsChanged(0); + notifyPrepared(); +} + void NuPlayer::StreamingSource::start() { mStreamListener = new NuPlayerStreamListener(mSource, 0); @@ -176,9 +182,5 @@ status_t NuPlayer::StreamingSource::dequeueAccessUnit( return err; } -uint32_t NuPlayer::StreamingSource::flags() const { - return 0; -} - } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h index dc616f7..80b061c 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.h +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h @@ -31,14 +31,13 @@ struct NuPlayer::StreamingSource : public NuPlayer::Source { const sp ¬ify, const sp &source); + virtual void prepareAsync(); virtual void start(); virtual status_t feedMoreTSData(); virtual status_t dequeueAccessUnit(bool audio, sp *accessUnit); - virtual uint32_t flags() const; - protected: virtual ~StreamingSource(); diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp index d659b73..d31d947 100644 --- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp +++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp @@ -117,6 +117,12 @@ MP4Source::MP4Source( MP4Source::~MP4Source() { } +void MP4Source::prepareAsync() { + notifyVideoSizeChanged(0, 0); + notifyFlagsChanged(0); + notifyPrepared(); +} + void MP4Source::start() { mLooper->start(false /* runOnCallingThread */); mParser->start(new StreamSource(mSource)); @@ -135,8 +141,4 @@ status_t MP4Source::dequeueAccessUnit( return mParser->dequeueAccessUnit(audio, accessUnit); } -uint32_t MP4Source::flags() const { - return 0; -} - } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h index b16a111..a6ef622 100644 --- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h +++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h @@ -26,6 +26,7 @@ struct FragmentedMP4Parser; struct MP4Source : public NuPlayer::Source { MP4Source(const sp ¬ify, const sp &source); + virtual void prepareAsync(); virtual void start(); virtual status_t feedMoreTSData(); @@ -35,8 +36,6 @@ struct MP4Source : public NuPlayer::Source { virtual status_t dequeueAccessUnit( bool audio, sp *accessUnit); - virtual uint32_t flags() const; - protected: virtual ~MP4Source(); -- cgit v1.1 From ec0c597cabf169ca646bcea5faac1bd81ed4484d Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 5 Feb 2013 14:47:13 -0800 Subject: RTSP now properly publishes its "seekable" flags after connection has successfully completed and only then signals that preparation is complete. Change-Id: I1a60f718e673fe1462c69369c40eafbed6a14326 --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 8 +++-- .../nuplayer/NuPlayerDriver.cpp | 14 +++++++- .../nuplayer/NuPlayerDriver.h | 1 + .../nuplayer/NuPlayerSource.h | 2 +- .../libmediaplayerservice/nuplayer/RTSPSource.cpp | 41 ++++++++++++++++------ media/libstagefright/rtsp/MyHandler.h | 4 +++ 6 files changed, 56 insertions(+), 14 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 78b94ba..bcefe63 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -1255,9 +1255,12 @@ void NuPlayer::onSourceNotify(const sp &msg) { switch (what) { case Source::kWhatPrepared: { + int32_t err; + CHECK(msg->findInt32("err", &err)); + sp driver = mDriver.promote(); if (driver != NULL) { - driver->notifyPrepareCompleted(OK); + driver->notifyPrepareCompleted(err); } break; } @@ -1312,9 +1315,10 @@ void NuPlayer::Source::notifyVideoSizeChanged(int32_t width, int32_t height) { notify->post(); } -void NuPlayer::Source::notifyPrepared() { +void NuPlayer::Source::notifyPrepared(status_t err) { sp notify = dupNotify(); notify->setInt32("what", kWhatPrepared); + notify->setInt32("err", err); notify->post(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index ab7b4e8..3c63e80 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -31,6 +31,7 @@ namespace android { NuPlayerDriver::NuPlayerDriver() : mState(STATE_IDLE), + mIsAsyncPrepare(false), mAsyncResult(UNKNOWN_ERROR), mSetSurfaceInProgress(false), mDurationUs(-1), @@ -160,6 +161,11 @@ status_t NuPlayerDriver::prepare_l() { switch (mState) { case STATE_UNPREPARED: mState = STATE_PREPARING; + + // Make sure we're not posting any notifications, success or + // failure information is only communicated through our result + // code. + mIsAsyncPrepare = false; mPlayer->prepareAsync(); while (mState == STATE_PREPARING) { mCondition.wait(mLock); @@ -176,6 +182,7 @@ status_t NuPlayerDriver::prepareAsync() { switch (mState) { case STATE_UNPREPARED: mState = STATE_PREPARING; + mIsAsyncPrepare = true; mPlayer->prepareAsync(); return OK; default: @@ -500,9 +507,14 @@ void NuPlayerDriver::notifyPrepareCompleted(status_t err) { mAsyncResult = err; if (err == OK) { - notifyListener(MEDIA_PREPARED); + if (mIsAsyncPrepare) { + notifyListener(MEDIA_PREPARED); + } mState = STATE_PREPARED; } else { + if (mIsAsyncPrepare) { + notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); + } mState = STATE_UNPREPARED; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 49b8ed2..5df0cfb 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -92,6 +92,7 @@ private: State mState; + bool mIsAsyncPrepare; status_t mAsyncResult; // The following are protected through "mLock" diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 53c7c12..f5d4c38 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -81,7 +81,7 @@ protected: void notifyFlagsChanged(uint32_t flags); void notifyVideoSizeChanged(int32_t width, int32_t height); - void notifyPrepared(); + void notifyPrepared(status_t err = OK); private: sp mNotify; diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index e4d72d9..d787647 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -96,16 +96,6 @@ void NuPlayer::RTSPSource::prepareAsync() { mHandler->connect(); } - - notifyVideoSizeChanged(0, 0); - - notifyFlagsChanged( - FLAG_CAN_PAUSE - | FLAG_CAN_SEEK_BACKWARD - | FLAG_CAN_SEEK_FORWARD - | FLAG_CAN_SEEK); - - notifyPrepared(); } void NuPlayer::RTSPSource::start() { @@ -270,12 +260,31 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp &msg) { switch (what) { case MyHandler::kWhatConnected: + { onConnected(); + + notifyVideoSizeChanged(0, 0); + + uint32_t flags = 0; + + if (mHandler->isSeekable()) { + flags = FLAG_CAN_PAUSE | FLAG_CAN_SEEK; + + // Seeking 10secs forward or backward is a very expensive + // operation for rtsp, so let's not enable that. + // The user can always use the seek bar. + } + + notifyFlagsChanged(flags); + notifyPrepared(); break; + } case MyHandler::kWhatDisconnected: + { onDisconnected(msg); break; + } case MyHandler::kWhatSeekDone: { @@ -520,6 +529,12 @@ void NuPlayer::RTSPSource::onSDPLoaded(const sp &msg) { } if (err != OK) { + if (mState == CONNECTING) { + // We're still in the preparation phase, signal that it + // failed. + notifyPrepared(err); + } + mState = DISCONNECTED; mFinalResult = err; @@ -537,6 +552,12 @@ void NuPlayer::RTSPSource::onDisconnected(const sp &msg) { mLooper->unregisterHandler(mHandler->id()); mHandler.clear(); + if (mState == CONNECTING) { + // We're still in the preparation phase, signal that it + // failed. + notifyPrepared(err); + } + mState = DISCONNECTED; mFinalResult = err; diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index cdd00e4..aa64060 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -195,6 +195,10 @@ struct MyHandler : public AHandler { msg->post(); } + bool isSeekable() const { + return mSeekable; + } + static void addRR(const sp &buf) { uint8_t *ptr = buf->data() + buf->size(); ptr[0] = 0x80 | 0; -- cgit v1.1 From 0df36ec3303c2c6bf9b42c07945ac8bd234153f3 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 6 Feb 2013 10:44:39 -0800 Subject: HLS now properly publishes its "seekable" flags after connection has successfully completed and a sufficient amount of data fetched, and only then signals that preparation is completed. Change-Id: I7684a14238b826909f518f2af506966e522dfcfc --- .../nuplayer/HTTPLiveSource.cpp | 74 +++++++++++++++++----- .../nuplayer/HTTPLiveSource.h | 8 +++ media/libstagefright/foundation/ALooperRoster.cpp | 3 +- media/libstagefright/httplive/LiveSession.cpp | 53 +++++++++++++--- media/libstagefright/include/LiveSession.h | 15 ++++- 5 files changed, 125 insertions(+), 28 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index ae67906..655ee55 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -71,7 +71,10 @@ void NuPlayer::HTTPLiveSource::prepareAsync() { mLiveLooper->setName("http live"); mLiveLooper->start(); + sp notify = new AMessage(kWhatSessionNotify, id()); + mLiveSession = new LiveSession( + notify, (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0, mUIDValid, mUID); @@ -81,23 +84,6 @@ void NuPlayer::HTTPLiveSource::prepareAsync() { mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); mTSParser = new ATSParser; - - notifyVideoSizeChanged(0, 0); - - uint32_t flags = FLAG_CAN_PAUSE; - if (mLiveSession->isSeekable()) { - flags |= FLAG_CAN_SEEK; - flags |= FLAG_CAN_SEEK_BACKWARD; - flags |= FLAG_CAN_SEEK_FORWARD; - } - - if (mLiveSession->hasDynamicDuration()) { - flags |= FLAG_DYNAMIC_DURATION; - } - - notifyFlagsChanged(flags); - - notifyPrepared(); } void NuPlayer::HTTPLiveSource::start() { @@ -214,5 +200,59 @@ status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) { return OK; } +void NuPlayer::HTTPLiveSource::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatSessionNotify: + { + onSessionNotify(msg); + break; + } + + default: + Source::onMessageReceived(msg); + break; + } +} + +void NuPlayer::HTTPLiveSource::onSessionNotify(const sp &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case LiveSession::kWhatPrepared: + { + notifyVideoSizeChanged(0, 0); + + uint32_t flags = FLAG_CAN_PAUSE; + if (mLiveSession->isSeekable()) { + flags |= FLAG_CAN_SEEK; + flags |= FLAG_CAN_SEEK_BACKWARD; + flags |= FLAG_CAN_SEEK_FORWARD; + } + + if (mLiveSession->hasDynamicDuration()) { + flags |= FLAG_DYNAMIC_DURATION; + } + + notifyFlagsChanged(flags); + + notifyPrepared(); + break; + } + + case LiveSession::kWhatPreparationFailed: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + + notifyPrepared(err); + break; + } + + default: + TRESPASS(); + } +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 269f3c0..067d1da 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -49,12 +49,18 @@ protected: virtual sp getFormatMeta(bool audio); + virtual void onMessageReceived(const sp &msg); + private: enum Flags { // Don't log any URLs. kFlagIncognito = 1, }; + enum { + kWhatSessionNotify, + }; + AString mURL; KeyedVector mExtraHeaders; bool mUIDValid; @@ -66,6 +72,8 @@ private: sp mLiveSession; sp mTSParser; + void onSessionNotify(const sp &msg); + DISALLOW_EVIL_CONSTRUCTORS(HTTPLiveSource); }; diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp index dff931d..ad10d2b 100644 --- a/media/libstagefright/foundation/ALooperRoster.cpp +++ b/media/libstagefright/foundation/ALooperRoster.cpp @@ -82,7 +82,8 @@ status_t ALooperRoster::postMessage_l( ssize_t index = mHandlers.indexOfKey(msg->target()); if (index < 0) { - ALOGW("failed to post message. Target handler not registered."); + ALOGW("failed to post message '%s'. Target handler not registered.", + msg->debugString().c_str()); return -ENOENT; } diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 733753b..962b01c 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -40,10 +40,13 @@ namespace android { -LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid) - : mFlags(flags), +LiveSession::LiveSession( + const sp ¬ify, uint32_t flags, bool uidValid, uid_t uid) + : mNotify(notify), + mFlags(flags), mUIDValid(uidValid), mUID(uid), + mInPreparationPhase(true), mDataSource(new LiveDataSource), mHTTPDataSource( HTTPBase::Create( @@ -179,7 +182,7 @@ void LiveSession::onConnect(const sp &msg) { if (playlist == NULL) { ALOGE("unable to fetch master playlist '%s'.", url.c_str()); - mDataSource->queueEOS(ERROR_IO); + signalEOS(ERROR_IO); return; } @@ -207,7 +210,7 @@ void LiveSession::onConnect(const sp &msg) { void LiveSession::onDisconnect() { ALOGI("onDisconnect"); - mDataSource->queueEOS(ERROR_END_OF_STREAM); + signalEOS(ERROR_END_OF_STREAM); Mutex::Autolock autoLock(mLock); mDisconnectPending = false; @@ -561,7 +564,8 @@ rinse_repeat: // unchanged from the last time we tried. } else { ALOGE("failed to load playlist at url '%s'", url.c_str()); - mDataSource->queueEOS(ERROR_IO); + signalEOS(ERROR_IO); + return; } } else { @@ -704,7 +708,7 @@ rinse_repeat: mSeqNumber, firstSeqNumberInPlaylist, firstSeqNumberInPlaylist + mPlaylist->size() - 1); - mDataSource->queueEOS(ERROR_END_OF_STREAM); + signalEOS(ERROR_END_OF_STREAM); return; } } @@ -737,7 +741,7 @@ rinse_repeat: status_t err = fetchFile(uri.c_str(), &buffer, range_offset, range_length); if (err != OK) { ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); - mDataSource->queueEOS(err); + signalEOS(err); return; } @@ -748,7 +752,7 @@ rinse_repeat: if (err != OK) { ALOGE("decryptBuffer failed w/ error %d", err); - mDataSource->queueEOS(err); + signalEOS(err); return; } @@ -760,7 +764,7 @@ rinse_repeat: mBandwidthItems.removeAt(bandwidthIndex); if (mBandwidthItems.isEmpty()) { - mDataSource->queueEOS(ERROR_UNSUPPORTED); + signalEOS(ERROR_UNSUPPORTED); return; } @@ -824,11 +828,42 @@ rinse_repeat: postMonitorQueue(); } +void LiveSession::signalEOS(status_t err) { + if (mInPreparationPhase && mNotify != NULL) { + sp notify = mNotify->dup(); + + notify->setInt32( + "what", + err == ERROR_END_OF_STREAM + ? kWhatPrepared : kWhatPreparationFailed); + + if (err != ERROR_END_OF_STREAM) { + notify->setInt32("err", err); + } + + notify->post(); + + mInPreparationPhase = false; + } + + mDataSource->queueEOS(err); +} + void LiveSession::onMonitorQueue() { if (mSeekTimeUs >= 0 || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) { onDownloadNext(); } else { + if (mInPreparationPhase) { + if (mNotify != NULL) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatPrepared); + notify->post(); + } + + mInPreparationPhase = false; + } + postMonitorQueue(1000000ll); } } diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h index f329cc9..db44a33 100644 --- a/media/libstagefright/include/LiveSession.h +++ b/media/libstagefright/include/LiveSession.h @@ -35,7 +35,9 @@ struct LiveSession : public AHandler { // Don't log any URLs. kFlagIncognito = 1, }; - LiveSession(uint32_t flags = 0, bool uidValid = false, uid_t uid = 0); + LiveSession( + const sp ¬ify, + uint32_t flags = 0, bool uidValid = false, uid_t uid = 0); sp getDataSource(); @@ -53,6 +55,12 @@ struct LiveSession : public AHandler { bool isSeekable() const; bool hasDynamicDuration() const; + // Posted notification's "what" field will carry one of the following: + enum { + kWhatPrepared, + kWhatPreparationFailed, + }; + protected: virtual ~LiveSession(); @@ -76,10 +84,13 @@ private: unsigned long mBandwidth; }; + sp mNotify; uint32_t mFlags; bool mUIDValid; uid_t mUID; + bool mInPreparationPhase; + sp mDataSource; sp mHTTPDataSource; @@ -144,6 +155,8 @@ private: // This is computed by summing the durations of all segments before it. int64_t getSegmentStartTimeUs(int32_t seqNumber) const; + void signalEOS(status_t err); + DISALLOW_EVIL_CONSTRUCTORS(LiveSession); }; -- cgit v1.1 From b50e83eca302a12f0fced6e7bab1b8617d63deaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20J=C3=B6nsson?= Date: Mon, 21 Jan 2013 16:26:41 +0100 Subject: RTSP buffering improvements Added buffering start and end notifications for RTSP. MEDIA_INFO_BUFFERING_START is sent when buffering is started and MEDIA_INFO_BUFFERING_END is sent when the buffer has filled up. This patch also adds RTSP end of stream handling. EOS is signalled when BYE is received OR when detecting end of stream even if no actual EOS is received. Change-Id: I5cccb6845060ae6afd66d9f735b89da81476cd13 --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 12 ++++ .../nuplayer/NuPlayerRenderer.cpp | 6 ++ .../nuplayer/NuPlayerSource.h | 2 + .../libmediaplayerservice/nuplayer/RTSPSource.cpp | 80 ++++++++++++++++++++-- media/libmediaplayerservice/nuplayer/RTSPSource.h | 7 +- .../libstagefright/mpeg2ts/AnotherPacketSource.cpp | 20 +++++- media/libstagefright/mpeg2ts/AnotherPacketSource.h | 3 + media/libstagefright/rtsp/MyHandler.h | 4 +- 8 files changed, 121 insertions(+), 13 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index bcefe63..ee25cc6 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -1293,6 +1293,18 @@ void NuPlayer::onSourceNotify(const sp &msg) { break; } + case Source::kWhatBufferingStart: + { + notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0); + break; + } + + case Source::kWhatBufferingEnd: + { + notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_END, 0); + break; + } + default: TRESPASS(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 8a75f83..1ba76a5 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -512,9 +512,15 @@ void NuPlayer::Renderer::onQueueEOS(const sp &msg) { entry.mFinalResult = finalResult; if (audio) { + if (mAudioQueue.empty() && mSyncQueues) { + syncQueuesDone(); + } mAudioQueue.push_back(entry); postDrainAudioQueue(); } else { + if (mVideoQueue.empty() && mSyncQueues) { + syncQueuesDone(); + } mVideoQueue.push_back(entry); postDrainVideoQueue(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index f5d4c38..df84123 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -40,6 +40,8 @@ struct NuPlayer::Source : public AHandler { kWhatPrepared, kWhatFlagsChanged, kWhatVideoSizeChanged, + kWhatBufferingStart, + kWhatBufferingEnd, }; // The provides message is used to notify the player about various diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index d787647..b70d550 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -29,6 +29,8 @@ namespace android { +const int64_t kNearEOSTimeoutUs = 2000000ll; // 2 secs + NuPlayer::RTSPSource::RTSPSource( const sp ¬ify, const char *url, @@ -45,8 +47,10 @@ NuPlayer::RTSPSource::RTSPSource( mState(DISCONNECTED), mFinalResult(OK), mDisconnectReplyID(0), - mStartingUp(true), - mSeekGeneration(0) { + mBuffering(true), + mSeekGeneration(0), + mEOSTimeoutAudio(0), + mEOSTimeoutVideo(0) { if (headers) { mExtraHeaders = *headers; @@ -96,6 +100,10 @@ void NuPlayer::RTSPSource::prepareAsync() { mHandler->connect(); } + + sp notifyStart = dupNotify(); + notifyStart->setInt32("what", kWhatBufferingStart); + notifyStart->post(); } void NuPlayer::RTSPSource::start() { @@ -131,6 +139,13 @@ bool NuPlayer::RTSPSource::haveSufficientDataOnAllTracks() { static const int64_t kMinDurationUs = 2000000ll; + int64_t mediaDurationUs = 0; + getDuration(&mediaDurationUs); + if ((mAudioTrack != NULL && mAudioTrack->isFinished(mediaDurationUs)) + || (mVideoTrack != NULL && mVideoTrack->isFinished(mediaDurationUs))) { + return true; + } + status_t err; int64_t durationUs; if (mAudioTrack != NULL @@ -156,12 +171,16 @@ bool NuPlayer::RTSPSource::haveSufficientDataOnAllTracks() { status_t NuPlayer::RTSPSource::dequeueAccessUnit( bool audio, sp *accessUnit) { - if (mStartingUp) { + if (mBuffering) { if (!haveSufficientDataOnAllTracks()) { return -EWOULDBLOCK; } - mStartingUp = false; + mBuffering = false; + + sp notify = dupNotify(); + notify->setInt32("what", kWhatBufferingEnd); + notify->post(); } sp source = getSource(audio); @@ -172,9 +191,51 @@ status_t NuPlayer::RTSPSource::dequeueAccessUnit( status_t finalResult; if (!source->hasBufferAvailable(&finalResult)) { - return finalResult == OK ? -EWOULDBLOCK : finalResult; + if (finalResult == OK) { + int64_t mediaDurationUs = 0; + getDuration(&mediaDurationUs); + sp otherSource = getSource(!audio); + status_t otherFinalResult; + + // If other source already signaled EOS, this source should also signal EOS + if (otherSource != NULL && + !otherSource->hasBufferAvailable(&otherFinalResult) && + otherFinalResult == ERROR_END_OF_STREAM) { + source->signalEOS(ERROR_END_OF_STREAM); + return ERROR_END_OF_STREAM; + } + + // If this source has detected near end, give it some time to retrieve more + // data before signaling EOS + if (source->isFinished(mediaDurationUs)) { + int64_t eosTimeout = audio ? mEOSTimeoutAudio : mEOSTimeoutVideo; + if (eosTimeout == 0) { + setEOSTimeout(audio, ALooper::GetNowUs()); + } else if ((ALooper::GetNowUs() - eosTimeout) > kNearEOSTimeoutUs) { + setEOSTimeout(audio, 0); + source->signalEOS(ERROR_END_OF_STREAM); + return ERROR_END_OF_STREAM; + } + return -EWOULDBLOCK; + } + + if (!(otherSource != NULL && otherSource->isFinished(mediaDurationUs))) { + // We should not enter buffering mode + // if any of the sources already have detected EOS. + mBuffering = true; + + sp notify = dupNotify(); + notify->setInt32("what", kWhatBufferingStart); + notify->post(); + } + + return -EWOULDBLOCK; + } + return finalResult; } + setEOSTimeout(audio, 0); + return source->dequeueAccessUnit(accessUnit); } @@ -189,6 +250,14 @@ sp NuPlayer::RTSPSource::getSource(bool audio) { return audio ? mAudioTrack : mVideoTrack; } +void NuPlayer::RTSPSource::setEOSTimeout(bool audio, int64_t timeout) { + if (audio) { + mEOSTimeoutAudio = timeout; + } else { + mEOSTimeoutVideo = timeout; + } +} + status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { *durationUs = 0ll; @@ -289,7 +358,6 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp &msg) { case MyHandler::kWhatSeekDone: { mState = CONNECTED; - mStartingUp = true; break; } diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index cbb6f90..8451b9e 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -95,7 +95,7 @@ private: State mState; status_t mFinalResult; uint32_t mDisconnectReplyID; - bool mStartingUp; + bool mBuffering; sp mLooper; sp > mReflector; @@ -110,6 +110,9 @@ private: int32_t mSeekGeneration; + int64_t mEOSTimeoutAudio; + int64_t mEOSTimeoutVideo; + sp getSource(bool audio); void onConnected(); @@ -121,6 +124,8 @@ private: bool haveSufficientDataOnAllTracks(); + void setEOSTimeout(bool audio, int64_t timeout); + DISALLOW_EVIL_CONSTRUCTORS(RTSPSource); }; diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index a605a05..3de3a61 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -28,9 +28,12 @@ namespace android { +const int64_t kNearEOSMarkUs = 2000000ll; // 2 secs + AnotherPacketSource::AnotherPacketSource(const sp &meta) : mIsAudio(false), mFormat(meta), + mLastQueuedTimeUs(0), mEOSResult(OK) { const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); @@ -141,9 +144,8 @@ void AnotherPacketSource::queueAccessUnit(const sp &buffer) { return; } - int64_t timeUs; - CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); - ALOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", timeUs, timeUs / 1E6); + CHECK(buffer->meta()->findInt64("timeUs", &mLastQueuedTimeUs)); + ALOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6); Mutex::Autolock autoLock(mLock); mBuffers.push_back(buffer); @@ -171,6 +173,7 @@ void AnotherPacketSource::queueDiscontinuity( } mEOSResult = OK; + mLastQueuedTimeUs = 0; sp buffer = new ABuffer(0); buffer->meta()->setInt32("discontinuity", static_cast(type)); @@ -247,4 +250,15 @@ status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) { return OK; } +bool AnotherPacketSource::isFinished(int64_t duration) const { + if (duration > 0) { + int64_t diff = duration - mLastQueuedTimeUs; + if (diff < kNearEOSMarkUs && diff > -kNearEOSMarkUs) { + ALOGV("Detecting EOS due to near end"); + return true; + } + } + return (mEOSResult != OK); +} + } // namespace android diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h index d685b98..1db4068 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -58,6 +58,8 @@ struct AnotherPacketSource : public MediaSource { status_t dequeueAccessUnit(sp *buffer); + bool isFinished(int64_t duration) const; + protected: virtual ~AnotherPacketSource(); @@ -67,6 +69,7 @@ private: bool mIsAudio; sp mFormat; + int64_t mLastQueuedTimeUs; List > mBuffers; status_t mEOSResult; diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index aa64060..cfbf501 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -945,9 +945,7 @@ struct MyHandler : public AHandler { int32_t eos; if (msg->findInt32("eos", &eos)) { ALOGI("received BYE on track index %d", trackIndex); -#if 0 - track->mPacketSource->signalEOS(ERROR_END_OF_STREAM); -#endif + postQueueEOS(trackIndex, ERROR_END_OF_STREAM); return; } -- cgit v1.1 From fba60daf77cc74a13ae3bf4b0e9925dd2ee4470c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20J=C3=B6nsson?= Date: Mon, 21 Jan 2013 17:15:45 +0100 Subject: Enable pause/resume for RTSP streaming When a stream is paused, RTSP Pause is also sent to the server. Otherwise the buffering might continue until the memory runs out. When the stream is resumed, RTSP Play will be sent in order to resume the buffering. Change-Id: I5dc1761140827c532451638c3fd3f34271e5b9ab --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 2 + .../nuplayer/NuPlayerSource.h | 2 + .../libmediaplayerservice/nuplayer/RTSPSource.cpp | 19 ++++ media/libmediaplayerservice/nuplayer/RTSPSource.h | 2 + media/libstagefright/rtsp/MyHandler.h | 126 ++++++++++++++++++++- 5 files changed, 147 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index ee25cc6..30eb4b9 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -732,6 +732,7 @@ void NuPlayer::onMessageReceived(const sp &msg) { case kWhatPause: { CHECK(mRenderer != NULL); + mSource->pause(); mRenderer->pause(); break; } @@ -739,6 +740,7 @@ void NuPlayer::onMessageReceived(const sp &msg) { case kWhatResume: { CHECK(mRenderer != NULL); + mSource->resume(); mRenderer->resume(); break; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index df84123..8622abe 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -54,6 +54,8 @@ struct NuPlayer::Source : public AHandler { virtual void start() = 0; virtual void stop() {} + virtual void pause() {} + virtual void resume() {} // Returns OK iff more data was available, // an error or ERROR_END_OF_STREAM if not. diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index b70d550..a5ff0ca 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -119,6 +119,25 @@ void NuPlayer::RTSPSource::stop() { msg->postAndAwaitResponse(&dummy); } +void NuPlayer::RTSPSource::pause() { + int64_t mediaDurationUs = 0; + getDuration(&mediaDurationUs); + for (size_t index = 0; index < mTracks.size(); index++) { + TrackInfo *info = &mTracks.editItemAt(index); + sp source = info->mSource; + + // Check if EOS or ERROR is received + if (source != NULL && source->isFinished(mediaDurationUs)) { + return; + } + } + mHandler->pause(); +} + +void NuPlayer::RTSPSource::resume() { + mHandler->resume(); +} + status_t NuPlayer::RTSPSource::feedMoreTSData() { return mFinalResult; } diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index 8451b9e..8cf34a0 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -43,6 +43,8 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source { virtual void prepareAsync(); virtual void start(); virtual void stop(); + virtual void pause(); + virtual void resume(); virtual status_t feedMoreTSData(); diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index cfbf501..4e5e2fa 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -135,7 +135,8 @@ struct MyHandler : public AHandler { mReceivedFirstRTPPacket(false), mSeekable(true), mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs), - mKeepAliveGeneration(0) { + mKeepAliveGeneration(0), + mPausing(false) { mNetLooper->setName("rtsp net"); mNetLooper->start(false /* runOnCallingThread */, false /* canCallJava */, @@ -199,6 +200,16 @@ struct MyHandler : public AHandler { return mSeekable; } + void pause() { + sp msg = new AMessage('paus', id()); + msg->post(); + } + + void resume() { + sp msg = new AMessage('resu', id()); + msg->post(); + } + static void addRR(const sp &buf) { uint8_t *ptr = buf->data() + buf->size(); ptr[0] = 0x80 | 0; @@ -824,6 +835,7 @@ struct MyHandler : public AHandler { mNumAccessUnitsReceived = 0; mReceivedFirstRTCPPacket = false; mReceivedFirstRTPPacket = false; + mPausing = false; mSeekable = true; sp reply = new AMessage('tear', id()); @@ -973,6 +985,100 @@ struct MyHandler : public AHandler { break; } + case 'paus': + { + if (!mSeekable) { + ALOGW("This is a live stream, ignoring pause request."); + break; + } + mCheckPending = true; + ++mCheckGeneration; + mPausing = true; + + AString request = "PAUSE "; + request.append(mSessionURL); + request.append(" RTSP/1.0\r\n"); + + request.append("Session: "); + request.append(mSessionID); + request.append("\r\n"); + + request.append("\r\n"); + + sp reply = new AMessage('pau2', id()); + mConn->sendRequest(request.c_str(), reply); + break; + } + + case 'pau2': + { + int32_t result; + CHECK(msg->findInt32("result", &result)); + + ALOGI("PAUSE completed with result %d (%s)", + result, strerror(-result)); + break; + } + + case 'resu': + { + if (mPausing && mSeekPending) { + // If seeking, Play will be sent from see1 instead + break; + } + + if (!mPausing) { + // Dont send PLAY if we have not paused + break; + } + AString request = "PLAY "; + request.append(mSessionURL); + request.append(" RTSP/1.0\r\n"); + + request.append("Session: "); + request.append(mSessionID); + request.append("\r\n"); + + request.append("\r\n"); + + sp reply = new AMessage('res2', id()); + mConn->sendRequest(request.c_str(), reply); + break; + } + + case 'res2': + { + int32_t result; + CHECK(msg->findInt32("result", &result)); + + ALOGI("PLAY completed with result %d (%s)", + result, strerror(-result)); + + mCheckPending = false; + postAccessUnitTimeoutCheck(); + + if (result == OK) { + sp obj; + CHECK(msg->findObject("response", &obj)); + sp response = + static_cast(obj.get()); + + if (response->mStatusCode != 200) { + result = UNKNOWN_ERROR; + } else { + parsePlayResponse(response); + } + } + + if (result != OK) { + ALOGE("resume failed, aborting."); + (new AMessage('abor', id()))->post(); + } + + mPausing = false; + break; + } + case 'seek': { if (!mSeekable) { @@ -994,6 +1100,15 @@ struct MyHandler : public AHandler { mCheckPending = true; ++mCheckGeneration; + sp reply = new AMessage('see1', id()); + reply->setInt64("time", timeUs); + + if (mPausing) { + // PAUSE already sent + ALOGI("Pause already sent"); + reply->post(); + break; + } AString request = "PAUSE "; request.append(mSessionURL); request.append(" RTSP/1.0\r\n"); @@ -1004,8 +1119,6 @@ struct MyHandler : public AHandler { request.append("\r\n"); - sp reply = new AMessage('see1', id()); - reply->setInt64("time", timeUs); mConn->sendRequest(request.c_str(), reply); break; } @@ -1049,7 +1162,10 @@ struct MyHandler : public AHandler { case 'see2': { - CHECK(mSeekPending); + if (mTracks.size() == 0) { + // We have already hit abor, break + break; + } int32_t result; CHECK(msg->findInt32("result", &result)); @@ -1085,6 +1201,7 @@ struct MyHandler : public AHandler { (new AMessage('abor', id()))->post(); } + mPausing = false; mSeekPending = false; sp msg = mNotify->dup(); @@ -1327,6 +1444,7 @@ private: bool mSeekable; int64_t mKeepAliveTimeoutUs; int32_t mKeepAliveGeneration; + bool mPausing; Vector mTracks; -- cgit v1.1 From 599b9655ddf95cdf6cb99970ce03c632bb2a576b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Zigher?= Date: Wed, 23 Jan 2013 14:48:57 +0100 Subject: RTSP: Parse session level control attribute from SDP If a=control: is present at session-level in the SDP response, RFC2326:C.1.1 defines the URL to be used for aggregate commands. This includes PLAY and PAUSE but not TEARDOWN. Change-Id: Iaa1dc2271d00df39dc83477a99fda6fbeb73c5b4 --- media/libstagefright/rtsp/MyHandler.h | 36 ++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 4e5e2fa..2dde422 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -186,6 +186,27 @@ struct MyHandler : public AHandler { mConn->connect(mOriginalSessionURL.c_str(), reply); } + AString getControlURL(sp desc) { + AString sessionLevelControlURL; + if (mSessionDesc->findAttribute( + 0, + "a=control", + &sessionLevelControlURL)) { + if (sessionLevelControlURL.compare("*") == 0) { + return mBaseURL; + } else { + AString controlURL; + CHECK(MakeURL( + mBaseURL.c_str(), + sessionLevelControlURL.c_str(), + &controlURL)); + return controlURL; + } + } else { + return mSessionURL; + } + } + void disconnect() { (new AMessage('abor', id()))->post(); } @@ -526,6 +547,8 @@ struct MyHandler : public AHandler { mBaseURL = tmp; } + mControlURL = getControlURL(mSessionDesc); + if (mSessionDesc->countTracks() < 2) { // There's no actual tracks in this session. // The first "track" is merely session meta @@ -570,6 +593,8 @@ struct MyHandler : public AHandler { mSeekable = !isLiveStream(mSessionDesc); + mControlURL = getControlURL(mSessionDesc); + if (mSessionDesc->countTracks() < 2) { // There's no actual tracks in this session. // The first "track" is merely session meta @@ -708,7 +733,7 @@ struct MyHandler : public AHandler { postKeepAlive(); AString request = "PLAY "; - request.append(mSessionURL); + request.append(mControlURL); request.append(" RTSP/1.0\r\n"); request.append("Session: "); @@ -996,7 +1021,7 @@ struct MyHandler : public AHandler { mPausing = true; AString request = "PAUSE "; - request.append(mSessionURL); + request.append(mControlURL); request.append(" RTSP/1.0\r\n"); request.append("Session: "); @@ -1032,7 +1057,7 @@ struct MyHandler : public AHandler { break; } AString request = "PLAY "; - request.append(mSessionURL); + request.append(mControlURL); request.append(" RTSP/1.0\r\n"); request.append("Session: "); @@ -1110,7 +1135,7 @@ struct MyHandler : public AHandler { break; } AString request = "PAUSE "; - request.append(mSessionURL); + request.append(mControlURL); request.append(" RTSP/1.0\r\n"); request.append("Session: "); @@ -1142,7 +1167,7 @@ struct MyHandler : public AHandler { CHECK(msg->findInt64("time", &timeUs)); AString request = "PLAY "; - request.append(mSessionURL); + request.append(mControlURL); request.append(" RTSP/1.0\r\n"); request.append("Session: "); @@ -1424,6 +1449,7 @@ private: AString mSessionURL; AString mSessionHost; AString mBaseURL; + AString mControlURL; AString mSessionID; bool mSetupTracksSuccessful; bool mSeekPending; -- cgit v1.1 From ba021d15cf7bc964bc813688e33d34845bfd89ea Mon Sep 17 00:00:00 2001 From: joakim johansson Date: Wed, 23 Jan 2013 17:18:56 +0100 Subject: EOS fixes for RTSP streams The fix takes care of several near end of stream use cases: seek, pause and fake timestamps. Change-Id: I5f5fa881b1f619dfd5e1afd2af957082345c59eb --- media/libstagefright/rtsp/MyHandler.h | 88 ++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 2dde422..8f86f3b 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -129,6 +129,7 @@ struct MyHandler : public AHandler { mNumAccessUnitsReceived(0), mCheckPending(false), mCheckGeneration(0), + mCheckTimeoutGeneration(0), mTryTCPInterleaving(false), mTryFakeRTCP(false), mReceivedFirstRTCPPacket(false), @@ -771,6 +772,8 @@ struct MyHandler : public AHandler { parsePlayResponse(response); sp timeout = new AMessage('tiou', id()); + mCheckTimeoutGeneration++; + timeout->setInt32("tioucheck", mCheckTimeoutGeneration); timeout->post(kStartupTimeoutUs); } } @@ -982,7 +985,16 @@ struct MyHandler : public AHandler { int32_t eos; if (msg->findInt32("eos", &eos)) { ALOGI("received BYE on track index %d", trackIndex); - postQueueEOS(trackIndex, ERROR_END_OF_STREAM); + if (!mAllTracksHaveTime && dataReceivedOnAllChannels()) { + ALOGI("No time established => fake existing data"); + + track->mEOSReceived = true; + mTryFakeRTCP = true; + mReceivedFirstRTCPPacket = true; + fakeTimestamps(); + } else { + postQueueEOS(trackIndex, ERROR_END_OF_STREAM); + } return; } @@ -1039,6 +1051,7 @@ struct MyHandler : public AHandler { { int32_t result; CHECK(msg->findInt32("result", &result)); + mCheckTimeoutGeneration++; ALOGI("PAUSE completed with result %d (%s)", result, strerror(-result)); @@ -1092,6 +1105,13 @@ struct MyHandler : public AHandler { result = UNKNOWN_ERROR; } else { parsePlayResponse(response); + + // Post new timeout in order to make sure to use + // fake timestamps if no new Sender Reports arrive + sp timeout = new AMessage('tiou', id()); + mCheckTimeoutGeneration++; + timeout->setInt32("tioucheck", mCheckTimeoutGeneration); + timeout->post(kStartupTimeoutUs); } } @@ -1155,6 +1175,7 @@ struct MyHandler : public AHandler { TrackInfo *info = &mTracks.editItemAt(i); postQueueSeekDiscontinuity(i); + info->mEOSReceived = false; info->mRTPAnchor = 0; info->mNTPAnchorUs = -1; @@ -1163,6 +1184,13 @@ struct MyHandler : public AHandler { mAllTracksHaveTime = false; mNTPAnchorUs = -1; + // Start new timeoutgeneration to avoid getting timeout + // before PLAY response arrive + sp timeout = new AMessage('tiou', id()); + mCheckTimeoutGeneration++; + timeout->setInt32("tioucheck", mCheckTimeoutGeneration); + timeout->post(kStartupTimeoutUs); + int64_t timeUs; CHECK(msg->findInt64("time", &timeUs)); @@ -1212,6 +1240,13 @@ struct MyHandler : public AHandler { } else { parsePlayResponse(response); + // Post new timeout in order to make sure to use + // fake timestamps if no new Sender Reports arrive + sp timeout = new AMessage('tiou', id()); + mCheckTimeoutGeneration++; + timeout->setInt32("tioucheck", mCheckTimeoutGeneration); + timeout->post(kStartupTimeoutUs); + ssize_t i = response->mHeaders.indexOfKey("rtp-info"); CHECK_GE(i, 0); @@ -1249,8 +1284,17 @@ struct MyHandler : public AHandler { case 'tiou': { + int32_t timeoutGenerationCheck; + CHECK(msg->findInt32("tioucheck", &timeoutGenerationCheck)); + if (timeoutGenerationCheck != mCheckTimeoutGeneration) { + // This is an outdated message. Ignore. + // This typically happens if a lot of seeks are + // performed, since new timeout messages now are + // posted at seek as well. + break; + } if (!mReceivedFirstRTCPPacket) { - if (mReceivedFirstRTPPacket && !mTryFakeRTCP) { + if (dataReceivedOnAllChannels() && !mTryFakeRTCP) { ALOGW("We received RTP packets but no RTCP packets, " "using fake timestamps."); @@ -1427,6 +1471,7 @@ private: uint32_t mRTPAnchor; int64_t mNTPAnchorUs; int32_t mTimeScale; + bool mEOSReceived; uint32_t mNormalPlayTimeRTP; int64_t mNormalPlayTimeUs; @@ -1463,6 +1508,7 @@ private: int64_t mNumAccessUnitsReceived; bool mCheckPending; int32_t mCheckGeneration; + int32_t mCheckTimeoutGeneration; bool mTryTCPInterleaving; bool mTryFakeRTCP; bool mReceivedFirstRTCPPacket; @@ -1517,6 +1563,7 @@ private: formatDesc.c_str(), ×cale, &numChannels); info->mTimeScale = timescale; + info->mEOSReceived = false; ALOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str()); @@ -1609,6 +1656,17 @@ private: } } + bool dataReceivedOnAllChannels() { + TrackInfo *track; + for (size_t i = 0; i < mTracks.size(); ++i) { + track = &mTracks.editItemAt(i); + if (track->mPackets.empty()) { + return false; + } + } + return true; + } + void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) { ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx", trackIndex, rtpTime, ntpTime); @@ -1639,6 +1697,27 @@ private: ALOGI("Time now established for all tracks."); } } + if (mAllTracksHaveTime && dataReceivedOnAllChannels()) { + // Time is now established, lets start timestamping immediately + for (size_t i = 0; i < mTracks.size(); ++i) { + TrackInfo *trackInfo = &mTracks.editItemAt(i); + while (!trackInfo->mPackets.empty()) { + sp accessUnit = *trackInfo->mPackets.begin(); + trackInfo->mPackets.erase(trackInfo->mPackets.begin()); + + if (addMediaTimestamp(i, trackInfo, accessUnit)) { + postQueueAccessUnit(i, accessUnit); + } + } + } + for (size_t i = 0; i < mTracks.size(); ++i) { + TrackInfo *trackInfo = &mTracks.editItemAt(i); + if (trackInfo->mEOSReceived) { + postQueueEOS(i, ERROR_END_OF_STREAM); + trackInfo->mEOSReceived = false; + } + } + } } void onAccessUnitComplete( @@ -1683,6 +1762,11 @@ private: if (addMediaTimestamp(trackIndex, track, accessUnit)) { postQueueAccessUnit(trackIndex, accessUnit); } + + if (track->mEOSReceived) { + postQueueEOS(trackIndex, ERROR_END_OF_STREAM); + track->mEOSReceived = false; + } } bool addMediaTimestamp( -- cgit v1.1 From a0dd006834f4a424b67773ab6724e961a61de923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20J=C3=B6nsson?= Date: Wed, 23 Jan 2013 18:18:08 +0100 Subject: Avoid rebuffering after RTSP pause If pausing an RTSP stream, an RTSP Pause request is sent and then if the stream is immediately resumed again, an RTSP Play request will be sent to the server. But the new data after the pause will not be buffered until Sender Reports have arrived again on both channels. Meanwhile the player will resume playback and continue consuming the already existing buffer. This means that there is a risk that the buffer is emptied while waiting for sender reports. This commit simply adds a delay before the RTSP pause request is sent, allowing some additional RTSP buffering that might be needed when the stream is resumed again. Also, if the stream is resumed again before the RTSP pause request is sent, there is no need for any RTSP pause request, hence it is omitted. Change-Id: I928c8bfb5e99a6a146dcda4e51e528973ecbe065 --- media/libstagefright/rtsp/MyHandler.h | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 8f86f3b..5d760d3 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -52,6 +52,8 @@ static int64_t kStartupTimeoutUs = 10000000ll; static int64_t kDefaultKeepAliveTimeoutUs = 60000000ll; +static int64_t kPauseDelayUs = 3000000ll; + namespace android { static void MakeUserAgentString(AString *s) { @@ -137,7 +139,8 @@ struct MyHandler : public AHandler { mSeekable(true), mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs), mKeepAliveGeneration(0), - mPausing(false) { + mPausing(false), + mPauseGeneration(0) { mNetLooper->setName("rtsp net"); mNetLooper->start(false /* runOnCallingThread */, false /* canCallJava */, @@ -215,6 +218,7 @@ struct MyHandler : public AHandler { void seek(int64_t timeUs) { sp msg = new AMessage('seek', id()); msg->setInt64("time", timeUs); + mPauseGeneration++; msg->post(); } @@ -224,11 +228,14 @@ struct MyHandler : public AHandler { void pause() { sp msg = new AMessage('paus', id()); - msg->post(); + mPauseGeneration++; + msg->setInt32("pausecheck", mPauseGeneration); + msg->post(kPauseDelayUs); } void resume() { sp msg = new AMessage('resu', id()); + mPauseGeneration++; msg->post(); } @@ -1024,6 +1031,13 @@ struct MyHandler : public AHandler { case 'paus': { + int32_t generation; + CHECK(msg->findInt32("pausecheck", &generation)); + if (generation != mPauseGeneration) { + ALOGV("Ignoring outdated pause message."); + break; + } + if (!mSeekable) { ALOGW("This is a live stream, ignoring pause request."); break; @@ -1517,6 +1531,7 @@ private: int64_t mKeepAliveTimeoutUs; int32_t mKeepAliveGeneration; bool mPausing; + int32_t mPauseGeneration; Vector mTracks; -- cgit v1.1 From 6c6bb9873f55853fe74d8f45ad3ae116636d8be7 Mon Sep 17 00:00:00 2001 From: Kunter Gultekin Date: Fri, 1 Feb 2013 17:01:15 +0200 Subject: Adds VPX encoding support for stagefright. Only following encoder settings are available - target bitrate - rate control (constant / variable) - frame rate - token partitioning - error resilience - reconstruction & loop filters Only following color formats are recognized - YUV420Planar - YUV420SemiPlanar - AndroidOpaque Following settings are not configurable by the client - encoding deadline is realtime - the algorithm interface for encoder is vp8 - fractional bits of frame rate is discarded - timebase is fixed to 1/1000000 Requires libvpx to be built with encoder support enabled. Requires openmax 1.1.2 extension headers. Relevant tests exist in cts repo. Change-Id: If759edb8db36acbd24dcb53d159a54e942766020 Signed-off-by: Kunter Gultekin --- media/libstagefright/codecs/on2/enc/Android.mk | 24 + .../codecs/on2/enc/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/on2/enc/NOTICE | 190 ++++++ .../codecs/on2/enc/SoftVPXEncoder.cpp | 685 +++++++++++++++++++++ .../libstagefright/codecs/on2/enc/SoftVPXEncoder.h | 203 ++++++ 5 files changed, 1102 insertions(+) create mode 100644 media/libstagefright/codecs/on2/enc/Android.mk create mode 100644 media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/on2/enc/NOTICE create mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp create mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h (limited to 'media') diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk new file mode 100644 index 0000000..5d3317c --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/Android.mk @@ -0,0 +1,24 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftVPXEncoder.cpp + +LOCAL_C_INCLUDES := \ + $(TOP)/external/libvpx/libvpx \ + $(TOP)/external/openssl/include \ + $(TOP)/external/libvpx/libvpx/vpx_codec \ + $(TOP)/external/libvpx/libvpx/vpx_ports \ + frameworks/av/media/libstagefright/include \ + frameworks/native/include/media/openmax \ + +LOCAL_STATIC_LIBRARIES := \ + libvpx + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils \ + +LOCAL_MODULE := libstagefright_soft_vpxenc +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/on2/enc/NOTICE b/media/libstagefright/codecs/on2/enc/NOTICE new file mode 100644 index 0000000..faed58a --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2013, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp new file mode 100644 index 0000000..cc38dc3 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -0,0 +1,685 @@ +/* + * Copyright (C) 2013 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 "SoftVPXEncoder" +#include "SoftVPXEncoder.h" + +#include + +#include +#include + +namespace android { + + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + // OMX IL 1.1.2 + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 1; + params->nVersion.s.nRevision = 2; + params->nVersion.s.nStep = 0; +} + + +static int GetCPUCoreCount() { + int cpuCoreCount = 1; +#if defined(_SC_NPROCESSORS_ONLN) + cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); +#else + // _SC_NPROC_ONLN must be defined... + cpuCoreCount = sysconf(_SC_NPROC_ONLN); +#endif + CHECK_GE(cpuCoreCount, 1); + return cpuCoreCount; +} + + +// This color conversion utility is copied from SoftMPEG4Encoder.cpp +inline static void ConvertSemiPlanarToPlanar(uint8_t *inyuv, + uint8_t* outyuv, + int32_t width, + int32_t height) { + int32_t outYsize = width * height; + uint32_t *outy = (uint32_t *) outyuv; + uint16_t *outcb = (uint16_t *) (outyuv + outYsize); + uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2)); + + /* Y copying */ + memcpy(outy, inyuv, outYsize); + + /* U & V copying */ + uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize); + for (int32_t i = height >> 1; i > 0; --i) { + for (int32_t j = width >> 2; j > 0; --j) { + uint32_t temp = *inyuv_4++; + uint32_t tempU = temp & 0xFF; + tempU = tempU | ((temp >> 8) & 0xFF00); + + uint32_t tempV = (temp >> 8) & 0xFF; + tempV = tempV | ((temp >> 16) & 0xFF00); + + // Flip U and V + *outcb++ = tempV; + *outcr++ = tempU; + } + } +} + + +SoftVPXEncoder::SoftVPXEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mCodecContext(NULL), + mCodecConfiguration(NULL), + mCodecInterface(NULL), + mWidth(176), + mHeight(144), + mBitrate(192000), // in bps + mBitrateControlMode(VPX_VBR), // variable bitrate + mFrameDurationUs(33333), // Defaults to 30 fps + mDCTPartitions(0), + mErrorResilience(OMX_FALSE), + mColorFormat(OMX_COLOR_FormatYUV420Planar), + mLevel(OMX_VIDEO_VP8Level_Version0), + mConversionBuffer(NULL) { + + initPorts(); +} + + +SoftVPXEncoder::~SoftVPXEncoder() { + releaseEncoder(); +} + + +void SoftVPXEncoder::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE inputPort; + OMX_PARAM_PORTDEFINITIONTYPE outputPort; + + InitOMXParams(&inputPort); + InitOMXParams(&outputPort); + + inputPort.nBufferCountMin = kNumBuffers; + inputPort.nBufferCountActual = inputPort.nBufferCountMin; + inputPort.bEnabled = OMX_TRUE; + inputPort.bPopulated = OMX_FALSE; + inputPort.eDomain = OMX_PortDomainVideo; + inputPort.bBuffersContiguous = OMX_FALSE; + inputPort.format.video.pNativeRender = NULL; + inputPort.format.video.nFrameWidth = mWidth; + inputPort.format.video.nFrameHeight = mHeight; + inputPort.format.video.nStride = inputPort.format.video.nFrameWidth; + inputPort.format.video.nSliceHeight = inputPort.format.video.nFrameHeight; + inputPort.format.video.nBitrate = 0; + // frameRate is reciprocal of frameDuration, which is + // in microseconds. It is also in Q16 format. + inputPort.format.video.xFramerate = (1000000/mFrameDurationUs) << 16; + inputPort.format.video.bFlagErrorConcealment = OMX_FALSE; + inputPort.nPortIndex = kInputPortIndex; + inputPort.eDir = OMX_DirInput; + inputPort.nBufferAlignment = kInputBufferAlignment; + inputPort.format.video.cMIMEType = + const_cast(MEDIA_MIMETYPE_VIDEO_RAW); + inputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + inputPort.format.video.eColorFormat = mColorFormat; + inputPort.format.video.pNativeWindow = NULL; + inputPort.nBufferSize = + (inputPort.format.video.nStride * + inputPort.format.video.nSliceHeight * 3) / 2; + + addPort(inputPort); + + outputPort.nBufferCountMin = kNumBuffers; + outputPort.nBufferCountActual = outputPort.nBufferCountMin; + outputPort.bEnabled = OMX_TRUE; + outputPort.bPopulated = OMX_FALSE; + outputPort.eDomain = OMX_PortDomainVideo; + outputPort.bBuffersContiguous = OMX_FALSE; + outputPort.format.video.pNativeRender = NULL; + outputPort.format.video.nFrameWidth = mWidth; + outputPort.format.video.nFrameHeight = mHeight; + outputPort.format.video.nStride = outputPort.format.video.nFrameWidth; + outputPort.format.video.nSliceHeight = outputPort.format.video.nFrameHeight; + outputPort.format.video.nBitrate = mBitrate; + outputPort.format.video.xFramerate = 0; + outputPort.format.video.bFlagErrorConcealment = OMX_FALSE; + outputPort.nPortIndex = kOutputPortIndex; + outputPort.eDir = OMX_DirOutput; + outputPort.nBufferAlignment = kOutputBufferAlignment; + outputPort.format.video.cMIMEType = + const_cast(MEDIA_MIMETYPE_VIDEO_VPX); + outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX; + outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused; + outputPort.format.video.pNativeWindow = NULL; + outputPort.nBufferSize = 256 * 1024; // arbitrary + + addPort(outputPort); +} + + +status_t SoftVPXEncoder::initEncoder() { + vpx_codec_err_t codec_return; + + mCodecContext = new vpx_codec_ctx_t; + mCodecConfiguration = new vpx_codec_enc_cfg_t; + mCodecInterface = vpx_codec_vp8_cx(); + + if (mCodecInterface == NULL) { + return UNKNOWN_ERROR; + } + + codec_return = vpx_codec_enc_config_default(mCodecInterface, + mCodecConfiguration, + 0); // Codec specific flags + + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error populating default configuration for vpx encoder."); + return UNKNOWN_ERROR; + } + + mCodecConfiguration->g_w = mWidth; + mCodecConfiguration->g_h = mHeight; + mCodecConfiguration->g_threads = GetCPUCoreCount(); + mCodecConfiguration->g_error_resilient = mErrorResilience; + + switch (mLevel) { + case OMX_VIDEO_VP8Level_Version0: + mCodecConfiguration->g_profile = 0; + break; + + case OMX_VIDEO_VP8Level_Version1: + mCodecConfiguration->g_profile = 1; + break; + + case OMX_VIDEO_VP8Level_Version2: + mCodecConfiguration->g_profile = 2; + break; + + case OMX_VIDEO_VP8Level_Version3: + mCodecConfiguration->g_profile = 3; + break; + + default: + mCodecConfiguration->g_profile = 0; + } + + // OMX timebase unit is microsecond + // g_timebase is in seconds (i.e. 1/1000000 seconds) + mCodecConfiguration->g_timebase.num = 1; + mCodecConfiguration->g_timebase.den = 1000000; + // rc_target_bitrate is in kbps, mBitrate in bps + mCodecConfiguration->rc_target_bitrate = mBitrate/1000; + mCodecConfiguration->rc_end_usage = mBitrateControlMode; + + codec_return = vpx_codec_enc_init(mCodecContext, + mCodecInterface, + mCodecConfiguration, + 0); // flags + + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error initializing vpx encoder"); + return UNKNOWN_ERROR; + } + + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_TOKEN_PARTITIONS, + mDCTPartitions); + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error setting dct partitions for vpx encoder."); + return UNKNOWN_ERROR; + } + + if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + if (mConversionBuffer == NULL) { + mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2); + if (mConversionBuffer == NULL) { + ALOGE("Allocating conversion buffer failed."); + return UNKNOWN_ERROR; + } + } + } + return OK; +} + + +status_t SoftVPXEncoder::releaseEncoder() { + if (mCodecContext != NULL) { + vpx_codec_destroy(mCodecContext); + delete mCodecContext; + mCodecContext = NULL; + } + + if (mCodecConfiguration != NULL) { + delete mCodecConfiguration; + mCodecConfiguration = NULL; + } + + if (mConversionBuffer != NULL) { + delete mConversionBuffer; + mConversionBuffer = NULL; + } + + // this one is not allocated by us + mCodecInterface = NULL; + + return OK; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, + OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoPortFormat: { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; + + if (formatParams->nPortIndex == kInputPortIndex) { + if (formatParams->nIndex >= kNumberOfSupportedColorFormats) { + return OMX_ErrorNoMore; + } + + // Color formats, in order of preference + if (formatParams->nIndex == 0) { + formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; + } else if (formatParams->nIndex == 1) { + formatParams->eColorFormat = + OMX_COLOR_FormatYUV420SemiPlanar; + } else { + formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque; + } + + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + // Converting from microseconds + // Also converting to Q16 format + formatParams->xFramerate = (1000000/mFrameDurationUs) << 16; + return OMX_ErrorNone; + } else if (formatParams->nPortIndex == kOutputPortIndex) { + formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX; + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + formatParams->xFramerate = 0; + return OMX_ErrorNone; + } else { + return OMX_ErrorBadPortIndex; + } + } + + case OMX_IndexParamVideoBitrate: { + OMX_VIDEO_PARAM_BITRATETYPE *bitrate = + (OMX_VIDEO_PARAM_BITRATETYPE *)param; + + if (bitrate->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + bitrate->nTargetBitrate = mBitrate; + + if (mBitrateControlMode == VPX_VBR) { + bitrate->eControlRate = OMX_Video_ControlRateVariable; + } else if (mBitrateControlMode == VPX_CBR) { + bitrate->eControlRate = OMX_Video_ControlRateConstant; + } else { + return OMX_ErrorUnsupportedSetting; + } + return OMX_ErrorNone; + } + + // VP8 specific parameters that use extension headers + case OMX_IndexParamVideoVp8: { + OMX_VIDEO_PARAM_VP8TYPE *vp8Params = + (OMX_VIDEO_PARAM_VP8TYPE *)param; + + if (vp8Params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + vp8Params->eProfile = OMX_VIDEO_VP8ProfileMain; + vp8Params->eLevel = mLevel; + vp8Params->nDCTPartitions = mDCTPartitions; + vp8Params->bErrorResilientMode = mErrorResilience; + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoProfileLevelQuerySupported: { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; + + if (profileAndLevel->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + switch (profileAndLevel->nProfileIndex) { + case 0: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version0; + break; + + case 1: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version1; + break; + + case 2: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version2; + break; + + case 3: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version3; + break; + + default: + return OMX_ErrorNoMore; + } + + profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoProfileLevelCurrent: { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; + + if (profileAndLevel->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + profileAndLevel->eLevel = mLevel; + profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, param); + } +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, + const OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamStandardComponentRole: + return internalSetRoleParams( + (const OMX_PARAM_COMPONENTROLETYPE *)param); + + case OMX_IndexParamVideoBitrate: + return internalSetBitrateParams( + (const OMX_VIDEO_PARAM_BITRATETYPE *)param); + + case OMX_IndexParamPortDefinition: + return internalSetPortParams( + (const OMX_PARAM_PORTDEFINITIONTYPE *)param); + + case OMX_IndexParamVideoPortFormat: + return internalSetFormatParams( + (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param); + + case OMX_IndexParamVideoVp8: + return internalSetVp8Params( + (const OMX_VIDEO_PARAM_VP8TYPE *)param); + + case OMX_IndexParamVideoProfileLevelCurrent: + return internalSetProfileLevel( + (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param); + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVPXEncoder::internalSetProfileLevel( + const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel) { + if (profileAndLevel->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + if (profileAndLevel->eProfile != OMX_VIDEO_VP8ProfileMain) { + return OMX_ErrorBadParameter; + } + + if (profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version0 || + profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version1 || + profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version2 || + profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version3) { + mLevel = (OMX_VIDEO_VP8LEVELTYPE)profileAndLevel->eLevel; + } else { + return OMX_ErrorBadParameter; + } + + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetVp8Params( + const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) { + if (vp8Params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + if (vp8Params->eProfile != OMX_VIDEO_VP8ProfileMain) { + return OMX_ErrorBadParameter; + } + + if (vp8Params->eLevel == OMX_VIDEO_VP8Level_Version0 || + vp8Params->eLevel == OMX_VIDEO_VP8Level_Version1 || + vp8Params->eLevel == OMX_VIDEO_VP8Level_Version2 || + vp8Params->eLevel == OMX_VIDEO_VP8Level_Version3) { + mLevel = vp8Params->eLevel; + } else { + return OMX_ErrorBadParameter; + } + + if (vp8Params->nDCTPartitions <= kMaxDCTPartitions) { + mDCTPartitions = vp8Params->nDCTPartitions; + } else { + return OMX_ErrorBadParameter; + } + + mErrorResilience = vp8Params->bErrorResilientMode; + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams( + const OMX_VIDEO_PARAM_PORTFORMATTYPE* format) { + if (format->nPortIndex == kInputPortIndex) { + if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar || + format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || + format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) { + mColorFormat = format->eColorFormat; + return OMX_ErrorNone; + } else { + ALOGE("Unsupported color format %i", format->eColorFormat); + return OMX_ErrorUnsupportedSetting; + } + } else if (format->nPortIndex == kOutputPortIndex) { + if (format->eCompressionFormat == OMX_VIDEO_CodingVPX) { + return OMX_ErrorNone; + } else { + return OMX_ErrorUnsupportedSetting; + } + } else { + return OMX_ErrorBadPortIndex; + } +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetRoleParams( + const OMX_PARAM_COMPONENTROLETYPE* role) { + const char* roleText = (const char*)role->cRole; + const size_t roleTextMaxSize = OMX_MAX_STRINGNAME_SIZE - 1; + + if (strncmp(roleText, "video_encoder.vpx", roleTextMaxSize)) { + ALOGE("Unsupported component role"); + return OMX_ErrorBadParameter; + } + + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( + const OMX_PARAM_PORTDEFINITIONTYPE* port) { + if (port->nPortIndex == kInputPortIndex) { + mWidth = port->format.video.nFrameWidth; + mHeight = port->format.video.nFrameHeight; + + // xFramerate comes in Q16 format, in frames per second unit + const uint32_t framerate = port->format.video.xFramerate >> 16; + // frame duration is in microseconds + mFrameDurationUs = (1000000/framerate); + + if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar || + port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || + port->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) { + mColorFormat = port->format.video.eColorFormat; + } else { + return OMX_ErrorUnsupportedSetting; + } + + return OMX_ErrorNone; + } else if (port->nPortIndex == kOutputPortIndex) { + mBitrate = port->format.video.nBitrate; + return OMX_ErrorNone; + } else { + return OMX_ErrorBadPortIndex; + } +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetBitrateParams( + const OMX_VIDEO_PARAM_BITRATETYPE* bitrate) { + if (bitrate->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + mBitrate = bitrate->nTargetBitrate; + + if (bitrate->eControlRate == OMX_Video_ControlRateVariable) { + mBitrateControlMode = VPX_VBR; + } else if (bitrate->eControlRate == OMX_Video_ControlRateConstant) { + mBitrateControlMode = VPX_CBR; + } else { + return OMX_ErrorUnsupportedSetting; + } + + return OMX_ErrorNone; +} + + +void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { + // Initialize encoder if not already + if (mCodecContext == NULL) { + if (OK != initEncoder()) { + ALOGE("Failed to initialize encoder"); + notify(OMX_EventError, + OMX_ErrorUndefined, + 0, // Extra notification data + NULL); // Notification data pointer + return; + } + } + + vpx_codec_err_t codec_return; + List &inputBufferInfoQueue = getPortQueue(kInputPortIndex); + List &outputBufferInfoQueue = getPortQueue(kOutputPortIndex); + + while (!inputBufferInfoQueue.empty() && !outputBufferInfoQueue.empty()) { + BufferInfo *inputBufferInfo = *inputBufferInfoQueue.begin(); + OMX_BUFFERHEADERTYPE *inputBufferHeader = inputBufferInfo->mHeader; + + BufferInfo *outputBufferInfo = *outputBufferInfoQueue.begin(); + OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader; + + if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inputBufferInfoQueue.erase(inputBufferInfoQueue.begin()); + inputBufferInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inputBufferHeader); + + outputBufferHeader->nFilledLen = 0; + outputBufferHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outputBufferInfoQueue.erase(outputBufferInfoQueue.begin()); + outputBufferInfo->mOwnedByUs = false; + notifyFillBufferDone(outputBufferHeader); + return; + } + + uint8_t* source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset; + + // NOTE: As much as nothing is known about color format + // when it is denoted as AndroidOpaque, it is at least + // assumed to be planar. + if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + ConvertSemiPlanarToPlanar(source, mConversionBuffer, mWidth, mHeight); + source = mConversionBuffer; + } + vpx_image_t raw_frame; + vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight, + kInputBufferAlignment, source); + codec_return = vpx_codec_encode(mCodecContext, + &raw_frame, + inputBufferHeader->nTimeStamp, // in timebase units + mFrameDurationUs, // frame duration in timebase units + 0, // frame flags + VPX_DL_REALTIME); // encoding deadline + if (codec_return != VPX_CODEC_OK) { + ALOGE("vpx encoder failed to encode frame"); + notify(OMX_EventError, + OMX_ErrorUndefined, + 0, // Extra notification data + NULL); // Notification data pointer + return; + } + + vpx_codec_iter_t encoded_packet_iterator = NULL; + const vpx_codec_cx_pkt_t* encoded_packet; + + while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) { + if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) { + outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts; + outputBufferHeader->nFlags = 0; + outputBufferHeader->nOffset = 0; + outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz; + memcpy(outputBufferHeader->pBuffer, + encoded_packet->data.frame.buf, + encoded_packet->data.frame.sz); + outputBufferInfo->mOwnedByUs = false; + outputBufferInfoQueue.erase(outputBufferInfoQueue.begin()); + notifyFillBufferDone(outputBufferHeader); + } + } + + inputBufferInfo->mOwnedByUs = false; + inputBufferInfoQueue.erase(inputBufferInfoQueue.begin()); + notifyEmptyBufferDone(inputBufferHeader); + } +} +} // namespace android + + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftVPXEncoder(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h new file mode 100644 index 0000000..3bc05c0 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2013 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 SOFT_VPX_ENCODER_H_ + +#define SOFT_VPX_ENCODER_H_ + +#include "SimpleSoftOMXComponent.h" + +#include +#include + +#include "vpx/vpx_encoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8cx.h" + +namespace android { + +// Exposes a vpx encoder as an OMX Component +// +// Boilerplate for callback bindings are taken care +// by the base class SimpleSoftOMXComponent and its +// parent SoftOMXComponent. +// +// Only following encoder settings are available +// - target bitrate +// - rate control (constant / variable) +// - frame rate +// - error resilience +// - token partitioning +// - reconstruction & loop filters (g_profile) +// +// Only following color formats are recognized +// - YUV420Planar +// - YUV420SemiPlanar +// - AndroidOpaque +// +// Following settings are not configurable by the client +// - encoding deadline is realtime +// - multithreaded encoding utilizes a number of threads equal +// to online cpu's available +// - the algorithm interface for encoder is vp8 +// - fractional bits of frame rate is discarded +// - OMX timestamps are in microseconds, therefore +// encoder timebase is fixed to 1/1000000 + +class SoftVPXEncoder : public SimpleSoftOMXComponent { + public: + SoftVPXEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + protected: + virtual ~SoftVPXEncoder(); + + // Returns current values for requested OMX + // parameters + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR param); + + // Validates, extracts and stores relevant OMX + // parameters + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR param); + + // OMX callback when buffers available + // Note that both an input and output buffer + // is expected to be available to carry out + // encoding of the frame + virtual void onQueueFilled(OMX_U32 portIndex); + + private: + // number of buffers allocated per port + static const uint32_t kNumBuffers = 4; + + // OMX port indexes that refer to input and + // output ports respectively + static const uint32_t kInputPortIndex = 0; + static const uint32_t kOutputPortIndex = 1; + + // Byte-alignment required for buffers + static const uint32_t kInputBufferAlignment = 1; + static const uint32_t kOutputBufferAlignment = 2; + + // Max value supported for DCT partitions + static const uint32_t kMaxDCTPartitions = 3; + + // Number of supported input color formats + static const uint32_t kNumberOfSupportedColorFormats = 3; + + // vpx specific opaque data structure that + // stores encoder state + vpx_codec_ctx_t* mCodecContext; + + // vpx specific data structure that + // stores encoder configuration + vpx_codec_enc_cfg_t* mCodecConfiguration; + + // vpx specific read-only data structure + // that specifies algorithm interface (e.g. vp8) + vpx_codec_iface_t* mCodecInterface; + + // Width of the input frames + int32_t mWidth; + + // Height of the input frames + int32_t mHeight; + + // Target bitrate set for the encoder, in bits per second. + int32_t mBitrate; + + // Bitrate control mode, either constant or variable + vpx_rc_mode mBitrateControlMode; + + // Frame duration is the reciprocal of framerate, denoted + // in microseconds + uint64_t mFrameDurationUs; + + // vp8 specific configuration parameter + // that enables token partitioning of + // the stream into substreams + int32_t mDCTPartitions; + + // Parameter that denotes whether error resilience + // is enabled in encoder + OMX_BOOL mErrorResilience; + + // Color format for the input port + OMX_COLOR_FORMATTYPE mColorFormat; + + // Encoder profile corresponding to OMX level parameter + // + // The inconsistency in the naming is caused by + // OMX spec referring vpx profiles (g_profile) + // as "levels" whereas using the name "profile" for + // something else. + OMX_VIDEO_VP8LEVELTYPE mLevel; + + // Conversion buffer is needed to convert semi + // planar yuv420 to planar format + // It is only allocated if input format is + // indeed YUV420SemiPlanar. + uint8_t* mConversionBuffer; + + // Initializes input and output OMX ports with sensible + // default values. + void initPorts(); + + // Initializes vpx encoder with available settings. + status_t initEncoder(); + + // Releases vpx encoder instance, with it's associated + // data structures. + // + // Unless called earlier, this is handled by the + // dtor. + status_t releaseEncoder(); + + // Handles port changes with respect to color formats + OMX_ERRORTYPE internalSetFormatParams( + const OMX_VIDEO_PARAM_PORTFORMATTYPE* format); + + // Verifies the component role tried to be set to this OMX component is + // strictly video_encoder.vpx + OMX_ERRORTYPE internalSetRoleParams( + const OMX_PARAM_COMPONENTROLETYPE* role); + + // Updates bitrate to reflect port settings. + OMX_ERRORTYPE internalSetBitrateParams( + const OMX_VIDEO_PARAM_BITRATETYPE* bitrate); + + // Handles port definition changes. + OMX_ERRORTYPE internalSetPortParams( + const OMX_PARAM_PORTDEFINITIONTYPE* port); + + // Handles vp8 specific parameters. + OMX_ERRORTYPE internalSetVp8Params( + const OMX_VIDEO_PARAM_VP8TYPE* vp8Params); + + // Updates encoder profile + OMX_ERRORTYPE internalSetProfileLevel( + const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel); + + DISALLOW_EVIL_CONSTRUCTORS(SoftVPXEncoder); +}; + +} // namespace android + +#endif // SOFT_VPX_ENCODER_H_ -- cgit v1.1 From a2eb22c1de262aa3fa7c356537ac2fe165afdf3d Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 7 Feb 2013 10:56:14 -0800 Subject: Allow for dynamic reconfiguration of the video bitrate used to encode video while running as a wfd source. Change-Id: I44f7b2350c88fc5807047c61bfe594ef8fa79275 --- media/libstagefright/ACodec.cpp | 48 ++++++++++++++++++++++ media/libstagefright/MediaCodec.cpp | 31 ++++++++++++++ .../wifi-display/source/Converter.cpp | 14 +++++++ .../libstagefright/wifi-display/source/Converter.h | 2 + 4 files changed, 95 insertions(+) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 7b27843..a6cc4eb 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -374,6 +374,12 @@ void ACodec::initiateSetup(const sp &msg) { msg->post(); } +void ACodec::signalSetParameters(const sp ¶ms) { + sp msg = new AMessage(kWhatSetParameters, id()); + msg->setMessage("params", params); + msg->post(); +} + void ACodec::initiateAllocateComponent(const sp &msg) { msg->setWhat(kWhatAllocateComponent); msg->setTarget(id()); @@ -3550,6 +3556,23 @@ bool ACodec::ExecutingState::onMessageReceived(const sp &msg) { break; } + case kWhatSetParameters: + { + sp params; + CHECK(msg->findMessage("params", ¶ms)); + + status_t err = mCodec->setParameters(params); + + sp reply; + if (msg->findMessage("reply", &reply)) { + reply->setInt32("err", err); + reply->post(); + } + + handled = true; + break; + } + default: handled = BaseState::onMessageReceived(msg); break; @@ -3558,6 +3581,31 @@ bool ACodec::ExecutingState::onMessageReceived(const sp &msg) { return handled; } +status_t ACodec::setParameters(const sp ¶ms) { + int32_t videoBitrate; + if (params->findInt32("videoBitrate", &videoBitrate)) { + OMX_VIDEO_CONFIG_BITRATETYPE configParams; + InitOMXParams(&configParams); + configParams.nPortIndex = kPortIndexOutput; + configParams.nEncodeBitrate = videoBitrate; + + status_t err = mOMX->setConfig( + mNode, + OMX_IndexConfigVideoBitrate, + &configParams, + sizeof(configParams)); + + if (err != OK) { + ALOGE("setConfig(OMX_IndexConfigVideoBitrate, %d) failed w/ err %d", + videoBitrate, err); + + return err; + } + } + + return OK; +} + bool ACodec::ExecutingState::onOMXEvent( OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { switch (event) { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index cb8a651..77aceb7 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1203,6 +1203,23 @@ void MediaCodec::onMessageReceived(const sp &msg) { break; } + case kWhatSetParameters: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp params; + CHECK(msg->findMessage("params", ¶ms)); + + status_t err = onSetParameters(params); + + sp response = new AMessage; + response->setInt32("err", err); + + response->postReply(replyID); + break; + } + default: TRESPASS(); } @@ -1556,4 +1573,18 @@ void MediaCodec::postActivityNotificationIfPossible() { } } +status_t MediaCodec::setParameters(const sp ¶ms) { + sp msg = new AMessage(kWhatSetParameters, id()); + msg->setMessage("params", params); + + sp response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::onSetParameters(const sp ¶ms) { + mCodec->signalSetParameters(params); + + return OK; +} + } // namespace android diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 5628dec..376b0df 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -54,6 +54,7 @@ Converter::Converter( ,mFirstSilentFrameUs(-1ll) ,mInSilentMode(false) #endif + ,mPrevVideoBitrate(-1) { AString mime; CHECK(mInputFormat->findString("mime", &mime)); @@ -185,6 +186,7 @@ status_t Converter::initEncoder() { int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 128000); int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000); + mPrevVideoBitrate = videoBitrate; ALOGI("using audio bitrate of %d bps, video bitrate of %d bps", audioBitrate, videoBitrate); @@ -606,6 +608,18 @@ status_t Converter::feedEncoderInputBuffers() { } status_t Converter::doMoreWork() { + if (mIsVideo) { + int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000); + if (videoBitrate != mPrevVideoBitrate) { + sp params = new AMessage; + + params->setInt32("videoBitrate", videoBitrate); + mEncoder->setParameters(params); + + mPrevVideoBitrate = videoBitrate; + } + } + status_t err; for (;;) { diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 3357d61..57802bd 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -100,6 +100,8 @@ private: sp mPartialAudioAU; + int32_t mPrevVideoBitrate; + status_t initEncoder(); void releaseEncoder(); -- cgit v1.1 From 26b0a9d007e77e088af9ff3810734728f0558e85 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 7 Feb 2013 11:38:08 -0800 Subject: A few more patches to fix wfd tcp unicast transport. Change-Id: Ie2f1b1e56c487ac4c3ef19d9e79022a35084e042 --- media/libstagefright/wifi-display/sink/DirectRenderer.cpp | 2 +- media/libstagefright/wifi-display/sink/RTPSink.cpp | 14 +++++++++++++- media/libstagefright/wifi-display/sink/RTPSink.h | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index 23cf6fd..d7f169f 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -35,7 +35,7 @@ namespace android { -#if 0 +#if 1 // static const int64_t DirectRenderer::kPacketLostDelayUs = 80000ll; diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp index be54595..3c90a1e 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.cpp +++ b/media/libstagefright/wifi-display/sink/RTPSink.cpp @@ -250,6 +250,8 @@ RTPSink::RTPSink( : mNetSession(netSession), mSurfaceTex(bufferProducer), mNotify(notify), + mUsingTCPTransport(false), + mUsingTCPInterleaving(false), mRTPPort(0), mRTPSessionID(0), mRTCPSessionID(0), @@ -280,6 +282,9 @@ RTPSink::~RTPSink() { } status_t RTPSink::init(bool usingTCPTransport, bool usingTCPInterleaving) { + mUsingTCPTransport = usingTCPTransport; + mUsingTCPInterleaving = usingTCPInterleaving; + if (usingTCPInterleaving) { return OK; } @@ -717,7 +722,9 @@ status_t RTPSink::connect( mRTCPSessionID, buf->data(), buf->size()); #endif - scheduleSendRR(); + if (!mUsingTCPTransport) { + scheduleSendRR(); + } return OK; } @@ -820,6 +827,11 @@ void RTPSink::onSendRR() { } void RTPSink::onPacketLost(const sp &msg) { + if (mUsingTCPTransport) { + ALOGW("huh? lost a packet even though using reliable transport?"); + return; + } + uint32_t srcId; CHECK(msg->findInt32("ssrc", (int32_t *)&srcId)); diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h index f9cbce9..4706c6d 100644 --- a/media/libstagefright/wifi-display/sink/RTPSink.h +++ b/media/libstagefright/wifi-display/sink/RTPSink.h @@ -78,6 +78,9 @@ private: sp mNotify; KeyedVector > mSources; + bool mUsingTCPTransport; + bool mUsingTCPInterleaving; + int32_t mRTPPort; int32_t mRTPSessionID; // in TCP unicast mode these are just server -- cgit v1.1 From 0a694951c00f2135c8968fd2205f71899997a8ad Mon Sep 17 00:00:00 2001 From: Mike Lockwoood Date: Fri, 8 Feb 2013 13:25:01 -0800 Subject: MTP: Write initial data to correct file offset in SendPartialObject Change-Id: I84288aeda3e65e6e6487f11d32a72910cd16cff2 --- media/mtp/MtpServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 662a93d..8568dfc 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -1118,7 +1118,7 @@ MtpResponseCode MtpServer::doSendPartialObject() { int initialData = ret - MTP_CONTAINER_HEADER_SIZE; if (initialData > 0) { - ret = write(edit->mFD, mData.getData(), initialData); + ret = pwrite(edit->mFD, mData.getData(), initialData, offset); offset += initialData; length -= initialData; } -- cgit v1.1 From 513b8b238caa52f8ddf5c85109dbf362c515185f Mon Sep 17 00:00:00 2001 From: James Dong Date: Fri, 8 Feb 2013 18:16:02 -0800 Subject: Revert "Adds VPX encoding support for stagefright." This lib was not part of the build, but it is built anyway for userdebug image (not for eng though). let me revert it for now... This reverts commit 6c6bb9873f55853fe74d8f45ad3ae116636d8be7. --- media/libstagefright/codecs/on2/enc/Android.mk | 24 - .../codecs/on2/enc/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/on2/enc/NOTICE | 190 ------ .../codecs/on2/enc/SoftVPXEncoder.cpp | 685 --------------------- .../libstagefright/codecs/on2/enc/SoftVPXEncoder.h | 203 ------ 5 files changed, 1102 deletions(-) delete mode 100644 media/libstagefright/codecs/on2/enc/Android.mk delete mode 100644 media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 delete mode 100644 media/libstagefright/codecs/on2/enc/NOTICE delete mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp delete mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h (limited to 'media') diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk deleted file mode 100644 index 5d3317c..0000000 --- a/media/libstagefright/codecs/on2/enc/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - SoftVPXEncoder.cpp - -LOCAL_C_INCLUDES := \ - $(TOP)/external/libvpx/libvpx \ - $(TOP)/external/openssl/include \ - $(TOP)/external/libvpx/libvpx/vpx_codec \ - $(TOP)/external/libvpx/libvpx/vpx_ports \ - frameworks/av/media/libstagefright/include \ - frameworks/native/include/media/openmax \ - -LOCAL_STATIC_LIBRARIES := \ - libvpx - -LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils \ - -LOCAL_MODULE := libstagefright_soft_vpxenc -LOCAL_MODULE_TAGS := optional - -include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 diff --git a/media/libstagefright/codecs/on2/enc/NOTICE b/media/libstagefright/codecs/on2/enc/NOTICE deleted file mode 100644 index faed58a..0000000 --- a/media/libstagefright/codecs/on2/enc/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2013, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp deleted file mode 100644 index cc38dc3..0000000 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ /dev/null @@ -1,685 +0,0 @@ -/* - * Copyright (C) 2013 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 "SoftVPXEncoder" -#include "SoftVPXEncoder.h" - -#include - -#include -#include - -namespace android { - - -template -static void InitOMXParams(T *params) { - params->nSize = sizeof(T); - // OMX IL 1.1.2 - params->nVersion.s.nVersionMajor = 1; - params->nVersion.s.nVersionMinor = 1; - params->nVersion.s.nRevision = 2; - params->nVersion.s.nStep = 0; -} - - -static int GetCPUCoreCount() { - int cpuCoreCount = 1; -#if defined(_SC_NPROCESSORS_ONLN) - cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); -#else - // _SC_NPROC_ONLN must be defined... - cpuCoreCount = sysconf(_SC_NPROC_ONLN); -#endif - CHECK_GE(cpuCoreCount, 1); - return cpuCoreCount; -} - - -// This color conversion utility is copied from SoftMPEG4Encoder.cpp -inline static void ConvertSemiPlanarToPlanar(uint8_t *inyuv, - uint8_t* outyuv, - int32_t width, - int32_t height) { - int32_t outYsize = width * height; - uint32_t *outy = (uint32_t *) outyuv; - uint16_t *outcb = (uint16_t *) (outyuv + outYsize); - uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2)); - - /* Y copying */ - memcpy(outy, inyuv, outYsize); - - /* U & V copying */ - uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize); - for (int32_t i = height >> 1; i > 0; --i) { - for (int32_t j = width >> 2; j > 0; --j) { - uint32_t temp = *inyuv_4++; - uint32_t tempU = temp & 0xFF; - tempU = tempU | ((temp >> 8) & 0xFF00); - - uint32_t tempV = (temp >> 8) & 0xFF; - tempV = tempV | ((temp >> 16) & 0xFF00); - - // Flip U and V - *outcb++ = tempV; - *outcr++ = tempU; - } - } -} - - -SoftVPXEncoder::SoftVPXEncoder(const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component) - : SimpleSoftOMXComponent(name, callbacks, appData, component), - mCodecContext(NULL), - mCodecConfiguration(NULL), - mCodecInterface(NULL), - mWidth(176), - mHeight(144), - mBitrate(192000), // in bps - mBitrateControlMode(VPX_VBR), // variable bitrate - mFrameDurationUs(33333), // Defaults to 30 fps - mDCTPartitions(0), - mErrorResilience(OMX_FALSE), - mColorFormat(OMX_COLOR_FormatYUV420Planar), - mLevel(OMX_VIDEO_VP8Level_Version0), - mConversionBuffer(NULL) { - - initPorts(); -} - - -SoftVPXEncoder::~SoftVPXEncoder() { - releaseEncoder(); -} - - -void SoftVPXEncoder::initPorts() { - OMX_PARAM_PORTDEFINITIONTYPE inputPort; - OMX_PARAM_PORTDEFINITIONTYPE outputPort; - - InitOMXParams(&inputPort); - InitOMXParams(&outputPort); - - inputPort.nBufferCountMin = kNumBuffers; - inputPort.nBufferCountActual = inputPort.nBufferCountMin; - inputPort.bEnabled = OMX_TRUE; - inputPort.bPopulated = OMX_FALSE; - inputPort.eDomain = OMX_PortDomainVideo; - inputPort.bBuffersContiguous = OMX_FALSE; - inputPort.format.video.pNativeRender = NULL; - inputPort.format.video.nFrameWidth = mWidth; - inputPort.format.video.nFrameHeight = mHeight; - inputPort.format.video.nStride = inputPort.format.video.nFrameWidth; - inputPort.format.video.nSliceHeight = inputPort.format.video.nFrameHeight; - inputPort.format.video.nBitrate = 0; - // frameRate is reciprocal of frameDuration, which is - // in microseconds. It is also in Q16 format. - inputPort.format.video.xFramerate = (1000000/mFrameDurationUs) << 16; - inputPort.format.video.bFlagErrorConcealment = OMX_FALSE; - inputPort.nPortIndex = kInputPortIndex; - inputPort.eDir = OMX_DirInput; - inputPort.nBufferAlignment = kInputBufferAlignment; - inputPort.format.video.cMIMEType = - const_cast(MEDIA_MIMETYPE_VIDEO_RAW); - inputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; - inputPort.format.video.eColorFormat = mColorFormat; - inputPort.format.video.pNativeWindow = NULL; - inputPort.nBufferSize = - (inputPort.format.video.nStride * - inputPort.format.video.nSliceHeight * 3) / 2; - - addPort(inputPort); - - outputPort.nBufferCountMin = kNumBuffers; - outputPort.nBufferCountActual = outputPort.nBufferCountMin; - outputPort.bEnabled = OMX_TRUE; - outputPort.bPopulated = OMX_FALSE; - outputPort.eDomain = OMX_PortDomainVideo; - outputPort.bBuffersContiguous = OMX_FALSE; - outputPort.format.video.pNativeRender = NULL; - outputPort.format.video.nFrameWidth = mWidth; - outputPort.format.video.nFrameHeight = mHeight; - outputPort.format.video.nStride = outputPort.format.video.nFrameWidth; - outputPort.format.video.nSliceHeight = outputPort.format.video.nFrameHeight; - outputPort.format.video.nBitrate = mBitrate; - outputPort.format.video.xFramerate = 0; - outputPort.format.video.bFlagErrorConcealment = OMX_FALSE; - outputPort.nPortIndex = kOutputPortIndex; - outputPort.eDir = OMX_DirOutput; - outputPort.nBufferAlignment = kOutputBufferAlignment; - outputPort.format.video.cMIMEType = - const_cast(MEDIA_MIMETYPE_VIDEO_VPX); - outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX; - outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused; - outputPort.format.video.pNativeWindow = NULL; - outputPort.nBufferSize = 256 * 1024; // arbitrary - - addPort(outputPort); -} - - -status_t SoftVPXEncoder::initEncoder() { - vpx_codec_err_t codec_return; - - mCodecContext = new vpx_codec_ctx_t; - mCodecConfiguration = new vpx_codec_enc_cfg_t; - mCodecInterface = vpx_codec_vp8_cx(); - - if (mCodecInterface == NULL) { - return UNKNOWN_ERROR; - } - - codec_return = vpx_codec_enc_config_default(mCodecInterface, - mCodecConfiguration, - 0); // Codec specific flags - - if (codec_return != VPX_CODEC_OK) { - ALOGE("Error populating default configuration for vpx encoder."); - return UNKNOWN_ERROR; - } - - mCodecConfiguration->g_w = mWidth; - mCodecConfiguration->g_h = mHeight; - mCodecConfiguration->g_threads = GetCPUCoreCount(); - mCodecConfiguration->g_error_resilient = mErrorResilience; - - switch (mLevel) { - case OMX_VIDEO_VP8Level_Version0: - mCodecConfiguration->g_profile = 0; - break; - - case OMX_VIDEO_VP8Level_Version1: - mCodecConfiguration->g_profile = 1; - break; - - case OMX_VIDEO_VP8Level_Version2: - mCodecConfiguration->g_profile = 2; - break; - - case OMX_VIDEO_VP8Level_Version3: - mCodecConfiguration->g_profile = 3; - break; - - default: - mCodecConfiguration->g_profile = 0; - } - - // OMX timebase unit is microsecond - // g_timebase is in seconds (i.e. 1/1000000 seconds) - mCodecConfiguration->g_timebase.num = 1; - mCodecConfiguration->g_timebase.den = 1000000; - // rc_target_bitrate is in kbps, mBitrate in bps - mCodecConfiguration->rc_target_bitrate = mBitrate/1000; - mCodecConfiguration->rc_end_usage = mBitrateControlMode; - - codec_return = vpx_codec_enc_init(mCodecContext, - mCodecInterface, - mCodecConfiguration, - 0); // flags - - if (codec_return != VPX_CODEC_OK) { - ALOGE("Error initializing vpx encoder"); - return UNKNOWN_ERROR; - } - - codec_return = vpx_codec_control(mCodecContext, - VP8E_SET_TOKEN_PARTITIONS, - mDCTPartitions); - if (codec_return != VPX_CODEC_OK) { - ALOGE("Error setting dct partitions for vpx encoder."); - return UNKNOWN_ERROR; - } - - if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { - if (mConversionBuffer == NULL) { - mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2); - if (mConversionBuffer == NULL) { - ALOGE("Allocating conversion buffer failed."); - return UNKNOWN_ERROR; - } - } - } - return OK; -} - - -status_t SoftVPXEncoder::releaseEncoder() { - if (mCodecContext != NULL) { - vpx_codec_destroy(mCodecContext); - delete mCodecContext; - mCodecContext = NULL; - } - - if (mCodecConfiguration != NULL) { - delete mCodecConfiguration; - mCodecConfiguration = NULL; - } - - if (mConversionBuffer != NULL) { - delete mConversionBuffer; - mConversionBuffer = NULL; - } - - // this one is not allocated by us - mCodecInterface = NULL; - - return OK; -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, - OMX_PTR param) { - // can include extension index OMX_INDEXEXTTYPE - const int32_t indexFull = index; - - switch (indexFull) { - case OMX_IndexParamVideoPortFormat: { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; - - if (formatParams->nPortIndex == kInputPortIndex) { - if (formatParams->nIndex >= kNumberOfSupportedColorFormats) { - return OMX_ErrorNoMore; - } - - // Color formats, in order of preference - if (formatParams->nIndex == 0) { - formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; - } else if (formatParams->nIndex == 1) { - formatParams->eColorFormat = - OMX_COLOR_FormatYUV420SemiPlanar; - } else { - formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque; - } - - formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - // Converting from microseconds - // Also converting to Q16 format - formatParams->xFramerate = (1000000/mFrameDurationUs) << 16; - return OMX_ErrorNone; - } else if (formatParams->nPortIndex == kOutputPortIndex) { - formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX; - formatParams->eColorFormat = OMX_COLOR_FormatUnused; - formatParams->xFramerate = 0; - return OMX_ErrorNone; - } else { - return OMX_ErrorBadPortIndex; - } - } - - case OMX_IndexParamVideoBitrate: { - OMX_VIDEO_PARAM_BITRATETYPE *bitrate = - (OMX_VIDEO_PARAM_BITRATETYPE *)param; - - if (bitrate->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - bitrate->nTargetBitrate = mBitrate; - - if (mBitrateControlMode == VPX_VBR) { - bitrate->eControlRate = OMX_Video_ControlRateVariable; - } else if (mBitrateControlMode == VPX_CBR) { - bitrate->eControlRate = OMX_Video_ControlRateConstant; - } else { - return OMX_ErrorUnsupportedSetting; - } - return OMX_ErrorNone; - } - - // VP8 specific parameters that use extension headers - case OMX_IndexParamVideoVp8: { - OMX_VIDEO_PARAM_VP8TYPE *vp8Params = - (OMX_VIDEO_PARAM_VP8TYPE *)param; - - if (vp8Params->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - vp8Params->eProfile = OMX_VIDEO_VP8ProfileMain; - vp8Params->eLevel = mLevel; - vp8Params->nDCTPartitions = mDCTPartitions; - vp8Params->bErrorResilientMode = mErrorResilience; - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoProfileLevelQuerySupported: { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; - - if (profileAndLevel->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - switch (profileAndLevel->nProfileIndex) { - case 0: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version0; - break; - - case 1: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version1; - break; - - case 2: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version2; - break; - - case 3: - profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version3; - break; - - default: - return OMX_ErrorNoMore; - } - - profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoProfileLevelCurrent: { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; - - if (profileAndLevel->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - profileAndLevel->eLevel = mLevel; - profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; - return OMX_ErrorNone; - } - - default: - return SimpleSoftOMXComponent::internalGetParameter(index, param); - } -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, - const OMX_PTR param) { - // can include extension index OMX_INDEXEXTTYPE - const int32_t indexFull = index; - - switch (indexFull) { - case OMX_IndexParamStandardComponentRole: - return internalSetRoleParams( - (const OMX_PARAM_COMPONENTROLETYPE *)param); - - case OMX_IndexParamVideoBitrate: - return internalSetBitrateParams( - (const OMX_VIDEO_PARAM_BITRATETYPE *)param); - - case OMX_IndexParamPortDefinition: - return internalSetPortParams( - (const OMX_PARAM_PORTDEFINITIONTYPE *)param); - - case OMX_IndexParamVideoPortFormat: - return internalSetFormatParams( - (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param); - - case OMX_IndexParamVideoVp8: - return internalSetVp8Params( - (const OMX_VIDEO_PARAM_VP8TYPE *)param); - - case OMX_IndexParamVideoProfileLevelCurrent: - return internalSetProfileLevel( - (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param); - - default: - return SimpleSoftOMXComponent::internalSetParameter(index, param); - } -} - -OMX_ERRORTYPE SoftVPXEncoder::internalSetProfileLevel( - const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel) { - if (profileAndLevel->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - if (profileAndLevel->eProfile != OMX_VIDEO_VP8ProfileMain) { - return OMX_ErrorBadParameter; - } - - if (profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version0 || - profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version1 || - profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version2 || - profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version3) { - mLevel = (OMX_VIDEO_VP8LEVELTYPE)profileAndLevel->eLevel; - } else { - return OMX_ErrorBadParameter; - } - - return OMX_ErrorNone; -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetVp8Params( - const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) { - if (vp8Params->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - if (vp8Params->eProfile != OMX_VIDEO_VP8ProfileMain) { - return OMX_ErrorBadParameter; - } - - if (vp8Params->eLevel == OMX_VIDEO_VP8Level_Version0 || - vp8Params->eLevel == OMX_VIDEO_VP8Level_Version1 || - vp8Params->eLevel == OMX_VIDEO_VP8Level_Version2 || - vp8Params->eLevel == OMX_VIDEO_VP8Level_Version3) { - mLevel = vp8Params->eLevel; - } else { - return OMX_ErrorBadParameter; - } - - if (vp8Params->nDCTPartitions <= kMaxDCTPartitions) { - mDCTPartitions = vp8Params->nDCTPartitions; - } else { - return OMX_ErrorBadParameter; - } - - mErrorResilience = vp8Params->bErrorResilientMode; - return OMX_ErrorNone; -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams( - const OMX_VIDEO_PARAM_PORTFORMATTYPE* format) { - if (format->nPortIndex == kInputPortIndex) { - if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar || - format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || - format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) { - mColorFormat = format->eColorFormat; - return OMX_ErrorNone; - } else { - ALOGE("Unsupported color format %i", format->eColorFormat); - return OMX_ErrorUnsupportedSetting; - } - } else if (format->nPortIndex == kOutputPortIndex) { - if (format->eCompressionFormat == OMX_VIDEO_CodingVPX) { - return OMX_ErrorNone; - } else { - return OMX_ErrorUnsupportedSetting; - } - } else { - return OMX_ErrorBadPortIndex; - } -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetRoleParams( - const OMX_PARAM_COMPONENTROLETYPE* role) { - const char* roleText = (const char*)role->cRole; - const size_t roleTextMaxSize = OMX_MAX_STRINGNAME_SIZE - 1; - - if (strncmp(roleText, "video_encoder.vpx", roleTextMaxSize)) { - ALOGE("Unsupported component role"); - return OMX_ErrorBadParameter; - } - - return OMX_ErrorNone; -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( - const OMX_PARAM_PORTDEFINITIONTYPE* port) { - if (port->nPortIndex == kInputPortIndex) { - mWidth = port->format.video.nFrameWidth; - mHeight = port->format.video.nFrameHeight; - - // xFramerate comes in Q16 format, in frames per second unit - const uint32_t framerate = port->format.video.xFramerate >> 16; - // frame duration is in microseconds - mFrameDurationUs = (1000000/framerate); - - if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar || - port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || - port->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) { - mColorFormat = port->format.video.eColorFormat; - } else { - return OMX_ErrorUnsupportedSetting; - } - - return OMX_ErrorNone; - } else if (port->nPortIndex == kOutputPortIndex) { - mBitrate = port->format.video.nBitrate; - return OMX_ErrorNone; - } else { - return OMX_ErrorBadPortIndex; - } -} - - -OMX_ERRORTYPE SoftVPXEncoder::internalSetBitrateParams( - const OMX_VIDEO_PARAM_BITRATETYPE* bitrate) { - if (bitrate->nPortIndex != kOutputPortIndex) { - return OMX_ErrorUnsupportedIndex; - } - - mBitrate = bitrate->nTargetBitrate; - - if (bitrate->eControlRate == OMX_Video_ControlRateVariable) { - mBitrateControlMode = VPX_VBR; - } else if (bitrate->eControlRate == OMX_Video_ControlRateConstant) { - mBitrateControlMode = VPX_CBR; - } else { - return OMX_ErrorUnsupportedSetting; - } - - return OMX_ErrorNone; -} - - -void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { - // Initialize encoder if not already - if (mCodecContext == NULL) { - if (OK != initEncoder()) { - ALOGE("Failed to initialize encoder"); - notify(OMX_EventError, - OMX_ErrorUndefined, - 0, // Extra notification data - NULL); // Notification data pointer - return; - } - } - - vpx_codec_err_t codec_return; - List &inputBufferInfoQueue = getPortQueue(kInputPortIndex); - List &outputBufferInfoQueue = getPortQueue(kOutputPortIndex); - - while (!inputBufferInfoQueue.empty() && !outputBufferInfoQueue.empty()) { - BufferInfo *inputBufferInfo = *inputBufferInfoQueue.begin(); - OMX_BUFFERHEADERTYPE *inputBufferHeader = inputBufferInfo->mHeader; - - BufferInfo *outputBufferInfo = *outputBufferInfoQueue.begin(); - OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader; - - if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inputBufferInfoQueue.erase(inputBufferInfoQueue.begin()); - inputBufferInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inputBufferHeader); - - outputBufferHeader->nFilledLen = 0; - outputBufferHeader->nFlags = OMX_BUFFERFLAG_EOS; - - outputBufferInfoQueue.erase(outputBufferInfoQueue.begin()); - outputBufferInfo->mOwnedByUs = false; - notifyFillBufferDone(outputBufferHeader); - return; - } - - uint8_t* source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset; - - // NOTE: As much as nothing is known about color format - // when it is denoted as AndroidOpaque, it is at least - // assumed to be planar. - if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { - ConvertSemiPlanarToPlanar(source, mConversionBuffer, mWidth, mHeight); - source = mConversionBuffer; - } - vpx_image_t raw_frame; - vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight, - kInputBufferAlignment, source); - codec_return = vpx_codec_encode(mCodecContext, - &raw_frame, - inputBufferHeader->nTimeStamp, // in timebase units - mFrameDurationUs, // frame duration in timebase units - 0, // frame flags - VPX_DL_REALTIME); // encoding deadline - if (codec_return != VPX_CODEC_OK) { - ALOGE("vpx encoder failed to encode frame"); - notify(OMX_EventError, - OMX_ErrorUndefined, - 0, // Extra notification data - NULL); // Notification data pointer - return; - } - - vpx_codec_iter_t encoded_packet_iterator = NULL; - const vpx_codec_cx_pkt_t* encoded_packet; - - while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) { - if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) { - outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts; - outputBufferHeader->nFlags = 0; - outputBufferHeader->nOffset = 0; - outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz; - memcpy(outputBufferHeader->pBuffer, - encoded_packet->data.frame.buf, - encoded_packet->data.frame.sz); - outputBufferInfo->mOwnedByUs = false; - outputBufferInfoQueue.erase(outputBufferInfoQueue.begin()); - notifyFillBufferDone(outputBufferHeader); - } - } - - inputBufferInfo->mOwnedByUs = false; - inputBufferInfoQueue.erase(inputBufferInfoQueue.begin()); - notifyEmptyBufferDone(inputBufferHeader); - } -} -} // namespace android - - -android::SoftOMXComponent *createSoftOMXComponent( - const char *name, const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, OMX_COMPONENTTYPE **component) { - return new android::SoftVPXEncoder(name, callbacks, appData, component); -} diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h deleted file mode 100644 index 3bc05c0..0000000 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2013 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 SOFT_VPX_ENCODER_H_ - -#define SOFT_VPX_ENCODER_H_ - -#include "SimpleSoftOMXComponent.h" - -#include -#include - -#include "vpx/vpx_encoder.h" -#include "vpx/vpx_codec.h" -#include "vpx/vp8cx.h" - -namespace android { - -// Exposes a vpx encoder as an OMX Component -// -// Boilerplate for callback bindings are taken care -// by the base class SimpleSoftOMXComponent and its -// parent SoftOMXComponent. -// -// Only following encoder settings are available -// - target bitrate -// - rate control (constant / variable) -// - frame rate -// - error resilience -// - token partitioning -// - reconstruction & loop filters (g_profile) -// -// Only following color formats are recognized -// - YUV420Planar -// - YUV420SemiPlanar -// - AndroidOpaque -// -// Following settings are not configurable by the client -// - encoding deadline is realtime -// - multithreaded encoding utilizes a number of threads equal -// to online cpu's available -// - the algorithm interface for encoder is vp8 -// - fractional bits of frame rate is discarded -// - OMX timestamps are in microseconds, therefore -// encoder timebase is fixed to 1/1000000 - -class SoftVPXEncoder : public SimpleSoftOMXComponent { - public: - SoftVPXEncoder(const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component); - - protected: - virtual ~SoftVPXEncoder(); - - // Returns current values for requested OMX - // parameters - virtual OMX_ERRORTYPE internalGetParameter( - OMX_INDEXTYPE index, OMX_PTR param); - - // Validates, extracts and stores relevant OMX - // parameters - virtual OMX_ERRORTYPE internalSetParameter( - OMX_INDEXTYPE index, const OMX_PTR param); - - // OMX callback when buffers available - // Note that both an input and output buffer - // is expected to be available to carry out - // encoding of the frame - virtual void onQueueFilled(OMX_U32 portIndex); - - private: - // number of buffers allocated per port - static const uint32_t kNumBuffers = 4; - - // OMX port indexes that refer to input and - // output ports respectively - static const uint32_t kInputPortIndex = 0; - static const uint32_t kOutputPortIndex = 1; - - // Byte-alignment required for buffers - static const uint32_t kInputBufferAlignment = 1; - static const uint32_t kOutputBufferAlignment = 2; - - // Max value supported for DCT partitions - static const uint32_t kMaxDCTPartitions = 3; - - // Number of supported input color formats - static const uint32_t kNumberOfSupportedColorFormats = 3; - - // vpx specific opaque data structure that - // stores encoder state - vpx_codec_ctx_t* mCodecContext; - - // vpx specific data structure that - // stores encoder configuration - vpx_codec_enc_cfg_t* mCodecConfiguration; - - // vpx specific read-only data structure - // that specifies algorithm interface (e.g. vp8) - vpx_codec_iface_t* mCodecInterface; - - // Width of the input frames - int32_t mWidth; - - // Height of the input frames - int32_t mHeight; - - // Target bitrate set for the encoder, in bits per second. - int32_t mBitrate; - - // Bitrate control mode, either constant or variable - vpx_rc_mode mBitrateControlMode; - - // Frame duration is the reciprocal of framerate, denoted - // in microseconds - uint64_t mFrameDurationUs; - - // vp8 specific configuration parameter - // that enables token partitioning of - // the stream into substreams - int32_t mDCTPartitions; - - // Parameter that denotes whether error resilience - // is enabled in encoder - OMX_BOOL mErrorResilience; - - // Color format for the input port - OMX_COLOR_FORMATTYPE mColorFormat; - - // Encoder profile corresponding to OMX level parameter - // - // The inconsistency in the naming is caused by - // OMX spec referring vpx profiles (g_profile) - // as "levels" whereas using the name "profile" for - // something else. - OMX_VIDEO_VP8LEVELTYPE mLevel; - - // Conversion buffer is needed to convert semi - // planar yuv420 to planar format - // It is only allocated if input format is - // indeed YUV420SemiPlanar. - uint8_t* mConversionBuffer; - - // Initializes input and output OMX ports with sensible - // default values. - void initPorts(); - - // Initializes vpx encoder with available settings. - status_t initEncoder(); - - // Releases vpx encoder instance, with it's associated - // data structures. - // - // Unless called earlier, this is handled by the - // dtor. - status_t releaseEncoder(); - - // Handles port changes with respect to color formats - OMX_ERRORTYPE internalSetFormatParams( - const OMX_VIDEO_PARAM_PORTFORMATTYPE* format); - - // Verifies the component role tried to be set to this OMX component is - // strictly video_encoder.vpx - OMX_ERRORTYPE internalSetRoleParams( - const OMX_PARAM_COMPONENTROLETYPE* role); - - // Updates bitrate to reflect port settings. - OMX_ERRORTYPE internalSetBitrateParams( - const OMX_VIDEO_PARAM_BITRATETYPE* bitrate); - - // Handles port definition changes. - OMX_ERRORTYPE internalSetPortParams( - const OMX_PARAM_PORTDEFINITIONTYPE* port); - - // Handles vp8 specific parameters. - OMX_ERRORTYPE internalSetVp8Params( - const OMX_VIDEO_PARAM_VP8TYPE* vp8Params); - - // Updates encoder profile - OMX_ERRORTYPE internalSetProfileLevel( - const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel); - - DISALLOW_EVIL_CONSTRUCTORS(SoftVPXEncoder); -}; - -} // namespace android - -#endif // SOFT_VPX_ENCODER_H_ -- cgit v1.1 From 272ab546940054ad7991bef4b3a36f15175721cd Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Mon, 4 Feb 2013 16:26:02 -0800 Subject: Add support for querying if a stream is active remotely Bug 7485803 Change-Id: I0744374f130fd2dd0714102354cffed2fa915361 --- media/libmedia/AudioSystem.cpp | 10 ++++++++++ media/libmedia/IAudioPolicyService.cpp | 21 ++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 028e4a3..693df60 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -731,6 +731,16 @@ status_t AudioSystem::isStreamActive(audio_stream_type_t stream, bool* state, ui return NO_ERROR; } +status_t AudioSystem::isStreamActiveRemotely(audio_stream_type_t stream, bool* state, + uint32_t inPastMs) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + if (state == NULL) return BAD_VALUE; + *state = aps->isStreamActiveRemotely(stream, inPastMs); + return NO_ERROR; +} + status_t AudioSystem::isSourceActive(audio_source_t stream, bool* state) { const sp& aps = AudioSystem::get_audio_policy_service(); diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 769deae..386c351 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -55,7 +55,8 @@ enum { IS_SOURCE_ACTIVE, GET_DEVICES_FOR_STREAM, QUERY_DEFAULT_PRE_PROCESSING, - SET_EFFECT_ENABLED + SET_EFFECT_ENABLED, + IS_STREAM_ACTIVE_REMOTELY }; class BpAudioPolicyService : public BpInterface @@ -330,6 +331,16 @@ public: return reply.readInt32(); } + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32((int32_t) stream); + data.writeInt32(inPastMs); + remote()->transact(IS_STREAM_ACTIVE_REMOTELY, data, &reply); + return reply.readInt32(); + } + virtual bool isSourceActive(audio_source_t source) const { Parcel data, reply; @@ -605,6 +616,14 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case IS_STREAM_ACTIVE_REMOTELY: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_stream_type_t stream = (audio_stream_type_t) data.readInt32(); + uint32_t inPastMs = (uint32_t)data.readInt32(); + reply->writeInt32( isStreamActiveRemotely((audio_stream_type_t) stream, inPastMs) ); + return NO_ERROR; + } break; + case IS_SOURCE_ACTIVE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_source_t source = (audio_source_t) data.readInt32(); -- cgit v1.1 From 4f1732b8068970b368a89271158ca29daf25650e Mon Sep 17 00:00:00 2001 From: ztenghui Date: Mon, 4 Feb 2013 15:59:38 -0800 Subject: Add the native MediaMuxer support. MediaAdapter: a helper class to convert the push model to pull model. MediaMuxer: the real muxer. bug:7991013 Change-Id: If3b79551bc6332bc81f5c2740885e579a5c4abf9 --- media/libstagefright/Android.mk | 2 + media/libstagefright/MediaAdapter.cpp | 126 +++++++++++++++++++++++++++++ media/libstagefright/MediaMuxer.cpp | 145 ++++++++++++++++++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 media/libstagefright/MediaAdapter.cpp create mode 100644 media/libstagefright/MediaMuxer.cpp (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 85662db..6934e59 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -26,12 +26,14 @@ LOCAL_SRC_FILES:= \ MPEG2TSWriter.cpp \ MPEG4Extractor.cpp \ MPEG4Writer.cpp \ + MediaAdapter.cpp \ MediaBuffer.cpp \ MediaBufferGroup.cpp \ MediaCodec.cpp \ MediaCodecList.cpp \ MediaDefs.cpp \ MediaExtractor.cpp \ + MediaMuxer.cpp \ MediaSource.cpp \ MetaData.cpp \ NuCachedSource2.cpp \ diff --git a/media/libstagefright/MediaAdapter.cpp b/media/libstagefright/MediaAdapter.cpp new file mode 100644 index 0000000..2484212 --- /dev/null +++ b/media/libstagefright/MediaAdapter.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 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 "MediaAdapter" +#include + +#include +#include +#include + +namespace android { + +MediaAdapter::MediaAdapter(const sp &meta) + : mCurrentMediaBuffer(NULL), + mStarted(false), + mOutputFormat(meta) { +} + +MediaAdapter::~MediaAdapter() { + Mutex::Autolock autoLock(mAdapterLock); + mOutputFormat.clear(); + CHECK(mCurrentMediaBuffer == NULL); +} + +status_t MediaAdapter::start(MetaData *params) { + Mutex::Autolock autoLock(mAdapterLock); + if (!mStarted) { + mStarted = true; + } + return OK; +} + +status_t MediaAdapter::stop() { + Mutex::Autolock autoLock(mAdapterLock); + if (mStarted) { + mStarted = false; + // If stop() happens immediately after a pushBuffer(), we should + // clean up the mCurrentMediaBuffer + if (mCurrentMediaBuffer != NULL) { + mCurrentMediaBuffer->release(); + mCurrentMediaBuffer = NULL; + } + // While read() is still waiting, we should signal it to finish. + mBufferReadCond.signal(); + } + return OK; +} + +sp MediaAdapter::getFormat() { + Mutex::Autolock autoLock(mAdapterLock); + return mOutputFormat; +} + +void MediaAdapter::signalBufferReturned(MediaBuffer *buffer) { + Mutex::Autolock autoLock(mAdapterLock); + CHECK(buffer != NULL); + buffer->setObserver(0); + buffer->release(); + ALOGV("buffer returned %p", buffer); + mBufferReturnedCond.signal(); +} + +status_t MediaAdapter::read( + MediaBuffer **buffer, const ReadOptions *options) { + Mutex::Autolock autoLock(mAdapterLock); + if (!mStarted) { + ALOGV("Read before even started!"); + return ERROR_END_OF_STREAM; + } + + while (mCurrentMediaBuffer == NULL && mStarted) { + ALOGV("waiting @ read()"); + mBufferReadCond.wait(mAdapterLock); + } + + if (!mStarted) { + ALOGV("read interrupted after stop"); + CHECK(mCurrentMediaBuffer == NULL); + return ERROR_END_OF_STREAM; + } + + CHECK(mCurrentMediaBuffer != NULL); + + *buffer = mCurrentMediaBuffer; + mCurrentMediaBuffer = NULL; + (*buffer)->setObserver(this); + + return OK; +} + +status_t MediaAdapter::pushBuffer(MediaBuffer *buffer) { + if (buffer == NULL) { + ALOGE("pushBuffer get an NULL buffer"); + return -EINVAL; + } + + Mutex::Autolock autoLock(mAdapterLock); + if (!mStarted) { + ALOGE("pushBuffer called before start"); + return INVALID_OPERATION; + } + mCurrentMediaBuffer = buffer; + mBufferReadCond.signal(); + + ALOGV("wait for the buffer returned @ pushBuffer! %p", buffer); + mBufferReturnedCond.wait(mAdapterLock); + + return OK; +} + +} // namespace android + diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp new file mode 100644 index 0000000..30bed90 --- /dev/null +++ b/media/libstagefright/MediaMuxer.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2013, 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 "MediaMuxer" +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +MediaMuxer::MediaMuxer(const char* pathOut) + : mState(INITED) { + mWriter = new MPEG4Writer(pathOut); +} + +MediaMuxer::MediaMuxer(int fd) + : mState(INITED) { + mWriter = new MPEG4Writer(fd); +} + +MediaMuxer::~MediaMuxer() { + Mutex::Autolock autoLock(mMuxerLock); + + // Clean up all the internal resources. + mWriter.clear(); + mTrackList.clear(); +} + +ssize_t MediaMuxer::addTrack(const sp &format) { + Mutex::Autolock autoLock(mMuxerLock); + + if (format.get() == NULL) { + ALOGE("addTrack() get a null format"); + return -EINVAL; + } + + if (mState != INITED) { + ALOGE("addTrack() must be called after constructor and before start()."); + return INVALID_OPERATION; + } + + sp meta = new MetaData; + convertMessageToMetaData(format, meta); + + sp newTrack = new MediaAdapter(meta); + return mTrackList.add(newTrack); +} + +status_t MediaMuxer::start() { + Mutex::Autolock autoLock(mMuxerLock); + + if (mState == INITED) { + mState = STARTED; + for (size_t i = 0 ; i < mTrackList.size(); i++) { + mWriter->addSource(mTrackList[i]); + } + return mWriter->start(); + } else { + ALOGE("start() is called in invalid state %d", mState); + return INVALID_OPERATION; + } +} + +status_t MediaMuxer::stop() { + Mutex::Autolock autoLock(mMuxerLock); + + if (mState == STARTED) { + mState = STOPPED; + for (size_t i = 0; i < mTrackList.size(); i++) { + mTrackList[i]->stop(); + } + return mWriter->stop(); + } else { + ALOGE("stop() is called in invalid state %d", mState); + return INVALID_OPERATION; + } +} + +status_t MediaMuxer::writeSampleData(const sp &buffer, size_t trackIndex, + int64_t timeUs, uint32_t flags) { + Mutex::Autolock autoLock(mMuxerLock); + + sp currentTrack = mTrackList[trackIndex]; + + if (buffer.get() == NULL) { + ALOGE("WriteSampleData() get an NULL buffer."); + return -EINVAL; + } + + if (mState != STARTED) { + ALOGE("WriteSampleData() is called in invalid state %d", mState); + return INVALID_OPERATION; + } + + if (trackIndex >= mTrackList.size()) { + ALOGE("WriteSampleData() get an invalid index %d", trackIndex); + return -EINVAL; + } + + MediaBuffer* mediaBuffer = new MediaBuffer(buffer); + + mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned(). + mediaBuffer->set_range(buffer->offset(), buffer->size()); + + sp metaData = mediaBuffer->meta_data(); + metaData->setInt64(kKeyTime, timeUs); + // Just set the kKeyDecodingTime as the presentation time for now. + metaData->setInt64(kKeyDecodingTime, timeUs); + + if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) { + metaData->setInt32(kKeyIsSyncFrame, true); + } + + // This pushBuffer will wait until the mediaBuffer is consumed. + return currentTrack->pushBuffer(mediaBuffer); +} + +} // namespace android -- cgit v1.1 From b4698f79230bbee15936641d951d49655f9e6da5 Mon Sep 17 00:00:00 2001 From: Kunter Gultekin Date: Fri, 1 Feb 2013 17:01:15 +0200 Subject: Adds VPX encoding support for stagefright. Only following encoder settings are available - target bitrate - rate control (constant / variable) - frame rate - token partitioning - error resilience - reconstruction & loop filters Only following color formats are recognized - YUV420Planar - YUV420SemiPlanar - AndroidOpaque Following settings are not configurable by the client - encoding deadline is realtime - the algorithm interface for encoder is vp8 - fractional bits of frame rate is discarded - timebase is fixed to 1/1000000 Requires libvpx to be built with encoder support enabled. Requires openmax 1.1.2 extension headers. Relevant tests exist in cts repo. Change-Id: I650f1aca83e7dc93f79d7e6cba7ac24f26e66d40 Signed-off-by: Kunter Gultekin --- media/libstagefright/codecs/on2/enc/Android.mk | 24 + .../codecs/on2/enc/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/on2/enc/NOTICE | 190 ++++++ .../codecs/on2/enc/SoftVPXEncoder.cpp | 685 +++++++++++++++++++++ .../libstagefright/codecs/on2/enc/SoftVPXEncoder.h | 203 ++++++ 5 files changed, 1102 insertions(+) create mode 100644 media/libstagefright/codecs/on2/enc/Android.mk create mode 100644 media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/on2/enc/NOTICE create mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp create mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h (limited to 'media') diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk new file mode 100644 index 0000000..5d3317c --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/Android.mk @@ -0,0 +1,24 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftVPXEncoder.cpp + +LOCAL_C_INCLUDES := \ + $(TOP)/external/libvpx/libvpx \ + $(TOP)/external/openssl/include \ + $(TOP)/external/libvpx/libvpx/vpx_codec \ + $(TOP)/external/libvpx/libvpx/vpx_ports \ + frameworks/av/media/libstagefright/include \ + frameworks/native/include/media/openmax \ + +LOCAL_STATIC_LIBRARIES := \ + libvpx + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils \ + +LOCAL_MODULE := libstagefright_soft_vpxenc +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/on2/enc/NOTICE b/media/libstagefright/codecs/on2/enc/NOTICE new file mode 100644 index 0000000..faed58a --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2013, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp new file mode 100644 index 0000000..cc38dc3 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -0,0 +1,685 @@ +/* + * Copyright (C) 2013 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 "SoftVPXEncoder" +#include "SoftVPXEncoder.h" + +#include + +#include +#include + +namespace android { + + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + // OMX IL 1.1.2 + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 1; + params->nVersion.s.nRevision = 2; + params->nVersion.s.nStep = 0; +} + + +static int GetCPUCoreCount() { + int cpuCoreCount = 1; +#if defined(_SC_NPROCESSORS_ONLN) + cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); +#else + // _SC_NPROC_ONLN must be defined... + cpuCoreCount = sysconf(_SC_NPROC_ONLN); +#endif + CHECK_GE(cpuCoreCount, 1); + return cpuCoreCount; +} + + +// This color conversion utility is copied from SoftMPEG4Encoder.cpp +inline static void ConvertSemiPlanarToPlanar(uint8_t *inyuv, + uint8_t* outyuv, + int32_t width, + int32_t height) { + int32_t outYsize = width * height; + uint32_t *outy = (uint32_t *) outyuv; + uint16_t *outcb = (uint16_t *) (outyuv + outYsize); + uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2)); + + /* Y copying */ + memcpy(outy, inyuv, outYsize); + + /* U & V copying */ + uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize); + for (int32_t i = height >> 1; i > 0; --i) { + for (int32_t j = width >> 2; j > 0; --j) { + uint32_t temp = *inyuv_4++; + uint32_t tempU = temp & 0xFF; + tempU = tempU | ((temp >> 8) & 0xFF00); + + uint32_t tempV = (temp >> 8) & 0xFF; + tempV = tempV | ((temp >> 16) & 0xFF00); + + // Flip U and V + *outcb++ = tempV; + *outcr++ = tempU; + } + } +} + + +SoftVPXEncoder::SoftVPXEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mCodecContext(NULL), + mCodecConfiguration(NULL), + mCodecInterface(NULL), + mWidth(176), + mHeight(144), + mBitrate(192000), // in bps + mBitrateControlMode(VPX_VBR), // variable bitrate + mFrameDurationUs(33333), // Defaults to 30 fps + mDCTPartitions(0), + mErrorResilience(OMX_FALSE), + mColorFormat(OMX_COLOR_FormatYUV420Planar), + mLevel(OMX_VIDEO_VP8Level_Version0), + mConversionBuffer(NULL) { + + initPorts(); +} + + +SoftVPXEncoder::~SoftVPXEncoder() { + releaseEncoder(); +} + + +void SoftVPXEncoder::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE inputPort; + OMX_PARAM_PORTDEFINITIONTYPE outputPort; + + InitOMXParams(&inputPort); + InitOMXParams(&outputPort); + + inputPort.nBufferCountMin = kNumBuffers; + inputPort.nBufferCountActual = inputPort.nBufferCountMin; + inputPort.bEnabled = OMX_TRUE; + inputPort.bPopulated = OMX_FALSE; + inputPort.eDomain = OMX_PortDomainVideo; + inputPort.bBuffersContiguous = OMX_FALSE; + inputPort.format.video.pNativeRender = NULL; + inputPort.format.video.nFrameWidth = mWidth; + inputPort.format.video.nFrameHeight = mHeight; + inputPort.format.video.nStride = inputPort.format.video.nFrameWidth; + inputPort.format.video.nSliceHeight = inputPort.format.video.nFrameHeight; + inputPort.format.video.nBitrate = 0; + // frameRate is reciprocal of frameDuration, which is + // in microseconds. It is also in Q16 format. + inputPort.format.video.xFramerate = (1000000/mFrameDurationUs) << 16; + inputPort.format.video.bFlagErrorConcealment = OMX_FALSE; + inputPort.nPortIndex = kInputPortIndex; + inputPort.eDir = OMX_DirInput; + inputPort.nBufferAlignment = kInputBufferAlignment; + inputPort.format.video.cMIMEType = + const_cast(MEDIA_MIMETYPE_VIDEO_RAW); + inputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + inputPort.format.video.eColorFormat = mColorFormat; + inputPort.format.video.pNativeWindow = NULL; + inputPort.nBufferSize = + (inputPort.format.video.nStride * + inputPort.format.video.nSliceHeight * 3) / 2; + + addPort(inputPort); + + outputPort.nBufferCountMin = kNumBuffers; + outputPort.nBufferCountActual = outputPort.nBufferCountMin; + outputPort.bEnabled = OMX_TRUE; + outputPort.bPopulated = OMX_FALSE; + outputPort.eDomain = OMX_PortDomainVideo; + outputPort.bBuffersContiguous = OMX_FALSE; + outputPort.format.video.pNativeRender = NULL; + outputPort.format.video.nFrameWidth = mWidth; + outputPort.format.video.nFrameHeight = mHeight; + outputPort.format.video.nStride = outputPort.format.video.nFrameWidth; + outputPort.format.video.nSliceHeight = outputPort.format.video.nFrameHeight; + outputPort.format.video.nBitrate = mBitrate; + outputPort.format.video.xFramerate = 0; + outputPort.format.video.bFlagErrorConcealment = OMX_FALSE; + outputPort.nPortIndex = kOutputPortIndex; + outputPort.eDir = OMX_DirOutput; + outputPort.nBufferAlignment = kOutputBufferAlignment; + outputPort.format.video.cMIMEType = + const_cast(MEDIA_MIMETYPE_VIDEO_VPX); + outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX; + outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused; + outputPort.format.video.pNativeWindow = NULL; + outputPort.nBufferSize = 256 * 1024; // arbitrary + + addPort(outputPort); +} + + +status_t SoftVPXEncoder::initEncoder() { + vpx_codec_err_t codec_return; + + mCodecContext = new vpx_codec_ctx_t; + mCodecConfiguration = new vpx_codec_enc_cfg_t; + mCodecInterface = vpx_codec_vp8_cx(); + + if (mCodecInterface == NULL) { + return UNKNOWN_ERROR; + } + + codec_return = vpx_codec_enc_config_default(mCodecInterface, + mCodecConfiguration, + 0); // Codec specific flags + + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error populating default configuration for vpx encoder."); + return UNKNOWN_ERROR; + } + + mCodecConfiguration->g_w = mWidth; + mCodecConfiguration->g_h = mHeight; + mCodecConfiguration->g_threads = GetCPUCoreCount(); + mCodecConfiguration->g_error_resilient = mErrorResilience; + + switch (mLevel) { + case OMX_VIDEO_VP8Level_Version0: + mCodecConfiguration->g_profile = 0; + break; + + case OMX_VIDEO_VP8Level_Version1: + mCodecConfiguration->g_profile = 1; + break; + + case OMX_VIDEO_VP8Level_Version2: + mCodecConfiguration->g_profile = 2; + break; + + case OMX_VIDEO_VP8Level_Version3: + mCodecConfiguration->g_profile = 3; + break; + + default: + mCodecConfiguration->g_profile = 0; + } + + // OMX timebase unit is microsecond + // g_timebase is in seconds (i.e. 1/1000000 seconds) + mCodecConfiguration->g_timebase.num = 1; + mCodecConfiguration->g_timebase.den = 1000000; + // rc_target_bitrate is in kbps, mBitrate in bps + mCodecConfiguration->rc_target_bitrate = mBitrate/1000; + mCodecConfiguration->rc_end_usage = mBitrateControlMode; + + codec_return = vpx_codec_enc_init(mCodecContext, + mCodecInterface, + mCodecConfiguration, + 0); // flags + + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error initializing vpx encoder"); + return UNKNOWN_ERROR; + } + + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_TOKEN_PARTITIONS, + mDCTPartitions); + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error setting dct partitions for vpx encoder."); + return UNKNOWN_ERROR; + } + + if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + if (mConversionBuffer == NULL) { + mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2); + if (mConversionBuffer == NULL) { + ALOGE("Allocating conversion buffer failed."); + return UNKNOWN_ERROR; + } + } + } + return OK; +} + + +status_t SoftVPXEncoder::releaseEncoder() { + if (mCodecContext != NULL) { + vpx_codec_destroy(mCodecContext); + delete mCodecContext; + mCodecContext = NULL; + } + + if (mCodecConfiguration != NULL) { + delete mCodecConfiguration; + mCodecConfiguration = NULL; + } + + if (mConversionBuffer != NULL) { + delete mConversionBuffer; + mConversionBuffer = NULL; + } + + // this one is not allocated by us + mCodecInterface = NULL; + + return OK; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, + OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoPortFormat: { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param; + + if (formatParams->nPortIndex == kInputPortIndex) { + if (formatParams->nIndex >= kNumberOfSupportedColorFormats) { + return OMX_ErrorNoMore; + } + + // Color formats, in order of preference + if (formatParams->nIndex == 0) { + formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; + } else if (formatParams->nIndex == 1) { + formatParams->eColorFormat = + OMX_COLOR_FormatYUV420SemiPlanar; + } else { + formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque; + } + + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + // Converting from microseconds + // Also converting to Q16 format + formatParams->xFramerate = (1000000/mFrameDurationUs) << 16; + return OMX_ErrorNone; + } else if (formatParams->nPortIndex == kOutputPortIndex) { + formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX; + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + formatParams->xFramerate = 0; + return OMX_ErrorNone; + } else { + return OMX_ErrorBadPortIndex; + } + } + + case OMX_IndexParamVideoBitrate: { + OMX_VIDEO_PARAM_BITRATETYPE *bitrate = + (OMX_VIDEO_PARAM_BITRATETYPE *)param; + + if (bitrate->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + bitrate->nTargetBitrate = mBitrate; + + if (mBitrateControlMode == VPX_VBR) { + bitrate->eControlRate = OMX_Video_ControlRateVariable; + } else if (mBitrateControlMode == VPX_CBR) { + bitrate->eControlRate = OMX_Video_ControlRateConstant; + } else { + return OMX_ErrorUnsupportedSetting; + } + return OMX_ErrorNone; + } + + // VP8 specific parameters that use extension headers + case OMX_IndexParamVideoVp8: { + OMX_VIDEO_PARAM_VP8TYPE *vp8Params = + (OMX_VIDEO_PARAM_VP8TYPE *)param; + + if (vp8Params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + vp8Params->eProfile = OMX_VIDEO_VP8ProfileMain; + vp8Params->eLevel = mLevel; + vp8Params->nDCTPartitions = mDCTPartitions; + vp8Params->bErrorResilientMode = mErrorResilience; + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoProfileLevelQuerySupported: { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; + + if (profileAndLevel->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + switch (profileAndLevel->nProfileIndex) { + case 0: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version0; + break; + + case 1: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version1; + break; + + case 2: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version2; + break; + + case 3: + profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version3; + break; + + default: + return OMX_ErrorNoMore; + } + + profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoProfileLevelCurrent: { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; + + if (profileAndLevel->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + profileAndLevel->eLevel = mLevel; + profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, param); + } +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, + const OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamStandardComponentRole: + return internalSetRoleParams( + (const OMX_PARAM_COMPONENTROLETYPE *)param); + + case OMX_IndexParamVideoBitrate: + return internalSetBitrateParams( + (const OMX_VIDEO_PARAM_BITRATETYPE *)param); + + case OMX_IndexParamPortDefinition: + return internalSetPortParams( + (const OMX_PARAM_PORTDEFINITIONTYPE *)param); + + case OMX_IndexParamVideoPortFormat: + return internalSetFormatParams( + (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param); + + case OMX_IndexParamVideoVp8: + return internalSetVp8Params( + (const OMX_VIDEO_PARAM_VP8TYPE *)param); + + case OMX_IndexParamVideoProfileLevelCurrent: + return internalSetProfileLevel( + (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param); + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVPXEncoder::internalSetProfileLevel( + const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel) { + if (profileAndLevel->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + if (profileAndLevel->eProfile != OMX_VIDEO_VP8ProfileMain) { + return OMX_ErrorBadParameter; + } + + if (profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version0 || + profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version1 || + profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version2 || + profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version3) { + mLevel = (OMX_VIDEO_VP8LEVELTYPE)profileAndLevel->eLevel; + } else { + return OMX_ErrorBadParameter; + } + + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetVp8Params( + const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) { + if (vp8Params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + if (vp8Params->eProfile != OMX_VIDEO_VP8ProfileMain) { + return OMX_ErrorBadParameter; + } + + if (vp8Params->eLevel == OMX_VIDEO_VP8Level_Version0 || + vp8Params->eLevel == OMX_VIDEO_VP8Level_Version1 || + vp8Params->eLevel == OMX_VIDEO_VP8Level_Version2 || + vp8Params->eLevel == OMX_VIDEO_VP8Level_Version3) { + mLevel = vp8Params->eLevel; + } else { + return OMX_ErrorBadParameter; + } + + if (vp8Params->nDCTPartitions <= kMaxDCTPartitions) { + mDCTPartitions = vp8Params->nDCTPartitions; + } else { + return OMX_ErrorBadParameter; + } + + mErrorResilience = vp8Params->bErrorResilientMode; + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams( + const OMX_VIDEO_PARAM_PORTFORMATTYPE* format) { + if (format->nPortIndex == kInputPortIndex) { + if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar || + format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || + format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) { + mColorFormat = format->eColorFormat; + return OMX_ErrorNone; + } else { + ALOGE("Unsupported color format %i", format->eColorFormat); + return OMX_ErrorUnsupportedSetting; + } + } else if (format->nPortIndex == kOutputPortIndex) { + if (format->eCompressionFormat == OMX_VIDEO_CodingVPX) { + return OMX_ErrorNone; + } else { + return OMX_ErrorUnsupportedSetting; + } + } else { + return OMX_ErrorBadPortIndex; + } +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetRoleParams( + const OMX_PARAM_COMPONENTROLETYPE* role) { + const char* roleText = (const char*)role->cRole; + const size_t roleTextMaxSize = OMX_MAX_STRINGNAME_SIZE - 1; + + if (strncmp(roleText, "video_encoder.vpx", roleTextMaxSize)) { + ALOGE("Unsupported component role"); + return OMX_ErrorBadParameter; + } + + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( + const OMX_PARAM_PORTDEFINITIONTYPE* port) { + if (port->nPortIndex == kInputPortIndex) { + mWidth = port->format.video.nFrameWidth; + mHeight = port->format.video.nFrameHeight; + + // xFramerate comes in Q16 format, in frames per second unit + const uint32_t framerate = port->format.video.xFramerate >> 16; + // frame duration is in microseconds + mFrameDurationUs = (1000000/framerate); + + if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar || + port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || + port->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) { + mColorFormat = port->format.video.eColorFormat; + } else { + return OMX_ErrorUnsupportedSetting; + } + + return OMX_ErrorNone; + } else if (port->nPortIndex == kOutputPortIndex) { + mBitrate = port->format.video.nBitrate; + return OMX_ErrorNone; + } else { + return OMX_ErrorBadPortIndex; + } +} + + +OMX_ERRORTYPE SoftVPXEncoder::internalSetBitrateParams( + const OMX_VIDEO_PARAM_BITRATETYPE* bitrate) { + if (bitrate->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + mBitrate = bitrate->nTargetBitrate; + + if (bitrate->eControlRate == OMX_Video_ControlRateVariable) { + mBitrateControlMode = VPX_VBR; + } else if (bitrate->eControlRate == OMX_Video_ControlRateConstant) { + mBitrateControlMode = VPX_CBR; + } else { + return OMX_ErrorUnsupportedSetting; + } + + return OMX_ErrorNone; +} + + +void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { + // Initialize encoder if not already + if (mCodecContext == NULL) { + if (OK != initEncoder()) { + ALOGE("Failed to initialize encoder"); + notify(OMX_EventError, + OMX_ErrorUndefined, + 0, // Extra notification data + NULL); // Notification data pointer + return; + } + } + + vpx_codec_err_t codec_return; + List &inputBufferInfoQueue = getPortQueue(kInputPortIndex); + List &outputBufferInfoQueue = getPortQueue(kOutputPortIndex); + + while (!inputBufferInfoQueue.empty() && !outputBufferInfoQueue.empty()) { + BufferInfo *inputBufferInfo = *inputBufferInfoQueue.begin(); + OMX_BUFFERHEADERTYPE *inputBufferHeader = inputBufferInfo->mHeader; + + BufferInfo *outputBufferInfo = *outputBufferInfoQueue.begin(); + OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader; + + if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inputBufferInfoQueue.erase(inputBufferInfoQueue.begin()); + inputBufferInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inputBufferHeader); + + outputBufferHeader->nFilledLen = 0; + outputBufferHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outputBufferInfoQueue.erase(outputBufferInfoQueue.begin()); + outputBufferInfo->mOwnedByUs = false; + notifyFillBufferDone(outputBufferHeader); + return; + } + + uint8_t* source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset; + + // NOTE: As much as nothing is known about color format + // when it is denoted as AndroidOpaque, it is at least + // assumed to be planar. + if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + ConvertSemiPlanarToPlanar(source, mConversionBuffer, mWidth, mHeight); + source = mConversionBuffer; + } + vpx_image_t raw_frame; + vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight, + kInputBufferAlignment, source); + codec_return = vpx_codec_encode(mCodecContext, + &raw_frame, + inputBufferHeader->nTimeStamp, // in timebase units + mFrameDurationUs, // frame duration in timebase units + 0, // frame flags + VPX_DL_REALTIME); // encoding deadline + if (codec_return != VPX_CODEC_OK) { + ALOGE("vpx encoder failed to encode frame"); + notify(OMX_EventError, + OMX_ErrorUndefined, + 0, // Extra notification data + NULL); // Notification data pointer + return; + } + + vpx_codec_iter_t encoded_packet_iterator = NULL; + const vpx_codec_cx_pkt_t* encoded_packet; + + while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) { + if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) { + outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts; + outputBufferHeader->nFlags = 0; + outputBufferHeader->nOffset = 0; + outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz; + memcpy(outputBufferHeader->pBuffer, + encoded_packet->data.frame.buf, + encoded_packet->data.frame.sz); + outputBufferInfo->mOwnedByUs = false; + outputBufferInfoQueue.erase(outputBufferInfoQueue.begin()); + notifyFillBufferDone(outputBufferHeader); + } + } + + inputBufferInfo->mOwnedByUs = false; + inputBufferInfoQueue.erase(inputBufferInfoQueue.begin()); + notifyEmptyBufferDone(inputBufferHeader); + } +} +} // namespace android + + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftVPXEncoder(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h new file mode 100644 index 0000000..3bc05c0 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2013 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 SOFT_VPX_ENCODER_H_ + +#define SOFT_VPX_ENCODER_H_ + +#include "SimpleSoftOMXComponent.h" + +#include +#include + +#include "vpx/vpx_encoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8cx.h" + +namespace android { + +// Exposes a vpx encoder as an OMX Component +// +// Boilerplate for callback bindings are taken care +// by the base class SimpleSoftOMXComponent and its +// parent SoftOMXComponent. +// +// Only following encoder settings are available +// - target bitrate +// - rate control (constant / variable) +// - frame rate +// - error resilience +// - token partitioning +// - reconstruction & loop filters (g_profile) +// +// Only following color formats are recognized +// - YUV420Planar +// - YUV420SemiPlanar +// - AndroidOpaque +// +// Following settings are not configurable by the client +// - encoding deadline is realtime +// - multithreaded encoding utilizes a number of threads equal +// to online cpu's available +// - the algorithm interface for encoder is vp8 +// - fractional bits of frame rate is discarded +// - OMX timestamps are in microseconds, therefore +// encoder timebase is fixed to 1/1000000 + +class SoftVPXEncoder : public SimpleSoftOMXComponent { + public: + SoftVPXEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + protected: + virtual ~SoftVPXEncoder(); + + // Returns current values for requested OMX + // parameters + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR param); + + // Validates, extracts and stores relevant OMX + // parameters + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR param); + + // OMX callback when buffers available + // Note that both an input and output buffer + // is expected to be available to carry out + // encoding of the frame + virtual void onQueueFilled(OMX_U32 portIndex); + + private: + // number of buffers allocated per port + static const uint32_t kNumBuffers = 4; + + // OMX port indexes that refer to input and + // output ports respectively + static const uint32_t kInputPortIndex = 0; + static const uint32_t kOutputPortIndex = 1; + + // Byte-alignment required for buffers + static const uint32_t kInputBufferAlignment = 1; + static const uint32_t kOutputBufferAlignment = 2; + + // Max value supported for DCT partitions + static const uint32_t kMaxDCTPartitions = 3; + + // Number of supported input color formats + static const uint32_t kNumberOfSupportedColorFormats = 3; + + // vpx specific opaque data structure that + // stores encoder state + vpx_codec_ctx_t* mCodecContext; + + // vpx specific data structure that + // stores encoder configuration + vpx_codec_enc_cfg_t* mCodecConfiguration; + + // vpx specific read-only data structure + // that specifies algorithm interface (e.g. vp8) + vpx_codec_iface_t* mCodecInterface; + + // Width of the input frames + int32_t mWidth; + + // Height of the input frames + int32_t mHeight; + + // Target bitrate set for the encoder, in bits per second. + int32_t mBitrate; + + // Bitrate control mode, either constant or variable + vpx_rc_mode mBitrateControlMode; + + // Frame duration is the reciprocal of framerate, denoted + // in microseconds + uint64_t mFrameDurationUs; + + // vp8 specific configuration parameter + // that enables token partitioning of + // the stream into substreams + int32_t mDCTPartitions; + + // Parameter that denotes whether error resilience + // is enabled in encoder + OMX_BOOL mErrorResilience; + + // Color format for the input port + OMX_COLOR_FORMATTYPE mColorFormat; + + // Encoder profile corresponding to OMX level parameter + // + // The inconsistency in the naming is caused by + // OMX spec referring vpx profiles (g_profile) + // as "levels" whereas using the name "profile" for + // something else. + OMX_VIDEO_VP8LEVELTYPE mLevel; + + // Conversion buffer is needed to convert semi + // planar yuv420 to planar format + // It is only allocated if input format is + // indeed YUV420SemiPlanar. + uint8_t* mConversionBuffer; + + // Initializes input and output OMX ports with sensible + // default values. + void initPorts(); + + // Initializes vpx encoder with available settings. + status_t initEncoder(); + + // Releases vpx encoder instance, with it's associated + // data structures. + // + // Unless called earlier, this is handled by the + // dtor. + status_t releaseEncoder(); + + // Handles port changes with respect to color formats + OMX_ERRORTYPE internalSetFormatParams( + const OMX_VIDEO_PARAM_PORTFORMATTYPE* format); + + // Verifies the component role tried to be set to this OMX component is + // strictly video_encoder.vpx + OMX_ERRORTYPE internalSetRoleParams( + const OMX_PARAM_COMPONENTROLETYPE* role); + + // Updates bitrate to reflect port settings. + OMX_ERRORTYPE internalSetBitrateParams( + const OMX_VIDEO_PARAM_BITRATETYPE* bitrate); + + // Handles port definition changes. + OMX_ERRORTYPE internalSetPortParams( + const OMX_PARAM_PORTDEFINITIONTYPE* port); + + // Handles vp8 specific parameters. + OMX_ERRORTYPE internalSetVp8Params( + const OMX_VIDEO_PARAM_VP8TYPE* vp8Params); + + // Updates encoder profile + OMX_ERRORTYPE internalSetProfileLevel( + const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel); + + DISALLOW_EVIL_CONSTRUCTORS(SoftVPXEncoder); +}; + +} // namespace android + +#endif // SOFT_VPX_ENCODER_H_ -- cgit v1.1 From f98ab74cdc06c1978762cb99d7b28061bc7d1044 Mon Sep 17 00:00:00 2001 From: James Dong Date: Tue, 12 Feb 2013 10:36:01 -0800 Subject: Add OMX.google.vpx.encoder Change-Id: I843e6e542533884a94e105e1cb56f16f0440af61 --- media/libstagefright/omx/SoftOMXPlugin.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'media') diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 6e1c04d..b3fe98e 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -51,6 +51,7 @@ static const struct { { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" }, { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" }, { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" }, + { "OMX.google.vpx.encoder", "vpxenc", "video_encoder.vpx" }, { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" }, { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" }, { "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" }, -- cgit v1.1 From 05f625c46b992ab66b8d1527a366fe2746b4e3c7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 13 Feb 2013 09:27:28 -0800 Subject: Add support for fragmented mp4 to MPEG4Extractor This makes FragmentedMP4Extractor obsolete. It will be removed in a separate change. Change-Id: Ida74c07ccf84983e20a1320ee24ffc7a5c083859 --- media/libstagefright/DataSource.cpp | 26 + media/libstagefright/MPEG4Extractor.cpp | 882 +++++++++++++++++++++++++- media/libstagefright/include/MPEG4Extractor.h | 18 + 3 files changed, 897 insertions(+), 29 deletions(-) (limited to 'media') diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 9d0eea2..bcf333e 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -59,6 +59,32 @@ bool DataSource::getUInt16(off64_t offset, uint16_t *x) { return true; } +bool DataSource::getUInt32(off64_t offset, uint32_t *x) { + *x = 0; + + uint32_t tmp; + if (readAt(offset, &tmp, 4) != 4) { + return false; + } + + *x = ntohl(tmp); + + return true; +} + +bool DataSource::getUInt64(off64_t offset, uint64_t *x) { + *x = 0; + + uint64_t tmp; + if (readAt(offset, &tmp, 8) != 8) { + return false; + } + + *x = ntoh64(tmp); + + return true; +} + status_t DataSource::getSize(off64_t *size) { *size = 0; diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 1a62f9d..b2e60be 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -22,8 +22,6 @@ #include "include/SampleTable.h" #include "include/ESDS.h" -#include - #include #include #include @@ -33,13 +31,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include namespace android { @@ -50,15 +46,17 @@ public: MPEG4Source(const sp &format, const sp &dataSource, int32_t timeScale, - const sp &sampleTable); + const sp &sampleTable, + Vector &sidx, + off64_t firstMoofOffset); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); virtual sp getFormat(); - virtual status_t read( - MediaBuffer **buffer, const ReadOptions *options = NULL); + virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL); + virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL); protected: virtual ~MPEG4Source(); @@ -71,6 +69,14 @@ private: int32_t mTimescale; sp mSampleTable; uint32_t mCurrentSampleIndex; + uint32_t mCurrentFragmentIndex; + Vector &mSegments; + off64_t mFirstMoofOffset; + off64_t mCurrentMoofOffset; + off64_t mNextMoofOffset; + uint32_t mCurrentTime; + int32_t mLastParsedTrackId; + int32_t mTrackId; bool mIsAVC; size_t mNALLengthSize; @@ -86,6 +92,38 @@ private: uint8_t *mSrcBuffer; size_t parseNALSize(const uint8_t *data) const; + status_t parseChunk(off64_t *offset); + status_t parseTrackFragmentHeader(off64_t offset, off64_t size); + status_t parseTrackFragmentRun(off64_t offset, off64_t size); + + struct TrackFragmentHeaderInfo { + enum Flags { + kBaseDataOffsetPresent = 0x01, + kSampleDescriptionIndexPresent = 0x02, + kDefaultSampleDurationPresent = 0x08, + kDefaultSampleSizePresent = 0x10, + kDefaultSampleFlagsPresent = 0x20, + kDurationIsEmpty = 0x10000, + }; + + uint32_t mTrackID; + uint32_t mFlags; + uint64_t mBaseDataOffset; + uint32_t mSampleDescriptionIndex; + uint32_t mDefaultSampleDuration; + uint32_t mDefaultSampleSize; + uint32_t mDefaultSampleFlags; + + uint64_t mDataOffset; + }; + TrackFragmentHeaderInfo mTrackFragmentHeaderInfo; + + struct Sample { + off64_t offset; + size_t size; + uint32_t duration; + }; + Vector mCurrentSamples; MPEG4Source(const MPEG4Source &); MPEG4Source &operator=(const MPEG4Source &); @@ -265,7 +303,9 @@ static const char *FourCC2MIME(uint32_t fourcc) { } MPEG4Extractor::MPEG4Extractor(const sp &source) - : mDataSource(source), + : mSidxDuration(0), + mMoofOffset(0), + mDataSource(source), mInitCheck(NO_INIT), mHasVideo(false), mFirstTrack(NULL), @@ -295,6 +335,12 @@ MPEG4Extractor::~MPEG4Extractor() { mFirstSINF = NULL; } +uint32_t MPEG4Extractor::flags() const { + return CAN_PAUSE | + ((mMoofOffset == 0 || mSidxEntries.size() != 0) ? + (CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0); +} + sp MPEG4Extractor::getMetaData() { status_t err; if ((err = readMetaData()) != OK) { @@ -348,15 +394,24 @@ sp MPEG4Extractor::getTrackMetaData( const char *mime; CHECK(track->meta->findCString(kKeyMIMEType, &mime)); if (!strncasecmp("video/", mime, 6)) { - uint32_t sampleIndex; - uint32_t sampleTime; - if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK - && track->sampleTable->getMetaDataForSample( - sampleIndex, NULL /* offset */, NULL /* size */, - &sampleTime) == OK) { - track->meta->setInt64( - kKeyThumbnailTime, - ((int64_t)sampleTime * 1000000) / track->timescale); + if (mMoofOffset > 0) { + int64_t duration; + if (track->meta->findInt64(kKeyDuration, &duration)) { + // nothing fancy, just pick a frame near 1/4th of the duration + track->meta->setInt64( + kKeyThumbnailTime, duration / 4); + } + } else { + uint32_t sampleIndex; + uint32_t sampleTime; + if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK + && track->sampleTable->getMetaDataForSample( + sampleIndex, NULL /* offset */, NULL /* size */, + &sampleTime) == OK) { + track->meta->setInt64( + kKeyThumbnailTime, + ((int64_t)sampleTime * 1000000) / track->timescale); + } } } } @@ -371,7 +426,25 @@ status_t MPEG4Extractor::readMetaData() { off64_t offset = 0; status_t err; - while ((err = parseChunk(&offset, 0)) == OK) { + while (true) { + err = parseChunk(&offset, 0); + if (err == OK) { + continue; + } + + uint32_t hdr[2]; + if (mDataSource->readAt(offset, hdr, 8) < 8) { + break; + } + uint32_t chunk_type = ntohl(hdr[1]); + if (chunk_type == FOURCC('s', 'i', 'd', 'x')) { + // parse the sidx box too + continue; + } else if (chunk_type == FOURCC('m', 'o', 'o', 'f')) { + // store the offset of the first segment + mMoofOffset = offset; + } + break; } if (mInitCheck == OK) { @@ -630,7 +703,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { char chunk[5]; MakeFourCCString(chunk_type, chunk); - ALOGV("chunk: %s @ %lld", chunk, *offset); + ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth); #if 0 static const char kWhitespace[] = " "; @@ -816,7 +889,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->timescale = ntohl(timescale); - int64_t duration; + int64_t duration = 0; if (version == 1) { if (mDataSource->readAt( timescale_offset + 4, &duration, sizeof(duration)) @@ -825,13 +898,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } duration = ntoh64(duration); } else { - int32_t duration32; + uint32_t duration32; if (mDataSource->readAt( timescale_offset + 4, &duration32, sizeof(duration32)) < (ssize_t)sizeof(duration32)) { return ERROR_IO; } - duration = ntohl(duration32); + // ffmpeg sets duration to -1, which is incorrect. + if (duration32 != 0xffffffff) { + duration = ntohl(duration32); + } } mLastTrack->meta->setInt64( kKeyDuration, (duration * 1000000) / mLastTrack->timescale); @@ -1075,11 +1151,23 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return err; } - // Assume that a given buffer only contains at most 10 fragments, - // each fragment originally prefixed with a 2 byte length will - // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion, - // and thus will grow by 2 bytes per fragment. - mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2); + if (max_size != 0) { + // Assume that a given buffer only contains at most 10 chunks, + // each chunk originally prefixed with a 2 byte length will + // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion, + // and thus will grow by 2 bytes per chunk. + mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2); + } else { + // No size was specified. Pick a conservatively large size. + int32_t width, height; + if (mLastTrack->meta->findInt32(kKeyWidth, &width) && + mLastTrack->meta->findInt32(kKeyHeight, &height)) { + mLastTrack->meta->setInt32(kKeyMaxInputSize, width * height * 3 / 2); + } else { + ALOGE("No width or height, assuming worst case 1080p"); + mLastTrack->meta->setInt32(kKeyMaxInputSize, 3110400); + } + } *offset += chunk_size; // Calculate average frame rate. @@ -1448,6 +1536,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('s', 'i', 'd', 'x'): + { + parseSegmentIndex(data_offset, chunk_data_size); + *offset += chunk_size; + return UNKNOWN_ERROR; // stop parsing after sidx + } + default: { *offset += chunk_size; @@ -1458,6 +1553,125 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return OK; } +status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { + ALOGV("MPEG4Extractor::parseSegmentIndex"); + + if (size < 12) { + return -EINVAL; + } + + uint32_t flags; + if (!mDataSource->getUInt32(offset, &flags)) { + return ERROR_MALFORMED; + } + + uint32_t version = flags >> 24; + flags &= 0xffffff; + + ALOGV("sidx version %d", version); + + uint32_t referenceId; + if (!mDataSource->getUInt32(offset + 4, &referenceId)) { + return ERROR_MALFORMED; + } + + uint32_t timeScale; + if (!mDataSource->getUInt32(offset + 8, &timeScale)) { + return ERROR_MALFORMED; + } + ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale); + + uint64_t earliestPresentationTime; + uint64_t firstOffset; + + offset += 12; + size -= 12; + + if (version == 0) { + if (size < 8) { + return -EINVAL; + } + uint32_t tmp; + if (!mDataSource->getUInt32(offset, &tmp)) { + return ERROR_MALFORMED; + } + earliestPresentationTime = tmp; + if (!mDataSource->getUInt32(offset + 4, &tmp)) { + return ERROR_MALFORMED; + } + firstOffset = tmp; + offset += 8; + size -= 8; + } else { + if (size < 16) { + return -EINVAL; + } + if (!mDataSource->getUInt64(offset, &earliestPresentationTime)) { + return ERROR_MALFORMED; + } + if (!mDataSource->getUInt64(offset + 8, &firstOffset)) { + return ERROR_MALFORMED; + } + offset += 16; + size -= 16; + } + ALOGV("sidx pres/off: %Ld/%Ld", earliestPresentationTime, firstOffset); + + if (size < 4) { + return -EINVAL; + } + + uint16_t referenceCount; + if (!mDataSource->getUInt16(offset + 2, &referenceCount)) { + return ERROR_MALFORMED; + } + offset += 4; + size -= 4; + ALOGV("refcount: %d", referenceCount); + + if (size < referenceCount * 12) { + return -EINVAL; + } + + uint64_t total_duration = 0; + for (unsigned int i = 0; i < referenceCount; i++) { + uint32_t d1, d2, d3; + + if (!mDataSource->getUInt32(offset, &d1) || // size + !mDataSource->getUInt32(offset + 4, &d2) || // duration + !mDataSource->getUInt32(offset + 8, &d3)) { // flags + return ERROR_MALFORMED; + } + + if (d1 & 0x80000000) { + ALOGW("sub-sidx boxes not supported yet"); + } + bool sap = d3 & 0x80000000; + bool saptype = d3 >> 28; + if (!sap || saptype > 2) { + ALOGW("not a stream access point, or unsupported type"); + } + total_duration += d2; + offset += 12; + ALOGV(" item %d, %08x %08x %08x", i, d1, d2, d3); + SidxEntry se; + se.mSize = d1 & 0x7fffffff; + se.mDurationUs = 1000000LL * d2 / timeScale; + mSidxEntries.add(se); + } + + mSidxDuration = total_duration * 1000000 / timeScale; + ALOGV("duration: %lld", mSidxDuration); + + int64_t metaDuration; + if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) { + mLastTrack->meta->setInt64(kKeyDuration, mSidxDuration); + } + return OK; +} + + + status_t MPEG4Extractor::parseTrackHeader( off64_t data_offset, off64_t data_size) { if (data_size < 4) { @@ -1755,7 +1969,8 @@ sp MPEG4Extractor::getTrack(size_t index) { } return new MPEG4Source( - track->meta, mDataSource, track->timescale, track->sampleTable); + track->meta, mDataSource, track->timescale, track->sampleTable, + mSidxEntries, mMoofOffset); } // static @@ -1898,12 +2113,19 @@ MPEG4Source::MPEG4Source( const sp &format, const sp &dataSource, int32_t timeScale, - const sp &sampleTable) + const sp &sampleTable, + Vector &sidx, + off64_t firstMoofOffset) : mFormat(format), mDataSource(dataSource), mTimescale(timeScale), mSampleTable(sampleTable), mCurrentSampleIndex(0), + mCurrentFragmentIndex(0), + mSegments(sidx), + mFirstMoofOffset(firstMoofOffset), + mCurrentMoofOffset(firstMoofOffset), + mCurrentTime(0), mIsAVC(false), mNALLengthSize(0), mStarted(false), @@ -1931,6 +2153,13 @@ MPEG4Source::MPEG4Source( // The number of bytes used to encode the length of a NAL unit. mNALLengthSize = 1 + (ptr[4] & 3); } + + CHECK(format->findInt32(kKeyTrackID, &mTrackId)); + + if (mFirstMoofOffset != 0) { + off64_t offset = mFirstMoofOffset; + parseChunk(&offset); + } } MPEG4Source::~MPEG4Source() { @@ -1988,6 +2217,344 @@ status_t MPEG4Source::stop() { return OK; } +status_t MPEG4Source::parseChunk(off64_t *offset) { + uint32_t hdr[2]; + if (mDataSource->readAt(*offset, hdr, 8) < 8) { + return ERROR_IO; + } + uint64_t chunk_size = ntohl(hdr[0]); + uint32_t chunk_type = ntohl(hdr[1]); + off64_t data_offset = *offset + 8; + + if (chunk_size == 1) { + if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) { + return ERROR_IO; + } + chunk_size = ntoh64(chunk_size); + data_offset += 8; + + if (chunk_size < 16) { + // The smallest valid chunk is 16 bytes long in this case. + return ERROR_MALFORMED; + } + } else if (chunk_size < 8) { + // The smallest valid chunk is 8 bytes long. + return ERROR_MALFORMED; + } + + char chunk[5]; + MakeFourCCString(chunk_type, chunk); + ALOGV("MPEG4Source chunk %s @ %llx", chunk, *offset); + + off64_t chunk_data_size = *offset + chunk_size - data_offset; + + switch(chunk_type) { + + case FOURCC('t', 'r', 'a', 'f'): + case FOURCC('m', 'o', 'o', 'f'): { + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset; + while (*offset < stop_offset) { + status_t err = parseChunk(offset); + if (err != OK) { + return err; + } + } + if (chunk_type == FOURCC('m', 'o', 'o', 'f')) { + // *offset points to then mdat box following this moof + parseChunk(offset); // doesn't actually parse it, just updates offset + mNextMoofOffset = *offset; + } + break; + } + + case FOURCC('t', 'f', 'h', 'd'): { + status_t err; + if ((err = parseTrackFragmentHeader(data_offset, chunk_data_size)) != OK) { + return err; + } + *offset += chunk_size; + break; + } + + case FOURCC('t', 'r', 'u', 'n'): { + status_t err; + if (mLastParsedTrackId == mTrackId) { + if ((err = parseTrackFragmentRun(data_offset, chunk_data_size)) != OK) { + return err; + } + } + + *offset += chunk_size; + break; + } + + default: { + *offset += chunk_size; + break; + } + } + return OK; +} + +status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) { + + if (size < 8) { + return -EINVAL; + } + + uint32_t flags; + if (!mDataSource->getUInt32(offset, &flags)) { + return ERROR_MALFORMED; + } + + if (flags & 0xff000000) { + return -EINVAL; + } + + if (!mDataSource->getUInt32(offset + 4, (uint32_t*)&mLastParsedTrackId)) { + return ERROR_MALFORMED; + } + + if (mLastParsedTrackId != mTrackId) { + // this is not the right track, skip it + return OK; + } + + mTrackFragmentHeaderInfo.mFlags = flags; + mTrackFragmentHeaderInfo.mTrackID = mLastParsedTrackId; + offset += 8; + size -= 8; + + ALOGV("fragment header: %08x %08x", flags, mTrackFragmentHeaderInfo.mTrackID); + + if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) { + if (size < 8) { + return -EINVAL; + } + + if (!mDataSource->getUInt64(offset, &mTrackFragmentHeaderInfo.mBaseDataOffset)) { + return ERROR_MALFORMED; + } + offset += 8; + size -= 8; + } + + if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) { + if (size < 4) { + return -EINVAL; + } + + if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mSampleDescriptionIndex)) { + return ERROR_MALFORMED; + } + offset += 4; + size -= 4; + } + + if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) { + if (size < 4) { + return -EINVAL; + } + + if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleDuration)) { + return ERROR_MALFORMED; + } + offset += 4; + size -= 4; + } + + if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) { + if (size < 4) { + return -EINVAL; + } + + if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleSize)) { + return ERROR_MALFORMED; + } + offset += 4; + size -= 4; + } + + if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) { + if (size < 4) { + return -EINVAL; + } + + if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleFlags)) { + return ERROR_MALFORMED; + } + offset += 4; + size -= 4; + } + + if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) { + mTrackFragmentHeaderInfo.mBaseDataOffset = mCurrentMoofOffset; + } + + mTrackFragmentHeaderInfo.mDataOffset = 0; + return OK; +} + +status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) { + + ALOGV("MPEG4Extractor::parseTrackFragmentRun"); + if (size < 8) { + return -EINVAL; + } + + enum { + kDataOffsetPresent = 0x01, + kFirstSampleFlagsPresent = 0x04, + kSampleDurationPresent = 0x100, + kSampleSizePresent = 0x200, + kSampleFlagsPresent = 0x400, + kSampleCompositionTimeOffsetPresent = 0x800, + }; + + uint32_t flags; + if (!mDataSource->getUInt32(offset, &flags)) { + return ERROR_MALFORMED; + } + ALOGV("fragment run flags: %08x", flags); + + if (flags & 0xff000000) { + return -EINVAL; + } + + if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) { + // These two shall not be used together. + return -EINVAL; + } + + uint32_t sampleCount; + if (!mDataSource->getUInt32(offset + 4, &sampleCount)) { + return ERROR_MALFORMED; + } + offset += 8; + size -= 8; + + uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset; + + uint32_t firstSampleFlags = 0; + + if (flags & kDataOffsetPresent) { + if (size < 4) { + return -EINVAL; + } + + int32_t dataOffsetDelta; + if (!mDataSource->getUInt32(offset, (uint32_t*)&dataOffsetDelta)) { + return ERROR_MALFORMED; + } + + dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta; + + offset += 4; + size -= 4; + } + + if (flags & kFirstSampleFlagsPresent) { + if (size < 4) { + return -EINVAL; + } + + if (!mDataSource->getUInt32(offset, &firstSampleFlags)) { + return ERROR_MALFORMED; + } + offset += 4; + size -= 4; + } + + uint32_t sampleDuration = 0, sampleSize = 0, sampleFlags = 0, + sampleCtsOffset = 0; + + size_t bytesPerSample = 0; + if (flags & kSampleDurationPresent) { + bytesPerSample += 4; + } else if (mTrackFragmentHeaderInfo.mFlags + & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) { + sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration; + } else { + sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration; + } + + if (flags & kSampleSizePresent) { + bytesPerSample += 4; + } else if (mTrackFragmentHeaderInfo.mFlags + & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) { + sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize; + } else { + sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize; + } + + if (flags & kSampleFlagsPresent) { + bytesPerSample += 4; + } else if (mTrackFragmentHeaderInfo.mFlags + & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) { + sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags; + } else { + sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags; + } + + if (flags & kSampleCompositionTimeOffsetPresent) { + bytesPerSample += 4; + } else { + sampleCtsOffset = 0; + } + + if (size < sampleCount * bytesPerSample) { + return -EINVAL; + } + + Sample tmp; + for (uint32_t i = 0; i < sampleCount; ++i) { + if (flags & kSampleDurationPresent) { + if (!mDataSource->getUInt32(offset, &sampleDuration)) { + return ERROR_MALFORMED; + } + offset += 4; + } + + if (flags & kSampleSizePresent) { + if (!mDataSource->getUInt32(offset, &sampleSize)) { + return ERROR_MALFORMED; + } + offset += 4; + } + + if (flags & kSampleFlagsPresent) { + if (!mDataSource->getUInt32(offset, &sampleFlags)) { + return ERROR_MALFORMED; + } + offset += 4; + } + + if (flags & kSampleCompositionTimeOffsetPresent) { + if (!mDataSource->getUInt32(offset, &sampleCtsOffset)) { + return ERROR_MALFORMED; + } + offset += 4; + } + + ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, " + " flags 0x%08x", + dataOffset, sampleSize, sampleDuration, + (flags & kFirstSampleFlagsPresent) && i == 0 + ? firstSampleFlags : sampleFlags); + tmp.offset = dataOffset; + tmp.size = sampleSize; + tmp.duration = sampleDuration; + mCurrentSamples.add(tmp); + + dataOffset += sampleSize; + } + + mTrackFragmentHeaderInfo.mDataOffset = dataOffset; + + return OK; +} + sp MPEG4Source::getFormat() { Mutex::Autolock autoLock(mLock); @@ -2019,6 +2586,10 @@ status_t MPEG4Source::read( CHECK(mStarted); + if (mFirstMoofOffset > 0) { + return fragmentedRead(out, options); + } + *out = NULL; int64_t targetSampleTimeUs = -1; @@ -2076,6 +2647,7 @@ status_t MPEG4Source::read( // we had seeked to the end of stream, ending normally. err = ERROR_END_OF_STREAM; } + ALOGV("end of stream"); return err; } @@ -2286,6 +2858,255 @@ status_t MPEG4Source::read( } } +status_t MPEG4Source::fragmentedRead( + MediaBuffer **out, const ReadOptions *options) { + + ALOGV("MPEG4Source::fragmentedRead"); + + CHECK(mStarted); + + *out = NULL; + + int64_t targetSampleTimeUs = -1; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + + int numSidxEntries = mSegments.size(); + if (numSidxEntries != 0) { + int64_t totalTime = 0; + off64_t totalOffset = mFirstMoofOffset; + for (int i = 0; i < numSidxEntries; i++) { + const SidxEntry *se = &mSegments[i]; + if (totalTime + se->mDurationUs > seekTimeUs) { + // The requested time is somewhere in this segment + if ((mode == ReadOptions::SEEK_NEXT_SYNC) || + (mode == ReadOptions::SEEK_CLOSEST_SYNC && + (seekTimeUs - totalTime) > (totalTime + se->mDurationUs - seekTimeUs))) { + // requested next sync, or closest sync and it was closer to the end of + // this segment + totalTime += se->mDurationUs; + totalOffset += se->mSize; + } + break; + } + totalTime += se->mDurationUs; + totalOffset += se->mSize; + } + mCurrentMoofOffset = totalOffset; + mCurrentSamples.clear(); + mCurrentSampleIndex = 0; + parseChunk(&totalOffset); + mCurrentTime = totalTime * mTimescale / 1000000ll; + } + + if (mBuffer != NULL) { + mBuffer->release(); + mBuffer = NULL; + } + + // fall through + } + + off64_t offset = 0; + size_t size; + uint32_t cts = 0; + bool isSyncSample = false; + bool newBuffer = false; + if (mBuffer == NULL) { + newBuffer = true; + + if (mCurrentSampleIndex >= mCurrentSamples.size()) { + // move to next fragment + Sample lastSample = mCurrentSamples[mCurrentSamples.size() - 1]; + off64_t nextMoof = mNextMoofOffset; // lastSample.offset + lastSample.size; + mCurrentMoofOffset = nextMoof; + mCurrentSamples.clear(); + mCurrentSampleIndex = 0; + parseChunk(&nextMoof); + if (mCurrentSampleIndex >= mCurrentSamples.size()) { + return ERROR_END_OF_STREAM; + } + } + + const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex]; + offset = smpl->offset; + size = smpl->size; + cts = mCurrentTime; + mCurrentTime += smpl->duration; + isSyncSample = (mCurrentSampleIndex == 0); // XXX + + status_t err = mGroup->acquire_buffer(&mBuffer); + + if (err != OK) { + CHECK(mBuffer == NULL); + ALOGV("acquire_buffer returned %d", err); + return err; + } + } + + if (!mIsAVC || mWantsNALFragments) { + if (newBuffer) { + ssize_t num_bytes_read = + mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); + + if (num_bytes_read < (ssize_t)size) { + mBuffer->release(); + mBuffer = NULL; + + ALOGV("i/o error"); + return ERROR_IO; + } + + CHECK(mBuffer != NULL); + mBuffer->set_range(0, size); + mBuffer->meta_data()->clear(); + mBuffer->meta_data()->setInt64( + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); + + if (targetSampleTimeUs >= 0) { + mBuffer->meta_data()->setInt64( + kKeyTargetTime, targetSampleTimeUs); + } + + if (isSyncSample) { + mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + } + + ++mCurrentSampleIndex; + } + + if (!mIsAVC) { + *out = mBuffer; + mBuffer = NULL; + + return OK; + } + + // Each NAL unit is split up into its constituent fragments and + // each one of them returned in its own buffer. + + CHECK(mBuffer->range_length() >= mNALLengthSize); + + const uint8_t *src = + (const uint8_t *)mBuffer->data() + mBuffer->range_offset(); + + size_t nal_size = parseNALSize(src); + if (mBuffer->range_length() < mNALLengthSize + nal_size) { + ALOGE("incomplete NAL unit."); + + mBuffer->release(); + mBuffer = NULL; + + return ERROR_MALFORMED; + } + + MediaBuffer *clone = mBuffer->clone(); + CHECK(clone != NULL); + clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size); + + CHECK(mBuffer != NULL); + mBuffer->set_range( + mBuffer->range_offset() + mNALLengthSize + nal_size, + mBuffer->range_length() - mNALLengthSize - nal_size); + + if (mBuffer->range_length() == 0) { + mBuffer->release(); + mBuffer = NULL; + } + + *out = clone; + + return OK; + } else { + ALOGV("whole NAL"); + // Whole NAL units are returned but each fragment is prefixed by + // the start code (0x00 00 00 01). + ssize_t num_bytes_read = 0; + int32_t drm = 0; + bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0); + if (usesDRM) { + num_bytes_read = + mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size); + } else { + num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size); + } + + if (num_bytes_read < (ssize_t)size) { + mBuffer->release(); + mBuffer = NULL; + + ALOGV("i/o error"); + return ERROR_IO; + } + + if (usesDRM) { + CHECK(mBuffer != NULL); + mBuffer->set_range(0, size); + + } else { + uint8_t *dstData = (uint8_t *)mBuffer->data(); + size_t srcOffset = 0; + size_t dstOffset = 0; + + while (srcOffset < size) { + bool isMalFormed = (srcOffset + mNALLengthSize > size); + size_t nalLength = 0; + if (!isMalFormed) { + nalLength = parseNALSize(&mSrcBuffer[srcOffset]); + srcOffset += mNALLengthSize; + isMalFormed = srcOffset + nalLength > size; + } + + if (isMalFormed) { + ALOGE("Video is malformed"); + mBuffer->release(); + mBuffer = NULL; + return ERROR_MALFORMED; + } + + if (nalLength == 0) { + continue; + } + + CHECK(dstOffset + 4 <= mBuffer->size()); + + dstData[dstOffset++] = 0; + dstData[dstOffset++] = 0; + dstData[dstOffset++] = 0; + dstData[dstOffset++] = 1; + memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength); + srcOffset += nalLength; + dstOffset += nalLength; + } + CHECK_EQ(srcOffset, size); + CHECK(mBuffer != NULL); + mBuffer->set_range(0, dstOffset); + } + + mBuffer->meta_data()->clear(); + mBuffer->meta_data()->setInt64( + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); + + if (targetSampleTimeUs >= 0) { + mBuffer->meta_data()->setInt64( + kKeyTargetTime, targetSampleTimeUs); + } + + if (isSyncSample) { + mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + } + + ++mCurrentSampleIndex; + + *out = mBuffer; + mBuffer = NULL; + + return OK; + } +} + MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix( const char *mimePrefix) { for (Track *track = mFirstTrack; track != NULL; track = track->next) { @@ -2398,6 +3219,9 @@ static bool BetterSniffMPEG4( off64_t chunkDataSize = offset + chunkSize - chunkDataOffset; + char chunkstring[5]; + MakeFourCCString(chunkType, chunkstring); + ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset); switch (chunkType) { case FOURCC('f', 't', 'y', 'p'): { diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 5c549e0..c68623a 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -18,7 +18,12 @@ #define MPEG4_EXTRACTOR_H_ +#include + +#include #include +#include +#include #include #include @@ -29,6 +34,11 @@ class DataSource; class SampleTable; class String8; +struct SidxEntry { + size_t mSize; + uint32_t mDurationUs; +}; + class MPEG4Extractor : public MediaExtractor { public: // Extractor assumes ownership of "source". @@ -39,6 +49,7 @@ public: virtual sp getTrackMetaData(size_t index, uint32_t flags); virtual sp getMetaData(); + virtual uint32_t flags() const; // for DRM virtual char* getDrmTrackInfo(size_t trackID, int *len); @@ -47,6 +58,7 @@ protected: virtual ~MPEG4Extractor(); private: + struct Track { Track *next; sp meta; @@ -56,6 +68,10 @@ private: bool skipTrack; }; + Vector mSidxEntries; + uint64_t mSidxDuration; + off64_t mMoofOffset; + sp mDataSource; status_t mInitCheck; bool mHasVideo; @@ -93,6 +109,8 @@ private: status_t parseTrackHeader(off64_t data_offset, off64_t data_size); + status_t parseSegmentIndex(off64_t data_offset, size_t data_size); + Track *findTrackByMimePrefix(const char *mimePrefix); MPEG4Extractor(const MPEG4Extractor &); -- cgit v1.1 From ab89ac209fd1c3b0a2227168a48d7f3ae9bc43f3 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 15 Feb 2013 08:26:24 -0800 Subject: Remove FragmentedMP4Extractor MPEG4Extractor now supports fragmented mp4 files. Change-Id: I5659a51f4e5e4407a12535e69238fe3abffda7dc --- media/libstagefright/Android.mk | 1 - media/libstagefright/DataSource.cpp | 2 - media/libstagefright/FragmentedMP4Extractor.cpp | 464 --------------------- media/libstagefright/MediaExtractor.cpp | 8 +- .../include/FragmentedMP4Extractor.h | 70 ---- 5 files changed, 1 insertion(+), 544 deletions(-) delete mode 100644 media/libstagefright/FragmentedMP4Extractor.cpp delete mode 100644 media/libstagefright/include/FragmentedMP4Extractor.h (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 6934e59..acc3abf 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -19,7 +19,6 @@ LOCAL_SRC_FILES:= \ ESDS.cpp \ FileSource.cpp \ FLACExtractor.cpp \ - FragmentedMP4Extractor.cpp \ HTTPBase.cpp \ JPEGSource.cpp \ MP3Extractor.cpp \ diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index bcf333e..19b38ee 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -23,7 +23,6 @@ #include "include/AACExtractor.h" #include "include/DRMExtractor.h" #include "include/FLACExtractor.h" -#include "include/FragmentedMP4Extractor.h" #include "include/HTTPBase.h" #include "include/MP3Extractor.h" #include "include/MPEG2PSExtractor.h" @@ -137,7 +136,6 @@ void DataSource::RegisterSniffer(SnifferFunc func) { // static void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffMPEG4); - RegisterSniffer(SniffFragmentedMP4); RegisterSniffer(SniffMatroska); RegisterSniffer(SniffOgg); RegisterSniffer(SniffWAV); diff --git a/media/libstagefright/FragmentedMP4Extractor.cpp b/media/libstagefright/FragmentedMP4Extractor.cpp deleted file mode 100644 index 496828d..0000000 --- a/media/libstagefright/FragmentedMP4Extractor.cpp +++ /dev/null @@ -1,464 +0,0 @@ -/* - * 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 "FragmentedMP4Extractor" -#include - -#include "include/FragmentedMP4Extractor.h" -#include "include/SampleTable.h" -#include "include/ESDS.h" - -#include - -#include -#include -#include -#include - -#include // for property_get - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -class FragmentedMPEG4Source : public MediaSource { -public: - // Caller retains ownership of the Parser - FragmentedMPEG4Source(bool audio, - const sp &format, - const sp &parser, - const sp &extractor); - - virtual status_t start(MetaData *params = NULL); - virtual status_t stop(); - - virtual sp getFormat(); - - virtual status_t read( - MediaBuffer **buffer, const ReadOptions *options = NULL); - -protected: - virtual ~FragmentedMPEG4Source(); - -private: - Mutex mLock; - - sp mFormat; - sp mParser; - sp mExtractor; - bool mIsAudioTrack; - uint32_t mCurrentSampleIndex; - - bool mIsAVC; - size_t mNALLengthSize; - - bool mStarted; - - MediaBufferGroup *mGroup; - - bool mWantsNALFragments; - - uint8_t *mSrcBuffer; - - FragmentedMPEG4Source(const FragmentedMPEG4Source &); - FragmentedMPEG4Source &operator=(const FragmentedMPEG4Source &); -}; - - -FragmentedMP4Extractor::FragmentedMP4Extractor(const sp &source) - : mLooper(new ALooper), - mParser(new FragmentedMP4Parser()), - mDataSource(source), - mInitCheck(NO_INIT), - mFileMetaData(new MetaData) { - ALOGV("FragmentedMP4Extractor"); - mLooper->registerHandler(mParser); - mLooper->start(false /* runOnCallingThread */); - mParser->start(mDataSource); - - bool hasVideo = mParser->getFormat(false /* audio */, true /* synchronous */) != NULL; - bool hasAudio = mParser->getFormat(true /* audio */, true /* synchronous */) != NULL; - - ALOGV("number of tracks: %d", countTracks()); - - if (hasVideo) { - mFileMetaData->setCString( - kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4); - } else if (hasAudio) { - mFileMetaData->setCString(kKeyMIMEType, "audio/mp4"); - } else { - ALOGE("no audio and no video, no idea what file type this is"); - } - // tracks are numbered such that video track is first, audio track is second - if (hasAudio && hasVideo) { - mTrackCount = 2; - mAudioTrackIndex = 1; - } else if (hasAudio) { - mTrackCount = 1; - mAudioTrackIndex = 0; - } else if (hasVideo) { - mTrackCount = 1; - mAudioTrackIndex = -1; - } else { - mTrackCount = 0; - mAudioTrackIndex = -1; - } -} - -FragmentedMP4Extractor::~FragmentedMP4Extractor() { - ALOGV("~FragmentedMP4Extractor"); - mLooper->stop(); -} - -uint32_t FragmentedMP4Extractor::flags() const { - return CAN_PAUSE | - (mParser->isSeekable() ? (CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0); -} - -sp FragmentedMP4Extractor::getMetaData() { - return mFileMetaData; -} - -size_t FragmentedMP4Extractor::countTracks() { - return mTrackCount; -} - - -sp FragmentedMP4Extractor::getTrackMetaData( - size_t index, uint32_t flags) { - if (index >= countTracks()) { - return NULL; - } - - sp msg = mParser->getFormat(index == mAudioTrackIndex, true /* synchronous */); - - if (msg == NULL) { - ALOGV("got null format for track %d", index); - return NULL; - } - - sp meta = new MetaData(); - convertMessageToMetaData(msg, meta); - return meta; -} - -static void MakeFourCCString(uint32_t x, char *s) { - s[0] = x >> 24; - s[1] = (x >> 16) & 0xff; - s[2] = (x >> 8) & 0xff; - s[3] = x & 0xff; - s[4] = '\0'; -} - -sp FragmentedMP4Extractor::getTrack(size_t index) { - if (index >= countTracks()) { - return NULL; - } - return new FragmentedMPEG4Source(index == mAudioTrackIndex, getTrackMetaData(index, 0), mParser, this); -} - - -//////////////////////////////////////////////////////////////////////////////// - -FragmentedMPEG4Source::FragmentedMPEG4Source( - bool audio, - const sp &format, - const sp &parser, - const sp &extractor) - : mFormat(format), - mParser(parser), - mExtractor(extractor), - mIsAudioTrack(audio), - mStarted(false), - mGroup(NULL), - mWantsNALFragments(false), - mSrcBuffer(NULL) { -} - -FragmentedMPEG4Source::~FragmentedMPEG4Source() { - if (mStarted) { - stop(); - } -} - -status_t FragmentedMPEG4Source::start(MetaData *params) { - Mutex::Autolock autoLock(mLock); - - CHECK(!mStarted); - - int32_t val; - if (params && params->findInt32(kKeyWantsNALFragments, &val) - && val != 0) { - mWantsNALFragments = true; - } else { - mWantsNALFragments = false; - } - ALOGV("caller wants NAL fragments: %s", mWantsNALFragments ? "yes" : "no"); - - mGroup = new MediaBufferGroup; - - // for video, make the buffer big enough for an extremely poorly compressed 1080p frame. - int32_t max_size = mIsAudioTrack ? 65536 : 3110400; - - mGroup->add_buffer(new MediaBuffer(max_size)); - - mSrcBuffer = new uint8_t[max_size]; - - mStarted = true; - - return OK; -} - -status_t FragmentedMPEG4Source::stop() { - Mutex::Autolock autoLock(mLock); - - CHECK(mStarted); - - delete[] mSrcBuffer; - mSrcBuffer = NULL; - - delete mGroup; - mGroup = NULL; - - mStarted = false; - mCurrentSampleIndex = 0; - - return OK; -} - -sp FragmentedMPEG4Source::getFormat() { - Mutex::Autolock autoLock(mLock); - - return mFormat; -} - - -status_t FragmentedMPEG4Source::read( - MediaBuffer **out, const ReadOptions *options) { - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - mParser->seekTo(mIsAudioTrack, seekTimeUs); - } - MediaBuffer *buffer = NULL; - mGroup->acquire_buffer(&buffer); - sp parseBuffer; - - status_t ret = mParser->dequeueAccessUnit(mIsAudioTrack, &parseBuffer, true /* synchronous */); - if (ret != OK) { - buffer->release(); - ALOGV("returning %d", ret); - return ret; - } - sp meta = parseBuffer->meta(); - int64_t timeUs; - CHECK(meta->findInt64("timeUs", &timeUs)); - int32_t isSync; - if (meta->findInt32("is-sync-frame", &isSync) && isSync != 0) { - buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); - } - buffer->meta_data()->setInt64(kKeyTime, timeUs); - buffer->set_range(0, parseBuffer->size()); - memcpy(buffer->data(), parseBuffer->data(), parseBuffer->size()); - *out = buffer; - return OK; -} - - -static bool isCompatibleBrand(uint32_t fourcc) { - static const uint32_t kCompatibleBrands[] = { - FOURCC('i', 's', 'o', 'm'), - FOURCC('i', 's', 'o', '2'), - FOURCC('a', 'v', 'c', '1'), - FOURCC('3', 'g', 'p', '4'), - FOURCC('m', 'p', '4', '1'), - FOURCC('m', 'p', '4', '2'), - - // Won't promise that the following file types can be played. - // Just give these file types a chance. - FOURCC('q', 't', ' ', ' '), // Apple's QuickTime - FOURCC('M', 'S', 'N', 'V'), // Sony's PSP - - FOURCC('3', 'g', '2', 'a'), // 3GPP2 - FOURCC('3', 'g', '2', 'b'), - }; - - for (size_t i = 0; - i < sizeof(kCompatibleBrands) / sizeof(kCompatibleBrands[0]); - ++i) { - if (kCompatibleBrands[i] == fourcc) { - return true; - } - } - - return false; -} - -// Attempt to actually parse the 'ftyp' atom and determine if a suitable -// compatible brand is present. -// Also try to identify where this file's metadata ends -// (end of the 'moov' atom) and report it to the caller as part of -// the metadata. -static bool Sniff( - const sp &source, String8 *mimeType, float *confidence, - sp *meta) { - // We scan up to 128k bytes to identify this file as an MP4. - static const off64_t kMaxScanOffset = 128ll * 1024ll; - - off64_t offset = 0ll; - bool foundGoodFileType = false; - bool isFragmented = false; - off64_t moovAtomEndOffset = -1ll; - bool done = false; - - while (!done && offset < kMaxScanOffset) { - uint32_t hdr[2]; - if (source->readAt(offset, hdr, 8) < 8) { - return false; - } - - uint64_t chunkSize = ntohl(hdr[0]); - uint32_t chunkType = ntohl(hdr[1]); - off64_t chunkDataOffset = offset + 8; - - if (chunkSize == 1) { - if (source->readAt(offset + 8, &chunkSize, 8) < 8) { - return false; - } - - chunkSize = ntoh64(chunkSize); - chunkDataOffset += 8; - - if (chunkSize < 16) { - // The smallest valid chunk is 16 bytes long in this case. - return false; - } - } else if (chunkSize < 8) { - // The smallest valid chunk is 8 bytes long. - return false; - } - - off64_t chunkDataSize = offset + chunkSize - chunkDataOffset; - - char chunkstring[5]; - MakeFourCCString(chunkType, chunkstring); - ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset); - switch (chunkType) { - case FOURCC('f', 't', 'y', 'p'): - { - if (chunkDataSize < 8) { - return false; - } - - uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4; - for (size_t i = 0; i < numCompatibleBrands + 2; ++i) { - if (i == 1) { - // Skip this index, it refers to the minorVersion, - // not a brand. - continue; - } - - uint32_t brand; - if (source->readAt( - chunkDataOffset + 4 * i, &brand, 4) < 4) { - return false; - } - - brand = ntohl(brand); - char brandstring[5]; - MakeFourCCString(brand, brandstring); - ALOGV("Brand: %s", brandstring); - - if (isCompatibleBrand(brand)) { - foundGoodFileType = true; - break; - } - } - - if (!foundGoodFileType) { - return false; - } - - break; - } - - case FOURCC('m', 'o', 'o', 'v'): - { - moovAtomEndOffset = offset + chunkSize; - break; - } - - case FOURCC('m', 'o', 'o', 'f'): - { - // this is kind of broken, since we might not actually find a - // moof box in the first 128k. - isFragmented = true; - done = true; - break; - } - - default: - break; - } - - offset += chunkSize; - } - - if (!foundGoodFileType || !isFragmented) { - return false; - } - - *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; - *confidence = 0.5f; // slightly more than MPEG4Extractor - - if (moovAtomEndOffset >= 0) { - *meta = new AMessage; - (*meta)->setInt64("meta-data-size", moovAtomEndOffset); - (*meta)->setInt32("fragmented", 1); // tell MediaExtractor what to instantiate - - ALOGV("found metadata size: %lld", moovAtomEndOffset); - } - - return true; -} - -// used by DataSource::RegisterDefaultSniffers -bool SniffFragmentedMP4( - const sp &source, String8 *mimeType, float *confidence, - sp *meta) { - ALOGV("SniffFragmentedMP4"); - char prop[PROPERTY_VALUE_MAX]; - if (property_get("media.stagefright.use-fragmp4", prop, NULL) - && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) { - return Sniff(source, mimeType, confidence, meta); - } - - return false; -} - -} // namespace android diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index b18c916..9ab6611 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -21,7 +21,6 @@ #include "include/AMRExtractor.h" #include "include/MP3Extractor.h" #include "include/MPEG4Extractor.h" -#include "include/FragmentedMP4Extractor.h" #include "include/WAVExtractor.h" #include "include/OggExtractor.h" #include "include/MPEG2PSExtractor.h" @@ -94,12 +93,7 @@ sp MediaExtractor::Create( MediaExtractor *ret = NULL; if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) || !strcasecmp(mime, "audio/mp4")) { - int fragmented = 0; - if (meta != NULL && meta->findInt32("fragmented", &fragmented) && fragmented) { - ret = new FragmentedMP4Extractor(source); - } else { - ret = new MPEG4Extractor(source); - } + ret = new MPEG4Extractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { ret = new MP3Extractor(source, meta); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) diff --git a/media/libstagefright/include/FragmentedMP4Extractor.h b/media/libstagefright/include/FragmentedMP4Extractor.h deleted file mode 100644 index 763cd3a..0000000 --- a/media/libstagefright/include/FragmentedMP4Extractor.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 FRAGMENTED_MP4_EXTRACTOR_H_ - -#define FRAGMENTED_MP4_EXTRACTOR_H_ - -#include "include/FragmentedMP4Parser.h" - -#include -#include -#include - -namespace android { - -struct AMessage; -class DataSource; -class SampleTable; -class String8; - -class FragmentedMP4Extractor : public MediaExtractor { -public: - // Extractor assumes ownership of "source". - FragmentedMP4Extractor(const sp &source); - - virtual size_t countTracks(); - virtual sp getTrack(size_t index); - virtual sp getTrackMetaData(size_t index, uint32_t flags); - virtual sp getMetaData(); - virtual uint32_t flags() const; - -protected: - virtual ~FragmentedMP4Extractor(); - -private: - sp mLooper; - sp mParser; - sp mDataSource; - status_t mInitCheck; - size_t mAudioTrackIndex; - size_t mTrackCount; - - sp mFileMetaData; - - Vector mPath; - - FragmentedMP4Extractor(const FragmentedMP4Extractor &); - FragmentedMP4Extractor &operator=(const FragmentedMP4Extractor &); -}; - -bool SniffFragmentedMP4( - const sp &source, String8 *mimeType, float *confidence, - sp *); - -} // namespace android - -#endif // MPEG4_EXTRACTOR_H_ -- cgit v1.1 From 1a2952aee048ca7b1765e2bc09ebe9aeddaeafa3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 14 Feb 2013 17:11:27 -0800 Subject: Refactoring: Rename SurfaceTextureClient to Surface Change-Id: I4e8a8b20914cb64edc37abe68233fbc9f2b5d830 --- media/libmedia/mediaplayer.cpp | 2 +- media/libmediaplayerservice/MediaPlayerService.cpp | 4 ++-- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 2 +- media/libstagefright/AwesomePlayer.cpp | 4 ++-- media/libstagefright/MediaCodec.cpp | 6 +++--- media/libstagefright/tests/SurfaceMediaSource_test.cpp | 18 +++++++++--------- .../wifi-display/sink/DirectRenderer.cpp | 3 ++- .../wifi-display/sink/TunnelRenderer.cpp | 2 +- media/libstagefright/wifi-display/source/Converter.cpp | 2 +- media/libstagefright/wifi-display/wfd.cpp | 2 +- 10 files changed, 23 insertions(+), 22 deletions(-) (limited to 'media') diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 14602bf..3defec3 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index f932131..16f1317 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include // for status_t #include #include @@ -731,7 +731,7 @@ status_t MediaPlayerService::Client::setVideoSurfaceTexture( sp anw; if (bufferProducer != NULL) { - anw = new SurfaceTextureClient(bufferProducer); + anw = new Surface(bufferProducer); status_t err = native_window_api_connect(anw.get(), NATIVE_WINDOW_API_MEDIA); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 30eb4b9..2ba6c22 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -224,7 +224,7 @@ void NuPlayer::setVideoSurfaceTextureAsync( msg->setObject( "native-window", new NativeWindowWrapper( - new SurfaceTextureClient(bufferProducer))); + new Surface(bufferProducer))); } msg->post(); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 0f4d866..bd28118 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -49,7 +49,7 @@ #include #include -#include +#include #include @@ -1183,7 +1183,7 @@ status_t AwesomePlayer::setSurfaceTexture(const sp &buff status_t err; if (bufferProducer != NULL) { - err = setNativeWindow_l(new SurfaceTextureClient(bufferProducer)); + err = setNativeWindow_l(new Surface(bufferProducer)); } else { err = setNativeWindow_l(NULL); } diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 77aceb7..83be0fd 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -22,7 +22,7 @@ #include "include/SoftwareRenderer.h" -#include +#include #include #include #include @@ -132,7 +132,7 @@ status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) { status_t MediaCodec::configure( const sp &format, - const sp &nativeWindow, + const sp &nativeWindow, const sp &crypto, uint32_t flags) { sp msg = new AMessage(kWhatConfigure, id()); @@ -1526,7 +1526,7 @@ ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) { } status_t MediaCodec::setNativeWindow( - const sp &surfaceTextureClient) { + const sp &surfaceTextureClient) { status_t err; if (mNativeWindow != NULL) { diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp index 6a98509..a5459fe 100644 --- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp +++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include @@ -109,7 +109,7 @@ protected: ALOGV("No actual display. Choosing EGLSurface based on SurfaceMediaSource"); sp sms = (new SurfaceMediaSource( getSurfaceWidth(), getSurfaceHeight()))->getBufferQueue(); - sp stc = new SurfaceTextureClient(sms); + sp stc = new Surface(sms); sp window = stc; mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, @@ -361,7 +361,7 @@ protected: mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); // Manual cast is required to avoid constructor ambiguity - mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); + mSTC = new Surface(static_cast >( mSMS->getBufferQueue())); mANW = mSTC; } @@ -375,7 +375,7 @@ protected: const int mYuvTexHeight; sp mSMS; - sp mSTC; + sp mSTC; sp mANW; }; @@ -396,7 +396,7 @@ protected: ALOGV("SMS-GLTest::SetUp()"); android::ProcessState::self()->startThreadPool(); mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); - mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); + mSTC = new Surface(static_cast >( mSMS->getBufferQueue())); mANW = mSTC; // Doing the setup related to the GL Side @@ -416,7 +416,7 @@ protected: const int mYuvTexHeight; sp mSMS; - sp mSTC; + sp mSTC; sp mANW; }; @@ -483,7 +483,7 @@ sp SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int video // query the mediarecorder for a surfacemeidasource and create an egl surface with that void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp& mr) { sp iST = mr->querySurfaceMediaSourceFromMediaServer(); - mSTC = new SurfaceTextureClient(iST); + mSTC = new Surface(iST); mANW = mSTC; if (mEglSurface != EGL_NO_SURFACE) { @@ -750,7 +750,7 @@ TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaS // get the reference to the surfacemediasource living in // mediaserver that is created by stagefrightrecorder sp iST = mr->querySurfaceMediaSourceFromMediaServer(); - mSTC = new SurfaceTextureClient(iST); + mSTC = new Surface(iST); mANW = mSTC; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), @@ -781,7 +781,7 @@ TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWriter) { ALOGV("Verify creating a surface w/ right config + dummy writer*********"); mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); - mSTC = new SurfaceTextureClient(static_cast >( mSMS->getBufferQueue())); + mSTC = new Surface(static_cast >( mSMS->getBufferQueue())); mANW = mSTC; DummyRecorder writer(mSMS); diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index d7f169f..70369bb 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -24,6 +24,7 @@ #include "ATSParser.h" #include +#include #include #include #include @@ -279,7 +280,7 @@ void DirectRenderer::dequeueAccessUnits() { err = mVideoDecoder->configure( videoFormat, mSurfaceTex == NULL - ? NULL : new SurfaceTextureClient(mSurfaceTex), + ? NULL : new Surface(mSurfaceTex), NULL /* crypto */, 0 /* flags */); diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index 04dbd7b..75f9d73 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -376,7 +376,7 @@ void TunnelRenderer::initPlayer() { CHECK_EQ(mPlayer->setDataSource(mStreamSource), (status_t)OK); mPlayer->setVideoSurfaceTexture( - mSurfaceTex != NULL ? mSurfaceTex : mSurface->getSurfaceTexture()); + mSurfaceTex != NULL ? mSurfaceTex : mSurface->getIGraphicBufferProducer()); mPlayer->start(); } diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 376b0df..2861aa9 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -23,7 +23,7 @@ #include "MediaPuller.h" #include -#include +#include #include #include #include diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 21d661e..3f4216a 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -321,7 +321,7 @@ int main(int argc, char **argv) { sp looper = new ALooper; sp sink = new WifiDisplaySink( - session, surface->getSurfaceTexture()); + session, surface->getIGraphicBufferProducer()); looper->registerHandler(sink); if (connectToPort >= 0) { -- cgit v1.1 From feb2179f15bde8241814c8c35f8dace13a923ee7 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 20 Feb 2013 16:47:28 -0800 Subject: Permit conditional registry of mediaserver extensions Change-Id: I94dc0d038e702dfe1779a50a1de0fae9bad15057 --- media/mediaserver/Android.mk | 12 ++++++++++++ media/mediaserver/RegisterExtensions.h | 22 ++++++++++++++++++++++ media/mediaserver/main_mediaserver.cpp | 2 ++ media/mediaserver/register.cpp | 21 +++++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 media/mediaserver/RegisterExtensions.h create mode 100644 media/mediaserver/register.cpp (limited to 'media') diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 0a0f4db..a485646 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -1,4 +1,13 @@ LOCAL_PATH:= $(call my-dir) + +ifneq ($(BOARD_USE_CUSTOM_MEDIASERVEREXTENSIONS),true) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := register.cpp +LOCAL_MODULE := libregistermsext +LOCAL_MODULE_TAGS := optional +include $(BUILD_STATIC_LIBRARY) +endif + include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -15,6 +24,9 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder +LOCAL_STATIC_LIBRARIES := \ + libregistermsext + LOCAL_C_INCLUDES := \ frameworks/av/media/libmediaplayerservice \ frameworks/av/services/medialog \ diff --git a/media/mediaserver/RegisterExtensions.h b/media/mediaserver/RegisterExtensions.h new file mode 100644 index 0000000..9a8c03c --- /dev/null +++ b/media/mediaserver/RegisterExtensions.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2013 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 REGISTER_EXTENSIONS_H +#define REGISTER_EXTENSIONS_H + +extern void registerExtensions(); + +#endif // REGISTER_EXTENSIONS_H diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index 0862952..d5207d5 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -26,6 +26,7 @@ #include #include #include +#include "RegisterExtensions.h" // from LOCAL_C_INCLUDES #include "AudioFlinger.h" @@ -127,6 +128,7 @@ int main(int argc, char** argv) MediaPlayerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); + registerExtensions(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } diff --git a/media/mediaserver/register.cpp b/media/mediaserver/register.cpp new file mode 100644 index 0000000..4ffb2ba --- /dev/null +++ b/media/mediaserver/register.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 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 "RegisterExtensions.h" + +void registerExtensions() +{ +} -- cgit v1.1 From 2b56065a51c49a6235ac974c033c5751e8055869 Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Fri, 23 Nov 2012 19:00:07 +0900 Subject: Handles duplicated NAL start code to fix crash on HLS streams. Some youtube live streams are encoded having duplicated NAL start code, for instance, 00 00 01 00 00 00 01 .... In previous code, zero NAL size causes crash by CHECK_GT(nalsize, 0) macro. With this patch, duplicated NAL start code will be just ignored. TESTED=Played problematic Youtube Live streams. Change-Id: I1d76f111a34bd29cb09b037eb1b0626fe5f5b140 --- media/libstagefright/mpeg2ts/ESQueue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 82fb637..9499712 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -536,7 +536,7 @@ sp ElementaryStreamQueue::dequeueAccessUnitH264() { size_t nalSize; bool foundSlice = false; while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) { - CHECK_GT(nalSize, 0u); + if (nalSize == 0) continue; unsigned nalType = nalStart[0] & 0x1f; bool flush = false; -- cgit v1.1 From ceb388d6c03c38b96dc41c0ea4804b749aa077c4 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Tue, 19 Feb 2013 10:40:14 -0800 Subject: CameraService and Stagefright: Support AppOps Camera: - Signal to AppOpsService when camera usage starts and stops - Listen to permissions revocations and act on them - Currently just kill camera connection when permissions lost Stagefright: - Pass on client name, UID to camera as needed Bug: 8181262 Change-Id: I9e33c9d05e9daa77dbb2d795045d08eb887ec8f0 --- media/libmedia/IMediaRecorder.cpp | 19 ++++++++++++++- media/libmedia/mediarecorder.cpp | 21 +++++++++++++++++ .../libmediaplayerservice/MediaRecorderClient.cpp | 12 +++++++++- media/libmediaplayerservice/MediaRecorderClient.h | 1 + .../libmediaplayerservice/StagefrightRecorder.cpp | 13 +++++++++-- media/libmediaplayerservice/StagefrightRecorder.h | 3 +++ media/libstagefright/CameraSource.cpp | 27 +++++++++++++++------- media/libstagefright/CameraSourceTimeLapse.cpp | 8 ++++++- 8 files changed, 91 insertions(+), 13 deletions(-) (limited to 'media') diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index fdbc747..c935d97 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -51,7 +51,8 @@ enum { SET_PARAMETERS, SET_PREVIEW_SURFACE, SET_CAMERA, - SET_LISTENER + SET_LISTENER, + SET_CLIENT_NAME }; class BpMediaRecorder: public BpInterface @@ -217,6 +218,16 @@ public: return reply.readInt32(); } + status_t setClientName(const String16& clientName) + { + ALOGV("setClientName(%s)", String8(clientName).string()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeString16(clientName); + remote()->transact(SET_CLIENT_NAME, data, &reply); + return reply.readInt32(); + } + status_t prepare() { ALOGV("prepare"); @@ -423,6 +434,12 @@ status_t BnMediaRecorder::onTransact( reply->writeInt32(setListener(listener)); return NO_ERROR; } break; + case SET_CLIENT_NAME: { + ALOGV("SET_CLIENT_NAME"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(setClientName(data.readString16())); + return NO_ERROR; + } case SET_PREVIEW_SURFACE: { ALOGV("SET_PREVIEW_SURFACE"); CHECK_INTERFACE(IMediaRecorder, data, reply); diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 660b1b2..3ac98cc 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -656,6 +656,27 @@ status_t MediaRecorder::setListener(const sp& listener) return NO_ERROR; } +status_t MediaRecorder::setClientName(const String16& clientName) +{ + ALOGV("setClientName"); + if (mMediaRecorder == NULL) { + ALOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + bool isInvalidState = (mCurrentState & + (MEDIA_RECORDER_PREPARED | + MEDIA_RECORDER_RECORDING | + MEDIA_RECORDER_ERROR)); + if (isInvalidState) { + ALOGE("setClientName is called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + mMediaRecorder->setClientName(clientName); + + return NO_ERROR; +} + void MediaRecorder::notify(int msg, int ext1, int ext2) { ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index c6d8b76..a52b238 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -99,7 +99,7 @@ status_t MediaRecorderClient::setVideoSource(int vs) return PERMISSION_DENIED; } Mutex::Autolock lock(mLock); - if (mRecorder == NULL) { + if (mRecorder == NULL) { ALOGE("recorder is not initialized"); return NO_INIT; } @@ -325,6 +325,16 @@ status_t MediaRecorderClient::setListener(const sp& listen return mRecorder->setListener(listener); } +status_t MediaRecorderClient::setClientName(const String16& clientName) { + ALOGV("setClientName(%s)", String8(clientName).string()); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + ALOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setClientName(clientName); +} + status_t MediaRecorderClient::dump(int fd, const Vector& args) const { if (mRecorder != NULL) { return mRecorder->dump(fd, args); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index 5623917..bd0eaf1 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -46,6 +46,7 @@ public: virtual status_t setParameters(const String8& params); virtual status_t setListener( const sp& listener); + virtual status_t setClientName(const String16& clientName); virtual status_t prepare(); virtual status_t getMaxAmplitude(int* max); virtual status_t start(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 497dda6..f570856 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -730,6 +730,12 @@ status_t StagefrightRecorder::setListener(const sp &listen return OK; } +status_t StagefrightRecorder::setClientName(const String16& clientName) { + mClientName = clientName; + + return OK; +} + status_t StagefrightRecorder::prepare() { return OK; } @@ -737,6 +743,8 @@ status_t StagefrightRecorder::prepare() { status_t StagefrightRecorder::start() { CHECK_GE(mOutputFd, 0); + // Get UID here for permission checking + mClientUid = IPCThreadState::self()->getCallingUid(); if (mWriter != NULL) { ALOGE("File writer is not avaialble"); return UNKNOWN_ERROR; @@ -1312,13 +1320,14 @@ status_t StagefrightRecorder::setupCameraSource( } mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera( - mCamera, mCameraProxy, mCameraId, + mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, videoSize, mFrameRate, mPreviewSurface, mTimeBetweenTimeLapseFrameCaptureUs); *cameraSource = mCameraSourceTimeLapse; } else { *cameraSource = CameraSource::CreateFromCamera( - mCamera, mCameraProxy, mCameraId, videoSize, mFrameRate, + mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, + videoSize, mFrameRate, mPreviewSurface, true /*storeMetaDataInVideoBuffers*/); } mCamera.clear(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 351efd4..fbe6fa6 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -56,6 +56,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setParameters(const String8& params); virtual status_t setListener(const sp& listener); + virtual status_t setClientName(const String16& clientName); virtual status_t prepare(); virtual status_t start(); virtual status_t pause(); @@ -72,6 +73,8 @@ private: sp mCameraProxy; sp mPreviewSurface; sp mListener; + String16 mClientName; + uid_t mClientUid; sp mWriter; int mOutputFd; sp mAudioSourceNode; diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index efd7af7..f8557d0 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -121,13 +121,14 @@ static int32_t getColorFormat(const char* colorFormat) { CHECK(!"Unknown color format"); } -CameraSource *CameraSource::Create() { +CameraSource *CameraSource::Create(const String16 &clientName) { Size size; size.width = -1; size.height = -1; sp camera; - return new CameraSource(camera, NULL, 0, size, -1, NULL, false); + return new CameraSource(camera, NULL, 0, clientName, -1, + size, -1, NULL, false); } // static @@ -135,14 +136,16 @@ CameraSource *CameraSource::CreateFromCamera( const sp& camera, const sp& proxy, int32_t cameraId, + const String16& clientName, + uid_t clientUid, Size videoSize, int32_t frameRate, const sp& surface, bool storeMetaDataInVideoBuffers) { CameraSource *source = new CameraSource(camera, proxy, cameraId, - videoSize, frameRate, surface, - storeMetaDataInVideoBuffers); + clientName, clientUid, videoSize, frameRate, surface, + storeMetaDataInVideoBuffers); return source; } @@ -150,6 +153,8 @@ CameraSource::CameraSource( const sp& camera, const sp& proxy, int32_t cameraId, + const String16& clientName, + uid_t clientUid, Size videoSize, int32_t frameRate, const sp& surface, @@ -173,6 +178,7 @@ CameraSource::CameraSource( mVideoSize.height = -1; mInitCheck = init(camera, proxy, cameraId, + clientName, clientUid, videoSize, frameRate, storeMetaDataInVideoBuffers); if (mInitCheck != OK) releaseCamera(); @@ -184,10 +190,10 @@ status_t CameraSource::initCheck() const { status_t CameraSource::isCameraAvailable( const sp& camera, const sp& proxy, - int32_t cameraId) { + int32_t cameraId, const String16& clientName, uid_t clientUid) { if (camera == 0) { - mCamera = Camera::connect(cameraId); + mCamera = Camera::connect(cameraId, clientName, clientUid); if (mCamera == 0) return -EBUSY; mCameraFlags &= ~FLAGS_HOT_CAMERA; } else { @@ -469,6 +475,8 @@ status_t CameraSource::init( const sp& camera, const sp& proxy, int32_t cameraId, + const String16& clientName, + uid_t clientUid, Size videoSize, int32_t frameRate, bool storeMetaDataInVideoBuffers) { @@ -476,7 +484,7 @@ status_t CameraSource::init( ALOGV("init"); status_t err = OK; int64_t token = IPCThreadState::self()->clearCallingIdentity(); - err = initWithCameraAccess(camera, proxy, cameraId, + err = initWithCameraAccess(camera, proxy, cameraId, clientName, clientUid, videoSize, frameRate, storeMetaDataInVideoBuffers); IPCThreadState::self()->restoreCallingIdentity(token); @@ -487,13 +495,16 @@ status_t CameraSource::initWithCameraAccess( const sp& camera, const sp& proxy, int32_t cameraId, + const String16& clientName, + uid_t clientUid, Size videoSize, int32_t frameRate, bool storeMetaDataInVideoBuffers) { ALOGV("initWithCameraAccess"); status_t err = OK; - if ((err = isCameraAvailable(camera, proxy, cameraId)) != OK) { + if ((err = isCameraAvailable(camera, proxy, cameraId, + clientName, clientUid)) != OK) { ALOGE("Camera connection could not be established."); return err; } diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 26ce7ae..2ed2223 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -36,6 +36,8 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera( const sp &camera, const sp &proxy, int32_t cameraId, + const String16& clientName, + uid_t clientUid, Size videoSize, int32_t videoFrameRate, const sp& surface, @@ -43,6 +45,7 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera( CameraSourceTimeLapse *source = new CameraSourceTimeLapse(camera, proxy, cameraId, + clientName, clientUid, videoSize, videoFrameRate, surface, timeBetweenFrameCaptureUs); @@ -59,11 +62,14 @@ CameraSourceTimeLapse::CameraSourceTimeLapse( const sp& camera, const sp& proxy, int32_t cameraId, + const String16& clientName, + uid_t clientUid, Size videoSize, int32_t videoFrameRate, const sp& surface, int64_t timeBetweenFrameCaptureUs) - : CameraSource(camera, proxy, cameraId, videoSize, videoFrameRate, surface, true), + : CameraSource(camera, proxy, cameraId, clientName, clientUid, + videoSize, videoFrameRate, surface, true), mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate), mLastTimeLapseFrameRealTimestampUs(0), mSkipCurrentFrame(false) { -- cgit v1.1 From c9b2e20f7c9a71e07ef398152709c76079decbcd Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 26 Feb 2013 11:32:32 -0800 Subject: Miscellaneous cleanup Abbreviation framesReady to fRdy for new systrace. Put inline const on one line. Use local copy of mState in state. Improve logging. Line length 100. Change-Id: I8201c3ce0e53fd464fd33d02544e52c342d40b68 --- media/libmedia/AudioRecord.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0a2b0b0..40ff1bf 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -47,9 +47,9 @@ status_t AudioRecord::getMinFrameCount( *frameCount = 0; size_t size = 0; - if (AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size) - != NO_ERROR) { - ALOGE("AudioSystem could not query the input buffer size."); + status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size); + if (status != NO_ERROR) { + ALOGE("AudioSystem could not query the input buffer size; status %d", status); return NO_INIT; } -- cgit v1.1 From 8602e5501a653a00e2f82d6fd3f7558382fea1dd Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 4 Mar 2013 13:05:22 -0800 Subject: Modified udptest to sync time across devices. Change-Id: Ib055cd8ab5931395907d017331e27f5d781d7019 --- media/libstagefright/wifi-display/udptest.cpp | 144 ++++++++++++++++---------- 1 file changed, 92 insertions(+), 52 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp index 1cd82c3..86437e0 100644 --- a/media/libstagefright/wifi-display/udptest.cpp +++ b/media/libstagefright/wifi-display/udptest.cpp @@ -47,8 +47,18 @@ private: kWhatStartClient, kWhatUDPNotify, kWhatSendPacket, + kWhatTimedOut, }; + struct TimeInfo { + int64_t mT1; // client timestamp at send + int64_t mT2; // server timestamp at receive + int64_t mT3; // server timestamp at send + int64_t mT4; // client timestamp at receive + }; + + static const int64_t kTimeoutDelayUs = 1000000ll; + sp mNetSession; bool mIsServer; @@ -57,9 +67,16 @@ private: uint32_t mSeqNo; double mTotalTimeUs; int32_t mCount; + int64_t mSumOffsets; + + int64_t mPendingT1; + int32_t mTimeoutGeneration; void postSendPacket(int64_t delayUs = 0ll); + void postTimeout(); + void cancelTimeout(); + DISALLOW_EVIL_CONSTRUCTORS(TestHandler); }; @@ -70,7 +87,10 @@ TestHandler::TestHandler(const sp &netSession) mUDPSession(0), mSeqNo(0), mTotalTimeUs(0.0), - mCount(0) { + mCount(0), + mSumOffsets(0ll), + mPendingT1(0ll), + mTimeoutGeneration(0) { } TestHandler::~TestHandler() { @@ -131,30 +151,31 @@ void TestHandler::onMessageReceived(const sp &msg) { case kWhatSendPacket: { - char buffer[12]; - memset(buffer, 0, sizeof(buffer)); - - buffer[0] = mSeqNo >> 24; - buffer[1] = (mSeqNo >> 16) & 0xff; - buffer[2] = (mSeqNo >> 8) & 0xff; - buffer[3] = mSeqNo & 0xff; - ++mSeqNo; - - int64_t nowUs = ALooper::GetNowUs(); - buffer[4] = nowUs >> 56; - buffer[5] = (nowUs >> 48) & 0xff; - buffer[6] = (nowUs >> 40) & 0xff; - buffer[7] = (nowUs >> 32) & 0xff; - buffer[8] = (nowUs >> 24) & 0xff; - buffer[9] = (nowUs >> 16) & 0xff; - buffer[10] = (nowUs >> 8) & 0xff; - buffer[11] = nowUs & 0xff; + TimeInfo ti; + memset(&ti, 0, sizeof(ti)); + + ti.mT1 = ALooper::GetNowUs(); CHECK_EQ((status_t)OK, mNetSession->sendRequest( - mUDPSession, buffer, sizeof(buffer))); + mUDPSession, &ti, sizeof(ti))); + + mPendingT1 = ti.mT1; + postTimeout(); + break; + } + + case kWhatTimedOut: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mTimeoutGeneration) { + break; + } - postSendPacket(20000ll); + ALOGI("timed out, sending another request"); + postSendPacket(); break; } @@ -182,6 +203,9 @@ void TestHandler::onMessageReceived(const sp &msg) { strerror(-err)); mNetSession->destroySession(sessionID); + + cancelTimeout(); + looper()->stop(); break; } @@ -190,8 +214,16 @@ void TestHandler::onMessageReceived(const sp &msg) { int32_t sessionID; CHECK(msg->findInt32("sessionID", &sessionID)); - sp data; - CHECK(msg->findBuffer("data", &data)); + sp packet; + CHECK(msg->findBuffer("data", &packet)); + + int64_t arrivalTimeUs; + CHECK(packet->meta()->findInt64( + "arrivalTimeUs", &arrivalTimeUs)); + + CHECK_EQ(packet->size(), sizeof(TimeInfo)); + + TimeInfo *ti = (TimeInfo *)packet->data(); if (mIsServer) { if (!mConnected) { @@ -208,43 +240,41 @@ void TestHandler::onMessageReceived(const sp &msg) { mConnected = true; } - int64_t nowUs = ALooper::GetNowUs(); - - sp buffer = new ABuffer(data->size() + 8); - memcpy(buffer->data(), data->data(), data->size()); - - uint8_t *ptr = buffer->data() + data->size(); - - *ptr++ = nowUs >> 56; - *ptr++ = (nowUs >> 48) & 0xff; - *ptr++ = (nowUs >> 40) & 0xff; - *ptr++ = (nowUs >> 32) & 0xff; - *ptr++ = (nowUs >> 24) & 0xff; - *ptr++ = (nowUs >> 16) & 0xff; - *ptr++ = (nowUs >> 8) & 0xff; - *ptr++ = nowUs & 0xff; + ti->mT2 = arrivalTimeUs; + ti->mT3 = ALooper::GetNowUs(); CHECK_EQ((status_t)OK, mNetSession->sendRequest( - mUDPSession, buffer->data(), buffer->size())); + mUDPSession, ti, sizeof(*ti))); } else { - CHECK_EQ(data->size(), 20u); + if (ti->mT1 != mPendingT1) { + break; + } + + cancelTimeout(); + mPendingT1 = 0; - uint32_t seqNo = U32_AT(data->data()); - int64_t t1 = U64_AT(data->data() + 4); - int64_t t2 = U64_AT(data->data() + 12); + ti->mT4 = arrivalTimeUs; - int64_t t3; - CHECK(data->meta()->findInt64("arrivalTimeUs", &t3)); + // One way delay for a packet to travel from client + // to server or back (assumed to be the same either way). + int64_t delay = + (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; -#if 0 - printf("roundtrip seqNo %u, time = %lld us\n", - seqNo, t3 - t1); -#else - mTotalTimeUs += t3 - t1; + // Offset between the client clock (T1, T4) and the + // server clock (T2, T3) timestamps. + int64_t offset = + (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; + + mSumOffsets += offset; ++mCount; - printf("avg. roundtrip time %.2f us\n", mTotalTimeUs / mCount); -#endif + + printf("delay = %lld us,\toffset %lld us\n", + delay, + offset); + fflush(stdout); + + postSendPacket(1000000ll / 30); } break; } @@ -265,6 +295,16 @@ void TestHandler::postSendPacket(int64_t delayUs) { (new AMessage(kWhatSendPacket, id()))->post(delayUs); } +void TestHandler::postTimeout() { + sp msg = new AMessage(kWhatTimedOut, id()); + msg->setInt32("generation", mTimeoutGeneration); + msg->post(kTimeoutDelayUs); +} + +void TestHandler::cancelTimeout() { + ++mTimeoutGeneration; +} + } // namespace android static void usage(const char *me) { -- cgit v1.1 From 7cd58537932ef6f481f68be0b9c597a89cebdfec Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 19 Feb 2013 07:28:30 -0800 Subject: Implement Surface input to MediaCodec. Also, renamed a CHECK_INTERFACE macro that was clashing with the Binder version. Bug 7991062 Change-Id: If5e6ed0a06d9f67975497676e4b05abe3aa3d6c0 --- media/libmedia/IOMX.cpp | 113 +++++- media/libstagefright/ACodec.cpp | 72 ++++ media/libstagefright/MediaCodec.cpp | 101 ++++++ media/libstagefright/OMXClient.cpp | 18 + media/libstagefright/include/OMX.h | 6 + media/libstagefright/include/OMXNodeInstance.h | 24 ++ media/libstagefright/omx/Android.mk | 2 + media/libstagefright/omx/GraphicBufferSource.cpp | 441 +++++++++++++++++++++++ media/libstagefright/omx/GraphicBufferSource.h | 176 +++++++++ media/libstagefright/omx/OMX.cpp | 14 + media/libstagefright/omx/OMXNodeInstance.cpp | 136 +++++++ 11 files changed, 1083 insertions(+), 20 deletions(-) create mode 100644 media/libstagefright/omx/GraphicBufferSource.cpp create mode 100644 media/libstagefright/omx/GraphicBufferSource.h (limited to 'media') diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 48e427a..d6cd43a 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -40,6 +40,8 @@ enum { ENABLE_GRAPHIC_BUFFERS, USE_BUFFER, USE_GRAPHIC_BUFFER, + CREATE_INPUT_SURFACE, + SIGNAL_END_OF_INPUT_STREAM, STORE_META_DATA_IN_BUFFERS, ALLOC_BUFFER, ALLOC_BUFFER_WITH_BACKUP, @@ -280,6 +282,45 @@ public: return err; } + virtual status_t createInputSurface( + node_id node, OMX_U32 port_index, + sp *bufferProducer) { + Parcel data, reply; + status_t err; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeIntPtr((intptr_t)node); + data.writeInt32(port_index); + err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply); + if (err != OK) { + ALOGW("binder transaction failed: %d", err); + return err; + } + + err = reply.readInt32(); + if (err != OK) { + return err; + } + + *bufferProducer = IGraphicBufferProducer::asInterface( + reply.readStrongBinder()); + + return err; + } + + virtual status_t signalEndOfInputStream(node_id node) { + Parcel data, reply; + status_t err; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeIntPtr((intptr_t)node); + err = remote()->transact(SIGNAL_END_OF_INPUT_STREAM, data, &reply); + if (err != OK) { + ALOGW("binder transaction failed: %d", err); + return err; + } + + return reply.readInt32(); + } + virtual status_t storeMetaDataInBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable) { Parcel data, reply; @@ -404,7 +445,7 @@ IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); //////////////////////////////////////////////////////////////////////////////// -#define CHECK_INTERFACE(interface, data, reply) \ +#define CHECK_OMX_INTERFACE(interface, data, reply) \ do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ ALOGW("Call incorrectly routed to " #interface); \ return PERMISSION_DENIED; \ @@ -415,7 +456,7 @@ status_t BnOMX::onTransact( switch (code) { case LIVES_LOCALLY: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void *)data.readIntPtr(); pid_t pid = (pid_t)data.readInt32(); reply->writeInt32(livesLocally(node, pid)); @@ -425,7 +466,7 @@ status_t BnOMX::onTransact( case LIST_NODES: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); List list; listNodes(&list); @@ -448,7 +489,7 @@ status_t BnOMX::onTransact( case ALLOCATE_NODE: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); const char *name = data.readCString(); @@ -468,7 +509,7 @@ status_t BnOMX::onTransact( case FREE_NODE: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); @@ -479,7 +520,7 @@ status_t BnOMX::onTransact( case SEND_COMMAND: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); @@ -497,7 +538,7 @@ status_t BnOMX::onTransact( case GET_CONFIG: case SET_CONFIG: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_INDEXTYPE index = static_cast(data.readInt32()); @@ -539,7 +580,7 @@ status_t BnOMX::onTransact( case GET_STATE: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_STATETYPE state = OMX_StateInvalid; @@ -553,7 +594,7 @@ status_t BnOMX::onTransact( case ENABLE_GRAPHIC_BUFFERS: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -567,7 +608,7 @@ status_t BnOMX::onTransact( case GET_GRAPHIC_BUFFER_USAGE: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -582,7 +623,7 @@ status_t BnOMX::onTransact( case USE_BUFFER: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -602,7 +643,7 @@ status_t BnOMX::onTransact( case USE_GRAPHIC_BUFFER: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -621,9 +662,41 @@ status_t BnOMX::onTransact( return NO_ERROR; } + case CREATE_INPUT_SURFACE: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + node_id node = (void*)data.readIntPtr(); + OMX_U32 port_index = data.readInt32(); + + sp bufferProducer; + status_t err = createInputSurface(node, port_index, + &bufferProducer); + + reply->writeInt32(err); + + if (err == OK) { + reply->writeStrongBinder(bufferProducer->asBinder()); + } + + return NO_ERROR; + } + + case SIGNAL_END_OF_INPUT_STREAM: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + node_id node = (void*)data.readIntPtr(); + + status_t err = signalEndOfInputStream(node); + reply->writeInt32(err); + + return NO_ERROR; + } + case STORE_META_DATA_IN_BUFFERS: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -637,7 +710,7 @@ status_t BnOMX::onTransact( case ALLOC_BUFFER: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -659,7 +732,7 @@ status_t BnOMX::onTransact( case ALLOC_BUFFER_WITH_BACKUP: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -681,7 +754,7 @@ status_t BnOMX::onTransact( case FREE_BUFFER: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); @@ -693,7 +766,7 @@ status_t BnOMX::onTransact( case FILL_BUFFER: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); buffer_id buffer = (void*)data.readIntPtr(); @@ -704,7 +777,7 @@ status_t BnOMX::onTransact( case EMPTY_BUFFER: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); buffer_id buffer = (void*)data.readIntPtr(); @@ -723,7 +796,7 @@ status_t BnOMX::onTransact( case GET_EXTENSION_INDEX: { - CHECK_INTERFACE(IOMX, data, reply); + CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (void*)data.readIntPtr(); const char *parameter_name = data.readCString(); @@ -769,7 +842,7 @@ status_t BnOMXObserver::onTransact( switch (code) { case OBSERVER_ON_MSG: { - CHECK_INTERFACE(IOMXObserver, data, reply); + CHECK_OMX_INTERFACE(IOMXObserver, data, reply); omx_message msg; data.read(&msg, sizeof(msg)); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index a6cc4eb..59fc45e 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -192,6 +193,7 @@ private: friend struct ACodec::UninitializedState; bool onConfigureComponent(const sp &msg); + void onCreateInputSurface(const sp &msg); void onStart(); void onShutdown(bool keepComponentAllocated); @@ -239,6 +241,9 @@ struct ACodec::ExecutingState : public ACodec::BaseState { // to fill with data. void resume(); + // Send EOS on input stream. + void onSignalEndOfInputStream(); + // Returns true iff input and output buffers are in play. bool active() const { return mActive; } @@ -392,6 +397,14 @@ void ACodec::initiateConfigureComponent(const sp &msg) { msg->post(); } +void ACodec::initiateCreateInputSurface() { + (new AMessage(kWhatCreateInputSurface, id()))->post(); +} + +void ACodec::signalEndOfInputStream() { + (new AMessage(kWhatSignalEndOfInputStream, id()))->post(); +} + void ACodec::initiateStart() { (new AMessage(kWhatStart, id()))->post(); } @@ -2469,6 +2482,14 @@ bool ACodec::BaseState::onMessageReceived(const sp &msg) { return onOMXMessage(msg); } + case ACodec::kWhatCreateInputSurface: + case ACodec::kWhatSignalEndOfInputStream: + { + ALOGE("Message 0x%x was not handled", msg->what()); + mCodec->signalError(OMX_ErrorUndefined, INVALID_OPERATION); + return true; + } + default: return false; } @@ -3232,6 +3253,13 @@ bool ACodec::LoadedState::onMessageReceived(const sp &msg) { break; } + case ACodec::kWhatCreateInputSurface: + { + onCreateInputSurface(msg); + handled = true; + break; + } + case ACodec::kWhatStart: { onStart(); @@ -3310,6 +3338,32 @@ bool ACodec::LoadedState::onConfigureComponent( return true; } +void ACodec::LoadedState::onCreateInputSurface( + const sp &msg) { + ALOGV("onCreateInputSurface"); + + sp notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatInputSurfaceCreated); + + sp bufferProducer; + status_t err; + + err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput, + &bufferProducer); + if (err == OK) { + notify->setObject("input-surface", + new BufferProducerWrapper(bufferProducer)); + } else { + // Can't use mCodec->signalError() here -- MediaCodec won't forward + // the error through because it's in the "configured" state. We + // send a kWhatInputSurfaceCreated with an error value instead. + ALOGE("[%s] onCreateInputSurface returning error %d", + mCodec->mComponentName.c_str(), err); + notify->setInt32("err", err); + } + notify->post(); +} + void ACodec::LoadedState::onStart() { ALOGV("onStart"); @@ -3484,6 +3538,17 @@ void ACodec::ExecutingState::resume() { mActive = true; } +void ACodec::ExecutingState::onSignalEndOfInputStream() { + sp notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatSignaledInputEOS); + + status_t err = mCodec->mOMX->signalEndOfInputStream(mCodec->mNode); + if (err != OK) { + notify->setInt32("err", err); + } + notify->post(); +} + void ACodec::ExecutingState::stateEntered() { ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str()); @@ -3573,6 +3638,13 @@ bool ACodec::ExecutingState::onMessageReceived(const sp &msg) { break; } + case ACodec::kWhatSignalEndOfInputStream: + { + onSignalEndOfInputStream(); + handled = true; + break; + } + default: handled = BaseState::onMessageReceived(msg); break; diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 83be0fd..79ea04c 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ MediaCodec::MediaCodec(const sp &looper) : mState(UNINITIALIZED), mLooper(looper), mCodec(new ACodec), + mReplyID(0), mFlags(0), mSoftRenderer(NULL), mDequeueInputTimeoutGeneration(0), @@ -154,6 +156,28 @@ status_t MediaCodec::configure( return PostAndAwaitResponse(msg, &response); } +status_t MediaCodec::createInputSurface( + sp* bufferProducer) { + sp msg = new AMessage(kWhatCreateInputSurface, id()); + + // TODO(fadden): require MediaFormat colorFormat == AndroidOpaque + + sp response; + status_t err = PostAndAwaitResponse(msg, &response); + if (err == NO_ERROR) { + // unwrap the sp + sp obj; + bool found = response->findObject("input-surface", &obj); + CHECK(found); + sp wrapper( + static_cast(obj.get())); + *bufferProducer = wrapper->getBufferProducer(); + } else { + ALOGW("createInputSurface failed, err=%d", err); + } + return err; +} + status_t MediaCodec::start() { sp msg = new AMessage(kWhatStart, id()); @@ -232,6 +256,8 @@ status_t MediaCodec::queueSecureInputBuffer( } status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { + // TODO(fadden): fail if an input Surface has been configured + sp msg = new AMessage(kWhatDequeueInputBuffer, id()); msg->setInt64("timeoutUs", timeoutUs); @@ -288,6 +314,13 @@ status_t MediaCodec::releaseOutputBuffer(size_t index) { return PostAndAwaitResponse(msg, &response); } +status_t MediaCodec::signalEndOfInputStream() { + sp msg = new AMessage(kWhatSignalEndOfInputStream, id()); + + sp response; + return PostAndAwaitResponse(msg, &response); +} + status_t MediaCodec::getOutputFormat(sp *format) const { sp msg = new AMessage(kWhatGetOutputFormat, id()); @@ -575,6 +608,36 @@ void MediaCodec::onMessageReceived(const sp &msg) { break; } + case ACodec::kWhatInputSurfaceCreated: + { + // response to ACodec::kWhatCreateInputSurface + status_t err = NO_ERROR; + sp response = new AMessage(); + if (!msg->findInt32("err", &err)) { + sp obj; + msg->findObject("input-surface", &obj); + CHECK(obj != NULL); + response->setObject("input-surface", obj); + } else { + response->setInt32("err", err); + } + response->postReply(mReplyID); + break; + } + + case ACodec::kWhatSignaledInputEOS: + { + // response to ACodec::kWhatSignalEndOfInputStream + sp response = new AMessage(); + status_t err; + if (msg->findInt32("err", &err)) { + response->setInt32("err", err); + } + response->postReply(mReplyID); + break; + } + + case ACodec::kWhatBuffersAllocated: { int32_t portIndex; @@ -881,6 +944,25 @@ void MediaCodec::onMessageReceived(const sp &msg) { break; } + case kWhatCreateInputSurface: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + // Must be configured, but can't have been started yet. + if (mState != CONFIGURED) { + sp response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + mReplyID = replyID; + mCodec->initiateCreateInputSurface(); + break; + } + case kWhatStart: { uint32_t replyID; @@ -947,6 +1029,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { case kWhatDequeueInputBuffer: { + // TODO(fadden): make this fail if we're using an input Surface uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); @@ -1093,6 +1176,24 @@ void MediaCodec::onMessageReceived(const sp &msg) { break; } + case kWhatSignalEndOfInputStream: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != STARTED || (mFlags & kFlagStickyError)) { + sp response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + mReplyID = replyID; + mCodec->signalEndOfInputStream(); + break; + } + case kWhatGetBuffers: { uint32_t replyID; diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index 7cdb793..ff72e0e 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -83,6 +83,12 @@ struct MuxOMX : public IOMX { node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id *buffer); + virtual status_t createInputSurface( + node_id node, OMX_U32 port_index, + sp *bufferProducer); + + virtual status_t signalEndOfInputStream(node_id node); + virtual status_t allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data); @@ -274,6 +280,18 @@ status_t MuxOMX::useGraphicBuffer( node, port_index, graphicBuffer, buffer); } +status_t MuxOMX::createInputSurface( + node_id node, OMX_U32 port_index, + sp *bufferProducer) { + status_t err = getOMX(node)->createInputSurface( + node, port_index, bufferProducer); + return err; +} + +status_t MuxOMX::signalEndOfInputStream(node_id node) { + return getOMX(node)->signalEndOfInputStream(node); +} + status_t MuxOMX::allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data) { diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index 2c87b34..24b8d98 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -79,6 +79,12 @@ public: node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id *buffer); + virtual status_t createInputSurface( + node_id node, OMX_U32 port_index, + sp *bufferProducer); + + virtual status_t signalEndOfInputStream(node_id node); + virtual status_t allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data); diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index 47ca579..67aba6b 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -27,6 +27,7 @@ namespace android { class IOMXObserver; struct OMXMaster; +struct GraphicBufferSource; struct OMXNodeInstance { OMXNodeInstance( @@ -65,6 +66,11 @@ struct OMXNodeInstance { OMX_U32 portIndex, const sp &graphicBuffer, OMX::buffer_id *buffer); + status_t createInputSurface( + OMX_U32 portIndex, sp *bufferProducer); + + status_t signalEndOfInputStream(); + status_t allocateBuffer( OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer, void **buffer_data); @@ -82,12 +88,18 @@ struct OMXNodeInstance { OMX_U32 rangeOffset, OMX_U32 rangeLength, OMX_U32 flags, OMX_TICKS timestamp); + status_t emptyDirectBuffer( + OMX_BUFFERHEADERTYPE *header, + OMX_U32 rangeOffset, OMX_U32 rangeLength, + OMX_U32 flags, OMX_TICKS timestamp); + status_t getExtensionIndex( const char *parameterName, OMX_INDEXTYPE *index); void onMessage(const omx_message &msg); void onObserverDied(OMXMaster *master); void onGetHandleFailed(); + void onEvent(OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2); static OMX_CALLBACKTYPE kCallbacks; @@ -100,6 +112,13 @@ private: sp mObserver; bool mDying; + // Lock only covers mGraphicBufferSource. We can't always use mLock + // because of rare instances where we'd end up locking it recursively. + Mutex mGraphicBufferSourceLock; + // Access this through getGraphicBufferSource(). + sp mGraphicBufferSource; + + struct ActiveBuffer { OMX_U32 mPortIndex; OMX::buffer_id mID; @@ -132,6 +151,11 @@ private: OMX_IN OMX_PTR pAppData, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + status_t storeMetaDataInBuffers_l(OMX_U32 portIndex, OMX_BOOL enable); + + sp getGraphicBufferSource(); + void setGraphicBufferSource(const sp& bufferSource); + OMXNodeInstance(const OMXNodeInstance &); OMXNodeInstance &operator=(const OMXNodeInstance &); }; diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index d7fbbbe..9129f08 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + GraphicBufferSource.cpp \ OMX.cpp \ OMXMaster.cpp \ OMXNodeInstance.cpp \ @@ -19,6 +20,7 @@ LOCAL_SHARED_LIBRARIES := \ libmedia \ libutils \ libui \ + libgui \ libcutils \ libstagefright_foundation \ libdl diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp new file mode 100644 index 0000000..f207954 --- /dev/null +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GraphicBufferSource" +#include + +#include + +#include +#include + +#include +#include + +namespace android { + +static const bool EXTRA_CHECK = true; + + +GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, + uint32_t bufferWidth, uint32_t bufferHeight) : + mInitCheck(UNKNOWN_ERROR), + mNodeInstance(nodeInstance), + mExecuting(false), + mNumFramesAvailable(0), + mEndOfStream(false), + mEndOfStreamSent(false) { + + ALOGV("GraphicBufferSource w=%u h=%u", bufferWidth, bufferHeight); + + if (bufferWidth == 0 || bufferHeight == 0) { + ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight); + mInitCheck = BAD_VALUE; + return; + } + + mBufferQueue = new BufferQueue(true); + mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight); + mBufferQueue->setSynchronousMode(true); + mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | + GRALLOC_USAGE_HW_TEXTURE); + + // Note that we can't create an sp<...>(this) in a ctor that will not keep a + // reference once the ctor ends, as that would cause the refcount of 'this' + // dropping to 0 at the end of the ctor. Since all we need is a wp<...> + // that's what we create. + wp listener; + listener = static_cast(this); + + sp proxy; + proxy = new BufferQueue::ProxyConsumerListener(listener); + + status_t err = mBufferQueue->consumerConnect(proxy); + if (err != NO_ERROR) { + ALOGE("Error connecting to BufferQueue: %s (%d)", + strerror(-err), err); + return; + } + + mInitCheck = OK; +} + +GraphicBufferSource::~GraphicBufferSource() { + ALOGV("~GraphicBufferSource"); + status_t err = mBufferQueue->consumerDisconnect(); + if (err != NO_ERROR) { + ALOGW("consumerDisconnect failed: %d", err); + } +} + +void GraphicBufferSource::omxExecuting() { + Mutex::Autolock autoLock(mMutex); + ALOGV("--> executing; avail=%d, codec vec size=%zd", + mNumFramesAvailable, mCodecBuffers.size()); + CHECK(!mExecuting); + mExecuting = true; + + // Start by loading up as many buffers as possible. We want to do this, + // rather than just submit the first buffer, to avoid a degenerate case: + // if all BQ buffers arrive before we start executing, and we only submit + // one here, the other BQ buffers will just sit until we get notified + // that the codec buffer has been released. We'd then acquire and + // submit a single additional buffer, repeatedly, never using more than + // one codec buffer simultaneously. (We could instead try to submit + // all BQ buffers whenever any codec buffer is freed, but if we get the + // initial conditions right that will never be useful.) + while (mNumFramesAvailable && isCodecBufferAvailable_l()) { + fillCodecBuffer_l(); + } + + ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable); + + // If EOS has already been signaled, and there are no more frames to + // submit, try to send EOS now as well. + if (mEndOfStream && mNumFramesAvailable == 0) { + submitEndOfInputStream_l(); + } +} + +void GraphicBufferSource::omxIdling(){ + Mutex::Autolock autoLock(mMutex); + ALOGV("--> idling"); + if (!mExecuting) { + // Transition from "loading" to "idling". Nothing to do. + return; + } + + ALOGV("Dropped down to idle, avail=%d eos=%d eosSent=%d", + mNumFramesAvailable, mEndOfStream, mEndOfStreamSent); + + // Codec is no longer executing. Discard all codec-related state. + mCodecBuffers.clear(); + // TODO: scan mCodecBuffers to verify that all mGraphicBuffer entries + // are null; complain if not + + mExecuting = false; +} + +void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) { + Mutex::Autolock autoLock(mMutex); + + if (mExecuting) { + // This should never happen -- buffers can only be allocated when + // transitioning from "loaded" to "idle". + ALOGE("addCodecBuffer: buffer added while executing"); + return; + } + + ALOGV("addCodecBuffer h=%p size=%lu p=%p", + header, header->nAllocLen, header->pBuffer); + CodecBuffer codecBuffer; + codecBuffer.mHeader = header; + mCodecBuffers.add(codecBuffer); +} + +void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { + Mutex::Autolock autoLock(mMutex); + + CHECK(mExecuting); // could this happen if app stop()s early? + + int cbi = findMatchingCodecBuffer_l(header); + if (cbi < 0) { + // This should never happen. + ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header); + return; + } + + ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p", + header, header->nAllocLen, header->nFilledLen, + header->pBuffer); + CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); + + // header->nFilledLen may not be the original value, so we can't compare + // that to zero to see of this was the EOS buffer. Instead we just + // see if the GraphicBuffer reference was null, which should only ever + // happen for EOS. + if (codecBuffer.mGraphicBuffer == NULL) { + CHECK(mEndOfStream); + // No GraphicBuffer to deal with, no additional input or output is + // expected, so just return. + return; + } + + if (EXTRA_CHECK) { + // Pull the graphic buffer handle back out of the buffer, and confirm + // that it matches expectations. + OMX_U8* data = header->pBuffer; + buffer_handle_t bufferHandle; + memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t)); + if (bufferHandle != codecBuffer.mGraphicBuffer->handle) { + // should never happen + ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", + bufferHandle, codecBuffer.mGraphicBuffer->handle); + CHECK(!"codecBufferEmptied: mismatched buffer"); + } + } + + // Find matching entry in our cached copy of the BufferQueue slots. + // If we find a match, release that slot. If we don't, the BufferQueue + // has dropped that GraphicBuffer, and there's nothing for us to release. + // + // (We could store "id" in CodecBuffer and avoid the slot search.) + int id; + for (id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) { + if (mBufferSlot[id] == NULL) { + continue; + } + + if (mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { + ALOGV("cbi %d matches bq slot %d, handle=%p", + cbi, id, mBufferSlot[id]->handle); + + mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, + Fence::NO_FENCE); + break; + } + } + if (id == BufferQueue::NUM_BUFFER_SLOTS) { + ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", + cbi); + } + + // Mark the codec buffer as available by clearing the GraphicBuffer ref. + codecBuffer.mGraphicBuffer = NULL; + + if (mNumFramesAvailable) { + // Fill this codec buffer. + CHECK(!mEndOfStream); + ALOGV("buffer freed, %d frames avail", mNumFramesAvailable); + fillCodecBuffer_l(); + } else if (mEndOfStream) { + // No frames available, but EOS is pending, so use this buffer to + // send that. + ALOGV("buffer freed, EOS pending"); + submitEndOfInputStream_l(); + } + return; +} + +status_t GraphicBufferSource::fillCodecBuffer_l() { + CHECK(mExecuting && mNumFramesAvailable > 0); + int cbi = findAvailableCodecBuffer_l(); + if (cbi < 0) { + // No buffers available, bail. + ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d", + mNumFramesAvailable); + } else { + ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", + mNumFramesAvailable); + BufferQueue::BufferItem item; + status_t err = mBufferQueue->acquireBuffer(&item); + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // shouldn't happen + ALOGW("fillCodecBuffer_l: frame was not available"); + return err; + } else if (err != OK) { + // now what? fake end-of-stream? + ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err); + return err; + } + + mNumFramesAvailable--; + + // Wait for it to become available. + err = item.mFence->waitForever(1000, + "GraphicBufferSource::fillCodecBuffer_l"); + if (err != OK) { + ALOGW("failed to wait for buffer fence: %d", err); + // keep going + } + + // If this is the first time we're seeing this buffer, add it to our + // slot table. + if (item.mGraphicBuffer != NULL) { + ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); + mBufferSlot[item.mBuf] = item.mGraphicBuffer; + } + + err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi); + if (err != OK) { + ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); + mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, Fence::NO_FENCE); + } else { + ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); + } + } + + return OK; +} + +void GraphicBufferSource::signalEndOfInputStream() { + Mutex::Autolock autoLock(mMutex); + ALOGV("signalEndOfInputStream: exec=%d avail=%d", + mExecuting, mNumFramesAvailable); + + // Set the end-of-stream flag. If no frames are pending from the + // BufferQueue, and a codec buffer is available, and we're executing, + // we initiate the EOS from here. Otherwise, we'll let + // codecBufferEmptied() (or omxExecuting) do it. + // + // Note: if there are no pending frames and all codec buffers are + // available, we *must* submit the EOS from here or we'll just + // stall since no future events are expected. + mEndOfStream = true; + + if (mExecuting && mNumFramesAvailable == 0) { + submitEndOfInputStream_l(); + } +} + +status_t GraphicBufferSource::submitBuffer_l(sp& graphicBuffer, + int64_t timestamp, int cbi) { + ALOGV("submitBuffer_l cbi=%d", cbi); + CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); + codecBuffer.mGraphicBuffer = graphicBuffer; + + OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; + CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t)); + OMX_U8* data = header->pBuffer; + const OMX_U32 type = kMetadataBufferTypeGrallocSource; + buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle; + memcpy(data, &type, 4); + memcpy(data + 4, &handle, sizeof(buffer_handle_t)); + + status_t err = mNodeInstance->emptyDirectBuffer(header, 0, + 4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME, + timestamp); + if (err != OK) { + ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err); + codecBuffer.mGraphicBuffer = NULL; + return err; + } + + ALOGV("emptyDirectBuffer succeeded, h=%p p=%p bufhandle=%p", + header, header->pBuffer, handle); + return OK; +} + +void GraphicBufferSource::submitEndOfInputStream_l() { + CHECK(mEndOfStream); + if (mEndOfStreamSent) { + ALOGV("EOS already sent"); + return; + } + + int cbi = findAvailableCodecBuffer_l(); + if (cbi < 0) { + ALOGV("submitEndOfInputStream_l: no codec buffers available"); + return; + } + + // We reject any additional incoming graphic buffers, so there's no need + // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as + // in-use. + CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); + + OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; + if (EXTRA_CHECK) { + // Guard against implementations that don't check nFilledLen. + size_t fillLen = 4 + sizeof(buffer_handle_t); + CHECK(header->nAllocLen >= fillLen); + OMX_U8* data = header->pBuffer; + memset(data, 0xcd, fillLen); + } + + uint64_t timestamp = 0; // does this matter? + + status_t err = mNodeInstance->emptyDirectBuffer(header, /*offset*/ 0, + /*length*/ 0, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS, + timestamp); + if (err != OK) { + ALOGW("emptyDirectBuffer EOS failed: 0x%x", err); + } else { + ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d", + header, cbi); + } +} + +int GraphicBufferSource::findAvailableCodecBuffer_l() { + CHECK(mCodecBuffers.size() > 0); + + for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) { + if (mCodecBuffers[i].mGraphicBuffer == NULL) { + return i; + } + } + return -1; +} + +int GraphicBufferSource::findMatchingCodecBuffer_l( + const OMX_BUFFERHEADERTYPE* header) { + for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) { + if (mCodecBuffers[i].mHeader == header) { + return i; + } + } + return -1; +} + +// BufferQueue::ConsumerListener callback +void GraphicBufferSource::onFrameAvailable() { + Mutex::Autolock autoLock(mMutex); + + ALOGV("onFrameAvailable exec=%d avail=%d", mExecuting, mNumFramesAvailable); + + if (mEndOfStream) { + // This should only be possible if a new buffer was queued after + // EOS was signaled, i.e. the app is misbehaving. + ALOGW("onFrameAvailable: EOS is set, ignoring frame"); + + BufferQueue::BufferItem item; + status_t err = mBufferQueue->acquireBuffer(&item); + if (err == OK) { + mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, item.mFence); + } + return; + } + + mNumFramesAvailable++; + + if (mExecuting) { + fillCodecBuffer_l(); + } +} + +// BufferQueue::ConsumerListener callback +void GraphicBufferSource::onBuffersReleased() { + Mutex::Autolock lock(mMutex); + + uint32_t slotMask; + if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) { + ALOGW("onBuffersReleased: unable to get released buffer set"); + slotMask = 0xffffffff; + } + + ALOGV("onBuffersReleased: 0x%08x", slotMask); + + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if ((slotMask & 0x01) != 0) { + mBufferSlot[i] = NULL; + } + slotMask >>= 1; + } +} + +} // namespace android diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h new file mode 100644 index 0000000..6d49f96 --- /dev/null +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2013 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 GRAPHIC_BUFFER_SOURCE_H_ + +#define GRAPHIC_BUFFER_SOURCE_H_ + +#include +#include +#include + +#include +#include "../include/OMXNodeInstance.h" +#include + +namespace android { + +/* + * This class is used to feed OMX codecs from a Surface via BufferQueue. + * + * Instances of the class don't run on a dedicated thread. Instead, + * various events trigger data movement: + * + * - Availability of a new frame of data from the BufferQueue (notified + * via the onFrameAvailable callback). + * - The return of a codec buffer (via OnEmptyBufferDone). + * - Application signaling end-of-stream. + * - Transition to or from "executing" state. + * + * Frames of data (and, perhaps, the end-of-stream indication) can arrive + * before the codec is in the "executing" state, so we need to queue + * things up until we're ready to go. + */ +class GraphicBufferSource : public BufferQueue::ConsumerListener { +public: + GraphicBufferSource(OMXNodeInstance* nodeInstance, + uint32_t bufferWidth, uint32_t bufferHeight); + virtual ~GraphicBufferSource(); + + // We can't throw an exception if the constructor fails, so we just set + // this and require that the caller test the value. + status_t initCheck() const { + return mInitCheck; + } + + // Returns the handle to the producer side of the BufferQueue. Buffers + // queued on this will be received by GraphicBufferSource. + sp getIGraphicBufferProducer() const { + return mBufferQueue; + } + + // This is called when OMX transitions to OMX_StateExecuting, which means + // we can start handing it buffers. If we already have buffers of data + // sitting in the BufferQueue, this will send them to the codec. + void omxExecuting(); + + // This is called when OMX transitions to OMX_StateIdle. If we were + // previously executing, this means we're about to be shut down. (We + // also enter Idle on the way up.) + void omxIdling(); + + // A "codec buffer", i.e. a buffer that can be used to pass data into + // the encoder, has been allocated. (This call does not call back into + // OMXNodeInstance.) + void addCodecBuffer(OMX_BUFFERHEADERTYPE* header); + + // Called from OnEmptyBufferDone. If we have a BQ buffer available, + // fill it with a new frame of data; otherwise, just mark it as available. + void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header); + + // This is called after the last input frame has been submitted. We + // need to submit an empty buffer with the EOS flag set. If we don't + // have a codec buffer ready, we just set the mEndOfStream flag. + void signalEndOfInputStream(); + +protected: + // BufferQueue::ConsumerListener interface, called when a new frame of + // data is available. If we're executing and a codec buffer is + // available, we acquire the buffer, copy the GraphicBuffer reference + // into the codec buffer, and call Empty[This]Buffer. If we're not yet + // executing or there's no codec buffer available, we just increment + // mNumFramesAvailable and return. + virtual void onFrameAvailable(); + + // BufferQueue::ConsumerListener interface, called when the client has + // released one or more GraphicBuffers. We clear out the appropriate + // set of mBufferSlot entries. + virtual void onBuffersReleased(); + +private: + // Keep track of codec input buffers. They may either be available + // (mGraphicBuffer == NULL) or in use by the codec. + struct CodecBuffer { + OMX_BUFFERHEADERTYPE* mHeader; + sp mGraphicBuffer; + }; + + // Returns the index of an available codec buffer. If none are + // available, returns -1. Mutex must be held by caller. + int findAvailableCodecBuffer_l(); + + // Returns true if a codec buffer is available. + bool isCodecBufferAvailable_l() { + return findAvailableCodecBuffer_l() >= 0; + } + + // Finds the mCodecBuffers entry that matches. Returns -1 if not found. + int findMatchingCodecBuffer_l(const OMX_BUFFERHEADERTYPE* header); + + // Fills a codec buffer with a frame from the BufferQueue. This must + // only be called when we know that a frame of data is ready (i.e. we're + // in the onFrameAvailable callback, or if we're in codecBufferEmptied + // and mNumFramesAvailable is nonzero). Returns without doing anything if + // we don't have a codec buffer available. + status_t fillCodecBuffer_l(); + + // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer + // reference into the codec buffer, and submits the data to the codec. + status_t submitBuffer_l(sp& graphicBuffer, + int64_t timestamp, int cbi); + + // Submits an empty buffer, with the EOS flag set. Returns without + // doing anything if we don't have a codec buffer available. + void submitEndOfInputStream_l(); + + // Lock, covers all member variables. + mutable Mutex mMutex; + + // Used to report constructor failure. + status_t mInitCheck; + + // Pointer back to the object that contains us. We send buffers here. + OMXNodeInstance* mNodeInstance; + + // Set by omxExecuting() / omxIdling(). + bool mExecuting; + + // We consume graphic buffers from this. + sp mBufferQueue; + + // Number of frames pending in BufferQueue that haven't yet been + // forwarded to the codec. + size_t mNumFramesAvailable; + + // Set to true if we want to send end-of-stream after we run out of + // frames in BufferQueue. + bool mEndOfStream; + bool mEndOfStreamSent; + + // Cache of GraphicBuffers from the buffer queue. When the codec + // is done processing a GraphicBuffer, we can use this to map back + // to a slot number. + sp mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS]; + + // Tracks codec buffers. + Vector mCodecBuffers; + + DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource); +}; + +} // namespace android + +#endif // GRAPHIC_BUFFER_SOURCE_H_ diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 29bc733..3987ead 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -345,6 +345,17 @@ status_t OMX::useGraphicBuffer( port_index, graphicBuffer, buffer); } +status_t OMX::createInputSurface( + node_id node, OMX_U32 port_index, + sp *bufferProducer) { + return findInstance(node)->createInputSurface( + port_index, bufferProducer); +} + +status_t OMX::signalEndOfInputStream(node_id node) { + return findInstance(node)->signalEndOfInputStream(); +} + status_t OMX::allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data) { @@ -393,6 +404,9 @@ OMX_ERRORTYPE OMX::OnEvent( OMX_IN OMX_PTR pEventData) { ALOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2); + // Forward to OMXNodeInstance. + findInstance(node)->onEvent(eEvent, nData1, nData2); + omx_message msg; msg.type = omx_message::EVENT; msg.node = node; diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index bff3def..6c2c33b 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -20,14 +20,18 @@ #include "../include/OMXNodeInstance.h" #include "OMXMaster.h" +#include "GraphicBufferSource.h" #include #include +#include #include #include #include +static const OMX_U32 kPortIndexInput = 0; + namespace android { struct BufferMeta { @@ -100,6 +104,17 @@ void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) { mHandle = handle; } +sp OMXNodeInstance::getGraphicBufferSource() { + Mutex::Autolock autoLock(mGraphicBufferSourceLock); + return mGraphicBufferSource; +} + +void OMXNodeInstance::setGraphicBufferSource( + const sp& bufferSource) { + Mutex::Autolock autoLock(mGraphicBufferSourceLock); + mGraphicBufferSource = bufferSource; +} + OMX *OMXNodeInstance::owner() { return mOwner; } @@ -354,7 +369,12 @@ status_t OMXNodeInstance::storeMetaDataInBuffers( OMX_U32 portIndex, OMX_BOOL enable) { Mutex::Autolock autolock(mLock); + return storeMetaDataInBuffers_l(portIndex, enable); +} +status_t OMXNodeInstance::storeMetaDataInBuffers_l( + OMX_U32 portIndex, + OMX_BOOL enable) { OMX_INDEXTYPE index; OMX_STRING name = const_cast( "OMX.google.android.index.storeMetaDataInBuffers"); @@ -411,6 +431,11 @@ status_t OMXNodeInstance::useBuffer( addActiveBuffer(portIndex, *buffer); + sp bufferSource(getGraphicBufferSource()); + if (bufferSource != NULL && portIndex == kPortIndexInput) { + bufferSource->addCodecBuffer(header); + } + return OK; } @@ -530,6 +555,60 @@ status_t OMXNodeInstance::useGraphicBuffer( return OK; } +status_t OMXNodeInstance::createInputSurface( + OMX_U32 portIndex, sp *bufferProducer) { + Mutex::Autolock autolock(mLock); + status_t err; + + const sp& surfaceCheck = getGraphicBufferSource(); + if (surfaceCheck != NULL) { + return ALREADY_EXISTS; + } + + // Input buffers will hold meta-data (gralloc references). + err = storeMetaDataInBuffers_l(portIndex, OMX_TRUE); + if (err != OK) { + return err; + } + + // Retrieve the width and height of the graphic buffer, set when the + // codec was configured. + OMX_PARAM_PORTDEFINITIONTYPE def; + def.nSize = sizeof(def); + def.nVersion.s.nVersionMajor = 1; + def.nVersion.s.nVersionMinor = 0; + def.nVersion.s.nRevision = 0; + def.nVersion.s.nStep = 0; + def.nPortIndex = portIndex; + OMX_ERRORTYPE oerr = OMX_GetParameter( + mHandle, OMX_IndexParamPortDefinition, &def); + CHECK(oerr == OMX_ErrorNone); + + GraphicBufferSource* bufferSource = new GraphicBufferSource( + this, def.format.video.nFrameWidth, def.format.video.nFrameHeight); + if ((err = bufferSource->initCheck()) != OK) { + delete bufferSource; + return err; + } + setGraphicBufferSource(bufferSource); + + *bufferProducer = bufferSource->getIGraphicBufferProducer(); + return OK; +} + +status_t OMXNodeInstance::signalEndOfInputStream() { + // For non-Surface input, the MediaCodec should convert the call to a + // pair of requests (dequeue input buffer, queue input buffer with EOS + // flag set). Seems easier than doing the equivalent from here. + sp bufferSource(getGraphicBufferSource()); + if (bufferSource == NULL) { + ALOGW("signalEndOfInputStream should only be used with Surface input"); + return INVALID_OPERATION; + }; + bufferSource->signalEndOfInputStream(); + return OK; +} + status_t OMXNodeInstance::allocateBuffer( OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer, void **buffer_data) { @@ -560,6 +639,11 @@ status_t OMXNodeInstance::allocateBuffer( addActiveBuffer(portIndex, *buffer); + sp bufferSource(getGraphicBufferSource()); + if (bufferSource != NULL && portIndex == kPortIndexInput) { + bufferSource->addCodecBuffer(header); + } + return OK; } @@ -592,6 +676,11 @@ status_t OMXNodeInstance::allocateBufferWithBackup( addActiveBuffer(portIndex, *buffer); + sp bufferSource(getGraphicBufferSource()); + if (bufferSource != NULL && portIndex == kPortIndexInput) { + bufferSource->addCodecBuffer(header); + } + return OK; } @@ -646,6 +735,26 @@ status_t OMXNodeInstance::emptyBuffer( return StatusFromOMXError(err); } +// like emptyBuffer, but the data is already in header->pBuffer +status_t OMXNodeInstance::emptyDirectBuffer( + OMX_BUFFERHEADERTYPE *header, + OMX_U32 rangeOffset, OMX_U32 rangeLength, + OMX_U32 flags, OMX_TICKS timestamp) { + Mutex::Autolock autoLock(mLock); + + header->nFilledLen = rangeLength; + header->nOffset = rangeOffset; + header->nFlags = flags; + header->nTimeStamp = timestamp; + + OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); + if (err != OMX_ErrorNone) { + ALOGW("emptyDirectBuffer failed, OMX err=0x%x", err); + } + + return StatusFromOMXError(err); +} + status_t OMXNodeInstance::getExtensionIndex( const char *parameterName, OMX_INDEXTYPE *index) { Mutex::Autolock autoLock(mLock); @@ -682,6 +791,22 @@ void OMXNodeInstance::onGetHandleFailed() { delete this; } +// OMXNodeInstance::OnEvent calls OMX::OnEvent, which then calls here. +// Don't try to acquire mLock here -- in rare circumstances this will hang. +void OMXNodeInstance::onEvent( + OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) { + const sp& bufferSource(getGraphicBufferSource()); + + if (bufferSource != NULL && event == OMX_EventCmdComplete && + arg1 == OMX_CommandStateSet) { + if (arg2 == OMX_StateExecuting) { + bufferSource->omxExecuting(); + } else if (arg2 == OMX_StateIdle) { + bufferSource->omxIdling(); + } + } +} + // static OMX_ERRORTYPE OMXNodeInstance::OnEvent( OMX_IN OMX_HANDLETYPE hComponent, @@ -707,6 +832,17 @@ OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone( if (instance->mDying) { return OMX_ErrorNone; } + const sp& bufferSource( + instance->getGraphicBufferSource()); + if (bufferSource != NULL) { + bufferSource->codecBufferEmptied(pBuffer); + + // This is one of the buffers used exclusively by GraphicBufferSource. + // Don't dispatch a message back to ACodec, since it doesn't + // know that anyone asked to have the buffer emptied and will + // be very confused. + return OMX_ErrorNone; + } return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer); } -- cgit v1.1 From 5c4cc0d99d3b1cb35c5d7c237272ee53142745fb Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 26 Nov 2012 10:40:24 -0800 Subject: Add template class SingleStateQueue Change-Id: If7e2bc9b2a216524ee9cbb68682e2634933b4973 --- media/libmedia/Android.mk | 7 ++ media/libmedia/SingleStateQueue.cpp | 107 ++++++++++++++++++++++ media/libmedia/SingleStateQueueInstantiations.cpp | 26 ++++++ 3 files changed, 140 insertions(+) create mode 100644 media/libmedia/SingleStateQueue.cpp create mode 100644 media/libmedia/SingleStateQueueInstantiations.cpp (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 52fa3e1..6b48991 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -53,6 +53,13 @@ LOCAL_SRC_FILES:= \ SoundPool.cpp \ SoundPoolThread.cpp +LOCAL_SRC_FILES += ../libnbaio/roundup.c + +# for +LOCAL_CFLAGS += -DANDROID_SMP=$(if $(findstring true,$(TARGET_CPU_SMP)),1,0) +LOCAL_SRC_FILES += SingleStateQueue.cpp +LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"' + LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat \ libcamera_client libstagefright_foundation \ diff --git a/media/libmedia/SingleStateQueue.cpp b/media/libmedia/SingleStateQueue.cpp new file mode 100644 index 0000000..3503baa --- /dev/null +++ b/media/libmedia/SingleStateQueue.cpp @@ -0,0 +1,107 @@ +/* + * 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 +#include +#include // for android_memory_barrier() +#include + +namespace android { + +template SingleStateQueue::Mutator::Mutator(Shared *shared) + : mSequence(0), mShared((Shared *) shared) +{ + // exactly one of Mutator and Observer must initialize, currently it is Observer + //shared->init(); +} + +template int32_t SingleStateQueue::Mutator::push(const T& value) +{ + Shared *shared = mShared; + int32_t sequence = mSequence; + sequence++; + android_atomic_acquire_store(sequence, &shared->mSequence); + shared->mValue = value; + sequence++; + android_atomic_release_store(sequence, &shared->mSequence); + mSequence = sequence; + // consider signalling a futex here, if we know that observer is waiting + return sequence; +} + +template bool SingleStateQueue::Mutator::ack() +{ + return mShared->mAck - mSequence == 0; +} + +template bool SingleStateQueue::Mutator::ack(int32_t sequence) +{ + // this relies on 2's complement rollover to detect an ancient sequence number + return mShared->mAck - sequence >= 0; +} + +template SingleStateQueue::Observer::Observer(Shared *shared) + : mSequence(0), mSeed(1), mShared((Shared *) shared) +{ + // exactly one of Mutator and Observer must initialize, currently it is Observer + shared->init(); +} + +template bool SingleStateQueue::Observer::poll(T& value) +{ + Shared *shared = mShared; + int32_t before = shared->mSequence; + if (before == mSequence) { + return false; + } + for (int tries = 0; ; ) { + const int MAX_TRIES = 5; + if (before & 1) { + if (++tries >= MAX_TRIES) { + return false; + } + before = shared->mSequence; + } else { + android_memory_barrier(); + T temp = shared->mValue; + int32_t after = android_atomic_release_load(&shared->mSequence); + if (after == before) { + value = temp; + shared->mAck = before; + mSequence = before; + return true; + } + if (++tries >= MAX_TRIES) { + return false; + } + before = after; + } + } +} + +#if 0 +template SingleStateQueue::SingleStateQueue(void /*Shared*/ *shared) +{ + ((Shared *) shared)->init(); +} +#endif + +} // namespace android + +// hack for gcc +#ifdef SINGLE_STATE_QUEUE_INSTANTIATIONS +#include SINGLE_STATE_QUEUE_INSTANTIATIONS +#endif diff --git a/media/libmedia/SingleStateQueueInstantiations.cpp b/media/libmedia/SingleStateQueueInstantiations.cpp new file mode 100644 index 0000000..2afebe9 --- /dev/null +++ b/media/libmedia/SingleStateQueueInstantiations.cpp @@ -0,0 +1,26 @@ +/* + * 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 +#include + +// FIXME hack for gcc + +namespace android { + +template class SingleStateQueue; // typedef StaticAudioTrackSingleStateQueue + +} -- cgit v1.1 From a556c4822fc205db0d27834ba5b637c351d73ffa Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 5 Mar 2013 10:56:27 -0800 Subject: Squashed commit of the following: commit e5919b1f57ea61fa1d380dfdb4e3e832ce73d79d Author: Andreas Huber Date: Wed Feb 27 16:38:48 2013 -0800 Configure TCP datagram sockets to be TCP_NODELAY. Change-Id: Ia724a81e6e27dccd00ac84603e712d69ca77a0cd commit 1b52b393183db8a6dc000a7c31baac544ccfc50c Author: Andreas Huber Date: Wed Feb 27 14:26:01 2013 -0800 Send IDR frame requests on packet loss. Change-Id: I53b7fb85cbd6923491113b93ec3e2175726d654a commit 68d76b4b3a0181b30abc57cd2915273210530a6d Author: Andreas Huber Date: Tue Feb 26 15:12:34 2013 -0800 Revive TunnelRenderer Change-Id: I8c5a9d982793b1c5b841c828227b354f1dab618c commit 3df28a8e9d8bcdc1430016bb088d097eca653b56 Author: Andreas Huber Date: Tue Feb 26 13:53:14 2013 -0800 Disable suspension of video updates. Change-Id: I7e3a16b8d7dd7a55d9f962a2236388931f664106 commit 2ec7a79de019a26ec415016c1478afd762f069cd Author: Andreas Huber Date: Tue Feb 26 08:54:40 2013 -0800 Adds an SNTP client to wfd. Change-Id: Icd7d6104e951e1443e4c1b81ccf6b3731d79d3ec commit c81c3bb5725bb4079a4d7fb02151ad0bb540632f Author: Andreas Huber Date: Mon Feb 25 10:00:58 2013 -0800 Squashed commit of the following: commit b83a4ec96659ef6f6b7c2090fdd866abe3ab78ba Author: Andreas Huber Date: Mon Feb 25 09:28:11 2013 -0800 Some reorganization of the rtp code, renamed StreamHub -> MediaSender Change-Id: I8cf67444960e60426bf74880af1acce41e8b2fef commit 7769cbd739f2a67c58e0c6a7b1a21a12210c7c4d Author: Andreas Huber Date: Fri Feb 22 16:12:18 2013 -0800 Choose a smaller MTU to avoid fragmented IPv4 packets, fix AVC assembler. Change-Id: I274b3cc1483c4e9f4d146dbf9f3d9f7557ef7ef9 commit 1f687ee80a88b56d614c2cf408ff729114ff86a0 Author: Andreas Huber Date: Fri Feb 22 11:38:31 2013 -0800 better reporting. Change-Id: I67f0bb51f106ea77f5cc75938b053c8e8e8f688e commit 7950c1cd59213eb5f281fcde44a772ecffae473d Author: Andreas Huber Date: Fri Feb 22 09:07:41 2013 -0800 stuff Change-Id: Ib99416366d3eec6e6ad69b4d791a8a9408410f3b commit 33c09045b0f86fcaa4619cbd679b47a074f71231 Author: Andreas Huber Date: Thu Feb 21 15:54:01 2013 -0800 Render frames according to their timestamps. Change-Id: I8143a95cffe775799d6a4bb093558bd7abb1f063 commit d8b6daae2160bf1c016d7c6251256b46bb89db42 Author: Andreas Huber Date: Thu Feb 21 15:01:27 2013 -0800 Better packet-lost logic. Change-Id: I611eee5a42bd089638cf45b0e16f628ff2a955ab commit 782c6b15717e2d062d96665a089d06c0577733d0 Author: Andreas Huber Date: Wed Feb 20 15:06:47 2013 -0800 Add a dedicated looper for the MediaReceiver Change-Id: I3b79cad367fb69c9a160a8d009af8c5f5142b98e commit 4c7b8b10861674b773270103bcabd1a99486a691 Author: Andreas Huber Date: Wed Feb 20 14:30:28 2013 -0800 Tweaks to RTPSender and RTPReceiver Change-Id: Ib535552f289a26cfead6df8c63e4c63d3987d4e9 commit 39226b28177a816cda5c67b321745d396b18277d Author: Andreas Huber Date: Tue Feb 19 08:48:25 2013 -0800 Playing around with non muxed delivery Change-Id: I845375f6938d04bc30502840c2ceb7688dc9b237 commit c16d21de75d8ecdbcd9abce14934afe484970061 Author: Andreas Huber Date: Wed Feb 13 14:43:35 2013 -0800 A more solid base for RTP communication. Change-Id: I52033eeb0feba0ff029d61553a821c82f2fa1c3f Change-Id: I57e3bcfc1c59a012b15aaaa42ed81f09c34c26bb Change-Id: I4b09db4a44d0eeded7a1658f6dc6c97d4b8be720 --- .../wifi-display/ANetworkSession.cpp | 12 + media/libstagefright/wifi-display/Android.mk | 32 +- .../libstagefright/wifi-display/MediaReceiver.cpp | 311 +++++++ media/libstagefright/wifi-display/MediaReceiver.h | 108 +++ media/libstagefright/wifi-display/MediaSender.cpp | 443 ++++++++++ media/libstagefright/wifi-display/MediaSender.h | 126 +++ media/libstagefright/wifi-display/SNTPClient.cpp | 174 ++++ media/libstagefright/wifi-display/SNTPClient.h | 62 ++ media/libstagefright/wifi-display/TimeSeries.cpp | 67 -- media/libstagefright/wifi-display/TimeSeries.h | 46 -- .../wifi-display/rtp/RTPAssembler.cpp | 324 ++++++++ .../libstagefright/wifi-display/rtp/RTPAssembler.h | 92 +++ media/libstagefright/wifi-display/rtp/RTPBase.h | 49 ++ .../wifi-display/rtp/RTPReceiver.cpp | 899 +++++++++++++++++++++ .../libstagefright/wifi-display/rtp/RTPReceiver.h | 110 +++ .../libstagefright/wifi-display/rtp/RTPSender.cpp | 701 ++++++++++++++++ media/libstagefright/wifi-display/rtp/RTPSender.h | 112 +++ media/libstagefright/wifi-display/rtptest.cpp | 382 +++++++++ .../wifi-display/sink/DirectRenderer.cpp | 359 +++----- .../wifi-display/sink/DirectRenderer.h | 43 +- .../wifi-display/sink/LinearRegression.cpp | 110 --- .../wifi-display/sink/LinearRegression.h | 52 -- media/libstagefright/wifi-display/sink/RTPSink.cpp | 870 -------------------- media/libstagefright/wifi-display/sink/RTPSink.h | 118 --- .../wifi-display/sink/TunnelRenderer.cpp | 188 +---- .../wifi-display/sink/TunnelRenderer.h | 20 +- .../wifi-display/sink/WifiDisplaySink.cpp | 169 +++- .../wifi-display/sink/WifiDisplaySink.h | 23 +- .../wifi-display/source/PlaybackSession.cpp | 423 ++-------- .../wifi-display/source/PlaybackSession.h | 38 +- .../wifi-display/source/RepeaterSource.h | 2 +- .../libstagefright/wifi-display/source/Sender.cpp | 878 -------------------- media/libstagefright/wifi-display/source/Sender.h | 169 ---- .../wifi-display/source/TSPacketizer.cpp | 26 +- .../wifi-display/source/TSPacketizer.h | 2 + .../wifi-display/source/WifiDisplaySource.cpp | 12 +- 36 files changed, 4346 insertions(+), 3206 deletions(-) create mode 100644 media/libstagefright/wifi-display/MediaReceiver.cpp create mode 100644 media/libstagefright/wifi-display/MediaReceiver.h create mode 100644 media/libstagefright/wifi-display/MediaSender.cpp create mode 100644 media/libstagefright/wifi-display/MediaSender.h create mode 100644 media/libstagefright/wifi-display/SNTPClient.cpp create mode 100644 media/libstagefright/wifi-display/SNTPClient.h delete mode 100644 media/libstagefright/wifi-display/TimeSeries.cpp delete mode 100644 media/libstagefright/wifi-display/TimeSeries.h create mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.cpp create mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.h create mode 100644 media/libstagefright/wifi-display/rtp/RTPBase.h create mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.cpp create mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.h create mode 100644 media/libstagefright/wifi-display/rtp/RTPSender.cpp create mode 100644 media/libstagefright/wifi-display/rtp/RTPSender.h create mode 100644 media/libstagefright/wifi-display/rtptest.cpp delete mode 100644 media/libstagefright/wifi-display/sink/LinearRegression.cpp delete mode 100644 media/libstagefright/wifi-display/sink/LinearRegression.h delete mode 100644 media/libstagefright/wifi-display/sink/RTPSink.cpp delete mode 100644 media/libstagefright/wifi-display/sink/RTPSink.h delete mode 100644 media/libstagefright/wifi-display/source/Sender.cpp delete mode 100644 media/libstagefright/wifi-display/source/Sender.h (limited to 'media') diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 06f71f4..cb6011c 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -314,6 +315,9 @@ status_t ANetworkSession::Session::readMore() { sp packet = new ABuffer(packetSize); memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize); + int64_t nowUs = ALooper::GetNowUs(); + packet->meta()->setInt64("arrivalTimeUs", nowUs); + sp notify = mNotify->dup(); notify->setInt32("sessionID", mSessionID); notify->setInt32("reason", kWhatDatagram); @@ -770,6 +774,14 @@ status_t ANetworkSession::createClientOrServer( err = -errno; goto bail2; } + } else if (mode == kModeCreateTCPDatagramSessionActive) { + int flag = 1; + res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + + if (res < 0) { + err = -errno; + goto bail2; + } } err = MakeSocketNonBlocking(s); diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 5095e82..19f560c 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -4,21 +4,23 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ANetworkSession.cpp \ + MediaReceiver.cpp \ + MediaSender.cpp \ Parameters.cpp \ ParsedMessage.cpp \ + rtp/RTPAssembler.cpp \ + rtp/RTPReceiver.cpp \ + rtp/RTPSender.cpp \ sink/DirectRenderer.cpp \ - sink/LinearRegression.cpp \ - sink/RTPSink.cpp \ sink/TunnelRenderer.cpp \ sink/WifiDisplaySink.cpp \ + SNTPClient.cpp \ source/Converter.cpp \ source/MediaPuller.cpp \ source/PlaybackSession.cpp \ source/RepeaterSource.cpp \ - source/Sender.cpp \ source/TSPacketizer.cpp \ source/WifiDisplaySource.cpp \ - TimeSeries.cpp \ VideoFormats.cpp \ LOCAL_C_INCLUDES:= \ @@ -85,3 +87,25 @@ LOCAL_MODULE:= udptest LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + rtptest.cpp \ + +LOCAL_SHARED_LIBRARIES:= \ + libbinder \ + libgui \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libstagefright_wfd \ + libutils \ + +LOCAL_MODULE:= rtptest + +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp new file mode 100644 index 0000000..3c92d41 --- /dev/null +++ b/media/libstagefright/wifi-display/MediaReceiver.cpp @@ -0,0 +1,311 @@ +/* + * Copyright 2013, 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 "MediaReceiver" +#include + +#include "MediaReceiver.h" + +#include "ANetworkSession.h" +#include "AnotherPacketSource.h" +#include "rtp/RTPReceiver.h" + +#include +#include +#include +#include +#include + +namespace android { + +MediaReceiver::MediaReceiver( + const sp &netSession, + const sp ¬ify) + : mNetSession(netSession), + mNotify(notify), + mMode(MODE_UNDEFINED), + mGeneration(0), + mInitStatus(OK), + mInitDoneCount(0) { +} + +MediaReceiver::~MediaReceiver() { +} + +ssize_t MediaReceiver::addTrack( + RTPReceiver::TransportMode transportMode, + int32_t *localRTPPort) { + if (mMode != MODE_UNDEFINED) { + return INVALID_OPERATION; + } + + size_t trackIndex = mTrackInfos.size(); + + TrackInfo info; + + sp notify = new AMessage(kWhatReceiverNotify, id()); + notify->setInt32("generation", mGeneration); + notify->setSize("trackIndex", trackIndex); + + info.mReceiver = new RTPReceiver(mNetSession, notify); + looper()->registerHandler(info.mReceiver); + + info.mReceiver->registerPacketType( + 33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM); + + info.mReceiver->registerPacketType( + 96, RTPReceiver::PACKETIZATION_AAC); + + info.mReceiver->registerPacketType( + 97, RTPReceiver::PACKETIZATION_H264); + + status_t err = info.mReceiver->initAsync(transportMode, localRTPPort); + + if (err != OK) { + looper()->unregisterHandler(info.mReceiver->id()); + info.mReceiver.clear(); + + return err; + } + + mTrackInfos.push_back(info); + + return trackIndex; +} + +status_t MediaReceiver::connectTrack( + size_t trackIndex, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort) { + if (trackIndex >= mTrackInfos.size()) { + return -ERANGE; + } + + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort); +} + +status_t MediaReceiver::initAsync(Mode mode) { + if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW) + && mTrackInfos.size() > 1) { + return INVALID_OPERATION; + } + + sp msg = new AMessage(kWhatInit, id()); + msg->setInt32("mode", mode); + msg->post(); + + return OK; +} + +void MediaReceiver::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatInit: + { + int32_t mode; + CHECK(msg->findInt32("mode", &mode)); + + CHECK_EQ(mMode, MODE_UNDEFINED); + mMode = (Mode)mode; + + if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { + notifyInitDone(mInitStatus); + } + + mTSParser = new ATSParser(ATSParser::ALIGNED_VIDEO_DATA); + mFormatKnownMask = 0; + break; + } + + case kWhatReceiverNotify: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + if (generation != mGeneration) { + break; + } + + onReceiverNotify(msg); + break; + } + + default: + TRESPASS(); + } +} + +void MediaReceiver::onReceiverNotify(const sp &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case RTPReceiver::kWhatInitDone: + { + ++mInitDoneCount; + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + if (err != OK) { + mInitStatus = err; + ++mGeneration; + } + + if (mMode != MODE_UNDEFINED) { + if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { + notifyInitDone(mInitStatus); + } + } + break; + } + + case RTPReceiver::kWhatError: + { + int32_t err; + CHECK(msg->findInt32("err", &err)); + + notifyError(err); + break; + } + + case RTPReceiver::kWhatAccessUnit: + { + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + int32_t followsDiscontinuity; + if (!msg->findInt32( + "followsDiscontinuity", &followsDiscontinuity)) { + followsDiscontinuity = 0; + } + + if (mMode == MODE_TRANSPORT_STREAM) { + if (followsDiscontinuity) { + mTSParser->signalDiscontinuity( + ATSParser::DISCONTINUITY_TIME, NULL /* extra */); + } + + for (size_t offset = 0; + offset < accessUnit->size(); offset += 188) { + status_t err = mTSParser->feedTSPacket( + accessUnit->data() + offset, 188); + + if (err != OK) { + notifyError(err); + break; + } + } + + drainPackets(0 /* trackIndex */, ATSParser::VIDEO); + drainPackets(1 /* trackIndex */, ATSParser::AUDIO); + } else { + postAccessUnit(trackIndex, accessUnit, NULL); + } + break; + } + + case RTPReceiver::kWhatPacketLost: + { + notifyPacketLost(); + break; + } + + default: + TRESPASS(); + } +} + +void MediaReceiver::drainPackets( + size_t trackIndex, ATSParser::SourceType type) { + sp source = + static_cast( + mTSParser->getSource(type).get()); + + if (source == NULL) { + return; + } + + sp format; + if (!(mFormatKnownMask & (1ul << trackIndex))) { + sp meta = source->getFormat(); + CHECK(meta != NULL); + + CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format)); + + mFormatKnownMask |= 1ul << trackIndex; + } + + status_t finalResult; + while (source->hasBufferAvailable(&finalResult)) { + sp accessUnit; + status_t err = source->dequeueAccessUnit(&accessUnit); + if (err == OK) { + postAccessUnit(trackIndex, accessUnit, format); + format.clear(); + } else if (err != INFO_DISCONTINUITY) { + notifyError(err); + } + } + + if (finalResult != OK) { + notifyError(finalResult); + } +} + +void MediaReceiver::notifyInitDone(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInitDone); + notify->setInt32("err", err); + notify->post(); +} + +void MediaReceiver::notifyError(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +void MediaReceiver::notifyPacketLost() { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatPacketLost); + notify->post(); +} + +void MediaReceiver::postAccessUnit( + size_t trackIndex, + const sp &accessUnit, + const sp &format) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatAccessUnit); + notify->setSize("trackIndex", trackIndex); + notify->setBuffer("accessUnit", accessUnit); + + if (format != NULL) { + notify->setMessage("format", format); + } + + notify->post(); +} + +} // namespace android + + diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h new file mode 100644 index 0000000..7adc3c4 --- /dev/null +++ b/media/libstagefright/wifi-display/MediaReceiver.h @@ -0,0 +1,108 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ATSParser.h" +#include "rtp/RTPReceiver.h" + +namespace android { + +struct ABuffer; +struct ANetworkSession; +struct AMessage; +struct ATSParser; + +// This class facilitates receiving of media data for one or more tracks +// over RTP. Either a 1:1 track to RTP channel mapping is used or a single +// RTP channel provides the data for a transport stream that is consequently +// demuxed and its track's data provided to the observer. +struct MediaReceiver : public AHandler { + enum { + kWhatInitDone, + kWhatError, + kWhatAccessUnit, + kWhatPacketLost, + }; + + MediaReceiver( + const sp &netSession, + const sp ¬ify); + + ssize_t addTrack( + RTPReceiver::TransportMode transportMode, + int32_t *localRTPPort); + + status_t connectTrack( + size_t trackIndex, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort); + + enum Mode { + MODE_UNDEFINED, + MODE_TRANSPORT_STREAM, + MODE_TRANSPORT_STREAM_RAW, + MODE_ELEMENTARY_STREAMS, + }; + status_t initAsync(Mode mode); + +protected: + virtual void onMessageReceived(const sp &msg); + virtual ~MediaReceiver(); + +private: + enum { + kWhatInit, + kWhatReceiverNotify, + }; + + struct TrackInfo { + sp mReceiver; + }; + + sp mNetSession; + sp mNotify; + + Mode mMode; + int32_t mGeneration; + + Vector mTrackInfos; + + status_t mInitStatus; + size_t mInitDoneCount; + + sp mTSParser; + uint32_t mFormatKnownMask; + + void onReceiverNotify(const sp &msg); + + void drainPackets(size_t trackIndex, ATSParser::SourceType type); + + void notifyInitDone(status_t err); + void notifyError(status_t err); + void notifyPacketLost(); + + void postAccessUnit( + size_t trackIndex, + const sp &accessUnit, + const sp &format); + + DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver); +}; + +} // namespace android + diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp new file mode 100644 index 0000000..900aa82 --- /dev/null +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -0,0 +1,443 @@ +/* + * Copyright 2013, 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 "MediaSender" +#include + +#include "MediaSender.h" + +#include "ANetworkSession.h" +#include "rtp/RTPSender.h" +#include "source/TSPacketizer.h" + +#include "include/avc_utils.h" + +#include +#include +#include +#include + +namespace android { + +MediaSender::MediaSender( + const sp &netSession, + const sp ¬ify) + : mNetSession(netSession), + mNotify(notify), + mMode(MODE_UNDEFINED), + mGeneration(0), + mPrevTimeUs(-1ll), + mInitDoneCount(0) { +} + +MediaSender::~MediaSender() { +} + +status_t MediaSender::setHDCP(const sp &hdcp) { + if (mMode != MODE_UNDEFINED) { + return INVALID_OPERATION; + } + + mHDCP = hdcp; + + return OK; +} + +ssize_t MediaSender::addTrack(const sp &format, uint32_t flags) { + if (mMode != MODE_UNDEFINED) { + return INVALID_OPERATION; + } + + TrackInfo info; + info.mFormat = format; + info.mFlags = flags; + info.mPacketizerTrackIndex = -1; + + AString mime; + CHECK(format->findString("mime", &mime)); + info.mIsAudio = !strncasecmp("audio/", mime.c_str(), 6); + + size_t index = mTrackInfos.size(); + mTrackInfos.push_back(info); + + return index; +} + +status_t MediaSender::initAsync( + ssize_t trackIndex, + RTPSender::TransportMode transportMode, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort, + int32_t *localRTPPort) { + if (trackIndex < 0) { + if (mMode != MODE_UNDEFINED) { + return INVALID_OPERATION; + } + + mTSPacketizer = new TSPacketizer; + + status_t err = OK; + for (size_t i = 0; i < mTrackInfos.size(); ++i) { + TrackInfo *info = &mTrackInfos.editItemAt(i); + + sp trackFormat = info->mFormat; + if (mHDCP != NULL && !info->mIsAudio) { + // HDCP2.0 _and_ HDCP 2.1 specs say to set the version + // inside the HDCP descriptor to 0x20!!! + trackFormat->setInt32("hdcp-version", 0x20); + } + + ssize_t packetizerTrackIndex = + mTSPacketizer->addTrack(trackFormat); + + if (packetizerTrackIndex < 0) { + err = packetizerTrackIndex; + break; + } + + info->mPacketizerTrackIndex = packetizerTrackIndex; + } + + if (err == OK) { + sp notify = new AMessage(kWhatSenderNotify, id()); + notify->setInt32("generation", mGeneration); + mTSSender = new RTPSender(mNetSession, notify); + looper()->registerHandler(mTSSender); + + err = mTSSender->initAsync( + transportMode, + remoteHost, + remoteRTPPort, + remoteRTCPPort, + localRTPPort); + + if (err != OK) { + looper()->unregisterHandler(mTSSender->id()); + mTSSender.clear(); + } + } + + if (err != OK) { + for (size_t i = 0; i < mTrackInfos.size(); ++i) { + TrackInfo *info = &mTrackInfos.editItemAt(i); + info->mPacketizerTrackIndex = -1; + } + + mTSPacketizer.clear(); + return err; + } + + mMode = MODE_TRANSPORT_STREAM; + mInitDoneCount = 1; + + return OK; + } + + if (mMode == MODE_TRANSPORT_STREAM) { + return INVALID_OPERATION; + } + + if ((size_t)trackIndex >= mTrackInfos.size()) { + return -ERANGE; + } + + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + + if (info->mSender != NULL) { + return INVALID_OPERATION; + } + + sp notify = new AMessage(kWhatSenderNotify, id()); + notify->setInt32("generation", mGeneration); + notify->setSize("trackIndex", trackIndex); + + info->mSender = new RTPSender(mNetSession, notify); + looper()->registerHandler(info->mSender); + + status_t err = info->mSender->initAsync( + transportMode, + remoteHost, + remoteRTPPort, + remoteRTCPPort, + localRTPPort); + + if (err != OK) { + looper()->unregisterHandler(info->mSender->id()); + info->mSender.clear(); + + return err; + } + + if (mMode == MODE_UNDEFINED) { + mInitDoneCount = mTrackInfos.size(); + } + + mMode = MODE_ELEMENTARY_STREAMS; + + return OK; +} + +status_t MediaSender::queueAccessUnit( + size_t trackIndex, const sp &accessUnit) { + if (mMode == MODE_UNDEFINED) { + return INVALID_OPERATION; + } + + if (trackIndex >= mTrackInfos.size()) { + return -ERANGE; + } + + if (mMode == MODE_TRANSPORT_STREAM) { + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + info->mAccessUnits.push_back(accessUnit); + + mTSPacketizer->extractCSDIfNecessary(info->mPacketizerTrackIndex); + + for (;;) { + ssize_t minTrackIndex = -1; + int64_t minTimeUs = -1ll; + + for (size_t i = 0; i < mTrackInfos.size(); ++i) { + const TrackInfo &info = mTrackInfos.itemAt(i); + + if (info.mAccessUnits.empty()) { + minTrackIndex = -1; + minTimeUs = -1ll; + break; + } + + int64_t timeUs; + const sp &accessUnit = *info.mAccessUnits.begin(); + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + if (minTrackIndex < 0 || timeUs < minTimeUs) { + minTrackIndex = i; + minTimeUs = timeUs; + } + } + + if (minTrackIndex < 0) { + return OK; + } + + TrackInfo *info = &mTrackInfos.editItemAt(minTrackIndex); + sp accessUnit = *info->mAccessUnits.begin(); + info->mAccessUnits.erase(info->mAccessUnits.begin()); + + sp tsPackets; + status_t err = packetizeAccessUnit( + minTrackIndex, accessUnit, &tsPackets); + + if (err == OK) { + err = mTSSender->queueBuffer( + tsPackets, + 33 /* packetType */, + RTPSender::PACKETIZATION_TRANSPORT_STREAM); + } + + if (err != OK) { + return err; + } + } + } + + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + + return info->mSender->queueBuffer( + accessUnit, + info->mIsAudio ? 96 : 97 /* packetType */, + info->mIsAudio + ? RTPSender::PACKETIZATION_AAC : RTPSender::PACKETIZATION_H264); +} + +void MediaSender::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatSenderNotify: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + if (generation != mGeneration) { + break; + } + + onSenderNotify(msg); + break; + } + + default: + TRESPASS(); + } +} + +void MediaSender::onSenderNotify(const sp &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case RTPSender::kWhatInitDone: + { + --mInitDoneCount; + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + if (err != OK) { + notifyInitDone(err); + ++mGeneration; + break; + } + + if (mInitDoneCount == 0) { + notifyInitDone(OK); + } + break; + } + + case RTPSender::kWhatError: + { + int32_t err; + CHECK(msg->findInt32("err", &err)); + + notifyError(err); + break; + } + + default: + TRESPASS(); + } +} + +void MediaSender::notifyInitDone(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInitDone); + notify->setInt32("err", err); + notify->post(); +} + +void MediaSender::notifyError(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +status_t MediaSender::packetizeAccessUnit( + size_t trackIndex, + sp accessUnit, + sp *tsPackets) { + const TrackInfo &info = mTrackInfos.itemAt(trackIndex); + + uint32_t flags = 0; + + bool isHDCPEncrypted = false; + uint64_t inputCTR; + uint8_t HDCP_private_data[16]; + + bool manuallyPrependSPSPPS = + !info.mIsAudio + && (info.mFlags & FLAG_MANUALLY_PREPEND_SPS_PPS) + && IsIDR(accessUnit); + + if (mHDCP != NULL && !info.mIsAudio) { + isHDCPEncrypted = true; + + if (manuallyPrependSPSPPS) { + accessUnit = mTSPacketizer->prependCSD( + info.mPacketizerTrackIndex, accessUnit); + } + + status_t err = mHDCP->encrypt( + accessUnit->data(), accessUnit->size(), + trackIndex /* streamCTR */, + &inputCTR, + accessUnit->data()); + + if (err != OK) { + ALOGE("Failed to HDCP-encrypt media data (err %d)", + err); + + return err; + } + + HDCP_private_data[0] = 0x00; + + HDCP_private_data[1] = + (((trackIndex >> 30) & 3) << 1) | 1; + + HDCP_private_data[2] = (trackIndex >> 22) & 0xff; + + HDCP_private_data[3] = + (((trackIndex >> 15) & 0x7f) << 1) | 1; + + HDCP_private_data[4] = (trackIndex >> 7) & 0xff; + + HDCP_private_data[5] = + ((trackIndex & 0x7f) << 1) | 1; + + HDCP_private_data[6] = 0x00; + + HDCP_private_data[7] = + (((inputCTR >> 60) & 0x0f) << 1) | 1; + + HDCP_private_data[8] = (inputCTR >> 52) & 0xff; + + HDCP_private_data[9] = + (((inputCTR >> 45) & 0x7f) << 1) | 1; + + HDCP_private_data[10] = (inputCTR >> 37) & 0xff; + + HDCP_private_data[11] = + (((inputCTR >> 30) & 0x7f) << 1) | 1; + + HDCP_private_data[12] = (inputCTR >> 22) & 0xff; + + HDCP_private_data[13] = + (((inputCTR >> 15) & 0x7f) << 1) | 1; + + HDCP_private_data[14] = (inputCTR >> 7) & 0xff; + + HDCP_private_data[15] = + ((inputCTR & 0x7f) << 1) | 1; + + flags |= TSPacketizer::IS_ENCRYPTED; + } else if (manuallyPrependSPSPPS) { + flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES; + } + + int64_t timeUs = ALooper::GetNowUs(); + if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) { + flags |= TSPacketizer::EMIT_PCR; + flags |= TSPacketizer::EMIT_PAT_AND_PMT; + + mPrevTimeUs = timeUs; + } + + mTSPacketizer->packetize( + info.mPacketizerTrackIndex, + accessUnit, + tsPackets, + flags, + !isHDCPEncrypted ? NULL : HDCP_private_data, + !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data), + info.mIsAudio ? 2 : 0 /* numStuffingBytes */); + + return OK; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h new file mode 100644 index 0000000..834780a --- /dev/null +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -0,0 +1,126 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MEDIA_SENDER_H_ + +#define MEDIA_SENDER_H_ + +#include "rtp/RTPSender.h" + +#include +#include +#include +#include + +namespace android { + +struct ABuffer; +struct ANetworkSession; +struct AMessage; +struct IHDCP; +struct TSPacketizer; + +// This class facilitates sending of data from one or more media tracks +// through one or more RTP channels, either providing a 1:1 mapping from +// track to RTP channel or muxing all tracks into a single RTP channel and +// using transport stream encapsulation. +// Optionally the (video) data is encrypted using the provided hdcp object. +struct MediaSender : public AHandler { + enum { + kWhatInitDone, + kWhatError, + }; + + MediaSender( + const sp &netSession, + const sp ¬ify); + + status_t setHDCP(const sp &hdcp); + + enum FlagBits { + FLAG_MANUALLY_PREPEND_SPS_PPS = 1, + }; + ssize_t addTrack(const sp &format, uint32_t flags); + + // If trackIndex == -1, initialize for transport stream muxing. + status_t initAsync( + ssize_t trackIndex, + RTPSender::TransportMode transportMode, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort, + int32_t *localRTPPort); + + status_t queueAccessUnit( + size_t trackIndex, const sp &accessUnit); + +protected: + virtual void onMessageReceived(const sp &msg); + virtual ~MediaSender(); + +private: + enum { + kWhatSenderNotify, + }; + + enum Mode { + MODE_UNDEFINED, + MODE_TRANSPORT_STREAM, + MODE_ELEMENTARY_STREAMS, + }; + + struct TrackInfo { + sp mFormat; + uint32_t mFlags; + sp mSender; + List > mAccessUnits; + ssize_t mPacketizerTrackIndex; + bool mIsAudio; + }; + + sp mNetSession; + sp mNotify; + + sp mHDCP; + + Mode mMode; + int32_t mGeneration; + + Vector mTrackInfos; + + sp mTSPacketizer; + sp mTSSender; + int64_t mPrevTimeUs; + + size_t mInitDoneCount; + + void onSenderNotify(const sp &msg); + + void notifyInitDone(status_t err); + void notifyError(status_t err); + + status_t packetizeAccessUnit( + size_t trackIndex, + sp accessUnit, + sp *tsPackets); + + DISALLOW_EVIL_CONSTRUCTORS(MediaSender); +}; + +} // namespace android + +#endif // MEDIA_SENDER_H_ + diff --git a/media/libstagefright/wifi-display/SNTPClient.cpp b/media/libstagefright/wifi-display/SNTPClient.cpp new file mode 100644 index 0000000..5c0af6a --- /dev/null +++ b/media/libstagefright/wifi-display/SNTPClient.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2013, 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 "SNTPClient.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace android { + +SNTPClient::SNTPClient() { +} + +status_t SNTPClient::requestTime(const char *host) { + struct hostent *ent; + int64_t requestTimeNTP, requestTimeUs; + ssize_t n; + int64_t responseTimeUs, responseTimeNTP; + int64_t originateTimeNTP, receiveTimeNTP, transmitTimeNTP; + int64_t roundTripTimeNTP, clockOffsetNTP; + + status_t err = UNKNOWN_ERROR; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + + if (s < 0) { + err = -errno; + + goto bail; + } + + ent = gethostbyname(host); + + if (ent == NULL) { + err = -ENOENT; + goto bail2; + } + + struct sockaddr_in hostAddr; + memset(hostAddr.sin_zero, 0, sizeof(hostAddr.sin_zero)); + hostAddr.sin_family = AF_INET; + hostAddr.sin_port = htons(kNTPPort); + hostAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; + + uint8_t packet[kNTPPacketSize]; + memset(packet, 0, sizeof(packet)); + + packet[0] = kNTPModeClient | (kNTPVersion << 3); + + requestTimeNTP = getNowNTP(); + requestTimeUs = ALooper::GetNowUs(); + writeTimeStamp(&packet[kNTPTransmitTimeOffset], requestTimeNTP); + + n = sendto( + s, packet, sizeof(packet), 0, + (const struct sockaddr *)&hostAddr, sizeof(hostAddr)); + + if (n < 0) { + err = -errno; + goto bail2; + } + + memset(packet, 0, sizeof(packet)); + + do { + n = recv(s, packet, sizeof(packet), 0); + } while (n < 0 && errno == EINTR); + + if (n < 0) { + err = -errno; + goto bail2; + } + + responseTimeUs = ALooper::GetNowUs(); + + responseTimeNTP = requestTimeNTP + makeNTP(responseTimeUs - requestTimeUs); + + originateTimeNTP = readTimeStamp(&packet[kNTPOriginateTimeOffset]); + receiveTimeNTP = readTimeStamp(&packet[kNTPReceiveTimeOffset]); + transmitTimeNTP = readTimeStamp(&packet[kNTPTransmitTimeOffset]); + + roundTripTimeNTP = + makeNTP(responseTimeUs - requestTimeUs) + - (transmitTimeNTP - receiveTimeNTP); + + clockOffsetNTP = + ((receiveTimeNTP - originateTimeNTP) + + (transmitTimeNTP - responseTimeNTP)) / 2; + + mTimeReferenceNTP = responseTimeNTP + clockOffsetNTP; + mTimeReferenceUs = responseTimeUs; + mRoundTripTimeNTP = roundTripTimeNTP; + + err = OK; + +bail2: + close(s); + s = -1; + +bail: + return err; +} + +int64_t SNTPClient::adjustTimeUs(int64_t timeUs) const { + uint64_t nowNTP = + mTimeReferenceNTP + makeNTP(timeUs - mTimeReferenceUs); + + int64_t nowUs = + (nowNTP >> 32) * 1000000ll + + ((nowNTP & 0xffffffff) * 1000000ll) / (1ll << 32); + + nowUs -= ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; + + return nowUs; +} + +// static +void SNTPClient::writeTimeStamp(uint8_t *dst, uint64_t ntpTime) { + *dst++ = (ntpTime >> 56) & 0xff; + *dst++ = (ntpTime >> 48) & 0xff; + *dst++ = (ntpTime >> 40) & 0xff; + *dst++ = (ntpTime >> 32) & 0xff; + *dst++ = (ntpTime >> 24) & 0xff; + *dst++ = (ntpTime >> 16) & 0xff; + *dst++ = (ntpTime >> 8) & 0xff; + *dst++ = ntpTime & 0xff; +} + +// static +uint64_t SNTPClient::readTimeStamp(const uint8_t *dst) { + return U64_AT(dst); +} + +// static +uint64_t SNTPClient::getNowNTP() { + struct timeval tv; + gettimeofday(&tv, NULL /* time zone */); + + uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec; + + nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; + + return makeNTP(nowUs); +} + +// static +uint64_t SNTPClient::makeNTP(uint64_t deltaUs) { + uint64_t hi = deltaUs / 1000000ll; + uint64_t lo = ((1ll << 32) * (deltaUs % 1000000ll)) / 1000000ll; + + return (hi << 32) | lo; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/SNTPClient.h b/media/libstagefright/wifi-display/SNTPClient.h new file mode 100644 index 0000000..967d1fc --- /dev/null +++ b/media/libstagefright/wifi-display/SNTPClient.h @@ -0,0 +1,62 @@ +/* + * Copyright 2013, 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 SNTP_CLIENT_H_ + +#define SNTP_CLIENT_H_ + +#include +#include + +namespace android { + +// Implementation of the SNTP (Simple Network Time Protocol) +struct SNTPClient { + SNTPClient(); + + status_t requestTime(const char *host); + + // given a time obtained from ALooper::GetNowUs() + // return the number of us elapsed since Jan 1 1970 00:00:00 (UTC). + int64_t adjustTimeUs(int64_t timeUs) const; + +private: + enum { + kNTPPort = 123, + kNTPPacketSize = 48, + kNTPModeClient = 3, + kNTPVersion = 3, + kNTPTransmitTimeOffset = 40, + kNTPOriginateTimeOffset = 24, + kNTPReceiveTimeOffset = 32, + }; + + uint64_t mTimeReferenceNTP; + int64_t mTimeReferenceUs; + int64_t mRoundTripTimeNTP; + + static void writeTimeStamp(uint8_t *dst, uint64_t ntpTime); + static uint64_t readTimeStamp(const uint8_t *dst); + + static uint64_t getNowNTP(); + static uint64_t makeNTP(uint64_t deltaUs); + + DISALLOW_EVIL_CONSTRUCTORS(SNTPClient); +}; + +} // namespace android + +#endif // SNTP_CLIENT_H_ diff --git a/media/libstagefright/wifi-display/TimeSeries.cpp b/media/libstagefright/wifi-display/TimeSeries.cpp deleted file mode 100644 index d882d98..0000000 --- a/media/libstagefright/wifi-display/TimeSeries.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 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 "TimeSeries.h" - -#include -#include - -namespace android { - -TimeSeries::TimeSeries() - : mCount(0), - mSum(0.0) { -} - -void TimeSeries::add(double val) { - if (mCount < kHistorySize) { - mValues[mCount++] = val; - mSum += val; - } else { - mSum -= mValues[0]; - memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double)); - mValues[kHistorySize - 1] = val; - mSum += val; - } -} - -double TimeSeries::mean() const { - if (mCount < 1) { - return 0.0; - } - - return mSum / mCount; -} - -double TimeSeries::sdev() const { - if (mCount < 1) { - return 0.0; - } - - double m = mean(); - - double sum = 0.0; - for (size_t i = 0; i < mCount; ++i) { - double tmp = mValues[i] - m; - tmp *= tmp; - - sum += tmp; - } - - return sqrt(sum / mCount); -} - -} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSeries.h b/media/libstagefright/wifi-display/TimeSeries.h deleted file mode 100644 index c818d51..0000000 --- a/media/libstagefright/wifi-display/TimeSeries.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 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 TIME_SERIES_H_ - -#define TIME_SERIES_H_ - -#include - -namespace android { - -struct TimeSeries { - TimeSeries(); - - void add(double val); - - double mean() const; - double sdev() const; - -private: - enum { - kHistorySize = 20 - }; - double mValues[kHistorySize]; - - size_t mCount; - double mSum; -}; - -} // namespace android - -#endif // TIME_SERIES_H_ - diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp new file mode 100644 index 0000000..d0ab60d --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp @@ -0,0 +1,324 @@ +/* + * Copyright 2013, 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 "RTPAssembler" +#include + +#include "RTPAssembler.h" + +#include +#include +#include +#include +#include + +namespace android { + +RTPReceiver::Assembler::Assembler(const sp ¬ify) + : mNotify(notify) { +} + +void RTPReceiver::Assembler::postAccessUnit( + const sp &accessUnit, bool followsDiscontinuity) { + sp notify = mNotify->dup(); + notify->setInt32("what", RTPReceiver::kWhatAccessUnit); + notify->setBuffer("accessUnit", accessUnit); + notify->setInt32("followsDiscontinuity", followsDiscontinuity); + notify->post(); +} + +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::TSAssembler::TSAssembler(const sp ¬ify) + : Assembler(notify), + mSawDiscontinuity(false) { +} + +void RTPReceiver::TSAssembler::signalDiscontinuity() { + mSawDiscontinuity = true; +} + +status_t RTPReceiver::TSAssembler::processPacket(const sp &packet) { + postAccessUnit(packet, mSawDiscontinuity); + + if (mSawDiscontinuity) { + mSawDiscontinuity = false; + } + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::H264Assembler::H264Assembler(const sp ¬ify) + : Assembler(notify), + mState(0), + mIndicator(0), + mNALType(0), + mAccessUnitRTPTime(0) { +} + +void RTPReceiver::H264Assembler::signalDiscontinuity() { + reset(); +} + +status_t RTPReceiver::H264Assembler::processPacket(const sp &packet) { + status_t err = internalProcessPacket(packet); + + if (err != OK) { + reset(); + } + + return err; +} + +status_t RTPReceiver::H264Assembler::internalProcessPacket( + const sp &packet) { + const uint8_t *data = packet->data(); + size_t size = packet->size(); + + switch (mState) { + case 0: + { + if (size < 1 || (data[0] & 0x80)) { + ALOGV("Malformed H264 RTP packet (empty or F-bit set)"); + return ERROR_MALFORMED; + } + + unsigned nalType = data[0] & 0x1f; + if (nalType >= 1 && nalType <= 23) { + addSingleNALUnit(packet); + ALOGV("added single NAL packet"); + } else if (nalType == 28) { + // FU-A + unsigned indicator = data[0]; + CHECK((indicator & 0x1f) == 28); + + if (size < 2) { + ALOGV("Malformed H264 FU-A packet (single byte)"); + return ERROR_MALFORMED; + } + + if (!(data[1] & 0x80)) { + ALOGV("Malformed H264 FU-A packet (no start bit)"); + return ERROR_MALFORMED; + } + + mIndicator = data[0]; + mNALType = data[1] & 0x1f; + uint32_t nri = (data[0] >> 5) & 3; + + clearAccumulator(); + + uint8_t byte = mNALType | (nri << 5); + appendToAccumulator(&byte, 1); + appendToAccumulator(data + 2, size - 2); + + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + mAccumulator->meta()->setInt32("rtp-time", rtpTime); + + if (data[1] & 0x40) { + // Huh? End bit also set on the first buffer. + addSingleNALUnit(mAccumulator); + clearAccumulator(); + + ALOGV("added FU-A"); + break; + } + + mState = 1; + } else if (nalType == 24) { + // STAP-A + + status_t err = addSingleTimeAggregationPacket(packet); + if (err != OK) { + return err; + } + } else { + ALOGV("Malformed H264 packet (unknown type %d)", nalType); + return ERROR_UNSUPPORTED; + } + break; + } + + case 1: + { + if (size < 2 + || data[0] != mIndicator + || (data[1] & 0x1f) != mNALType + || (data[1] & 0x80)) { + ALOGV("Malformed H264 FU-A packet (indicator, " + "type or start bit mismatch)"); + + return ERROR_MALFORMED; + } + + appendToAccumulator(data + 2, size - 2); + + if (data[1] & 0x40) { + addSingleNALUnit(mAccumulator); + + clearAccumulator(); + mState = 0; + + ALOGV("added FU-A"); + } + break; + } + + default: + TRESPASS(); + } + + int32_t marker; + CHECK(packet->meta()->findInt32("M", &marker)); + + if (marker) { + flushAccessUnit(); + } + + return OK; +} + +void RTPReceiver::H264Assembler::reset() { + mNALUnits.clear(); + + clearAccumulator(); + mState = 0; +} + +void RTPReceiver::H264Assembler::clearAccumulator() { + if (mAccumulator != NULL) { + // XXX Too expensive. + mAccumulator.clear(); + } +} + +void RTPReceiver::H264Assembler::appendToAccumulator( + const void *data, size_t size) { + if (mAccumulator == NULL) { + mAccumulator = new ABuffer(size); + memcpy(mAccumulator->data(), data, size); + return; + } + + if (mAccumulator->size() + size > mAccumulator->capacity()) { + sp buf = new ABuffer(mAccumulator->size() + size); + memcpy(buf->data(), mAccumulator->data(), mAccumulator->size()); + buf->setRange(0, mAccumulator->size()); + + int32_t rtpTime; + if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) { + buf->meta()->setInt32("rtp-time", rtpTime); + } + + mAccumulator = buf; + } + + memcpy(mAccumulator->data() + mAccumulator->size(), data, size); + mAccumulator->setRange(0, mAccumulator->size() + size); +} + +void RTPReceiver::H264Assembler::addSingleNALUnit(const sp &packet) { + if (mNALUnits.empty()) { + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + + mAccessUnitRTPTime = rtpTime; + } + + mNALUnits.push_back(packet); +} + +void RTPReceiver::H264Assembler::flushAccessUnit() { + if (mNALUnits.empty()) { + return; + } + + size_t totalSize = 0; + for (List >::iterator it = mNALUnits.begin(); + it != mNALUnits.end(); ++it) { + totalSize += 4 + (*it)->size(); + } + + sp accessUnit = new ABuffer(totalSize); + size_t offset = 0; + for (List >::iterator it = mNALUnits.begin(); + it != mNALUnits.end(); ++it) { + const sp nalUnit = *it; + + memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4); + + memcpy(accessUnit->data() + offset + 4, + nalUnit->data(), + nalUnit->size()); + + offset += 4 + nalUnit->size(); + } + + mNALUnits.clear(); + + accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll); + postAccessUnit(accessUnit, false /* followsDiscontinuity */); +} + +status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket( + const sp &packet) { + const uint8_t *data = packet->data(); + size_t size = packet->size(); + + if (size < 3) { + ALOGV("Malformed H264 STAP-A packet (too small)"); + return ERROR_MALFORMED; + } + + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + + ++data; + --size; + while (size >= 2) { + size_t nalSize = (data[0] << 8) | data[1]; + + if (size < nalSize + 2) { + ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)"); + return ERROR_MALFORMED; + } + + sp unit = new ABuffer(nalSize); + memcpy(unit->data(), &data[2], nalSize); + + unit->meta()->setInt32("rtp-time", rtpTime); + + addSingleNALUnit(unit); + + data += 2 + nalSize; + size -= 2 + nalSize; + } + + if (size != 0) { + ALOGV("Unexpected padding at end of STAP-A packet."); + } + + ALOGV("added STAP-A"); + + return OK; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.h b/media/libstagefright/wifi-display/rtp/RTPAssembler.h new file mode 100644 index 0000000..e456d32 --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.h @@ -0,0 +1,92 @@ +/* + * Copyright 2013, 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 RTP_ASSEMBLER_H_ + +#define RTP_ASSEMBLER_H_ + +#include "RTPReceiver.h" + +namespace android { + +// A helper class to reassemble the payload of RTP packets into access +// units depending on the packetization scheme. +struct RTPReceiver::Assembler : public RefBase { + Assembler(const sp ¬ify); + + virtual void signalDiscontinuity() = 0; + virtual status_t processPacket(const sp &packet) = 0; + +protected: + virtual ~Assembler() {} + + void postAccessUnit( + const sp &accessUnit, bool followsDiscontinuity); + +private: + sp mNotify; + + DISALLOW_EVIL_CONSTRUCTORS(Assembler); +}; + +struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler { + TSAssembler(const sp ¬ify); + + virtual void signalDiscontinuity(); + virtual status_t processPacket(const sp &packet); + +private: + bool mSawDiscontinuity; + + DISALLOW_EVIL_CONSTRUCTORS(TSAssembler); +}; + +struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler { + H264Assembler(const sp ¬ify); + + virtual void signalDiscontinuity(); + virtual status_t processPacket(const sp &packet); + +private: + int32_t mState; + + uint8_t mIndicator; + uint8_t mNALType; + + sp mAccumulator; + + List > mNALUnits; + int32_t mAccessUnitRTPTime; + + status_t internalProcessPacket(const sp &packet); + + void addSingleNALUnit(const sp &packet); + status_t addSingleTimeAggregationPacket(const sp &packet); + + void flushAccessUnit(); + + void clearAccumulator(); + void appendToAccumulator(const void *data, size_t size); + + void reset(); + + DISALLOW_EVIL_CONSTRUCTORS(H264Assembler); +}; + +} // namespace android + +#endif // RTP_ASSEMBLER_H_ + diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h new file mode 100644 index 0000000..6507a6f --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPBase.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013, 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 RTP_BASE_H_ + +#define RTP_BASE_H_ + +namespace android { + +struct RTPBase { + enum PacketizationMode { + PACKETIZATION_TRANSPORT_STREAM, + PACKETIZATION_H264, + PACKETIZATION_AAC, + }; + + enum TransportMode { + TRANSPORT_UNDEFINED, + TRANSPORT_UDP, + TRANSPORT_TCP, + TRANSPORT_TCP_INTERLEAVED, + }; + + enum { + // Really UDP _payload_ size + kMaxUDPPacketSize = 1472, // 1472 good, 1473 bad on Android@Home + }; + + static int32_t PickRandomRTPPort(); +}; + +} // namespace android + +#endif // RTP_BASE_H_ + + diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp new file mode 100644 index 0000000..29482af --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -0,0 +1,899 @@ +/* + * Copyright 2013, 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 "RTPReceiver" +#include + +#include "RTPAssembler.h" +#include "RTPReceiver.h" + +#include "ANetworkSession.h" + +#include +#include +#include +#include +#include +#include + +namespace android { + +//////////////////////////////////////////////////////////////////////////////// + +struct RTPReceiver::Source : public RefBase { + Source(RTPReceiver *receiver, uint32_t ssrc); + + void onPacketReceived(uint16_t seq, const sp &buffer); + + void addReportBlock(uint32_t ssrc, const sp &buf); + +protected: + virtual ~Source(); + +private: + static const uint32_t kMinSequential = 2; + static const uint32_t kMaxDropout = 3000; + static const uint32_t kMaxMisorder = 100; + static const uint32_t kRTPSeqMod = 1u << 16; + static const int64_t kReportIntervalUs = 10000000ll; + + RTPReceiver *mReceiver; + uint32_t mSSRC; + bool mFirst; + uint16_t mMaxSeq; + uint32_t mCycles; + uint32_t mBaseSeq; + uint32_t mReceived; + uint32_t mExpectedPrior; + uint32_t mReceivedPrior; + + int64_t mFirstArrivalTimeUs; + int64_t mFirstRTPTimeUs; + + // Ordered by extended seq number. + List > mPackets; + + int32_t mAwaitingExtSeqNo; + bool mRequestedRetransmission; + + int32_t mActivePacketType; + sp mActiveAssembler; + + int64_t mNextReportTimeUs; + + int32_t mNumDeclaredLost; + int32_t mNumDeclaredLostPrior; + + void queuePacket(const sp &packet); + void dequeueMore(); + + sp getNextPacket(); + void resync(); + + DISALLOW_EVIL_CONSTRUCTORS(Source); +}; + +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc) + : mReceiver(receiver), + mSSRC(ssrc), + mFirst(true), + mMaxSeq(0), + mCycles(0), + mBaseSeq(0), + mReceived(0), + mExpectedPrior(0), + mReceivedPrior(0), + mFirstArrivalTimeUs(-1ll), + mFirstRTPTimeUs(-1ll), + mAwaitingExtSeqNo(-1), + mRequestedRetransmission(false), + mActivePacketType(-1), + mNextReportTimeUs(-1ll), + mNumDeclaredLost(0), + mNumDeclaredLostPrior(0) { +} + +RTPReceiver::Source::~Source() { +} + +void RTPReceiver::Source::onPacketReceived( + uint16_t seq, const sp &buffer) { + if (mFirst) { + buffer->setInt32Data(mCycles | seq); + queuePacket(buffer); + + mFirst = false; + mBaseSeq = seq; + mMaxSeq = seq; + ++mReceived; + return; + } + + uint16_t udelta = seq - mMaxSeq; + + if (udelta < kMaxDropout) { + // In order, with permissible gap. + + if (seq < mMaxSeq) { + // Sequence number wrapped - count another 64K cycle + mCycles += kRTPSeqMod; + } + + mMaxSeq = seq; + + ++mReceived; + } else if (udelta <= kRTPSeqMod - kMaxMisorder) { + // The sequence number made a very large jump + return; + } else { + // Duplicate or reordered packet. + } + + buffer->setInt32Data(mCycles | seq); + queuePacket(buffer); +} + +void RTPReceiver::Source::queuePacket(const sp &packet) { + int32_t newExtendedSeqNo = packet->int32Data(); + + if (mFirstArrivalTimeUs < 0ll) { + mFirstArrivalTimeUs = ALooper::GetNowUs(); + + uint32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); + + mFirstRTPTimeUs = (rtpTime * 100ll) / 9ll; + } + + if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) { + // We're no longer interested in these. They're old. + ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo); + return; + } + + if (mPackets.empty()) { + mPackets.push_back(packet); + dequeueMore(); + return; + } + + List >::iterator firstIt = mPackets.begin(); + List >::iterator it = --mPackets.end(); + for (;;) { + int32_t extendedSeqNo = (*it)->int32Data(); + + if (extendedSeqNo == newExtendedSeqNo) { + // Duplicate packet. + return; + } + + if (extendedSeqNo < newExtendedSeqNo) { + // Insert new packet after the one at "it". + mPackets.insert(++it, packet); + break; + } + + if (it == firstIt) { + // Insert new packet before the first existing one. + mPackets.insert(it, packet); + break; + } + + --it; + } + + dequeueMore(); +} + +void RTPReceiver::Source::dequeueMore() { + int64_t nowUs = ALooper::GetNowUs(); + if (mNextReportTimeUs < 0ll || nowUs >= mNextReportTimeUs) { + if (mNextReportTimeUs >= 0ll) { + uint32_t expected = (mMaxSeq | mCycles) - mBaseSeq + 1; + + uint32_t expectedInterval = expected - mExpectedPrior; + mExpectedPrior = expected; + + uint32_t receivedInterval = mReceived - mReceivedPrior; + mReceivedPrior = mReceived; + + int64_t lostInterval = + (int64_t)expectedInterval - (int64_t)receivedInterval; + + int32_t declaredLostInterval = + mNumDeclaredLost - mNumDeclaredLostPrior; + + mNumDeclaredLostPrior = mNumDeclaredLost; + + ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", + lostInterval, + 100.0f * lostInterval / expectedInterval, + declaredLostInterval); + } + + mNextReportTimeUs = nowUs + kReportIntervalUs; + } + + for (;;) { + sp packet = getNextPacket(); + + if (packet == NULL) { + if (mPackets.empty()) { + break; + } + + CHECK_GE(mAwaitingExtSeqNo, 0); + + const sp &firstPacket = *mPackets.begin(); + + uint32_t rtpTime; + CHECK(firstPacket->meta()->findInt32( + "rtp-time", (int32_t *)&rtpTime)); + + + int64_t rtpUs = (rtpTime * 100ll) / 9ll; + + int64_t maxArrivalTimeUs = + mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs; + + int64_t nowUs = ALooper::GetNowUs(); + + CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data()); + + ALOGV("waiting for %d, comparing against %d, %lld us left", + mAwaitingExtSeqNo, + firstPacket->int32Data(), + maxArrivalTimeUs - nowUs); + + if (maxArrivalTimeUs + kPacketLostAfterUs <= nowUs) { + ALOGV("Lost packet extSeqNo %d %s", + mAwaitingExtSeqNo, + mRequestedRetransmission ? "*" : ""); + + mRequestedRetransmission = false; + if (mActiveAssembler != NULL) { + mActiveAssembler->signalDiscontinuity(); + } + + // resync(); + ++mAwaitingExtSeqNo; + ++mNumDeclaredLost; + + mReceiver->notifyPacketLost(); + continue; + } else if (kRequestRetransmissionAfterUs > 0 + && maxArrivalTimeUs + kRequestRetransmissionAfterUs <= nowUs + && !mRequestedRetransmission + && mAwaitingExtSeqNo >= 0) { + mRequestedRetransmission = true; + mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo); + break; + } else { + break; + } + } + + mRequestedRetransmission = false; + + int32_t packetType; + CHECK(packet->meta()->findInt32("PT", &packetType)); + + if (packetType != mActivePacketType) { + mActiveAssembler = mReceiver->makeAssembler(packetType); + mActivePacketType = packetType; + } + + if (mActiveAssembler == NULL) { + continue; + } + + status_t err = mActiveAssembler->processPacket(packet); + if (err != OK) { + ALOGV("assembler returned error %d", err); + } + } +} + +sp RTPReceiver::Source::getNextPacket() { + if (mPackets.empty()) { + return NULL; + } + + int32_t extSeqNo = (*mPackets.begin())->int32Data(); + + if (mAwaitingExtSeqNo < 0) { + mAwaitingExtSeqNo = extSeqNo; + } else if (extSeqNo != mAwaitingExtSeqNo) { + return NULL; + } + + sp packet = *mPackets.begin(); + mPackets.erase(mPackets.begin()); + + ++mAwaitingExtSeqNo; + + return packet; +} + +void RTPReceiver::Source::resync() { + mAwaitingExtSeqNo = -1; +} + +void RTPReceiver::Source::addReportBlock( + uint32_t ssrc, const sp &buf) { + uint32_t extMaxSeq = mMaxSeq | mCycles; + uint32_t expected = extMaxSeq - mBaseSeq + 1; + + int64_t lost = (int64_t)expected - (int64_t)mReceived; + if (lost > 0x7fffff) { + lost = 0x7fffff; + } else if (lost < -0x800000) { + lost = -0x800000; + } + + uint32_t expectedInterval = expected - mExpectedPrior; + mExpectedPrior = expected; + + uint32_t receivedInterval = mReceived - mReceivedPrior; + mReceivedPrior = mReceived; + + int64_t lostInterval = expectedInterval - receivedInterval; + + uint8_t fractionLost; + if (expectedInterval == 0 || lostInterval <=0) { + fractionLost = 0; + } else { + fractionLost = (lostInterval << 8) / expectedInterval; + } + + uint8_t *ptr = buf->data() + buf->size(); + + ptr[0] = ssrc >> 24; + ptr[1] = (ssrc >> 16) & 0xff; + ptr[2] = (ssrc >> 8) & 0xff; + ptr[3] = ssrc & 0xff; + + ptr[4] = fractionLost; + + ptr[5] = (lost >> 16) & 0xff; + ptr[6] = (lost >> 8) & 0xff; + ptr[7] = lost & 0xff; + + ptr[8] = extMaxSeq >> 24; + ptr[9] = (extMaxSeq >> 16) & 0xff; + ptr[10] = (extMaxSeq >> 8) & 0xff; + ptr[11] = extMaxSeq & 0xff; + + // XXX TODO: + + ptr[12] = 0x00; // interarrival jitter + ptr[13] = 0x00; + ptr[14] = 0x00; + ptr[15] = 0x00; + + ptr[16] = 0x00; // last SR + ptr[17] = 0x00; + ptr[18] = 0x00; + ptr[19] = 0x00; + + ptr[20] = 0x00; // delay since last SR + ptr[21] = 0x00; + ptr[22] = 0x00; + ptr[23] = 0x00; +} + +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::RTPReceiver( + const sp &netSession, + const sp ¬ify) + : mNetSession(netSession), + mNotify(notify), + mMode(TRANSPORT_UNDEFINED), + mRTPSessionID(0), + mRTCPSessionID(0), + mRTPClientSessionID(0) { +} + +RTPReceiver::~RTPReceiver() { + if (mRTPClientSessionID != 0) { + mNetSession->destroySession(mRTPClientSessionID); + mRTPClientSessionID = 0; + } + + if (mRTCPSessionID != 0) { + mNetSession->destroySession(mRTCPSessionID); + mRTCPSessionID = 0; + } + + if (mRTPSessionID != 0) { + mNetSession->destroySession(mRTPSessionID); + mRTPSessionID = 0; + } +} + +status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) { + if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) { + return INVALID_OPERATION; + } + + CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED); + + sp rtpNotify = new AMessage(kWhatRTPNotify, id()); + + sp rtcpNotify; + if (mode == TRANSPORT_UDP) { + rtcpNotify = new AMessage(kWhatRTCPNotify, id()); + } + + CHECK_EQ(mRTPSessionID, 0); + CHECK_EQ(mRTCPSessionID, 0); + + int32_t localRTPPort; + + struct in_addr ifaceAddr; + ifaceAddr.s_addr = INADDR_ANY; + + for (;;) { + localRTPPort = PickRandomRTPPort(); + + status_t err; + if (mode == TRANSPORT_UDP) { + err = mNetSession->createUDPSession( + localRTPPort, + rtpNotify, + &mRTPSessionID); + } else { + CHECK_EQ(mode, TRANSPORT_TCP); + err = mNetSession->createTCPDatagramSession( + ifaceAddr, + localRTPPort, + rtpNotify, + &mRTPSessionID); + } + + if (err != OK) { + continue; + } + + if (mode == TRANSPORT_TCP) { + break; + } + + err = mNetSession->createUDPSession( + localRTPPort + 1, + rtcpNotify, + &mRTCPSessionID); + + if (err == OK) { + break; + } + + mNetSession->destroySession(mRTPSessionID); + mRTPSessionID = 0; + } + + mMode = mode; + *outLocalRTPPort = localRTPPort; + + return OK; +} + +status_t RTPReceiver::connect( + const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) { + if (mMode == TRANSPORT_TCP) { + return OK; + } + + status_t err = mNetSession->connectUDPSession( + mRTPSessionID, remoteHost, remoteRTPPort); + + if (err != OK) { + notifyInitDone(err); + return err; + } + + ALOGI("connectUDPSession RTP successful."); + + if (remoteRTCPPort >= 0) { + err = mNetSession->connectUDPSession( + mRTCPSessionID, remoteHost, remoteRTCPPort); + + if (err != OK) { + ALOGI("connect failed w/ err %d", err); + + notifyInitDone(err); + return err; + } + + scheduleSendRR(); + } + + notifyInitDone(OK); + + return OK; +} + +void RTPReceiver::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatRTPNotify: + case kWhatRTCPNotify: + onNetNotify(msg->what() == kWhatRTPNotify, msg); + break; + + case kWhatSendRR: + { + onSendRR(); + break; + } + + default: + TRESPASS(); + } +} + +void RTPReceiver::onNetNotify(bool isRTP, const sp &msg) { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + int32_t errorOccuredDuringSend; + CHECK(msg->findInt32("send", &errorOccuredDuringSend)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred during %s in session %d " + "(%d, '%s' (%s)).", + errorOccuredDuringSend ? "send" : "receive", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + mNetSession->destroySession(sessionID); + + if (sessionID == mRTPSessionID) { + mRTPSessionID = 0; + + if (mMode == TRANSPORT_TCP && mRTPClientSessionID == 0) { + notifyInitDone(err); + break; + } + } else if (sessionID == mRTCPSessionID) { + mRTCPSessionID = 0; + } else if (sessionID == mRTPClientSessionID) { + mRTPClientSessionID = 0; + } + + notifyError(err); + break; + } + + case ANetworkSession::kWhatDatagram: + { + sp data; + CHECK(msg->findBuffer("data", &data)); + + if (isRTP) { + onRTPData(data); + } else { + onRTCPData(data); + } + break; + } + + case ANetworkSession::kWhatClientConnected: + { + CHECK_EQ(mMode, TRANSPORT_TCP); + CHECK(isRTP); + + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + if (mRTPClientSessionID != 0) { + // We only allow a single client connection. + mNetSession->destroySession(sessionID); + sessionID = 0; + break; + } + + mRTPClientSessionID = sessionID; + + notifyInitDone(OK); + break; + } + } +} + +void RTPReceiver::notifyInitDone(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInitDone); + notify->setInt32("err", err); + notify->post(); +} + +void RTPReceiver::notifyError(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +void RTPReceiver::notifyPacketLost() { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatPacketLost); + notify->post(); +} + +status_t RTPReceiver::onRTPData(const sp &buffer) { + size_t size = buffer->size(); + if (size < 12) { + // Too short to be a valid RTP header. + return ERROR_MALFORMED; + } + + const uint8_t *data = buffer->data(); + + if ((data[0] >> 6) != 2) { + // Unsupported version. + return ERROR_UNSUPPORTED; + } + + if (data[0] & 0x20) { + // Padding present. + + size_t paddingLength = data[size - 1]; + + if (paddingLength + 12 > size) { + // If we removed this much padding we'd end up with something + // that's too short to be a valid RTP header. + return ERROR_MALFORMED; + } + + size -= paddingLength; + } + + int numCSRCs = data[0] & 0x0f; + + size_t payloadOffset = 12 + 4 * numCSRCs; + + if (size < payloadOffset) { + // Not enough data to fit the basic header and all the CSRC entries. + return ERROR_MALFORMED; + } + + if (data[0] & 0x10) { + // Header eXtension present. + + if (size < payloadOffset + 4) { + // Not enough data to fit the basic header, all CSRC entries + // and the first 4 bytes of the extension header. + + return ERROR_MALFORMED; + } + + const uint8_t *extensionData = &data[payloadOffset]; + + size_t extensionLength = + 4 * (extensionData[2] << 8 | extensionData[3]); + + if (size < payloadOffset + 4 + extensionLength) { + return ERROR_MALFORMED; + } + + payloadOffset += 4 + extensionLength; + } + + uint32_t srcId = U32_AT(&data[8]); + uint32_t rtpTime = U32_AT(&data[4]); + uint16_t seqNo = U16_AT(&data[2]); + + sp meta = buffer->meta(); + meta->setInt32("ssrc", srcId); + meta->setInt32("rtp-time", rtpTime); + meta->setInt32("PT", data[1] & 0x7f); + meta->setInt32("M", data[1] >> 7); + + buffer->setRange(payloadOffset, size - payloadOffset); + + ssize_t index = mSources.indexOfKey(srcId); + sp source; + if (index < 0) { + source = new Source(this, srcId); + mSources.add(srcId, source); + } else { + source = mSources.valueAt(index); + } + + source->onPacketReceived(seqNo, buffer); + + return OK; +} + +status_t RTPReceiver::onRTCPData(const sp &data) { + ALOGI("onRTCPData"); + return OK; +} + +void RTPReceiver::addSDES(const sp &buffer) { + uint8_t *data = buffer->data() + buffer->size(); + data[0] = 0x80 | 1; + data[1] = 202; // SDES + data[4] = kSourceID >> 24; // SSRC + data[5] = (kSourceID >> 16) & 0xff; + data[6] = (kSourceID >> 8) & 0xff; + data[7] = kSourceID & 0xff; + + size_t offset = 8; + + data[offset++] = 1; // CNAME + + AString cname = "stagefright@somewhere"; + data[offset++] = cname.size(); + + memcpy(&data[offset], cname.c_str(), cname.size()); + offset += cname.size(); + + data[offset++] = 6; // TOOL + + AString tool = "stagefright/1.0"; + data[offset++] = tool.size(); + + memcpy(&data[offset], tool.c_str(), tool.size()); + offset += tool.size(); + + data[offset++] = 0; + + if ((offset % 4) > 0) { + size_t count = 4 - (offset % 4); + switch (count) { + case 3: + data[offset++] = 0; + case 2: + data[offset++] = 0; + case 1: + data[offset++] = 0; + } + } + + size_t numWords = (offset / 4) - 1; + data[2] = numWords >> 8; + data[3] = numWords & 0xff; + + buffer->setRange(buffer->offset(), buffer->size() + offset); +} + +void RTPReceiver::scheduleSendRR() { + (new AMessage(kWhatSendRR, id()))->post(5000000ll); +} + +void RTPReceiver::onSendRR() { +#if 0 + sp buf = new ABuffer(kMaxUDPPacketSize); + buf->setRange(0, 0); + + uint8_t *ptr = buf->data(); + ptr[0] = 0x80 | 0; + ptr[1] = 201; // RR + ptr[2] = 0; + ptr[3] = 1; + ptr[4] = kSourceID >> 24; // SSRC + ptr[5] = (kSourceID >> 16) & 0xff; + ptr[6] = (kSourceID >> 8) & 0xff; + ptr[7] = kSourceID & 0xff; + + buf->setRange(0, 8); + + size_t numReportBlocks = 0; + for (size_t i = 0; i < mSources.size(); ++i) { + uint32_t ssrc = mSources.keyAt(i); + sp source = mSources.valueAt(i); + + if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) { + // Cannot fit another report block. + break; + } + + source->addReportBlock(ssrc, buf); + ++numReportBlocks; + } + + ptr[0] |= numReportBlocks; // 5 bit + + size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks; + ptr[2] = sizeInWordsMinus1 >> 8; + ptr[3] = sizeInWordsMinus1 & 0xff; + + buf->setRange(0, (sizeInWordsMinus1 + 1) * 4); + + addSDES(buf); + + mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); +#endif + + scheduleSendRR(); +} + +status_t RTPReceiver::registerPacketType( + uint8_t packetType, PacketizationMode mode) { + mPacketTypes.add(packetType, mode); + + return OK; +} + +sp RTPReceiver::makeAssembler(uint8_t packetType) { + ssize_t index = mPacketTypes.indexOfKey(packetType); + if (index < 0) { + return NULL; + } + + PacketizationMode mode = mPacketTypes.valueAt(index); + + switch (mode) { + case PACKETIZATION_TRANSPORT_STREAM: + return new TSAssembler(mNotify); + + case PACKETIZATION_H264: + return new H264Assembler(mNotify); + + default: + return NULL; + } +} + +void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) { + int32_t blp = 0; + + sp buf = new ABuffer(16); + buf->setRange(0, 0); + + uint8_t *ptr = buf->data(); + ptr[0] = 0x80 | 1; // generic NACK + ptr[1] = 205; // TSFB + ptr[2] = 0; + ptr[3] = 3; + ptr[8] = (senderSSRC >> 24) & 0xff; + ptr[9] = (senderSSRC >> 16) & 0xff; + ptr[10] = (senderSSRC >> 8) & 0xff; + ptr[11] = (senderSSRC & 0xff); + ptr[8] = (kSourceID >> 24) & 0xff; + ptr[9] = (kSourceID >> 16) & 0xff; + ptr[10] = (kSourceID >> 8) & 0xff; + ptr[11] = (kSourceID & 0xff); + ptr[12] = (extSeqNo >> 8) & 0xff; + ptr[13] = (extSeqNo & 0xff); + ptr[14] = (blp >> 8) & 0xff; + ptr[15] = (blp & 0xff); + + buf->setRange(0, 16); + + mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h new file mode 100644 index 0000000..2ae864a --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h @@ -0,0 +1,110 @@ +/* + * Copyright 2013, 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 RTP_RECEIVER_H_ + +#define RTP_RECEIVER_H_ + +#include "RTPBase.h" + +#include + +namespace android { + +struct ABuffer; +struct ANetworkSession; + +// An object of this class facilitates receiving of media data on an RTP +// channel. The channel is established over a UDP or TCP connection depending +// on which "TransportMode" was chosen. In addition different RTP packetization +// schemes are supported such as "Transport Stream Packets over RTP", +// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)" +struct RTPReceiver : public RTPBase, public AHandler { + enum { + kWhatInitDone, + kWhatError, + kWhatAccessUnit, + kWhatPacketLost, + }; + RTPReceiver( + const sp &netSession, + const sp ¬ify); + + status_t registerPacketType( + uint8_t packetType, PacketizationMode mode); + + status_t initAsync(TransportMode mode, int32_t *outLocalRTPPort); + + status_t connect( + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort); + +protected: + virtual ~RTPReceiver(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatRTPNotify, + kWhatRTCPNotify, + kWhatSendRR, + }; + + enum { + kSourceID = 0xdeadbeef, + kPacketLostAfterUs = 100000, + kRequestRetransmissionAfterUs = -1, + }; + + struct Assembler; + struct H264Assembler; + struct Source; + struct TSAssembler; + + sp mNetSession; + sp mNotify; + TransportMode mMode; + int32_t mRTPSessionID; + int32_t mRTCPSessionID; + + int32_t mRTPClientSessionID; // in TRANSPORT_TCP mode. + + KeyedVector mPacketTypes; + KeyedVector > mSources; + + void onNetNotify(bool isRTP, const sp &msg); + status_t onRTPData(const sp &data); + status_t onRTCPData(const sp &data); + void onSendRR(); + + void scheduleSendRR(); + void addSDES(const sp &buffer); + + void notifyInitDone(status_t err); + void notifyError(status_t err); + void notifyPacketLost(); + + sp makeAssembler(uint8_t packetType); + + void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo); + + DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver); +}; + +} // namespace android + +#endif // RTP_RECEIVER_H_ diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp new file mode 100644 index 0000000..85c5933 --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -0,0 +1,701 @@ +/* + * Copyright 2013, 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 "RTPSender" +#include + +#include "RTPSender.h" + +#include "ANetworkSession.h" + +#include +#include +#include +#include +#include +#include + +#include "include/avc_utils.h" + +namespace android { + +RTPSender::RTPSender( + const sp &netSession, + const sp ¬ify) + : mNetSession(netSession), + mNotify(notify), + mMode(TRANSPORT_UNDEFINED), + mRTPSessionID(0), + mRTCPSessionID(0), + mRTPConnected(false), + mRTCPConnected(false), + mLastNTPTime(0), + mLastRTPTime(0), + mNumRTPSent(0), + mNumRTPOctetsSent(0), + mNumSRsSent(0), + mRTPSeqNo(0), + mHistorySize(0) { +} + +RTPSender::~RTPSender() { + if (mRTCPSessionID != 0) { + mNetSession->destroySession(mRTCPSessionID); + mRTCPSessionID = 0; + } + + if (mRTPSessionID != 0) { + mNetSession->destroySession(mRTPSessionID); + mRTPSessionID = 0; + } +} + +// static +int32_t RTPBase::PickRandomRTPPort() { + // Pick an even integer in range [1024, 65534) + + static const size_t kRange = (65534 - 1024) / 2; + + return (int32_t)(((float)(kRange + 1) * rand()) / RAND_MAX) * 2 + 1024; +} + +status_t RTPSender::initAsync( + TransportMode mode, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort, + int32_t *outLocalRTPPort) { + if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) { + return INVALID_OPERATION; + } + + CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED); + + if (mode == TRANSPORT_TCP && remoteRTCPPort >= 0) { + return INVALID_OPERATION; + } + + sp rtpNotify = new AMessage(kWhatRTPNotify, id()); + + sp rtcpNotify; + if (remoteRTCPPort >= 0) { + rtcpNotify = new AMessage(kWhatRTCPNotify, id()); + } + + CHECK_EQ(mRTPSessionID, 0); + CHECK_EQ(mRTCPSessionID, 0); + + int32_t localRTPPort; + + for (;;) { + localRTPPort = PickRandomRTPPort(); + + status_t err; + if (mode == TRANSPORT_UDP) { + err = mNetSession->createUDPSession( + localRTPPort, + remoteHost, + remoteRTPPort, + rtpNotify, + &mRTPSessionID); + } else { + CHECK_EQ(mode, TRANSPORT_TCP); + err = mNetSession->createTCPDatagramSession( + localRTPPort, + remoteHost, + remoteRTPPort, + rtpNotify, + &mRTPSessionID); + } + + if (err != OK) { + continue; + } + + if (remoteRTCPPort < 0) { + break; + } + + if (mode == TRANSPORT_UDP) { + err = mNetSession->createUDPSession( + localRTPPort + 1, + remoteHost, + remoteRTCPPort, + rtcpNotify, + &mRTCPSessionID); + } else { + CHECK_EQ(mode, TRANSPORT_TCP); + err = mNetSession->createTCPDatagramSession( + localRTPPort + 1, + remoteHost, + remoteRTCPPort, + rtcpNotify, + &mRTCPSessionID); + } + + if (err == OK) { + break; + } + + mNetSession->destroySession(mRTPSessionID); + mRTPSessionID = 0; + } + + if (mode == TRANSPORT_UDP) { + mRTPConnected = true; + mRTCPConnected = true; + } + + mMode = mode; + *outLocalRTPPort = localRTPPort; + + if (mMode == TRANSPORT_UDP) { + notifyInitDone(OK); + } + + return OK; +} + +status_t RTPSender::queueBuffer( + const sp &buffer, uint8_t packetType, PacketizationMode mode) { + status_t err; + + switch (mode) { + case PACKETIZATION_TRANSPORT_STREAM: + err = queueTSPackets(buffer, packetType); + break; + + case PACKETIZATION_H264: + err = queueAVCBuffer(buffer, packetType); + break; + + default: + TRESPASS(); + } + + return err; +} + +status_t RTPSender::queueTSPackets( + const sp &tsPackets, uint8_t packetType) { + CHECK_EQ(0, tsPackets->size() % 188); + + const size_t numTSPackets = tsPackets->size() / 188; + + size_t srcOffset = 0; + while (srcOffset < tsPackets->size()) { + sp udpPacket = + new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188); + + udpPacket->setInt32Data(mRTPSeqNo); + + uint8_t *rtp = udpPacket->data(); + rtp[0] = 0x80; + rtp[1] = packetType; + + rtp[2] = (mRTPSeqNo >> 8) & 0xff; + rtp[3] = mRTPSeqNo & 0xff; + ++mRTPSeqNo; + + int64_t nowUs = ALooper::GetNowUs(); + uint32_t rtpTime = (nowUs * 9) / 100ll; + + rtp[4] = rtpTime >> 24; + rtp[5] = (rtpTime >> 16) & 0xff; + rtp[6] = (rtpTime >> 8) & 0xff; + rtp[7] = rtpTime & 0xff; + + rtp[8] = kSourceID >> 24; + rtp[9] = (kSourceID >> 16) & 0xff; + rtp[10] = (kSourceID >> 8) & 0xff; + rtp[11] = kSourceID & 0xff; + + size_t numTSPackets = (tsPackets->size() - srcOffset) / 188; + if (numTSPackets > kMaxNumTSPacketsPerRTPPacket) { + numTSPackets = kMaxNumTSPacketsPerRTPPacket; + } + + memcpy(&rtp[12], tsPackets->data() + srcOffset, numTSPackets * 188); + + udpPacket->setRange(0, 12 + numTSPackets * 188); + status_t err = sendRTPPacket(udpPacket, true /* storeInHistory */); + + if (err != OK) { + return err; + } + + srcOffset += numTSPackets * 188; + } + + return OK; +} + +status_t RTPSender::queueAVCBuffer( + const sp &accessUnit, uint8_t packetType) { + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + uint32_t rtpTime = (timeUs * 9 / 100ll); + + List > packets; + + sp out = new ABuffer(kMaxUDPPacketSize); + size_t outBytesUsed = 12; // Placeholder for RTP header. + + const uint8_t *data = accessUnit->data(); + size_t size = accessUnit->size(); + const uint8_t *nalStart; + size_t nalSize; + while (getNextNALUnit( + &data, &size, &nalStart, &nalSize, + true /* startCodeFollows */) == OK) { + size_t bytesNeeded = nalSize + 2; + if (outBytesUsed == 12) { + ++bytesNeeded; + } + + if (outBytesUsed + bytesNeeded > out->capacity()) { + bool emitSingleNALPacket = false; + + if (outBytesUsed == 12 + && outBytesUsed + nalSize <= out->capacity()) { + // We haven't emitted anything into the current packet yet and + // this NAL unit fits into a single-NAL-unit-packet while + // it wouldn't have fit as part of a STAP-A packet. + + memcpy(out->data() + outBytesUsed, nalStart, nalSize); + outBytesUsed += nalSize; + + emitSingleNALPacket = true; + } + + if (outBytesUsed > 12) { + out->setRange(0, outBytesUsed); + packets.push_back(out); + out = new ABuffer(kMaxUDPPacketSize); + outBytesUsed = 12; // Placeholder for RTP header + } + + if (emitSingleNALPacket) { + continue; + } + } + + if (outBytesUsed + bytesNeeded <= out->capacity()) { + uint8_t *dst = out->data() + outBytesUsed; + + if (outBytesUsed == 12) { + *dst++ = 24; // STAP-A header + } + + *dst++ = (nalSize >> 8) & 0xff; + *dst++ = nalSize & 0xff; + memcpy(dst, nalStart, nalSize); + + outBytesUsed += bytesNeeded; + continue; + } + + // This single NAL unit does not fit into a single RTP packet, + // we need to emit an FU-A. + + CHECK_EQ(outBytesUsed, 12u); + + uint8_t nalType = nalStart[0] & 0x1f; + uint8_t nri = (nalStart[0] >> 5) & 3; + + size_t srcOffset = 1; + while (srcOffset < nalSize) { + size_t copy = out->capacity() - outBytesUsed - 2; + if (copy > nalSize - srcOffset) { + copy = nalSize - srcOffset; + } + + uint8_t *dst = out->data() + outBytesUsed; + dst[0] = (nri << 5) | 28; + + dst[1] = nalType; + + if (srcOffset == 1) { + dst[1] |= 0x80; + } + + if (srcOffset + copy == nalSize) { + dst[1] |= 0x40; + } + + memcpy(&dst[2], nalStart + srcOffset, copy); + srcOffset += copy; + + out->setRange(0, outBytesUsed + copy + 2); + + packets.push_back(out); + out = new ABuffer(kMaxUDPPacketSize); + outBytesUsed = 12; // Placeholder for RTP header + } + } + + if (outBytesUsed > 12) { + out->setRange(0, outBytesUsed); + packets.push_back(out); + } + + while (!packets.empty()) { + sp out = *packets.begin(); + packets.erase(packets.begin()); + + out->setInt32Data(mRTPSeqNo); + + bool last = packets.empty(); + + uint8_t *dst = out->data(); + + dst[0] = 0x80; + + dst[1] = packetType; + if (last) { + dst[1] |= 1 << 7; // M-bit + } + + dst[2] = (mRTPSeqNo >> 8) & 0xff; + dst[3] = mRTPSeqNo & 0xff; + ++mRTPSeqNo; + + dst[4] = rtpTime >> 24; + dst[5] = (rtpTime >> 16) & 0xff; + dst[6] = (rtpTime >> 8) & 0xff; + dst[7] = rtpTime & 0xff; + dst[8] = kSourceID >> 24; + dst[9] = (kSourceID >> 16) & 0xff; + dst[10] = (kSourceID >> 8) & 0xff; + dst[11] = kSourceID & 0xff; + + status_t err = sendRTPPacket(out, true /* storeInHistory */); + + if (err != OK) { + return err; + } + } + + return OK; +} + +status_t RTPSender::sendRTPPacket( + const sp &buffer, bool storeInHistory) { + CHECK(mRTPConnected); + + status_t err = mNetSession->sendRequest( + mRTPSessionID, buffer->data(), buffer->size()); + + if (err != OK) { + return err; + } + + mLastNTPTime = GetNowNTP(); + mLastRTPTime = U32_AT(buffer->data() + 4); + + ++mNumRTPSent; + mNumRTPOctetsSent += buffer->size() - 12; + + if (storeInHistory) { + if (mHistorySize == kMaxHistorySize) { + mHistory.erase(mHistory.begin()); + } else { + ++mHistorySize; + } + mHistory.push_back(buffer); + } + + return OK; +} + +// static +uint64_t RTPSender::GetNowNTP() { + struct timeval tv; + gettimeofday(&tv, NULL /* timezone */); + + uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec; + + nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; + + uint64_t hi = nowUs / 1000000ll; + uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll; + + return (hi << 32) | lo; +} + +void RTPSender::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatRTPNotify: + case kWhatRTCPNotify: + onNetNotify(msg->what() == kWhatRTPNotify, msg); + break; + + default: + TRESPASS(); + } +} + +void RTPSender::onNetNotify(bool isRTP, const sp &msg) { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + int32_t errorOccuredDuringSend; + CHECK(msg->findInt32("send", &errorOccuredDuringSend)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred during %s in session %d " + "(%d, '%s' (%s)).", + errorOccuredDuringSend ? "send" : "receive", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + mNetSession->destroySession(sessionID); + + if (sessionID == mRTPSessionID) { + mRTPSessionID = 0; + } else if (sessionID == mRTCPSessionID) { + mRTCPSessionID = 0; + } + + if (mMode == TRANSPORT_TCP) { + if (!mRTPConnected + || (mRTCPSessionID > 0 && !mRTCPConnected)) { + notifyInitDone(err); + break; + } + } + + notifyError(err); + break; + } + + case ANetworkSession::kWhatDatagram: + { + sp data; + CHECK(msg->findBuffer("data", &data)); + + if (isRTP) { + ALOGW("Huh? Received data on RTP connection..."); + } else { + onRTCPData(data); + } + break; + } + + case ANetworkSession::kWhatConnected: + { + CHECK_EQ(mMode, TRANSPORT_TCP); + + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + if (isRTP) { + CHECK_EQ(sessionID, mRTPSessionID); + mRTPConnected = true; + } else { + CHECK_EQ(sessionID, mRTCPSessionID); + mRTCPConnected = true; + } + + if (mRTPConnected && (mRTCPSessionID == 0 || mRTCPConnected)) { + notifyInitDone(OK); + } + break; + } + } +} + +status_t RTPSender::onRTCPData(const sp &buffer) { + const uint8_t *data = buffer->data(); + size_t size = buffer->size(); + + while (size > 0) { + if (size < 8) { + // Too short to be a valid RTCP header + return ERROR_MALFORMED; + } + + if ((data[0] >> 6) != 2) { + // Unsupported version. + return ERROR_UNSUPPORTED; + } + + if (data[0] & 0x20) { + // Padding present. + + size_t paddingLength = data[size - 1]; + + if (paddingLength + 12 > size) { + // If we removed this much padding we'd end up with something + // that's too short to be a valid RTP header. + return ERROR_MALFORMED; + } + + size -= paddingLength; + } + + size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4; + + if (size < headerLength) { + // Only received a partial packet? + return ERROR_MALFORMED; + } + + switch (data[1]) { + case 200: + case 201: // RR + parseReceiverReport(data, headerLength); + break; + + case 202: // SDES + case 203: + case 204: // APP + break; + + case 205: // TSFB (transport layer specific feedback) + parseTSFB(data, headerLength); + break; + + case 206: // PSFB (payload specific feedback) + // hexdump(data, headerLength); + break; + + default: + { + ALOGW("Unknown RTCP packet type %u of size %d", + (unsigned)data[1], headerLength); + break; + } + } + + data += headerLength; + size -= headerLength; + } + + return OK; +} + +status_t RTPSender::parseReceiverReport(const uint8_t *data, size_t size) { + // hexdump(data, size); + + float fractionLost = data[12] / 256.0f; + + ALOGI("lost %.2f %% of packets during report interval.", + 100.0f * fractionLost); + + return OK; +} + +status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) { + if ((data[0] & 0x1f) != 1) { + return ERROR_UNSUPPORTED; // We only support NACK for now. + } + + uint32_t srcId = U32_AT(&data[8]); + if (srcId != kSourceID) { + return ERROR_MALFORMED; + } + + for (size_t i = 12; i < size; i += 4) { + uint16_t seqNo = U16_AT(&data[i]); + uint16_t blp = U16_AT(&data[i + 2]); + + List >::iterator it = mHistory.begin(); + bool foundSeqNo = false; + while (it != mHistory.end()) { + const sp &buffer = *it; + + uint16_t bufferSeqNo = buffer->int32Data() & 0xffff; + + bool retransmit = false; + if (bufferSeqNo == seqNo) { + retransmit = true; + } else if (blp != 0) { + for (size_t i = 0; i < 16; ++i) { + if ((blp & (1 << i)) + && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) { + blp &= ~(1 << i); + retransmit = true; + } + } + } + + if (retransmit) { + ALOGV("retransmitting seqNo %d", bufferSeqNo); + + CHECK_EQ((status_t)OK, + sendRTPPacket(buffer, false /* storeInHistory */)); + + if (bufferSeqNo == seqNo) { + foundSeqNo = true; + } + + if (foundSeqNo && blp == 0) { + break; + } + } + + ++it; + } + + if (!foundSeqNo || blp != 0) { + ALOGI("Some sequence numbers were no longer available for " + "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)", + seqNo, foundSeqNo, blp); + + if (!mHistory.empty()) { + int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff; + int32_t latest = (*--mHistory.end())->int32Data() & 0xffff; + + ALOGI("have seq numbers from %d - %d", earliest, latest); + } + } + } + + return OK; +} + +void RTPSender::notifyInitDone(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInitDone); + notify->setInt32("err", err); + notify->post(); +} + +void RTPSender::notifyError(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h new file mode 100644 index 0000000..2b683a4 --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -0,0 +1,112 @@ +/* + * Copyright 2013, 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 RTP_SENDER_H_ + +#define RTP_SENDER_H_ + +#include "RTPBase.h" + +#include + +namespace android { + +struct ABuffer; +struct ANetworkSession; + +// An object of this class facilitates sending of media data over an RTP +// channel. The channel is established over a UDP or TCP connection depending +// on which "TransportMode" was chosen. In addition different RTP packetization +// schemes are supported such as "Transport Stream Packets over RTP", +// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)" +struct RTPSender : public RTPBase, public AHandler { + enum { + kWhatInitDone, + kWhatError, + }; + RTPSender( + const sp &netSession, + const sp ¬ify); + + status_t initAsync( + TransportMode mode, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort, + int32_t *outLocalRTPPort); + + status_t queueBuffer( + const sp &buffer, + uint8_t packetType, + PacketizationMode mode); + +protected: + virtual ~RTPSender(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatRTPNotify, + kWhatRTCPNotify, + }; + + enum { + kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188, + kMaxHistorySize = 1024, + kSourceID = 0xdeadbeef, + }; + + sp mNetSession; + sp mNotify; + TransportMode mMode; + int32_t mRTPSessionID; + int32_t mRTCPSessionID; + bool mRTPConnected; + bool mRTCPConnected; + + uint64_t mLastNTPTime; + uint32_t mLastRTPTime; + uint32_t mNumRTPSent; + uint32_t mNumRTPOctetsSent; + uint32_t mNumSRsSent; + + uint32_t mRTPSeqNo; + + List > mHistory; + size_t mHistorySize; + + static uint64_t GetNowNTP(); + + status_t queueTSPackets(const sp &tsPackets, uint8_t packetType); + status_t queueAVCBuffer(const sp &accessUnit, uint8_t packetType); + + status_t sendRTPPacket(const sp &packet, bool storeInHistory); + + void onNetNotify(bool isRTP, const sp &msg); + + status_t onRTCPData(const sp &data); + status_t parseReceiverReport(const uint8_t *data, size_t size); + status_t parseTSFB(const uint8_t *data, size_t size); + + void notifyInitDone(status_t err); + void notifyError(status_t err); + + DISALLOW_EVIL_CONSTRUCTORS(RTPSender); +}; + +} // namespace android + +#endif // RTP_SENDER_H_ diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp new file mode 100644 index 0000000..607d9d2 --- /dev/null +++ b/media/libstagefright/wifi-display/rtptest.cpp @@ -0,0 +1,382 @@ +/* + * Copyright 2013, 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_NEBUG 0 +#define LOG_TAG "rtptest" +#include + +#include "ANetworkSession.h" +#include "rtp/RTPSender.h" +#include "rtp/RTPReceiver.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +struct TestHandler : public AHandler { + TestHandler(const sp &netSession); + + void listen(); + void connect(const char *host, int32_t port); + +protected: + virtual ~TestHandler(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatListen, + kWhatConnect, + kWhatReceiverNotify, + kWhatSenderNotify, + kWhatSendMore, + kWhatStop, + }; + + sp mNetSession; + sp mExtractor; + sp mSender; + sp mReceiver; + + size_t mMaxSampleSize; + + int64_t mFirstTimeRealUs; + int64_t mFirstTimeMediaUs; + + status_t readMore(); + + DISALLOW_EVIL_CONSTRUCTORS(TestHandler); +}; + +TestHandler::TestHandler(const sp &netSession) + : mNetSession(netSession), + mMaxSampleSize(1024 * 1024), + mFirstTimeRealUs(-1ll), + mFirstTimeMediaUs(-1ll) { +} + +TestHandler::~TestHandler() { +} + +void TestHandler::listen() { + sp msg = new AMessage(kWhatListen, id()); + msg->post(); +} + +void TestHandler::connect(const char *host, int32_t port) { + sp msg = new AMessage(kWhatConnect, id()); + msg->setString("host", host); + msg->setInt32("port", port); + msg->post(); +} + +void TestHandler::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatListen: + { + sp notify = new AMessage(kWhatReceiverNotify, id()); + mReceiver = new RTPReceiver(mNetSession, notify); + looper()->registerHandler(mReceiver); + + CHECK_EQ((status_t)OK, + mReceiver->registerPacketType( + 33, RTPReceiver::PACKETIZATION_H264)); + + int32_t receiverRTPPort; + CHECK_EQ((status_t)OK, + mReceiver->initAsync( + RTPReceiver::TRANSPORT_UDP, &receiverRTPPort)); + + printf("picked receiverRTPPort %d\n", receiverRTPPort); + +#if 0 + CHECK_EQ((status_t)OK, + mReceiver->connect( + "127.0.0.1", senderRTPPort, senderRTPPort + 1)); +#endif + break; + } + + case kWhatConnect: + { + AString host; + CHECK(msg->findString("host", &host)); + + int32_t receiverRTPPort; + CHECK(msg->findInt32("port", &receiverRTPPort)); + + mExtractor = new NuMediaExtractor; + CHECK_EQ((status_t)OK, + mExtractor->setDataSource( + "/sdcard/Frame Counter HD 30FPS_1080p.mp4")); + + bool haveVideo = false; + for (size_t i = 0; i < mExtractor->countTracks(); ++i) { + sp format; + CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format)); + + AString mime; + CHECK(format->findString("mime", &mime)); + + if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) { + mExtractor->selectTrack(i); + haveVideo = true; + break; + } + } + + CHECK(haveVideo); + + sp notify = new AMessage(kWhatSenderNotify, id()); + mSender = new RTPSender(mNetSession, notify); + looper()->registerHandler(mSender); + + int32_t senderRTPPort; + CHECK_EQ((status_t)OK, + mSender->initAsync( + RTPSender::TRANSPORT_UDP, + host.c_str(), + receiverRTPPort, + receiverRTPPort + 1, + &senderRTPPort)); + + printf("picked senderRTPPort %d\n", senderRTPPort); + break; + } + + case kWhatSenderNotify: + { + ALOGI("kWhatSenderNotify"); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case RTPSender::kWhatInitDone: + { + int32_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGI("RTPSender::initAsync completed w/ err %d", err); + + if (err == OK) { + err = readMore(); + + if (err != OK) { + (new AMessage(kWhatStop, id()))->post(); + } + } + break; + } + + case RTPSender::kWhatError: + break; + } + break; + } + + case kWhatReceiverNotify: + { + ALOGI("kWhatReceiverNotify"); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case RTPReceiver::kWhatInitDone: + { + int32_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGI("RTPReceiver::initAsync completed w/ err %d", err); + break; + } + + case RTPSender::kWhatError: + break; + } + break; + } + + case kWhatSendMore: + { + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + CHECK_EQ((status_t)OK, + mSender->queueBuffer( + accessUnit, + 33, + RTPSender::PACKETIZATION_H264)); + + status_t err = readMore(); + + if (err != OK) { + (new AMessage(kWhatStop, id()))->post(); + } + break; + } + + case kWhatStop: + { + if (mReceiver != NULL) { + looper()->unregisterHandler(mReceiver->id()); + mReceiver.clear(); + } + + if (mSender != NULL) { + looper()->unregisterHandler(mSender->id()); + mSender.clear(); + } + + mExtractor.clear(); + + looper()->stop(); + break; + } + + default: + TRESPASS(); + } +} + +status_t TestHandler::readMore() { + int64_t timeUs; + status_t err = mExtractor->getSampleTime(&timeUs); + + if (err != OK) { + return err; + } + + sp accessUnit = new ABuffer(mMaxSampleSize); + CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit)); + + accessUnit->meta()->setInt64("timeUs", timeUs); + + CHECK_EQ((status_t)OK, mExtractor->advance()); + + int64_t nowUs = ALooper::GetNowUs(); + int64_t whenUs; + + if (mFirstTimeRealUs < 0ll) { + mFirstTimeRealUs = whenUs = nowUs; + mFirstTimeMediaUs = timeUs; + } else { + whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs; + } + + sp msg = new AMessage(kWhatSendMore, id()); + msg->setBuffer("accessUnit", accessUnit); + msg->post(whenUs - nowUs); + + return OK; +} + +} // namespace android + +static void usage(const char *me) { + fprintf(stderr, + "usage: %s -c host:port\tconnect to remote host\n" + " -l \tlisten\n", + me); +} + +int main(int argc, char **argv) { + using namespace android; + + // srand(time(NULL)); + + ProcessState::self()->startThreadPool(); + + DataSource::RegisterDefaultSniffers(); + + bool listen = false; + int32_t connectToPort = -1; + AString connectToHost; + + int res; + while ((res = getopt(argc, argv, "hc:l")) >= 0) { + switch (res) { + case 'c': + { + const char *colonPos = strrchr(optarg, ':'); + + if (colonPos == NULL) { + usage(argv[0]); + exit(1); + } + + connectToHost.setTo(optarg, colonPos - optarg); + + char *end; + connectToPort = strtol(colonPos + 1, &end, 10); + + if (*end != '\0' || end == colonPos + 1 + || connectToPort < 1 || connectToPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + break; + } + + case 'l': + { + listen = true; + break; + } + + case '?': + case 'h': + usage(argv[0]); + exit(1); + } + } + + if (!listen && connectToPort < 0) { + fprintf(stderr, + "You need to select either client or server mode.\n"); + exit(1); + } + + sp netSession = new ANetworkSession; + netSession->start(); + + sp looper = new ALooper; + + sp handler = new TestHandler(netSession); + looper->registerHandler(handler); + + if (listen) { + handler->listen(); + } + + if (connectToPort >= 0) { + handler->connect(connectToHost.c_str(), connectToPort); + } + + looper->start(true /* runOnCallingThread */); + + return 0; +} + diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index 70369bb..b53252d 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -20,15 +20,13 @@ #include "DirectRenderer.h" -#include "AnotherPacketSource.h" -#include "ATSParser.h" - #include #include #include #include #include #include +#include #include #include #include @@ -36,30 +34,13 @@ namespace android { -#if 1 -// static -const int64_t DirectRenderer::kPacketLostDelayUs = 80000ll; - -// static -const int64_t DirectRenderer::kPacketLateDelayUs = 60000ll; -#else -// static -const int64_t DirectRenderer::kPacketLostDelayUs = 1000000ll; - -// static -const int64_t DirectRenderer::kPacketLateDelayUs = -1ll; -#endif - DirectRenderer::DirectRenderer( - const sp ¬ifyLost, const sp &bufferProducer) - : mNotifyLost(notifyLost), - mSurfaceTex(bufferProducer), - mTSParser(new ATSParser(ATSParser::ALIGNED_VIDEO_DATA)), + : mSurfaceTex(bufferProducer), mVideoDecoderNotificationPending(false), - mAwaitingExtSeqNo(-1), - mRequestedRetransmission(false), - mPacketLostGeneration(0) { + mRenderPending(false), + mFirstRenderTimeUs(-1ll), + mFirstRenderRealUs(-1ll) { } DirectRenderer::~DirectRenderer() { @@ -74,58 +55,15 @@ DirectRenderer::~DirectRenderer() { void DirectRenderer::onMessageReceived(const sp &msg) { switch (msg->what()) { - case kWhatQueueBuffer: - { - sp buffer; - CHECK(msg->findBuffer("buffer", &buffer)); - - onQueueBuffer(buffer); - - dequeueMore(); - break; - } - - case kWhatPacketLate: - case kWhatPacketLost: + case kWhatVideoDecoderNotify: { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mPacketLostGeneration) { - // stale. - break; - } - - if (msg->what() == kWhatPacketLate) { - CHECK(!mRequestedRetransmission); - CHECK_GE(mAwaitingExtSeqNo, 0); - - ALOGV("packet extSeqNo %d is late, requesting retransmission.", - mAwaitingExtSeqNo); - - sp notify = mNotifyLost->dup(); - notify->setInt32("seqNo", (mAwaitingExtSeqNo & 0xffff)); - notify->post(); - - mRequestedRetransmission = true; - break; - } - - ALOGW("lost packet extSeqNo %d", mAwaitingExtSeqNo); - - sp extra; - mTSParser->signalDiscontinuity( - ATSParser::DISCONTINUITY_TIME, extra); - - mAwaitingExtSeqNo = -1; - mRequestedRetransmission = false; - dequeueMore(); + onVideoDecoderNotify(); break; } - case kWhatVideoDecoderNotify: + case kWhatRender: { - onVideoDecoderNotify(); + onRender(); break; } @@ -134,203 +72,67 @@ void DirectRenderer::onMessageReceived(const sp &msg) { } } -void DirectRenderer::onQueueBuffer(const sp &buffer) { - int32_t newExtendedSeqNo = buffer->int32Data(); - - if (mPackets.empty()) { - mPackets.push_back(buffer); - return; - } - - if (mAwaitingExtSeqNo > 0 && newExtendedSeqNo < mAwaitingExtSeqNo) { - // We're no longer interested in these. They're old. +void DirectRenderer::setFormat( + size_t trackIndex, const sp &format) { + if (trackIndex == 1) { + // Ignore audio for now. return; } - List >::iterator firstIt = mPackets.begin(); - List >::iterator it = --mPackets.end(); - for (;;) { - int32_t extendedSeqNo = (*it)->int32Data(); - - if (extendedSeqNo == newExtendedSeqNo) { - // Duplicate packet. - return; - } + CHECK(mVideoDecoder == NULL); - if (extendedSeqNo < newExtendedSeqNo) { - // Insert new packet after the one at "it". - mPackets.insert(++it, buffer); - return; - } - - if (it == firstIt) { - // Insert new packet before the first existing one. - mPackets.insert(it, buffer); - return; - } + AString mime; + CHECK(format->findString("mime", &mime)); - --it; - } -} + mVideoDecoderLooper = new ALooper; + mVideoDecoderLooper->setName("video codec looper"); -void DirectRenderer::dequeueMore() { - if (mAwaitingExtSeqNo >= 0) { - // Remove all packets before the one we're looking for, they had - // their chance. - while (!mPackets.empty() - && (*mPackets.begin())->int32Data() < mAwaitingExtSeqNo) { - ALOGV("dropping late packet extSeqNo %d", - (*mPackets.begin())->int32Data()); + mVideoDecoderLooper->start( + false /* runOnCallingThread */, + false /* canCallJava */, + PRIORITY_DEFAULT); - mPackets.erase(mPackets.begin()); - } - } + mVideoDecoder = MediaCodec::CreateByType( + mVideoDecoderLooper, mime.c_str(), false /* encoder */); - bool packetLostScheduled = (mAwaitingExtSeqNo >= 0); + CHECK(mVideoDecoder != NULL); - while (!mPackets.empty()) { - sp buffer = *mPackets.begin(); - int32_t extSeqNo = buffer->int32Data(); + status_t err = mVideoDecoder->configure( + format, + mSurfaceTex == NULL + ? NULL : new Surface(mSurfaceTex), + NULL /* crypto */, + 0 /* flags */); + CHECK_EQ(err, (status_t)OK); - if (mAwaitingExtSeqNo >= 0 && extSeqNo != mAwaitingExtSeqNo) { - break; - } + err = mVideoDecoder->start(); + CHECK_EQ(err, (status_t)OK); - mPackets.erase(mPackets.begin()); + err = mVideoDecoder->getInputBuffers( + &mVideoDecoderInputBuffers); + CHECK_EQ(err, (status_t)OK); - if (packetLostScheduled) { - packetLostScheduled = false; - cancelPacketLost(); - } - - if (mRequestedRetransmission) { - ALOGV("recovered after requesting retransmission of extSeqNo %d", - mAwaitingExtSeqNo); - } - - CHECK_EQ(buffer->size() % 188, 0u); - - for (size_t offset = 0; offset < buffer->size(); offset += 188) { - status_t err = mTSParser->feedTSPacket( - buffer->data() + offset, 188); - - CHECK_EQ(err, (status_t)OK); - } - - mAwaitingExtSeqNo = extSeqNo + 1; - mRequestedRetransmission = false; - } - - if (!packetLostScheduled && mAwaitingExtSeqNo >= 0) { - schedulePacketLost(); - } - - dequeueAccessUnits(); + scheduleVideoDecoderNotification(); } -void DirectRenderer::dequeueAccessUnits() { - sp audioSource = - static_cast( - mTSParser->getSource(ATSParser::AUDIO).get()); - - if (audioSource != NULL) { - status_t finalResult; - size_t n = 0; - while (audioSource->hasBufferAvailable(&finalResult)) { - sp accessUnit; - status_t err = audioSource->dequeueAccessUnit(&accessUnit); - if (err == OK) { - ++n; - } - } - - if (n > 0) { - ALOGV("dequeued %d audio access units.", n); - } - } - - sp videoSource = - static_cast( - mTSParser->getSource(ATSParser::VIDEO).get()); - - if (videoSource != NULL) { - if (mVideoDecoder == NULL) { - sp meta = videoSource->getFormat(); - if (meta != NULL) { - sp videoFormat; - status_t err = convertMetaDataToMessage(meta, &videoFormat); - CHECK_EQ(err, (status_t)OK); - - AString mime; - CHECK(videoFormat->findString("mime", &mime)); - - mVideoDecoderLooper = new ALooper; - mVideoDecoderLooper->setName("video codec looper"); - - mVideoDecoderLooper->start( - false /* runOnCallingThread */, - false /* canCallJava */, - PRIORITY_DEFAULT); - - mVideoDecoder = MediaCodec::CreateByType( - mVideoDecoderLooper, mime.c_str(), false /* encoder */); - - CHECK(mVideoDecoder != NULL); - - err = mVideoDecoder->configure( - videoFormat, - mSurfaceTex == NULL - ? NULL : new Surface(mSurfaceTex), - NULL /* crypto */, - 0 /* flags */); - - CHECK_EQ(err, (status_t)OK); - - err = mVideoDecoder->start(); - CHECK_EQ(err, (status_t)OK); - - err = mVideoDecoder->getInputBuffers( - &mVideoDecoderInputBuffers); - CHECK_EQ(err, (status_t)OK); - - scheduleVideoDecoderNotification(); - } - } - - status_t finalResult; - size_t n = 0; - while (videoSource->hasBufferAvailable(&finalResult)) { - sp accessUnit; - status_t err = videoSource->dequeueAccessUnit(&accessUnit); - if (err == OK) { - mVideoAccessUnits.push_back(accessUnit); - ++n; - } - } - - if (n > 0) { - ALOGV("dequeued %d video access units.", n); - queueVideoDecoderInputBuffers(); - } +void DirectRenderer::queueAccessUnit( + size_t trackIndex, const sp &accessUnit) { + if (trackIndex == 1) { + // Ignore audio for now. + return; } -} -void DirectRenderer::schedulePacketLost() { - sp msg; + if (mVideoDecoder == NULL) { + sp format = new AMessage; + format->setString("mime", "video/avc"); + format->setInt32("width", 640); + format->setInt32("height", 360); - if (kPacketLateDelayUs > 0ll) { - msg = new AMessage(kWhatPacketLate, id()); - msg->setInt32("generation", mPacketLostGeneration); - msg->post(kPacketLateDelayUs); + setFormat(0, format); } - msg = new AMessage(kWhatPacketLost, id()); - msg->setInt32("generation", mPacketLostGeneration); - msg->post(kPacketLostDelayUs); -} - -void DirectRenderer::cancelPacketLost() { - ++mPacketLostGeneration; + mVideoAccessUnits.push_back(accessUnit); + queueVideoDecoderInputBuffers(); } void DirectRenderer::queueVideoDecoderInputBuffers() { @@ -406,8 +208,7 @@ void DirectRenderer::onVideoDecoderNotify() { &flags); if (err == OK) { - err = mVideoDecoder->renderOutputBufferAndRelease(index); - CHECK_EQ(err, (status_t)OK); + queueOutputBuffer(index, timeUs); } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { // We don't care. } else if (err == INFO_FORMAT_CHANGED) { @@ -422,6 +223,62 @@ void DirectRenderer::onVideoDecoderNotify() { scheduleVideoDecoderNotification(); } +void DirectRenderer::queueOutputBuffer(size_t index, int64_t timeUs) { +#if 0 + OutputInfo info; + info.mIndex = index; + info.mTimeUs = timeUs; + mOutputBuffers.push_back(info); + + scheduleRenderIfNecessary(); +#else + status_t err = mVideoDecoder->renderOutputBufferAndRelease(index); + CHECK_EQ(err, (status_t)OK); +#endif +} + +void DirectRenderer::scheduleRenderIfNecessary() { + if (mRenderPending || mOutputBuffers.empty()) { + return; + } + + mRenderPending = true; + + int64_t timeUs = (*mOutputBuffers.begin()).mTimeUs; + int64_t nowUs = ALooper::GetNowUs(); + + if (mFirstRenderTimeUs < 0ll) { + mFirstRenderTimeUs = timeUs; + mFirstRenderRealUs = nowUs; + } + + int64_t whenUs = timeUs - mFirstRenderTimeUs + mFirstRenderRealUs; + int64_t delayUs = whenUs - nowUs; + + (new AMessage(kWhatRender, id()))->post(delayUs); +} + +void DirectRenderer::onRender() { + mRenderPending = false; + + int64_t nowUs = ALooper::GetNowUs(); + + while (!mOutputBuffers.empty()) { + const OutputInfo &info = *mOutputBuffers.begin(); + + if (info.mTimeUs > nowUs) { + break; + } + + status_t err = mVideoDecoder->renderOutputBufferAndRelease(info.mIndex); + CHECK_EQ(err, (status_t)OK); + + mOutputBuffers.erase(mOutputBuffers.begin()); + } + + scheduleRenderIfNecessary(); +} + void DirectRenderer::scheduleVideoDecoderNotification() { if (mVideoDecoderNotificationPending) { return; diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h index 2babcb8..7219080 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -23,7 +23,6 @@ namespace android { struct ABuffer; -struct ATSParser; struct IGraphicBufferProducer; struct MediaCodec; @@ -32,13 +31,10 @@ struct MediaCodec; // delay. Primarily meant to finetune packet loss discovery and minimize // latency. struct DirectRenderer : public AHandler { - DirectRenderer( - const sp ¬ifyLost, - const sp &bufferProducer); + DirectRenderer(const sp &bufferProducer); - enum { - kWhatQueueBuffer = 'queB', - }; + void setFormat(size_t trackIndex, const sp &format); + void queueAccessUnit(size_t trackIndex, const sp &accessUnit); protected: virtual void onMessageReceived(const sp &msg); @@ -46,22 +42,17 @@ protected: private: enum { - kWhatPacketLate, - kWhatPacketLost, kWhatVideoDecoderNotify, + kWhatRender, }; - static const int64_t kPacketLateDelayUs; - static const int64_t kPacketLostDelayUs; + struct OutputInfo { + size_t mIndex; + int64_t mTimeUs; + }; - sp mNotifyLost; sp mSurfaceTex; - // Ordered by extended seq number. - List > mPackets; - - sp mTSParser; - sp mVideoDecoderLooper; sp mVideoDecoder; Vector > mVideoDecoderInputBuffers; @@ -70,21 +61,19 @@ private: List > mVideoAccessUnits; - int32_t mAwaitingExtSeqNo; - bool mRequestedRetransmission; - int32_t mPacketLostGeneration; + List mOutputBuffers; + bool mRenderPending; + int64_t mFirstRenderTimeUs; + int64_t mFirstRenderRealUs; - void onQueueBuffer(const sp &buffer); void onVideoDecoderNotify(); - - void dequeueMore(); - void dequeueAccessUnits(); - - void schedulePacketLost(); - void cancelPacketLost(); + void onRender(); void queueVideoDecoderInputBuffers(); void scheduleVideoDecoderNotification(); + void scheduleRenderIfNecessary(); + + void queueOutputBuffer(size_t index, int64_t timeUs); DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); }; diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.cpp b/media/libstagefright/wifi-display/sink/LinearRegression.cpp deleted file mode 100644 index 8cfce37..0000000 --- a/media/libstagefright/wifi-display/sink/LinearRegression.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 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 "LinearRegression" -#include - -#include "LinearRegression.h" - -#include -#include - -namespace android { - -LinearRegression::LinearRegression(size_t historySize) - : mHistorySize(historySize), - mCount(0), - mHistory(new Point[mHistorySize]), - mSumX(0.0), - mSumY(0.0) { -} - -LinearRegression::~LinearRegression() { - delete[] mHistory; - mHistory = NULL; -} - -void LinearRegression::addPoint(float x, float y) { - if (mCount == mHistorySize) { - const Point &oldest = mHistory[0]; - - mSumX -= oldest.mX; - mSumY -= oldest.mY; - - memmove(&mHistory[0], &mHistory[1], (mHistorySize - 1) * sizeof(Point)); - --mCount; - } - - Point *newest = &mHistory[mCount++]; - newest->mX = x; - newest->mY = y; - - mSumX += x; - mSumY += y; -} - -bool LinearRegression::approxLine(float *n1, float *n2, float *b) const { - static const float kEpsilon = 1.0E-4; - - if (mCount < 2) { - return false; - } - - float sumX2 = 0.0f; - float sumY2 = 0.0f; - float sumXY = 0.0f; - - float meanX = mSumX / (float)mCount; - float meanY = mSumY / (float)mCount; - - for (size_t i = 0; i < mCount; ++i) { - const Point &p = mHistory[i]; - - float x = p.mX - meanX; - float y = p.mY - meanY; - - sumX2 += x * x; - sumY2 += y * y; - sumXY += x * y; - } - - float T = sumX2 + sumY2; - float D = sumX2 * sumY2 - sumXY * sumXY; - float root = sqrt(T * T * 0.25 - D); - - float L1 = T * 0.5 - root; - - if (fabs(sumXY) > kEpsilon) { - *n1 = 1.0; - *n2 = (2.0 * L1 - sumX2) / sumXY; - - float mag = sqrt((*n1) * (*n1) + (*n2) * (*n2)); - - *n1 /= mag; - *n2 /= mag; - } else { - *n1 = 0.0; - *n2 = 1.0; - } - - *b = (*n1) * meanX + (*n2) * meanY; - - return true; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.h b/media/libstagefright/wifi-display/sink/LinearRegression.h deleted file mode 100644 index ca6f5a1..0000000 --- a/media/libstagefright/wifi-display/sink/LinearRegression.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 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 LINEAR_REGRESSION_H_ - -#define LINEAR_REGRESSION_H_ - -#include -#include - -namespace android { - -// Helper class to fit a line to a set of points minimizing the sum of -// squared (orthogonal) distances from line to individual points. -struct LinearRegression { - LinearRegression(size_t historySize); - ~LinearRegression(); - - void addPoint(float x, float y); - - bool approxLine(float *n1, float *n2, float *b) const; - -private: - struct Point { - float mX, mY; - }; - - size_t mHistorySize; - size_t mCount; - Point *mHistory; - - float mSumX, mSumY; - - DISALLOW_EVIL_CONSTRUCTORS(LinearRegression); -}; - -} // namespace android - -#endif // LINEAR_REGRESSION_H_ diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp deleted file mode 100644 index 3c90a1e..0000000 --- a/media/libstagefright/wifi-display/sink/RTPSink.cpp +++ /dev/null @@ -1,870 +0,0 @@ -/* - * Copyright 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 "RTPSink" -#include - -#include "RTPSink.h" - -#include "ANetworkSession.h" - -#if USE_TUNNEL_RENDERER -#include "TunnelRenderer.h" -#define RENDERER_CLASS TunnelRenderer -#else -#include "DirectRenderer.h" -#define RENDERER_CLASS DirectRenderer -#endif - -#include -#include -#include -#include -#include -#include - -namespace android { - -struct RTPSink::Source : public RefBase { - Source(uint16_t seq, const sp &buffer, - const sp queueBufferMsg); - - bool updateSeq(uint16_t seq, const sp &buffer); - - void addReportBlock(uint32_t ssrc, const sp &buf); - -protected: - virtual ~Source(); - -private: - static const uint32_t kMinSequential = 2; - static const uint32_t kMaxDropout = 3000; - static const uint32_t kMaxMisorder = 100; - static const uint32_t kRTPSeqMod = 1u << 16; - - sp mQueueBufferMsg; - - uint16_t mMaxSeq; - uint32_t mCycles; - uint32_t mBaseSeq; - uint32_t mBadSeq; - uint32_t mProbation; - uint32_t mReceived; - uint32_t mExpectedPrior; - uint32_t mReceivedPrior; - - void initSeq(uint16_t seq); - void queuePacket(const sp &buffer); - - DISALLOW_EVIL_CONSTRUCTORS(Source); -}; - -//////////////////////////////////////////////////////////////////////////////// - -RTPSink::Source::Source( - uint16_t seq, const sp &buffer, - const sp queueBufferMsg) - : mQueueBufferMsg(queueBufferMsg), - mProbation(kMinSequential) { - initSeq(seq); - mMaxSeq = seq - 1; - - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); -} - -RTPSink::Source::~Source() { -} - -void RTPSink::Source::initSeq(uint16_t seq) { - mMaxSeq = seq; - mCycles = 0; - mBaseSeq = seq; - mBadSeq = kRTPSeqMod + 1; - mReceived = 0; - mExpectedPrior = 0; - mReceivedPrior = 0; -} - -bool RTPSink::Source::updateSeq(uint16_t seq, const sp &buffer) { - uint16_t udelta = seq - mMaxSeq; - - if (mProbation) { - // Startup phase - - if (seq == mMaxSeq + 1) { - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); - - --mProbation; - mMaxSeq = seq; - if (mProbation == 0) { - initSeq(seq); - ++mReceived; - - return true; - } - } else { - // Packet out of sequence, restart startup phase - - mProbation = kMinSequential - 1; - mMaxSeq = seq; - -#if 0 - mPackets.clear(); - mTotalBytesQueued = 0; - ALOGI("XXX cleared packets"); -#endif - - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); - } - - return false; - } - - if (udelta < kMaxDropout) { - // In order, with permissible gap. - - if (seq < mMaxSeq) { - // Sequence number wrapped - count another 64K cycle - mCycles += kRTPSeqMod; - } - - mMaxSeq = seq; - } else if (udelta <= kRTPSeqMod - kMaxMisorder) { - // The sequence number made a very large jump - - if (seq == mBadSeq) { - // Two sequential packets -- assume that the other side - // restarted without telling us so just re-sync - // (i.e. pretend this was the first packet) - - initSeq(seq); - } else { - mBadSeq = (seq + 1) & (kRTPSeqMod - 1); - - return false; - } - } else { - // Duplicate or reordered packet. - } - - ++mReceived; - - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); - - return true; -} - -void RTPSink::Source::queuePacket(const sp &buffer) { - sp msg = mQueueBufferMsg->dup(); - msg->setBuffer("buffer", buffer); - msg->post(); -} - -void RTPSink::Source::addReportBlock( - uint32_t ssrc, const sp &buf) { - uint32_t extMaxSeq = mMaxSeq | mCycles; - uint32_t expected = extMaxSeq - mBaseSeq + 1; - - int64_t lost = (int64_t)expected - (int64_t)mReceived; - if (lost > 0x7fffff) { - lost = 0x7fffff; - } else if (lost < -0x800000) { - lost = -0x800000; - } - - uint32_t expectedInterval = expected - mExpectedPrior; - mExpectedPrior = expected; - - uint32_t receivedInterval = mReceived - mReceivedPrior; - mReceivedPrior = mReceived; - - int64_t lostInterval = expectedInterval - receivedInterval; - - uint8_t fractionLost; - if (expectedInterval == 0 || lostInterval <=0) { - fractionLost = 0; - } else { - fractionLost = (lostInterval << 8) / expectedInterval; - } - - uint8_t *ptr = buf->data() + buf->size(); - - ptr[0] = ssrc >> 24; - ptr[1] = (ssrc >> 16) & 0xff; - ptr[2] = (ssrc >> 8) & 0xff; - ptr[3] = ssrc & 0xff; - - ptr[4] = fractionLost; - - ptr[5] = (lost >> 16) & 0xff; - ptr[6] = (lost >> 8) & 0xff; - ptr[7] = lost & 0xff; - - ptr[8] = extMaxSeq >> 24; - ptr[9] = (extMaxSeq >> 16) & 0xff; - ptr[10] = (extMaxSeq >> 8) & 0xff; - ptr[11] = extMaxSeq & 0xff; - - // XXX TODO: - - ptr[12] = 0x00; // interarrival jitter - ptr[13] = 0x00; - ptr[14] = 0x00; - ptr[15] = 0x00; - - ptr[16] = 0x00; // last SR - ptr[17] = 0x00; - ptr[18] = 0x00; - ptr[19] = 0x00; - - ptr[20] = 0x00; // delay since last SR - ptr[21] = 0x00; - ptr[22] = 0x00; - ptr[23] = 0x00; -} - -//////////////////////////////////////////////////////////////////////////////// - -RTPSink::RTPSink( - const sp &netSession, - const sp &bufferProducer, - const sp ¬ify) - : mNetSession(netSession), - mSurfaceTex(bufferProducer), - mNotify(notify), - mUsingTCPTransport(false), - mUsingTCPInterleaving(false), - mRTPPort(0), - mRTPSessionID(0), - mRTCPSessionID(0), - mRTPClientSessionID(0), - mRTCPClientSessionID(0), - mFirstArrivalTimeUs(-1ll), - mNumPacketsReceived(0ll), - mRegression(1000), - mMaxDelayMs(-1ll) { -} - -RTPSink::~RTPSink() { - if (mRTCPClientSessionID != 0) { - mNetSession->destroySession(mRTCPClientSessionID); - } - - if (mRTPClientSessionID != 0) { - mNetSession->destroySession(mRTPClientSessionID); - } - - if (mRTCPSessionID != 0) { - mNetSession->destroySession(mRTCPSessionID); - } - - if (mRTPSessionID != 0) { - mNetSession->destroySession(mRTPSessionID); - } -} - -status_t RTPSink::init(bool usingTCPTransport, bool usingTCPInterleaving) { - mUsingTCPTransport = usingTCPTransport; - mUsingTCPInterleaving = usingTCPInterleaving; - - if (usingTCPInterleaving) { - return OK; - } - - int clientRtp; - - sp rtpNotify = new AMessage(kWhatRTPNotify, id()); - sp rtcpNotify = new AMessage(kWhatRTCPNotify, id()); - for (clientRtp = 15550;; clientRtp += 2) { - int32_t rtpSession; - status_t err; - struct in_addr ifaceAddr; - if (usingTCPTransport) { - ifaceAddr.s_addr = INADDR_ANY; - err = mNetSession->createTCPDatagramSession( - ifaceAddr, clientRtp, rtpNotify, &rtpSession); - } else { - err = mNetSession->createUDPSession( - clientRtp, rtpNotify, &rtpSession); - } - - if (err != OK) { - ALOGI("failed to create RTP socket on port %d", clientRtp); - continue; - } - - int32_t rtcpSession; - if (usingTCPTransport) { - err = mNetSession->createTCPDatagramSession( - ifaceAddr, clientRtp + 1, rtcpNotify, &rtcpSession); - } else { - err = mNetSession->createUDPSession( - clientRtp + 1, rtcpNotify, &rtcpSession); - } - - if (err == OK) { - mRTPPort = clientRtp; - mRTPSessionID = rtpSession; - mRTCPSessionID = rtcpSession; - break; - } - - ALOGI("failed to create RTCP socket on port %d", clientRtp + 1); - mNetSession->destroySession(rtpSession); - } - - if (mRTPPort == 0) { - return UNKNOWN_ERROR; - } - - return OK; -} - -int32_t RTPSink::getRTPPort() const { - return mRTPPort; -} - -void RTPSink::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatRTPNotify: - case kWhatRTCPNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred in session %d (%d, '%s/%s').", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - if (sessionID == mRTPSessionID) { - mRTPSessionID = 0; - } else if (sessionID == mRTCPSessionID) { - mRTCPSessionID = 0; - } - break; - } - - case ANetworkSession::kWhatDatagram: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp data; - CHECK(msg->findBuffer("data", &data)); - - status_t err; - if (msg->what() == kWhatRTPNotify) { - err = parseRTP(data); - } else { - err = parseRTCP(data); - } - break; - } - - case ANetworkSession::kWhatClientConnected: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - ALOGI("TCP session %d now connected", sessionID); - - int32_t serverPort; - CHECK(msg->findInt32("server-port", &serverPort)); - - if (serverPort == mRTPPort) { - mRTPClientSessionID = sessionID; - } else { - CHECK_EQ(serverPort, mRTPPort + 1); - mRTCPClientSessionID = sessionID; - } - break; - } - - default: - TRESPASS(); - } - break; - } - - case kWhatSendRR: - { - onSendRR(); - break; - } - - case kWhatPacketLost: - { - onPacketLost(msg); - break; - } - - case kWhatInject: - { - int32_t isRTP; - CHECK(msg->findInt32("isRTP", &isRTP)); - - sp buffer; - CHECK(msg->findBuffer("buffer", &buffer)); - - status_t err; - if (isRTP) { - err = parseRTP(buffer); - } else { - err = parseRTCP(buffer); - } - break; - } - - default: - TRESPASS(); - } -} - -status_t RTPSink::injectPacket(bool isRTP, const sp &buffer) { - sp msg = new AMessage(kWhatInject, id()); - msg->setInt32("isRTP", isRTP); - msg->setBuffer("buffer", buffer); - msg->post(); - - return OK; -} - -status_t RTPSink::parseRTP(const sp &buffer) { - size_t size = buffer->size(); - if (size < 12) { - // Too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - const uint8_t *data = buffer->data(); - - if ((data[0] >> 6) != 2) { - // Unsupported version. - return ERROR_UNSUPPORTED; - } - - if (data[0] & 0x20) { - // Padding present. - - size_t paddingLength = data[size - 1]; - - if (paddingLength + 12 > size) { - // If we removed this much padding we'd end up with something - // that's too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - size -= paddingLength; - } - - int numCSRCs = data[0] & 0x0f; - - size_t payloadOffset = 12 + 4 * numCSRCs; - - if (size < payloadOffset) { - // Not enough data to fit the basic header and all the CSRC entries. - return ERROR_MALFORMED; - } - - if (data[0] & 0x10) { - // Header eXtension present. - - if (size < payloadOffset + 4) { - // Not enough data to fit the basic header, all CSRC entries - // and the first 4 bytes of the extension header. - - return ERROR_MALFORMED; - } - - const uint8_t *extensionData = &data[payloadOffset]; - - size_t extensionLength = - 4 * (extensionData[2] << 8 | extensionData[3]); - - if (size < payloadOffset + 4 + extensionLength) { - return ERROR_MALFORMED; - } - - payloadOffset += 4 + extensionLength; - } - - uint32_t srcId = U32_AT(&data[8]); - uint32_t rtpTime = U32_AT(&data[4]); - uint16_t seqNo = U16_AT(&data[2]); - -#if 0 - int64_t arrivalTimeUs; - CHECK(buffer->meta()->findInt64("arrivalTimeUs", &arrivalTimeUs)); - - if (mFirstArrivalTimeUs < 0ll) { - mFirstArrivalTimeUs = arrivalTimeUs; - } - arrivalTimeUs -= mFirstArrivalTimeUs; - - int64_t arrivalTimeMedia = (arrivalTimeUs * 9ll) / 100ll; - - ALOGV("seqNo: %d, SSRC 0x%08x, diff %lld", - seqNo, srcId, rtpTime - arrivalTimeMedia); - - mRegression.addPoint((float)rtpTime, (float)arrivalTimeMedia); - - ++mNumPacketsReceived; - - float n1, n2, b; - if (mRegression.approxLine(&n1, &n2, &b)) { - ALOGV("Line %lld: %.2f %.2f %.2f, slope %.2f", - mNumPacketsReceived, n1, n2, b, -n1 / n2); - - float expectedArrivalTimeMedia = (b - n1 * (float)rtpTime) / n2; - float latenessMs = (arrivalTimeMedia - expectedArrivalTimeMedia) / 90.0; - - if (mMaxDelayMs < 0ll || latenessMs > mMaxDelayMs) { - mMaxDelayMs = latenessMs; - ALOGI("packet was %.2f ms late", latenessMs); - } - } -#endif - - sp meta = buffer->meta(); - meta->setInt32("ssrc", srcId); - meta->setInt32("rtp-time", rtpTime); - meta->setInt32("PT", data[1] & 0x7f); - meta->setInt32("M", data[1] >> 7); - - buffer->setRange(payloadOffset, size - payloadOffset); - - ssize_t index = mSources.indexOfKey(srcId); - if (index < 0) { - if (mRenderer == NULL) { - sp notifyLost = new AMessage(kWhatPacketLost, id()); - notifyLost->setInt32("ssrc", srcId); - - mRenderer = new RENDERER_CLASS(notifyLost, mSurfaceTex); - looper()->registerHandler(mRenderer); - } - - sp queueBufferMsg = - new AMessage(RENDERER_CLASS::kWhatQueueBuffer, mRenderer->id()); - - sp source = new Source(seqNo, buffer, queueBufferMsg); - mSources.add(srcId, source); - } else { - mSources.valueAt(index)->updateSeq(seqNo, buffer); - } - - return OK; -} - -status_t RTPSink::parseRTCP(const sp &buffer) { - const uint8_t *data = buffer->data(); - size_t size = buffer->size(); - - while (size > 0) { - if (size < 8) { - // Too short to be a valid RTCP header - return ERROR_MALFORMED; - } - - if ((data[0] >> 6) != 2) { - // Unsupported version. - return ERROR_UNSUPPORTED; - } - - if (data[0] & 0x20) { - // Padding present. - - size_t paddingLength = data[size - 1]; - - if (paddingLength + 12 > size) { - // If we removed this much padding we'd end up with something - // that's too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - size -= paddingLength; - } - - size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4; - - if (size < headerLength) { - // Only received a partial packet? - return ERROR_MALFORMED; - } - - switch (data[1]) { - case 200: - { - parseSR(data, headerLength); - break; - } - - case 201: // RR - case 202: // SDES - case 204: // APP - break; - - case 205: // TSFB (transport layer specific feedback) - case 206: // PSFB (payload specific feedback) - // hexdump(data, headerLength); - break; - - case 203: - { - parseBYE(data, headerLength); - break; - } - - default: - { - ALOGW("Unknown RTCP packet type %u of size %d", - (unsigned)data[1], headerLength); - break; - } - } - - data += headerLength; - size -= headerLength; - } - - return OK; -} - -status_t RTPSink::parseBYE(const uint8_t *data, size_t size) { - size_t SC = data[0] & 0x3f; - - if (SC == 0 || size < (4 + SC * 4)) { - // Packet too short for the minimal BYE header. - return ERROR_MALFORMED; - } - - uint32_t id = U32_AT(&data[4]); - - return OK; -} - -status_t RTPSink::parseSR(const uint8_t *data, size_t size) { - size_t RC = data[0] & 0x1f; - - if (size < (7 + RC * 6) * 4) { - // Packet too short for the minimal SR header. - return ERROR_MALFORMED; - } - - uint32_t id = U32_AT(&data[4]); - uint64_t ntpTime = U64_AT(&data[8]); - uint32_t rtpTime = U32_AT(&data[16]); - - ALOGV("SR: ssrc 0x%08x, ntpTime 0x%016llx, rtpTime 0x%08x", - id, ntpTime, rtpTime); - - return OK; -} - -status_t RTPSink::connect( - const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort) { - ALOGI("connecting RTP/RTCP sockets to %s:{%d,%d}", - host, remoteRtpPort, remoteRtcpPort); - - status_t err = - mNetSession->connectUDPSession(mRTPSessionID, host, remoteRtpPort); - - if (err != OK) { - return err; - } - - err = mNetSession->connectUDPSession(mRTCPSessionID, host, remoteRtcpPort); - - if (err != OK) { - return err; - } - -#if 0 - sp buf = new ABuffer(1500); - memset(buf->data(), 0, buf->size()); - - mNetSession->sendRequest( - mRTPSessionID, buf->data(), buf->size()); - - mNetSession->sendRequest( - mRTCPSessionID, buf->data(), buf->size()); -#endif - - if (!mUsingTCPTransport) { - scheduleSendRR(); - } - - return OK; -} - -void RTPSink::scheduleSendRR() { - (new AMessage(kWhatSendRR, id()))->post(2000000ll); -} - -void RTPSink::addSDES(const sp &buffer) { - uint8_t *data = buffer->data() + buffer->size(); - data[0] = 0x80 | 1; - data[1] = 202; // SDES - data[4] = 0xde; // SSRC - data[5] = 0xad; - data[6] = 0xbe; - data[7] = 0xef; - - size_t offset = 8; - - data[offset++] = 1; // CNAME - - AString cname = "stagefright@somewhere"; - data[offset++] = cname.size(); - - memcpy(&data[offset], cname.c_str(), cname.size()); - offset += cname.size(); - - data[offset++] = 6; // TOOL - - AString tool = "stagefright/1.0"; - data[offset++] = tool.size(); - - memcpy(&data[offset], tool.c_str(), tool.size()); - offset += tool.size(); - - data[offset++] = 0; - - if ((offset % 4) > 0) { - size_t count = 4 - (offset % 4); - switch (count) { - case 3: - data[offset++] = 0; - case 2: - data[offset++] = 0; - case 1: - data[offset++] = 0; - } - } - - size_t numWords = (offset / 4) - 1; - data[2] = numWords >> 8; - data[3] = numWords & 0xff; - - buffer->setRange(buffer->offset(), buffer->size() + offset); -} - -void RTPSink::onSendRR() { - sp buf = new ABuffer(1500); - buf->setRange(0, 0); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 0; - ptr[1] = 201; // RR - ptr[2] = 0; - ptr[3] = 1; - ptr[4] = 0xde; // SSRC - ptr[5] = 0xad; - ptr[6] = 0xbe; - ptr[7] = 0xef; - - buf->setRange(0, 8); - - size_t numReportBlocks = 0; - for (size_t i = 0; i < mSources.size(); ++i) { - uint32_t ssrc = mSources.keyAt(i); - sp source = mSources.valueAt(i); - - if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) { - // Cannot fit another report block. - break; - } - - source->addReportBlock(ssrc, buf); - ++numReportBlocks; - } - - ptr[0] |= numReportBlocks; // 5 bit - - size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks; - ptr[2] = sizeInWordsMinus1 >> 8; - ptr[3] = sizeInWordsMinus1 & 0xff; - - buf->setRange(0, (sizeInWordsMinus1 + 1) * 4); - - addSDES(buf); - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); - - scheduleSendRR(); -} - -void RTPSink::onPacketLost(const sp &msg) { - if (mUsingTCPTransport) { - ALOGW("huh? lost a packet even though using reliable transport?"); - return; - } - - uint32_t srcId; - CHECK(msg->findInt32("ssrc", (int32_t *)&srcId)); - - int32_t seqNo; - CHECK(msg->findInt32("seqNo", &seqNo)); - - int32_t blp = 0; - - sp buf = new ABuffer(16); - buf->setRange(0, 0); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 1; // generic NACK - ptr[1] = 205; // TSFB - ptr[2] = 0; - ptr[3] = 3; - ptr[4] = 0xde; // sender SSRC - ptr[5] = 0xad; - ptr[6] = 0xbe; - ptr[7] = 0xef; - ptr[8] = (srcId >> 24) & 0xff; - ptr[9] = (srcId >> 16) & 0xff; - ptr[10] = (srcId >> 8) & 0xff; - ptr[11] = (srcId & 0xff); - ptr[12] = (seqNo >> 8) & 0xff; - ptr[13] = (seqNo & 0xff); - ptr[14] = (blp >> 8) & 0xff; - ptr[15] = (blp & 0xff); - - buf->setRange(0, 16); - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h deleted file mode 100644 index 4706c6d..0000000 --- a/media/libstagefright/wifi-display/sink/RTPSink.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 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 RTP_SINK_H_ - -#define RTP_SINK_H_ - -#include - -#include "LinearRegression.h" - -#include - -#define USE_TUNNEL_RENDERER 0 - -namespace android { - -struct ABuffer; -struct ANetworkSession; - -#if USE_TUNNEL_RENDERER -struct TunnelRenderer; -#else -struct DirectRenderer; -#endif - -// Creates a pair of sockets for RTP/RTCP traffic, instantiates a renderer -// for incoming transport stream data and occasionally sends statistics over -// the RTCP channel. -struct RTPSink : public AHandler { - RTPSink(const sp &netSession, - const sp &bufferProducer, - const sp ¬ify); - - // If TCP interleaving is used, no UDP sockets are created, instead - // incoming RTP/RTCP packets (arriving on the RTSP control connection) - // are manually injected by WifiDisplaySink. - status_t init(bool usingTCPTransport, bool usingTCPInterleaving); - - status_t connect( - const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort); - - int32_t getRTPPort() const; - - status_t injectPacket(bool isRTP, const sp &buffer); - -protected: - virtual void onMessageReceived(const sp &msg); - virtual ~RTPSink(); - -private: - enum { - kWhatRTPNotify, - kWhatRTCPNotify, - kWhatSendRR, - kWhatPacketLost, - kWhatInject, - }; - - struct Source; - struct StreamSource; - - sp mNetSession; - sp mSurfaceTex; - sp mNotify; - KeyedVector > mSources; - - bool mUsingTCPTransport; - bool mUsingTCPInterleaving; - - int32_t mRTPPort; - - int32_t mRTPSessionID; // in TCP unicast mode these are just server - int32_t mRTCPSessionID; // sockets. No data is transferred through them. - - int32_t mRTPClientSessionID; // in TCP unicast mode - int32_t mRTCPClientSessionID; - - int64_t mFirstArrivalTimeUs; - int64_t mNumPacketsReceived; - LinearRegression mRegression; - int64_t mMaxDelayMs; - -#if USE_TUNNEL_RENDERER - sp mRenderer; -#else - sp mRenderer; -#endif - - status_t parseRTP(const sp &buffer); - status_t parseRTCP(const sp &buffer); - status_t parseBYE(const uint8_t *data, size_t size); - status_t parseSR(const uint8_t *data, size_t size); - - void addSDES(const sp &buffer); - void onSendRR(); - void onPacketLost(const sp &msg); - void scheduleSendRR(); - - DISALLOW_EVIL_CONSTRUCTORS(RTPSink); -}; - -} // namespace android - -#endif // RTP_SINK_H_ diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index 75f9d73..d9d8a76 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -158,175 +158,17 @@ void TunnelRenderer::StreamSource::doSomeWork() { //////////////////////////////////////////////////////////////////////////////// TunnelRenderer::TunnelRenderer( - const sp ¬ifyLost, const sp &bufferProducer) - : mNotifyLost(notifyLost), - mSurfaceTex(bufferProducer), - mTotalBytesQueued(0ll), - mLastDequeuedExtSeqNo(-1), - mFirstFailedAttemptUs(-1ll), - mRequestedRetransmission(false) { + : mSurfaceTex(bufferProducer), + mStartup(true) { } TunnelRenderer::~TunnelRenderer() { destroyPlayer(); } -void TunnelRenderer::queueBuffer(const sp &buffer) { - Mutex::Autolock autoLock(mLock); - - mTotalBytesQueued += buffer->size(); - - if (mPackets.empty()) { - mPackets.push_back(buffer); - return; - } - - int32_t newExtendedSeqNo = buffer->int32Data(); - - List >::iterator firstIt = mPackets.begin(); - List >::iterator it = --mPackets.end(); - for (;;) { - int32_t extendedSeqNo = (*it)->int32Data(); - - if (extendedSeqNo == newExtendedSeqNo) { - // Duplicate packet. - return; - } - - if (extendedSeqNo < newExtendedSeqNo) { - // Insert new packet after the one at "it". - mPackets.insert(++it, buffer); - return; - } - - if (it == firstIt) { - // Insert new packet before the first existing one. - mPackets.insert(it, buffer); - return; - } - - --it; - } -} - -sp TunnelRenderer::dequeueBuffer() { - Mutex::Autolock autoLock(mLock); - - sp buffer; - int32_t extSeqNo; - while (!mPackets.empty()) { - buffer = *mPackets.begin(); - extSeqNo = buffer->int32Data(); - - if (mLastDequeuedExtSeqNo < 0 || extSeqNo > mLastDequeuedExtSeqNo) { - break; - } - - // This is a retransmission of a packet we've already returned. - - mTotalBytesQueued -= buffer->size(); - buffer.clear(); - extSeqNo = -1; - - mPackets.erase(mPackets.begin()); - } - - if (mPackets.empty()) { - if (mFirstFailedAttemptUs < 0ll) { - mFirstFailedAttemptUs = ALooper::GetNowUs(); - mRequestedRetransmission = false; - } else { - ALOGV("no packets available for %.2f secs", - (ALooper::GetNowUs() - mFirstFailedAttemptUs) / 1E6); - } - - return NULL; - } - - if (mLastDequeuedExtSeqNo < 0 || extSeqNo == mLastDequeuedExtSeqNo + 1) { - if (mRequestedRetransmission) { - ALOGI("Recovered after requesting retransmission of %d", - extSeqNo); - } - - mLastDequeuedExtSeqNo = extSeqNo; - mFirstFailedAttemptUs = -1ll; - mRequestedRetransmission = false; - - mPackets.erase(mPackets.begin()); - - mTotalBytesQueued -= buffer->size(); - - return buffer; - } - - if (mFirstFailedAttemptUs < 0ll) { - mFirstFailedAttemptUs = ALooper::GetNowUs(); - - ALOGV("failed to get the correct packet the first time."); - return NULL; - } - - if (mFirstFailedAttemptUs + 50000ll > ALooper::GetNowUs()) { - // We're willing to wait a little while to get the right packet. - -#if 1 - if (!mRequestedRetransmission) { - ALOGI("requesting retransmission of extSeqNo %d (seqNo %d)", - mLastDequeuedExtSeqNo + 1, - (mLastDequeuedExtSeqNo + 1) & 0xffff); - - sp notify = mNotifyLost->dup(); - notify->setInt32("seqNo", (mLastDequeuedExtSeqNo + 1) & 0xffff); - notify->post(); - - mRequestedRetransmission = true; - } else -#endif - { - ALOGV("still waiting for the correct packet to arrive."); - } - - return NULL; - } - - ALOGI("dropping packet. extSeqNo %d didn't arrive in time", - mLastDequeuedExtSeqNo + 1); - - // Permanent failure, we never received the packet. - mLastDequeuedExtSeqNo = extSeqNo; - mFirstFailedAttemptUs = -1ll; - mRequestedRetransmission = false; - - mTotalBytesQueued -= buffer->size(); - - mPackets.erase(mPackets.begin()); - - return buffer; -} - void TunnelRenderer::onMessageReceived(const sp &msg) { switch (msg->what()) { - case kWhatQueueBuffer: - { - sp buffer; - CHECK(msg->findBuffer("buffer", &buffer)); - - queueBuffer(buffer); - - if (mStreamSource == NULL) { - if (mTotalBytesQueued > 0ll) { - initPlayer(); - } else { - ALOGI("Have %lld bytes queued...", mTotalBytesQueued); - } - } else { - mStreamSource->doSomeWork(); - } - break; - } - default: TRESPASS(); } @@ -396,5 +238,31 @@ void TunnelRenderer::destroyPlayer() { } } +void TunnelRenderer::queueBuffer(const sp &buffer) { + { + Mutex::Autolock autoLock(mLock); + mBuffers.push_back(buffer); + } + + if (mStartup) { + initPlayer(); + mStartup = false; + } + + mStreamSource->doSomeWork(); +} + +sp TunnelRenderer::dequeueBuffer() { + Mutex::Autolock autoLock(mLock); + if (mBuffers.empty()) { + return NULL; + } + + sp buf = *mBuffers.begin(); + mBuffers.erase(mBuffers.begin()); + + return buf; +} + } // namespace android diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h index 52e6e66..8e96665 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.h @@ -34,16 +34,11 @@ struct IStreamListener; // and sends the resulting transport stream to a mediaplayer instance // for playback. struct TunnelRenderer : public AHandler { - TunnelRenderer( - const sp ¬ifyLost, - const sp &bufferProducer); + TunnelRenderer(const sp &bufferProducer); + void queueBuffer(const sp &buffer); sp dequeueBuffer(); - enum { - kWhatQueueBuffer, - }; - protected: virtual void onMessageReceived(const sp &msg); virtual ~TunnelRenderer(); @@ -54,11 +49,10 @@ private: mutable Mutex mLock; - sp mNotifyLost; sp mSurfaceTex; - List > mPackets; - int64_t mTotalBytesQueued; + bool mStartup; + List > mBuffers; sp mComposerClient; sp mSurfaceControl; @@ -67,15 +61,9 @@ private: sp mPlayer; sp mStreamSource; - int32_t mLastDequeuedExtSeqNo; - int64_t mFirstFailedAttemptUs; - bool mRequestedRetransmission; - void initPlayer(); void destroyPlayer(); - void queueBuffer(const sp &buffer); - DISALLOW_EVIL_CONSTRUCTORS(TunnelRenderer); }; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 55581a6..a6f58cd 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -19,8 +19,11 @@ #include #include "WifiDisplaySink.h" + +#include "DirectRenderer.h" +#include "MediaReceiver.h" #include "ParsedMessage.h" -#include "RTPSink.h" +#include "TunnelRenderer.h" #include #include @@ -42,7 +45,8 @@ WifiDisplaySink::WifiDisplaySink( mUsingTCPTransport(false), mUsingTCPInterleaving(false), mSessionID(0), - mNextCSeq(1) { + mNextCSeq(1), + mIDRFrameRequestPending(false) { #if 1 // We support any and all resolutions, but prefer 720p30 mSinkSupportedVideoFormats.setNativeResolution( @@ -50,11 +54,11 @@ WifiDisplaySink::WifiDisplaySink( mSinkSupportedVideoFormats.enableAll(); #else - // We only support 800 x 600 p60. + // We only support 640 x 360 p30. mSinkSupportedVideoFormats.disableAll(); mSinkSupportedVideoFormats.setNativeResolution( - VideoFormats::RESOLUTION_VESA, 1); // 800 x 600 p60 + VideoFormats::RESOLUTION_HH, 6); // 640 x 360 p30 #endif } @@ -212,20 +216,6 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { break; } - case ANetworkSession::kWhatBinaryData: - { - CHECK(mUsingTCPInterleaving); - - int32_t channel; - CHECK(msg->findInt32("channel", &channel)); - - sp data; - CHECK(msg->findBuffer("data", &data)); - - mRTPSink->injectPacket(channel == 0 /* isRTP */, data); - break; - } - default: TRESPASS(); } @@ -238,15 +228,80 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { break; } - case kWhatRequestIDRFrame: + case kWhatMediaReceiverNotify: { - ALOGI("requesting IDR frame"); - sendIDRFrameRequest(mSessionID); + onMediaReceiverNotify(msg); break; } - case kWhatRTPSinkNotify: + default: + TRESPASS(); + } +} + +void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case MediaReceiver::kWhatInitDone: { + status_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGI("MediaReceiver initialization completed w/ err %d", err); + break; + } + + case MediaReceiver::kWhatError: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGE("MediaReceiver signaled error %d", err); + break; + } + + case MediaReceiver::kWhatAccessUnit: + { + if (mRenderer == NULL) { +#if USE_TUNNEL_RENDERER + mRenderer = new TunnelRenderer(mSurfaceTex); +#else + mRenderer = new DirectRenderer(mSurfaceTex); +#endif + + looper()->registerHandler(mRenderer); + } + + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + +#if USE_TUNNEL_RENDERER + mRenderer->queueBuffer(accessUnit); +#else + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + sp format; + if (msg->findMessage("format", &format)) { + mRenderer->setFormat(trackIndex, format); + } + + mRenderer->queueAccessUnit(trackIndex, accessUnit); +#endif + break; + } + + case MediaReceiver::kWhatPacketLost: + { +#if 0 + if (!mIDRFrameRequestPending) { + ALOGI("requesting IDR frame"); + + sendIDRFrameRequest(mSessionID); + } +#endif break; } @@ -381,7 +436,8 @@ status_t WifiDisplaySink::configureTransport(const sp &msg) { ALOGW("Server picked an odd numbered RTP port."); } - return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort); + return mMediaReceiver->connectTrack( + 0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort); } status_t WifiDisplaySink::onReceivePlayResponse( @@ -402,6 +458,9 @@ status_t WifiDisplaySink::onReceivePlayResponse( status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse( int32_t sessionID, const sp &msg) { + CHECK(mIDRFrameRequestPending); + mIDRFrameRequestPending = false; + return OK; } @@ -539,16 +598,48 @@ void WifiDisplaySink::onGetParameterRequest( } status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { - sp notify = new AMessage(kWhatRTPSinkNotify, id()); + sp notify = new AMessage(kWhatMediaReceiverNotify, id()); + + mMediaReceiverLooper = new ALooper; + mMediaReceiverLooper->setName("media_receiver"); + + mMediaReceiverLooper->start( + false /* runOnCallingThread */, + false /* canCallJava */, + PRIORITY_AUDIO); + + mMediaReceiver = new MediaReceiver(mNetSession, notify); + mMediaReceiverLooper->registerHandler(mMediaReceiver); - mRTPSink = new RTPSink(mNetSession, mSurfaceTex, notify); - looper()->registerHandler(mRTPSink); + RTPReceiver::TransportMode mode = RTPReceiver::TRANSPORT_UDP; + if (mUsingTCPTransport) { + if (mUsingTCPInterleaving) { + mode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED; + } else { + mode = RTPReceiver::TRANSPORT_TCP; + } + } - status_t err = mRTPSink->init(mUsingTCPTransport, mUsingTCPInterleaving); + int32_t localRTPPort; + status_t err = mMediaReceiver->addTrack(mode, &localRTPPort); + + if (err == OK) { + err = mMediaReceiver->initAsync( +#if USE_TUNNEL_RENDERER + MediaReceiver::MODE_TRANSPORT_STREAM_RAW +#else + MediaReceiver::MODE_TRANSPORT_STREAM +#endif + ); + } if (err != OK) { - looper()->unregisterHandler(mRTPSink->id()); - mRTPSink.clear(); + mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id()); + mMediaReceiver.clear(); + + mMediaReceiverLooper->stop(); + mMediaReceiverLooper.clear(); + return err; } @@ -556,17 +647,19 @@ status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { AppendCommonResponse(&request, mNextCSeq); - if (mUsingTCPInterleaving) { + if (mode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) { request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n"); + } else if (mode == RTPReceiver::TRANSPORT_TCP) { + request.append( + StringPrintf( + "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n", + localRTPPort)); } else { - int32_t rtpPort = mRTPSink->getRTPPort(); - request.append( StringPrintf( - "Transport: RTP/AVP/%s;unicast;client_port=%d-%d\r\n", - mUsingTCPTransport ? "TCP" : "UDP", - rtpPort, - rtpPort + 1)); + "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", + localRTPPort, + localRTPPort + 1)); } request.append("\r\n"); @@ -611,6 +704,8 @@ status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) { } status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) { + CHECK(!mIDRFrameRequestPending); + AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; AppendCommonResponse(&request, mNextCSeq); @@ -636,6 +731,8 @@ status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) { ++mNextCSeq; + mIDRFrameRequestPending = true; + return OK; } diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index 8b5ff6b..01af58b 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -28,8 +28,12 @@ namespace android { struct AMessage; +struct DirectRenderer; +struct MediaReceiver; struct ParsedMessage; -struct RTPSink; +struct TunnelRenderer; + +#define USE_TUNNEL_RENDERER 0 // Represents the RTSP client acting as a wifi display sink. // Connects to a wifi display source and renders the incoming @@ -68,8 +72,7 @@ private: kWhatStart, kWhatRTSPNotify, kWhatStop, - kWhatRequestIDRFrame, - kWhatRTPSinkNotify, + kWhatMediaReceiverNotify, }; struct ResponseID { @@ -100,10 +103,20 @@ private: KeyedVector mResponseHandlers; - sp mRTPSink; + sp mMediaReceiverLooper; + sp mMediaReceiver; + +#if USE_TUNNEL_RENDERER + sp mRenderer; +#else + sp mRenderer; +#endif + AString mPlaybackSessionID; int32_t mPlaybackSessionTimeoutSecs; + bool mIDRFrameRequestPending; + status_t sendM2(int32_t sessionID); status_t sendSetup(int32_t sessionID, const char *uri); status_t sendPlay(int32_t sessionID, const char *uri); @@ -143,6 +156,8 @@ private: int32_t cseq, const sp &data); + void onMediaReceiverNotify(const sp &msg); + void sendErrorResponse( int32_t sessionID, const char *errorDetail, diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index ede4e60..ea195b3 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -23,8 +23,6 @@ #include "Converter.h" #include "MediaPuller.h" #include "RepeaterSource.h" -#include "Sender.h" -#include "TSPacketizer.h" #include "include/avc_utils.h" #include "WifiDisplaySource.h" @@ -65,9 +63,9 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler { bool isAudio() const; const sp &converter() const; - ssize_t packetizerTrackIndex() const; - void setPacketizerTrackIndex(size_t index); + ssize_t mediaSenderTrackIndex() const; + void setMediaSenderTrackIndex(size_t index); status_t start(); void stopAsync(); @@ -107,7 +105,7 @@ private: sp mMediaPuller; sp mConverter; bool mStarted; - ssize_t mPacketizerTrackIndex; + ssize_t mMediaSenderTrackIndex; bool mIsAudio; List > mQueuedAccessUnits; sp mRepeaterSource; @@ -131,7 +129,6 @@ WifiDisplaySource::PlaybackSession::Track::Track( mMediaPuller(mediaPuller), mConverter(converter), mStarted(false), - mPacketizerTrackIndex(-1), mIsAudio(IsAudioFormat(mConverter->getOutputFormat())), mLastOutputBufferQueuedTimeUs(-1ll) { } @@ -161,13 +158,14 @@ const sp &WifiDisplaySource::PlaybackSession::Track::converter() cons return mConverter; } -ssize_t WifiDisplaySource::PlaybackSession::Track::packetizerTrackIndex() const { - return mPacketizerTrackIndex; +ssize_t WifiDisplaySource::PlaybackSession::Track::mediaSenderTrackIndex() const { + CHECK_GE(mMediaSenderTrackIndex, 0); + return mMediaSenderTrackIndex; } -void WifiDisplaySource::PlaybackSession::Track::setPacketizerTrackIndex(size_t index) { - CHECK_LT(mPacketizerTrackIndex, 0); - mPacketizerTrackIndex = index; +void WifiDisplaySource::PlaybackSession::Track::setMediaSenderTrackIndex( + size_t index) { + mMediaSenderTrackIndex = index; } status_t WifiDisplaySource::PlaybackSession::Track::start() { @@ -331,22 +329,28 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( mNotify(notify), mInterfaceAddr(interfaceAddr), mHDCP(hdcp), + mLocalRTPPort(-1), mWeAreDead(false), mPaused(false), mLastLifesignUs(), mVideoTrackIndex(-1), - mPrevTimeUs(-1ll), - mAllTracksHavePacketizerIndex(false) { + mPrevTimeUs(-1ll) { } status_t WifiDisplaySource::PlaybackSession::init( const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - Sender::TransportMode transportMode, + RTPSender::TransportMode transportMode, bool enableAudio, bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, size_t videoResolutionIndex) { + sp notify = new AMessage(kWhatMediaSenderNotify, id()); + mMediaSender = new MediaSender(mNetSession, notify); + looper()->registerHandler(mMediaSender); + + mMediaSender->setHDCP(mHDCP); + status_t err = setupPacketizer( enableAudio, usePCMAudio, @@ -354,26 +358,22 @@ status_t WifiDisplaySource::PlaybackSession::init( videoResolutionType, videoResolutionIndex); - if (err != OK) { - return err; + if (err == OK) { + err = mMediaSender->initAsync( + -1 /* trackIndex */, + transportMode, + clientIP, + clientRtp, + clientRtcp, + &mLocalRTPPort); } - sp notify = new AMessage(kWhatSenderNotify, id()); - mSender = new Sender(mNetSession, notify); - - mSenderLooper = new ALooper; - mSenderLooper->setName("sender_looper"); - - mSenderLooper->start( - false /* runOnCallingThread */, - false /* canCallJava */, - PRIORITY_AUDIO); - - mSenderLooper->registerHandler(mSender); + if (err != OK) { + mLocalRTPPort = -1; - err = mSender->init(clientIP, clientRtp, clientRtcp, transportMode); + looper()->unregisterHandler(mMediaSender->id()); + mMediaSender.clear(); - if (err != OK) { return err; } @@ -386,7 +386,7 @@ WifiDisplaySource::PlaybackSession::~PlaybackSession() { } int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const { - return mSender->getRTPPort(); + return mLocalRTPPort; } int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const { @@ -406,18 +406,10 @@ status_t WifiDisplaySource::PlaybackSession::play() { } status_t WifiDisplaySource::PlaybackSession::finishPlay() { - // XXX Give the dongle a second to bind its sockets. - (new AMessage(kWhatFinishPlay, id()))->post(1000000ll); return OK; } -status_t WifiDisplaySource::PlaybackSession::onFinishPlay() { - return mSender->finishInit(); -} - -status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() { - mSender->scheduleSendSR(); - +status_t WifiDisplaySource::PlaybackSession::onMediaSenderInitialized() { for (size_t i = 0; i < mTracks.size(); ++i) { CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start()); } @@ -464,44 +456,18 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( CHECK(msg->findSize("trackIndex", &trackIndex)); if (what == Converter::kWhatAccessUnit) { - const sp &track = mTracks.valueFor(trackIndex); - - ssize_t packetizerTrackIndex = track->packetizerTrackIndex(); - - if (packetizerTrackIndex < 0) { - sp trackFormat = track->getFormat()->dup(); - if (mHDCP != NULL && !track->isAudio()) { - // HDCP2.0 _and_ HDCP 2.1 specs say to set the version - // inside the HDCP descriptor to 0x20!!! - trackFormat->setInt32("hdcp-version", 0x20); - } - packetizerTrackIndex = mPacketizer->addTrack(trackFormat); - - CHECK_GE(packetizerTrackIndex, 0); - - track->setPacketizerTrackIndex(packetizerTrackIndex); - - if (allTracksHavePacketizerIndex()) { - status_t err = packetizeQueuedAccessUnits(); - - if (err != OK) { - notifySessionDead(); - break; - } - } - } - sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); - if (!allTracksHavePacketizerIndex()) { - track->queueAccessUnit(accessUnit); - break; - } + const sp &track = mTracks.valueFor(trackIndex); - track->queueOutputBuffer(accessUnit); + status_t err = mMediaSender->queueAccessUnit( + track->mediaSenderTrackIndex(), + accessUnit); - drainAccessUnits(); + if (err != OK) { + notifySessionDead(); + } break; } else if (what == Converter::kWhatEOS) { CHECK_EQ(what, Converter::kWhatEOS); @@ -533,37 +499,25 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } - case kWhatSenderNotify: + case kWhatMediaSenderNotify: { int32_t what; CHECK(msg->findInt32("what", &what)); - if (what == Sender::kWhatInitDone) { - onFinishPlay2(); - } else if (what == Sender::kWhatSessionDead) { - notifySessionDead(); - } else if (what == Sender::kWhatBinaryData) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatBinaryData); - - int32_t channel; - CHECK(msg->findInt32("channel", &channel)); - notify->setInt32("channel", channel); + if (what == MediaSender::kWhatInitDone) { + status_t err; + CHECK(msg->findInt32("err", &err)); - sp data; - CHECK(msg->findBuffer("data", &data)); - notify->setBuffer("data", data); - notify->post(); + if (err == OK) { + onMediaSenderInitialized(); + } else { + notifySessionDead(); + } + } else if (what == MediaSender::kWhatError) { + notifySessionDead(); } else { TRESPASS(); } - - break; - } - - case kWhatFinishPlay: - { - onFinishPlay(); break; } @@ -588,11 +542,8 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } - mSenderLooper->unregisterHandler(mSender->id()); - mSender.clear(); - mSenderLooper.clear(); - - mPacketizer.clear(); + looper()->unregisterHandler(mMediaSender->id()); + mMediaSender.clear(); sp notify = mNotify->dup(); notify->setInt32("what", kWhatSessionDestroyed); @@ -601,28 +552,6 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } - case kWhatPacketize: - { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - -#if 0 - if ((ssize_t)trackIndex == mVideoTrackIndex) { - int64_t nowUs = ALooper::GetNowUs(); - static int64_t prevNowUs = 0ll; - - ALOGI("sending AU, dNowUs=%lld us", nowUs - prevNowUs); - - prevNowUs = nowUs; - } -#endif - - break; - } - case kWhatPause: { if (mPaused) { @@ -664,8 +593,6 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( size_t videoResolutionIndex) { CHECK(enableAudio || enableVideo); - mPacketizer = new TSPacketizer; - if (enableVideo) { status_t err = addVideoSource( videoResolutionType, videoResolutionIndex); @@ -763,6 +690,17 @@ status_t WifiDisplaySource::PlaybackSession::addSource( mVideoTrackIndex = trackIndex; } + uint32_t flags = 0; + if (converter->needToManuallyPrependSPSPPS()) { + flags |= MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS; + } + + ssize_t mediaSenderTrackIndex = + mMediaSender->addTrack(converter->getOutputFormat(), flags); + CHECK_GE(mediaSenderTrackIndex, 0); + + track->setMediaSenderTrackIndex(mediaSenderTrackIndex); + return OK; } @@ -832,168 +770,6 @@ void WifiDisplaySource::PlaybackSession::requestIDRFrame() { } } -bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() { - if (mAllTracksHavePacketizerIndex) { - return true; - } - - for (size_t i = 0; i < mTracks.size(); ++i) { - if (mTracks.valueAt(i)->packetizerTrackIndex() < 0) { - return false; - } - } - - mAllTracksHavePacketizerIndex = true; - - return true; -} - -status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit( - size_t trackIndex, sp accessUnit, - sp *packets) { - const sp &track = mTracks.valueFor(trackIndex); - - uint32_t flags = 0; - - bool isHDCPEncrypted = false; - uint64_t inputCTR; - uint8_t HDCP_private_data[16]; - - bool manuallyPrependSPSPPS = - !track->isAudio() - && track->converter()->needToManuallyPrependSPSPPS() - && IsIDR(accessUnit); - - if (mHDCP != NULL && !track->isAudio()) { - isHDCPEncrypted = true; - - if (manuallyPrependSPSPPS) { - accessUnit = mPacketizer->prependCSD( - track->packetizerTrackIndex(), accessUnit); - } - - status_t err = mHDCP->encrypt( - accessUnit->data(), accessUnit->size(), - trackIndex /* streamCTR */, - &inputCTR, - accessUnit->data()); - - if (err != OK) { - ALOGE("Failed to HDCP-encrypt media data (err %d)", - err); - - return err; - } - - HDCP_private_data[0] = 0x00; - - HDCP_private_data[1] = - (((trackIndex >> 30) & 3) << 1) | 1; - - HDCP_private_data[2] = (trackIndex >> 22) & 0xff; - - HDCP_private_data[3] = - (((trackIndex >> 15) & 0x7f) << 1) | 1; - - HDCP_private_data[4] = (trackIndex >> 7) & 0xff; - - HDCP_private_data[5] = - ((trackIndex & 0x7f) << 1) | 1; - - HDCP_private_data[6] = 0x00; - - HDCP_private_data[7] = - (((inputCTR >> 60) & 0x0f) << 1) | 1; - - HDCP_private_data[8] = (inputCTR >> 52) & 0xff; - - HDCP_private_data[9] = - (((inputCTR >> 45) & 0x7f) << 1) | 1; - - HDCP_private_data[10] = (inputCTR >> 37) & 0xff; - - HDCP_private_data[11] = - (((inputCTR >> 30) & 0x7f) << 1) | 1; - - HDCP_private_data[12] = (inputCTR >> 22) & 0xff; - - HDCP_private_data[13] = - (((inputCTR >> 15) & 0x7f) << 1) | 1; - - HDCP_private_data[14] = (inputCTR >> 7) & 0xff; - - HDCP_private_data[15] = - ((inputCTR & 0x7f) << 1) | 1; - -#if 0 - ALOGI("HDCP_private_data:"); - hexdump(HDCP_private_data, sizeof(HDCP_private_data)); - - ABitReader br(HDCP_private_data, sizeof(HDCP_private_data)); - CHECK_EQ(br.getBits(13), 0); - CHECK_EQ(br.getBits(2), (trackIndex >> 30) & 3); - CHECK_EQ(br.getBits(1), 1u); - CHECK_EQ(br.getBits(15), (trackIndex >> 15) & 0x7fff); - CHECK_EQ(br.getBits(1), 1u); - CHECK_EQ(br.getBits(15), trackIndex & 0x7fff); - CHECK_EQ(br.getBits(1), 1u); - CHECK_EQ(br.getBits(11), 0); - CHECK_EQ(br.getBits(4), (inputCTR >> 60) & 0xf); - CHECK_EQ(br.getBits(1), 1u); - CHECK_EQ(br.getBits(15), (inputCTR >> 45) & 0x7fff); - CHECK_EQ(br.getBits(1), 1u); - CHECK_EQ(br.getBits(15), (inputCTR >> 30) & 0x7fff); - CHECK_EQ(br.getBits(1), 1u); - CHECK_EQ(br.getBits(15), (inputCTR >> 15) & 0x7fff); - CHECK_EQ(br.getBits(1), 1u); - CHECK_EQ(br.getBits(15), inputCTR & 0x7fff); - CHECK_EQ(br.getBits(1), 1u); -#endif - - flags |= TSPacketizer::IS_ENCRYPTED; - } else if (manuallyPrependSPSPPS) { - flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES; - } - - int64_t timeUs = ALooper::GetNowUs(); - if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) { - flags |= TSPacketizer::EMIT_PCR; - flags |= TSPacketizer::EMIT_PAT_AND_PMT; - - mPrevTimeUs = timeUs; - } - - mPacketizer->packetize( - track->packetizerTrackIndex(), accessUnit, packets, flags, - !isHDCPEncrypted ? NULL : HDCP_private_data, - !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data), - track->isAudio() ? 2 : 0 /* numStuffingBytes */); - - return OK; -} - -status_t WifiDisplaySource::PlaybackSession::packetizeQueuedAccessUnits() { - for (;;) { - bool gotMoreData = false; - for (size_t i = 0; i < mTracks.size(); ++i) { - size_t trackIndex = mTracks.keyAt(i); - const sp &track = mTracks.valueAt(i); - - sp accessUnit = track->dequeueAccessUnit(); - if (accessUnit != NULL) { - track->queueOutputBuffer(accessUnit); - gotMoreData = true; - } - } - - if (!gotMoreData) { - break; - } - } - - return OK; -} - void WifiDisplaySource::PlaybackSession::notifySessionDead() { // Inform WifiDisplaySource of our premature death (wish). sp notify = mNotify->dup(); @@ -1003,78 +779,5 @@ void WifiDisplaySource::PlaybackSession::notifySessionDead() { mWeAreDead = true; } -void WifiDisplaySource::PlaybackSession::drainAccessUnits() { - ALOGV("audio/video has %d/%d buffers ready.", - mTracks.valueFor(1)->countQueuedOutputBuffers(), - mTracks.valueFor(0)->countQueuedOutputBuffers()); - - while (drainAccessUnit()) { - } -} - -bool WifiDisplaySource::PlaybackSession::drainAccessUnit() { - ssize_t minTrackIndex = -1; - int64_t minTimeUs = -1ll; - - for (size_t i = 0; i < mTracks.size(); ++i) { - const sp &track = mTracks.valueAt(i); - - int64_t timeUs; - if (track->hasOutputBuffer(&timeUs)) { - if (minTrackIndex < 0 || timeUs < minTimeUs) { - minTrackIndex = mTracks.keyAt(i); - minTimeUs = timeUs; - } - } -#if SUSPEND_VIDEO_IF_IDLE - else if (!track->isSuspended()) { - // We still consider this track "live", so it should keep - // delivering output data whose time stamps we'll have to - // consider for proper interleaving. - return false; - } -#else - else { - // We need access units available on all tracks to be able to - // dequeue the earliest one. - return false; - } -#endif - } - - if (minTrackIndex < 0) { - return false; - } - - const sp &track = mTracks.valueFor(minTrackIndex); - sp accessUnit = track->dequeueOutputBuffer(); - - sp packets; - status_t err = packetizeAccessUnit(minTrackIndex, accessUnit, &packets); - - if (err != OK) { - notifySessionDead(); - return false; - } - - if ((ssize_t)minTrackIndex == mVideoTrackIndex) { - packets->meta()->setInt32("isVideo", 1); - } - mSender->queuePackets(minTimeUs, packets); - -#if 0 - if (minTrackIndex == mVideoTrackIndex) { - int64_t nowUs = ALooper::GetNowUs(); - - // Latency from "data acquired" to "ready to send if we wanted to". - ALOGI("[%s] latencyUs = %lld ms", - minTrackIndex == mVideoTrackIndex ? "video" : "audio", - (nowUs - minTimeUs) / 1000ll); - } -#endif - - return true; -} - } // namespace android diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index 7365c78..cd6da85 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -18,7 +18,7 @@ #define PLAYBACK_SESSION_H_ -#include "Sender.h" +#include "MediaSender.h" #include "VideoFormats.h" #include "WifiDisplaySource.h" @@ -30,7 +30,7 @@ struct IHDCP; struct IGraphicBufferProducer; struct MediaPuller; struct MediaSource; -struct TSPacketizer; +struct MediaSender; // Encapsulates the state of an RTP/RTCP session in the context of wifi // display. @@ -43,7 +43,7 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { status_t init( const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - Sender::TransportMode transportMode, + RTPSender::TransportMode transportMode, bool enableAudio, bool usePCMAudio, bool enableVideo, @@ -83,26 +83,25 @@ private: kWhatMediaPullerNotify, kWhatConverterNotify, kWhatTrackNotify, - kWhatSenderNotify, kWhatUpdateSurface, - kWhatFinishPlay, - kWhatPacketize, kWhatPause, kWhatResume, + kWhatMediaSenderNotify, }; sp mNetSession; - sp mSender; - sp mSenderLooper; sp mNotify; in_addr mInterfaceAddr; sp mHDCP; + + sp mMediaSender; + int32_t mLocalRTPPort; + bool mWeAreDead; bool mPaused; int64_t mLastLifesignUs; - sp mPacketizer; sp mBufferQueue; KeyedVector > mTracks; @@ -110,8 +109,6 @@ private: int64_t mPrevTimeUs; - bool mAllTracksHavePacketizerIndex; - status_t setupPacketizer( bool enableAudio, bool usePCMAudio, @@ -132,27 +129,10 @@ private: status_t addAudioSource(bool usePCMAudio); - ssize_t appendTSData( - const void *data, size_t size, bool timeDiscontinuity, bool flush); - - status_t onFinishPlay(); - status_t onFinishPlay2(); - - bool allTracksHavePacketizerIndex(); - - status_t packetizeAccessUnit( - size_t trackIndex, sp accessUnit, - sp *packets); - - status_t packetizeQueuedAccessUnits(); + status_t onMediaSenderInitialized(); void notifySessionDead(); - void drainAccessUnits(); - - // Returns true iff an access unit was successfully drained. - bool drainAccessUnit(); - DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession); }; diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h index a13973c..146af32 100644 --- a/media/libstagefright/wifi-display/source/RepeaterSource.h +++ b/media/libstagefright/wifi-display/source/RepeaterSource.h @@ -6,7 +6,7 @@ #include #include -#define SUSPEND_VIDEO_IF_IDLE 1 +#define SUSPEND_VIDEO_IF_IDLE 0 namespace android { diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp deleted file mode 100644 index 8b7d93f..0000000 --- a/media/libstagefright/wifi-display/source/Sender.cpp +++ /dev/null @@ -1,878 +0,0 @@ -/* - * Copyright 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 "Sender" -#include - -#include "Sender.h" - -#include "ANetworkSession.h" -#include "TimeSeries.h" - -#include -#include -#include -#include -#include -#include - -namespace android { - -static size_t kMaxRTPPacketSize = 1500; -static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188; - -Sender::Sender( - const sp &netSession, - const sp ¬ify) - : mNetSession(netSession), - mNotify(notify), - mTransportMode(TRANSPORT_UDP), - mRTPChannel(0), - mRTCPChannel(0), - mRTPPort(0), - mRTPSessionID(0), - mRTCPSessionID(0), -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - mRTPRetransmissionSessionID(0), - mRTCPRetransmissionSessionID(0), -#endif - mClientRTPPort(0), - mClientRTCPPort(0), - mRTPConnected(false), - mRTCPConnected(false), - mFirstOutputBufferReadyTimeUs(-1ll), - mFirstOutputBufferSentTimeUs(-1ll), - mRTPSeqNo(0), -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - mRTPRetransmissionSeqNo(0), -#endif - mLastNTPTime(0), - mLastRTPTime(0), - mNumRTPSent(0), - mNumRTPOctetsSent(0), - mNumSRsSent(0), - mSendSRPending(false) -#if ENABLE_RETRANSMISSION - ,mHistoryLength(0) -#endif -#if TRACK_BANDWIDTH - ,mFirstPacketTimeUs(-1ll) - ,mTotalBytesSent(0ll) -#endif -#if LOG_TRANSPORT_STREAM - ,mLogFile(NULL) -#endif -{ -#if LOG_TRANSPORT_STREAM - mLogFile = fopen("/system/etc/log.ts", "wb"); -#endif -} - -Sender::~Sender() { -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - if (mRTCPRetransmissionSessionID != 0) { - mNetSession->destroySession(mRTCPRetransmissionSessionID); - } - - if (mRTPRetransmissionSessionID != 0) { - mNetSession->destroySession(mRTPRetransmissionSessionID); - } -#endif - - if (mRTCPSessionID != 0) { - mNetSession->destroySession(mRTCPSessionID); - } - - if (mRTPSessionID != 0) { - mNetSession->destroySession(mRTPSessionID); - } - -#if LOG_TRANSPORT_STREAM - if (mLogFile != NULL) { - fclose(mLogFile); - mLogFile = NULL; - } -#endif -} - -status_t Sender::init( - const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - TransportMode transportMode) { - mClientIP = clientIP; - mTransportMode = transportMode; - - if (transportMode == TRANSPORT_TCP_INTERLEAVED) { - mRTPChannel = clientRtp; - mRTCPChannel = clientRtcp; - mRTPPort = 0; - mRTPSessionID = 0; - mRTCPSessionID = 0; - return OK; - } - - mRTPChannel = 0; - mRTCPChannel = 0; - - if (mTransportMode == TRANSPORT_TCP) { - // XXX This is wrong, we need to allocate sockets here, we only - // need to do this because the dongles are not establishing their - // end until after PLAY instead of before SETUP. - mRTPPort = 20000; - mRTPSessionID = 0; - mRTCPSessionID = 0; - mClientRTPPort = clientRtp; - mClientRTCPPort = clientRtcp; - return OK; - } - - int serverRtp; - - sp rtpNotify = new AMessage(kWhatRTPNotify, id()); - sp rtcpNotify = new AMessage(kWhatRTCPNotify, id()); - -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - sp rtpRetransmissionNotify = - new AMessage(kWhatRTPRetransmissionNotify, id()); - - sp rtcpRetransmissionNotify = - new AMessage(kWhatRTCPRetransmissionNotify, id()); -#endif - - status_t err; - for (serverRtp = 15550;; serverRtp += 2) { - int32_t rtpSession; - if (mTransportMode == TRANSPORT_UDP) { - err = mNetSession->createUDPSession( - serverRtp, clientIP, clientRtp, - rtpNotify, &rtpSession); - } else { - err = mNetSession->createTCPDatagramSession( - serverRtp, clientIP, clientRtp, - rtpNotify, &rtpSession); - } - - if (err != OK) { - ALOGI("failed to create RTP socket on port %d", serverRtp); - continue; - } - - int32_t rtcpSession = 0; - - if (clientRtcp >= 0) { - if (mTransportMode == TRANSPORT_UDP) { - err = mNetSession->createUDPSession( - serverRtp + 1, clientIP, clientRtcp, - rtcpNotify, &rtcpSession); - } else { - err = mNetSession->createTCPDatagramSession( - serverRtp + 1, clientIP, clientRtcp, - rtcpNotify, &rtcpSession); - } - - if (err != OK) { - ALOGI("failed to create RTCP socket on port %d", serverRtp + 1); - - mNetSession->destroySession(rtpSession); - continue; - } - } - -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - if (mTransportMode == TRANSPORT_UDP) { - int32_t rtpRetransmissionSession; - - err = mNetSession->createUDPSession( - serverRtp + kRetransmissionPortOffset, - clientIP, - clientRtp + kRetransmissionPortOffset, - rtpRetransmissionNotify, - &rtpRetransmissionSession); - - if (err != OK) { - mNetSession->destroySession(rtcpSession); - mNetSession->destroySession(rtpSession); - continue; - } - - CHECK_GE(clientRtcp, 0); - - int32_t rtcpRetransmissionSession; - err = mNetSession->createUDPSession( - serverRtp + 1 + kRetransmissionPortOffset, - clientIP, - clientRtp + 1 + kRetransmissionPortOffset, - rtcpRetransmissionNotify, - &rtcpRetransmissionSession); - - if (err != OK) { - mNetSession->destroySession(rtpRetransmissionSession); - mNetSession->destroySession(rtcpSession); - mNetSession->destroySession(rtpSession); - continue; - } - - mRTPRetransmissionSessionID = rtpRetransmissionSession; - mRTCPRetransmissionSessionID = rtcpRetransmissionSession; - - ALOGI("rtpRetransmissionSessionID = %d, " - "rtcpRetransmissionSessionID = %d", - rtpRetransmissionSession, rtcpRetransmissionSession); - } -#endif - - mRTPPort = serverRtp; - mRTPSessionID = rtpSession; - mRTCPSessionID = rtcpSession; - - ALOGI("rtpSessionID = %d, rtcpSessionID = %d", rtpSession, rtcpSession); - break; - } - - if (mRTPPort == 0) { - return UNKNOWN_ERROR; - } - - return OK; -} - -status_t Sender::finishInit() { - if (mTransportMode != TRANSPORT_TCP) { - notifyInitDone(); - return OK; - } - - sp rtpNotify = new AMessage(kWhatRTPNotify, id()); - - status_t err = mNetSession->createTCPDatagramSession( - mRTPPort, mClientIP.c_str(), mClientRTPPort, - rtpNotify, &mRTPSessionID); - - if (err != OK) { - return err; - } - - if (mClientRTCPPort >= 0) { - sp rtcpNotify = new AMessage(kWhatRTCPNotify, id()); - - err = mNetSession->createTCPDatagramSession( - mRTPPort + 1, mClientIP.c_str(), mClientRTCPPort, - rtcpNotify, &mRTCPSessionID); - - if (err != OK) { - return err; - } - } - - return OK; -} - -int32_t Sender::getRTPPort() const { - return mRTPPort; -} - -void Sender::queuePackets( - int64_t timeUs, const sp &tsPackets) { - const size_t numTSPackets = tsPackets->size() / 188; - - const size_t numRTPPackets = - (numTSPackets + kMaxNumTSPacketsPerRTPPacket - 1) - / kMaxNumTSPacketsPerRTPPacket; - - sp udpPackets = new ABuffer( - numRTPPackets * (12 + kMaxNumTSPacketsPerRTPPacket * 188)); - - udpPackets->meta()->setInt64("timeUs", timeUs); - - size_t dstOffset = 0; - for (size_t i = 0; i < numTSPackets; ++i) { - if ((i % kMaxNumTSPacketsPerRTPPacket) == 0) { - static const bool kMarkerBit = false; - - uint8_t *rtp = udpPackets->data() + dstOffset; - rtp[0] = 0x80; - rtp[1] = 33 | (kMarkerBit ? (1 << 7) : 0); // M-bit - rtp[2] = (mRTPSeqNo >> 8) & 0xff; - rtp[3] = mRTPSeqNo & 0xff; - rtp[4] = 0x00; // rtp time to be filled in later. - rtp[5] = 0x00; - rtp[6] = 0x00; - rtp[7] = 0x00; - rtp[8] = kSourceID >> 24; - rtp[9] = (kSourceID >> 16) & 0xff; - rtp[10] = (kSourceID >> 8) & 0xff; - rtp[11] = kSourceID & 0xff; - - ++mRTPSeqNo; - - dstOffset += 12; - } - - memcpy(udpPackets->data() + dstOffset, - tsPackets->data() + 188 * i, - 188); - - dstOffset += 188; - } - - udpPackets->setRange(0, dstOffset); - - sp msg = new AMessage(kWhatDrainQueue, id()); - msg->setBuffer("udpPackets", udpPackets); - msg->post(); - -#if LOG_TRANSPORT_STREAM - if (mLogFile != NULL) { - fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile); - } -#endif -} - -void Sender::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatRTPNotify: - case kWhatRTCPNotify: -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - case kWhatRTPRetransmissionNotify: - case kWhatRTCPRetransmissionNotify: -#endif - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - int32_t errorOccuredDuringSend; - CHECK(msg->findInt32("send", &errorOccuredDuringSend)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - if ((msg->what() == kWhatRTPNotify -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - || msg->what() == kWhatRTPRetransmissionNotify -#endif - ) && !errorOccuredDuringSend) { - // This is ok, we don't expect to receive anything on - // the RTP socket. - break; - } - - ALOGE("An error occurred during %s in session %d " - "(%d, '%s' (%s)).", - errorOccuredDuringSend ? "send" : "receive", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - if (sessionID == mRTPSessionID) { - mRTPSessionID = 0; - } else if (sessionID == mRTCPSessionID) { - mRTCPSessionID = 0; - } -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - else if (sessionID == mRTPRetransmissionSessionID) { - mRTPRetransmissionSessionID = 0; - } else if (sessionID == mRTCPRetransmissionSessionID) { - mRTCPRetransmissionSessionID = 0; - } -#endif - - notifySessionDead(); - break; - } - - case ANetworkSession::kWhatDatagram: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp data; - CHECK(msg->findBuffer("data", &data)); - - status_t err; - if (msg->what() == kWhatRTCPNotify -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - || msg->what() == kWhatRTCPRetransmissionNotify -#endif - ) - { - err = parseRTCP(data); - } - break; - } - - case ANetworkSession::kWhatConnected: - { - CHECK_EQ(mTransportMode, TRANSPORT_TCP); - - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - if (sessionID == mRTPSessionID) { - CHECK(!mRTPConnected); - mRTPConnected = true; - ALOGI("RTP Session now connected."); - } else if (sessionID == mRTCPSessionID) { - CHECK(!mRTCPConnected); - mRTCPConnected = true; - ALOGI("RTCP Session now connected."); - } else { - TRESPASS(); - } - - if (mRTPConnected - && (mClientRTCPPort < 0 || mRTCPConnected)) { - notifyInitDone(); - } - break; - } - - default: - TRESPASS(); - } - break; - } - - case kWhatDrainQueue: - { - sp udpPackets; - CHECK(msg->findBuffer("udpPackets", &udpPackets)); - - onDrainQueue(udpPackets); - break; - } - - case kWhatSendSR: - { - mSendSRPending = false; - - if (mRTCPSessionID == 0) { - break; - } - - onSendSR(); - - scheduleSendSR(); - break; - } - } -} - -void Sender::scheduleSendSR() { - if (mSendSRPending || mRTCPSessionID == 0) { - return; - } - - mSendSRPending = true; - (new AMessage(kWhatSendSR, id()))->post(kSendSRIntervalUs); -} - -void Sender::addSR(const sp &buffer) { - uint8_t *data = buffer->data() + buffer->size(); - - // TODO: Use macros/utility functions to clean up all the bitshifts below. - - data[0] = 0x80 | 0; - data[1] = 200; // SR - data[2] = 0; - data[3] = 6; - data[4] = kSourceID >> 24; - data[5] = (kSourceID >> 16) & 0xff; - data[6] = (kSourceID >> 8) & 0xff; - data[7] = kSourceID & 0xff; - - data[8] = mLastNTPTime >> (64 - 8); - data[9] = (mLastNTPTime >> (64 - 16)) & 0xff; - data[10] = (mLastNTPTime >> (64 - 24)) & 0xff; - data[11] = (mLastNTPTime >> 32) & 0xff; - data[12] = (mLastNTPTime >> 24) & 0xff; - data[13] = (mLastNTPTime >> 16) & 0xff; - data[14] = (mLastNTPTime >> 8) & 0xff; - data[15] = mLastNTPTime & 0xff; - - data[16] = (mLastRTPTime >> 24) & 0xff; - data[17] = (mLastRTPTime >> 16) & 0xff; - data[18] = (mLastRTPTime >> 8) & 0xff; - data[19] = mLastRTPTime & 0xff; - - data[20] = mNumRTPSent >> 24; - data[21] = (mNumRTPSent >> 16) & 0xff; - data[22] = (mNumRTPSent >> 8) & 0xff; - data[23] = mNumRTPSent & 0xff; - - data[24] = mNumRTPOctetsSent >> 24; - data[25] = (mNumRTPOctetsSent >> 16) & 0xff; - data[26] = (mNumRTPOctetsSent >> 8) & 0xff; - data[27] = mNumRTPOctetsSent & 0xff; - - buffer->setRange(buffer->offset(), buffer->size() + 28); -} - -void Sender::addSDES(const sp &buffer) { - uint8_t *data = buffer->data() + buffer->size(); - data[0] = 0x80 | 1; - data[1] = 202; // SDES - data[4] = kSourceID >> 24; - data[5] = (kSourceID >> 16) & 0xff; - data[6] = (kSourceID >> 8) & 0xff; - data[7] = kSourceID & 0xff; - - size_t offset = 8; - - data[offset++] = 1; // CNAME - - static const char *kCNAME = "someone@somewhere"; - data[offset++] = strlen(kCNAME); - - memcpy(&data[offset], kCNAME, strlen(kCNAME)); - offset += strlen(kCNAME); - - data[offset++] = 7; // NOTE - - static const char *kNOTE = "Hell's frozen over."; - data[offset++] = strlen(kNOTE); - - memcpy(&data[offset], kNOTE, strlen(kNOTE)); - offset += strlen(kNOTE); - - data[offset++] = 0; - - if ((offset % 4) > 0) { - size_t count = 4 - (offset % 4); - switch (count) { - case 3: - data[offset++] = 0; - case 2: - data[offset++] = 0; - case 1: - data[offset++] = 0; - } - } - - size_t numWords = (offset / 4) - 1; - data[2] = numWords >> 8; - data[3] = numWords & 0xff; - - buffer->setRange(buffer->offset(), buffer->size() + offset); -} - -// static -uint64_t Sender::GetNowNTP() { - uint64_t nowUs = ALooper::GetNowUs(); - - nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; - - uint64_t hi = nowUs / 1000000ll; - uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll; - - return (hi << 32) | lo; -} - -void Sender::onSendSR() { - sp buffer = new ABuffer(1500); - buffer->setRange(0, 0); - - addSR(buffer); - addSDES(buffer); - - if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatBinaryData); - notify->setInt32("channel", mRTCPChannel); - notify->setBuffer("data", buffer); - notify->post(); - } else { - sendPacket(mRTCPSessionID, buffer->data(), buffer->size()); - } - - ++mNumSRsSent; -} - -#if ENABLE_RETRANSMISSION -status_t Sender::parseTSFB( - const uint8_t *data, size_t size) { - if ((data[0] & 0x1f) != 1) { - return ERROR_UNSUPPORTED; // We only support NACK for now. - } - - uint32_t srcId = U32_AT(&data[8]); - if (srcId != kSourceID) { - return ERROR_MALFORMED; - } - - for (size_t i = 12; i < size; i += 4) { - uint16_t seqNo = U16_AT(&data[i]); - uint16_t blp = U16_AT(&data[i + 2]); - - List >::iterator it = mHistory.begin(); - bool foundSeqNo = false; - while (it != mHistory.end()) { - const sp &buffer = *it; - - uint16_t bufferSeqNo = buffer->int32Data() & 0xffff; - - bool retransmit = false; - if (bufferSeqNo == seqNo) { - retransmit = true; - } else if (blp != 0) { - for (size_t i = 0; i < 16; ++i) { - if ((blp & (1 << i)) - && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) { - blp &= ~(1 << i); - retransmit = true; - } - } - } - - if (retransmit) { - ALOGI("retransmitting seqNo %d", bufferSeqNo); - -#if RETRANSMISSION_ACCORDING_TO_RFC_XXXX - sp retransRTP = new ABuffer(2 + buffer->size()); - uint8_t *rtp = retransRTP->data(); - memcpy(rtp, buffer->data(), 12); - rtp[2] = (mRTPRetransmissionSeqNo >> 8) & 0xff; - rtp[3] = mRTPRetransmissionSeqNo & 0xff; - rtp[12] = (bufferSeqNo >> 8) & 0xff; - rtp[13] = bufferSeqNo & 0xff; - memcpy(&rtp[14], buffer->data() + 12, buffer->size() - 12); - - ++mRTPRetransmissionSeqNo; - - sendPacket( - mRTPRetransmissionSessionID, - retransRTP->data(), retransRTP->size()); -#else - sendPacket( - mRTPSessionID, buffer->data(), buffer->size()); -#endif - - if (bufferSeqNo == seqNo) { - foundSeqNo = true; - } - - if (foundSeqNo && blp == 0) { - break; - } - } - - ++it; - } - - if (!foundSeqNo || blp != 0) { - ALOGI("Some sequence numbers were no longer available for " - "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)", - seqNo, foundSeqNo, blp); - - if (!mHistory.empty()) { - int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff; - int32_t latest = (*--mHistory.end())->int32Data() & 0xffff; - - ALOGI("have seq numbers from %d - %d", earliest, latest); - } - } - } - - return OK; -} -#endif - -status_t Sender::parseRTCP( - const sp &buffer) { - const uint8_t *data = buffer->data(); - size_t size = buffer->size(); - - while (size > 0) { - if (size < 8) { - // Too short to be a valid RTCP header - return ERROR_MALFORMED; - } - - if ((data[0] >> 6) != 2) { - // Unsupported version. - return ERROR_UNSUPPORTED; - } - - if (data[0] & 0x20) { - // Padding present. - - size_t paddingLength = data[size - 1]; - - if (paddingLength + 12 > size) { - // If we removed this much padding we'd end up with something - // that's too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - size -= paddingLength; - } - - size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4; - - if (size < headerLength) { - // Only received a partial packet? - return ERROR_MALFORMED; - } - - switch (data[1]) { - case 200: - case 201: // RR - case 202: // SDES - case 203: - case 204: // APP - break; - -#if ENABLE_RETRANSMISSION - case 205: // TSFB (transport layer specific feedback) - parseTSFB(data, headerLength); - break; -#endif - - case 206: // PSFB (payload specific feedback) - hexdump(data, headerLength); - break; - - default: - { - ALOGW("Unknown RTCP packet type %u of size %d", - (unsigned)data[1], headerLength); - break; - } - } - - data += headerLength; - size -= headerLength; - } - - return OK; -} - -status_t Sender::sendPacket( - int32_t sessionID, const void *data, size_t size) { - return mNetSession->sendRequest(sessionID, data, size); -} - -void Sender::notifyInitDone() { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatInitDone); - notify->post(); -} - -void Sender::notifySessionDead() { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatSessionDead); - notify->post(); -} - -void Sender::onDrainQueue(const sp &udpPackets) { - static const size_t kFullRTPPacketSize = - 12 + 188 * kMaxNumTSPacketsPerRTPPacket; - - size_t srcOffset = 0; - while (srcOffset < udpPackets->size()) { - uint8_t *rtp = udpPackets->data() + srcOffset; - - size_t rtpPacketSize = udpPackets->size() - srcOffset; - if (rtpPacketSize > kFullRTPPacketSize) { - rtpPacketSize = kFullRTPPacketSize; - } - - int64_t nowUs = ALooper::GetNowUs(); - mLastNTPTime = GetNowNTP(); - - // 90kHz time scale - uint32_t rtpTime = (nowUs * 9ll) / 100ll; - - rtp[4] = rtpTime >> 24; - rtp[5] = (rtpTime >> 16) & 0xff; - rtp[6] = (rtpTime >> 8) & 0xff; - rtp[7] = rtpTime & 0xff; - - ++mNumRTPSent; - mNumRTPOctetsSent += rtpPacketSize - 12; - - mLastRTPTime = rtpTime; - - if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatBinaryData); - - sp data = new ABuffer(rtpPacketSize); - memcpy(data->data(), rtp, rtpPacketSize); - - notify->setInt32("channel", mRTPChannel); - notify->setBuffer("data", data); - notify->post(); - } else { - sendPacket(mRTPSessionID, rtp, rtpPacketSize); - -#if TRACK_BANDWIDTH - mTotalBytesSent += rtpPacketSize->size(); - int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs; - - if (delayUs > 0ll) { - ALOGI("approx. net bandwidth used: %.2f Mbit/sec", - mTotalBytesSent * 8.0 / delayUs); - } -#endif - } - -#if ENABLE_RETRANSMISSION - addToHistory(rtp, rtpPacketSize); -#endif - - srcOffset += rtpPacketSize; - } - -#if 0 - int64_t timeUs; - CHECK(udpPackets->meta()->findInt64("timeUs", &timeUs)); - - ALOGI("dTimeUs = %lld us", ALooper::GetNowUs() - timeUs); -#endif -} - -#if ENABLE_RETRANSMISSION -void Sender::addToHistory(const uint8_t *rtp, size_t rtpPacketSize) { - sp packet = new ABuffer(rtpPacketSize); - memcpy(packet->data(), rtp, rtpPacketSize); - - unsigned rtpSeqNo = U16_AT(&rtp[2]); - packet->setInt32Data(rtpSeqNo); - - mHistory.push_back(packet); - ++mHistoryLength; - - if (mHistoryLength > kMaxHistoryLength) { - mHistory.erase(mHistory.begin()); - --mHistoryLength; - } -} -#endif - -} // namespace android - diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h deleted file mode 100644 index 66951f7..0000000 --- a/media/libstagefright/wifi-display/source/Sender.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 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 SENDER_H_ - -#define SENDER_H_ - -#include - -namespace android { - -#define LOG_TRANSPORT_STREAM 0 -#define TRACK_BANDWIDTH 0 - -#define ENABLE_RETRANSMISSION 1 - -// If retransmission is enabled the following define determines what -// kind we support, if RETRANSMISSION_ACCORDING_TO_RFC_XXXX is 0 -// we'll send NACKs on the original RTCP channel and retransmit packets -// on the original RTP channel, otherwise a separate channel pair is used -// for this purpose. -#define RETRANSMISSION_ACCORDING_TO_RFC_XXXX 0 - -struct ABuffer; -struct ANetworkSession; - -struct Sender : public AHandler { - Sender(const sp &netSession, const sp ¬ify); - - enum { - kWhatInitDone, - kWhatSessionDead, - kWhatBinaryData, - }; - - enum TransportMode { - TRANSPORT_UDP, - TRANSPORT_TCP_INTERLEAVED, - TRANSPORT_TCP, - }; - status_t init( - const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - TransportMode transportMode); - - status_t finishInit(); - - int32_t getRTPPort() const; - - void queuePackets(int64_t timeUs, const sp &tsPackets); - void scheduleSendSR(); - -protected: - virtual ~Sender(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatDrainQueue, - kWhatSendSR, - kWhatRTPNotify, - kWhatRTCPNotify, -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - kWhatRTPRetransmissionNotify, - kWhatRTCPRetransmissionNotify, -#endif - }; - - static const int64_t kSendSRIntervalUs = 10000000ll; - - static const uint32_t kSourceID = 0xdeadbeef; - static const size_t kMaxHistoryLength = 128; - -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - static const size_t kRetransmissionPortOffset = 120; -#endif - - sp mNetSession; - sp mNotify; - - TransportMode mTransportMode; - AString mClientIP; - - // in TCP mode - int32_t mRTPChannel; - int32_t mRTCPChannel; - - // in UDP mode - int32_t mRTPPort; - int32_t mRTPSessionID; - int32_t mRTCPSessionID; - -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - int32_t mRTPRetransmissionSessionID; - int32_t mRTCPRetransmissionSessionID; -#endif - - int32_t mClientRTPPort; - int32_t mClientRTCPPort; - bool mRTPConnected; - bool mRTCPConnected; - - int64_t mFirstOutputBufferReadyTimeUs; - int64_t mFirstOutputBufferSentTimeUs; - - uint32_t mRTPSeqNo; -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - uint32_t mRTPRetransmissionSeqNo; -#endif - - uint64_t mLastNTPTime; - uint32_t mLastRTPTime; - uint32_t mNumRTPSent; - uint32_t mNumRTPOctetsSent; - uint32_t mNumSRsSent; - - bool mSendSRPending; - -#if ENABLE_RETRANSMISSION - List > mHistory; - size_t mHistoryLength; -#endif - -#if TRACK_BANDWIDTH - int64_t mFirstPacketTimeUs; - uint64_t mTotalBytesSent; -#endif - -#if LOG_TRANSPORT_STREAM - FILE *mLogFile; -#endif - - void onSendSR(); - void addSR(const sp &buffer); - void addSDES(const sp &buffer); - static uint64_t GetNowNTP(); - -#if ENABLE_RETRANSMISSION - status_t parseTSFB(const uint8_t *data, size_t size); - void addToHistory(const uint8_t *rtp, size_t rtpPacketSize); -#endif - - status_t parseRTCP(const sp &buffer); - - status_t sendPacket(int32_t sessionID, const void *data, size_t size); - - void notifyInitDone(); - void notifySessionDead(); - - void onDrainQueue(const sp &udpPackets); - - DISALLOW_EVIL_CONSTRUCTORS(Sender); -}; - -} // namespace android - -#endif // SENDER_H_ diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index ef57a4d..8420529 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -58,6 +58,7 @@ struct TSPacketizer::Track : public RefBase { sp descriptorAt(size_t index) const; void finalize(); + void extractCSDIfNecessary(); protected: virtual ~Track(); @@ -77,6 +78,7 @@ private: bool mAudioLacksATDSHeaders; bool mFinalized; + bool mExtractedCSD; DISALLOW_EVIL_CONSTRUCTORS(Track); }; @@ -90,14 +92,21 @@ TSPacketizer::Track::Track( mStreamID(streamID), mContinuityCounter(0), mAudioLacksATDSHeaders(false), - mFinalized(false) { + mFinalized(false), + mExtractedCSD(false) { CHECK(format->findString("mime", &mMIME)); +} + +void TSPacketizer::Track::extractCSDIfNecessary() { + if (mExtractedCSD) { + return; + } if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC) || !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { for (size_t i = 0;; ++i) { sp csd; - if (!format->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) { + if (!mFormat->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) { break; } @@ -111,6 +120,8 @@ TSPacketizer::Track::Track( } } } + + mExtractedCSD = true; } TSPacketizer::Track::~Track() { @@ -407,6 +418,17 @@ ssize_t TSPacketizer::addTrack(const sp &format) { return mTracks.add(track); } +status_t TSPacketizer::extractCSDIfNecessary(size_t trackIndex) { + if (trackIndex >= mTracks.size()) { + return -ERANGE; + } + + const sp &track = mTracks.itemAt(trackIndex); + track->extractCSDIfNecessary(); + + return OK; +} + status_t TSPacketizer::packetize( size_t trackIndex, const sp &_accessUnit, diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h index a37917d..5d1d70e 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.h +++ b/media/libstagefright/wifi-display/source/TSPacketizer.h @@ -50,6 +50,8 @@ struct TSPacketizer : public RefBase { const uint8_t *PES_private_data, size_t PES_private_data_len, size_t numStuffingBytes = 0); + status_t extractCSDIfNecessary(size_t trackIndex); + // XXX to be removed once encoder config option takes care of this for // encrypted mode. sp prependCSD( diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 825ebc6..07eb237 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -22,7 +22,7 @@ #include "PlaybackSession.h" #include "Parameters.h" #include "ParsedMessage.h" -#include "Sender.h" +#include "rtp/RTPSender.h" #include #include @@ -1140,7 +1140,7 @@ status_t WifiDisplaySource::onSetupRequest( return ERROR_MALFORMED; } - Sender::TransportMode transportMode = Sender::TRANSPORT_UDP; + RTPSender::TransportMode transportMode = RTPSender::TRANSPORT_UDP; int clientRtp, clientRtcp; if (transport.startsWith("RTP/AVP/TCP;")) { @@ -1149,7 +1149,7 @@ status_t WifiDisplaySource::onSetupRequest( transport.c_str(), "interleaved", &interleaved) && sscanf(interleaved.c_str(), "%d-%d", &clientRtp, &clientRtcp) == 2) { - transportMode = Sender::TRANSPORT_TCP_INTERLEAVED; + transportMode = RTPSender::TRANSPORT_TCP_INTERLEAVED; } else { bool badRequest = false; @@ -1171,7 +1171,7 @@ status_t WifiDisplaySource::onSetupRequest( return ERROR_MALFORMED; } - transportMode = Sender::TRANSPORT_TCP; + transportMode = RTPSender::TRANSPORT_TCP; } } else if (transport.startsWith("RTP/AVP;unicast;") || transport.startsWith("RTP/AVP/UDP;unicast;")) { @@ -1263,7 +1263,7 @@ status_t WifiDisplaySource::onSetupRequest( AString response = "RTSP/1.0 200 OK\r\n"; AppendCommonResponse(&response, cseq, playbackSessionID); - if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) { + if (transportMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) { response.append( StringPrintf( "Transport: RTP/AVP/TCP;interleaved=%d-%d;", @@ -1272,7 +1272,7 @@ status_t WifiDisplaySource::onSetupRequest( int32_t serverRtp = playbackSession->getRTPPort(); AString transportString = "UDP"; - if (transportMode == Sender::TRANSPORT_TCP) { + if (transportMode == RTPSender::TRANSPORT_TCP) { transportString = "TCP"; } -- cgit v1.1 From 6507d14c6d10f93d390de62b9eed267f9b544985 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 5 Mar 2013 14:31:02 -0800 Subject: Correct MediaCodec + Surface behavior Assorted tweaks: - Allow signalEndOfInputStream() before ACodec is in Executing state (added message to two more states). - Return an error if signalEndOfInputStream() is called a second time on the same stream. - Require AndroidOpaque color format in createInputSurface(). - Disallow dequeueInputBuffer() after an input surface has been created (boolean flag in MediaCodec tracks it). - Discard input surface when encoder is re-configure()ed (drop OMXNodeInstance's ref when we go back to Loaded). Bug 7991062 Change-Id: Iff30f3036e14eb5a2f6536910dcf11aba33031ee --- media/libstagefright/ACodec.cpp | 39 +++++++++++++++--------- media/libstagefright/MediaCodec.cpp | 20 ++++++++---- media/libstagefright/omx/GraphicBufferSource.cpp | 25 +++++++++------ media/libstagefright/omx/GraphicBufferSource.h | 9 +++--- media/libstagefright/omx/OMXNodeInstance.cpp | 17 ++++++++--- 5 files changed, 69 insertions(+), 41 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 59fc45e..1a2eeb1 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -241,9 +241,6 @@ struct ACodec::ExecutingState : public ACodec::BaseState { // to fill with data. void resume(); - // Send EOS on input stream. - void onSignalEndOfInputStream(); - // Returns true iff input and output buffers are in play. bool active() const { return mActive; } @@ -3413,6 +3410,12 @@ bool ACodec::LoadedToIdleState::onMessageReceived(const sp &msg) { return true; } + case kWhatSignalEndOfInputStream: + { + mCodec->onSignalEndOfInputStream(); + return true; + } + default: return BaseState::onMessageReceived(msg); } @@ -3458,6 +3461,12 @@ bool ACodec::IdleToExecutingState::onMessageReceived(const sp &msg) { return true; } + case kWhatSignalEndOfInputStream: + { + mCodec->onSignalEndOfInputStream(); + return true; + } + default: return BaseState::onMessageReceived(msg); } @@ -3538,17 +3547,6 @@ void ACodec::ExecutingState::resume() { mActive = true; } -void ACodec::ExecutingState::onSignalEndOfInputStream() { - sp notify = mCodec->mNotify->dup(); - notify->setInt32("what", ACodec::kWhatSignaledInputEOS); - - status_t err = mCodec->mOMX->signalEndOfInputStream(mCodec->mNode); - if (err != OK) { - notify->setInt32("err", err); - } - notify->post(); -} - void ACodec::ExecutingState::stateEntered() { ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str()); @@ -3640,7 +3638,7 @@ bool ACodec::ExecutingState::onMessageReceived(const sp &msg) { case ACodec::kWhatSignalEndOfInputStream: { - onSignalEndOfInputStream(); + mCodec->onSignalEndOfInputStream(); handled = true; break; } @@ -3678,6 +3676,17 @@ status_t ACodec::setParameters(const sp ¶ms) { return OK; } +void ACodec::onSignalEndOfInputStream() { + sp notify = mNotify->dup(); + notify->setInt32("what", ACodec::kWhatSignaledInputEOS); + + status_t err = mOMX->signalEndOfInputStream(mNode); + if (err != OK) { + notify->setInt32("err", err); + } + notify->post(); +} + bool ACodec::ExecutingState::onOMXEvent( OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { switch (event) { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 79ea04c..0d89c0f 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -69,7 +69,8 @@ MediaCodec::MediaCodec(const sp &looper) mDequeueInputTimeoutGeneration(0), mDequeueInputReplyID(0), mDequeueOutputTimeoutGeneration(0), - mDequeueOutputReplyID(0) { + mDequeueOutputReplyID(0), + mHaveInputSurface(false) { } MediaCodec::~MediaCodec() { @@ -160,8 +161,6 @@ status_t MediaCodec::createInputSurface( sp* bufferProducer) { sp msg = new AMessage(kWhatCreateInputSurface, id()); - // TODO(fadden): require MediaFormat colorFormat == AndroidOpaque - sp response; status_t err = PostAndAwaitResponse(msg, &response); if (err == NO_ERROR) { @@ -256,8 +255,6 @@ status_t MediaCodec::queueSecureInputBuffer( } status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { - // TODO(fadden): fail if an input Surface has been configured - sp msg = new AMessage(kWhatDequeueInputBuffer, id()); msg->setInt64("timeoutUs", timeoutUs); @@ -604,6 +601,9 @@ void MediaCodec::onMessageReceived(const sp &msg) { CHECK_EQ(mState, CONFIGURING); setState(CONFIGURED); + // reset input surface flag + mHaveInputSurface = false; + (new AMessage)->postReply(mReplyID); break; } @@ -618,6 +618,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { msg->findObject("input-surface", &obj); CHECK(obj != NULL); response->setObject("input-surface", obj); + mHaveInputSurface = true; } else { response->setInt32("err", err); } @@ -1029,10 +1030,17 @@ void MediaCodec::onMessageReceived(const sp &msg) { case kWhatDequeueInputBuffer: { - // TODO(fadden): make this fail if we're using an input Surface uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + if (mHaveInputSurface) { + ALOGE("dequeueInputBuffer can't be used with input surface"); + sp response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + response->postReply(replyID); + break; + } + if (handleDequeueInputBuffer(replyID, true /* new request */)) { break; } diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index f207954..211e1d1 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "GraphicBufferSource" +//#define LOG_NDEBUG 0 #include #include @@ -110,15 +111,12 @@ void GraphicBufferSource::omxExecuting() { } } -void GraphicBufferSource::omxIdling(){ +void GraphicBufferSource::omxLoaded(){ Mutex::Autolock autoLock(mMutex); - ALOGV("--> idling"); - if (!mExecuting) { - // Transition from "loading" to "idling". Nothing to do. - return; - } + ALOGV("--> loaded"); + CHECK(mExecuting); - ALOGV("Dropped down to idle, avail=%d eos=%d eosSent=%d", + ALOGV("Dropped down to loaded, avail=%d eos=%d eosSent=%d", mNumFramesAvailable, mEndOfStream, mEndOfStreamSent); // Codec is no longer executing. Discard all codec-related state. @@ -282,10 +280,15 @@ status_t GraphicBufferSource::fillCodecBuffer_l() { return OK; } -void GraphicBufferSource::signalEndOfInputStream() { +status_t GraphicBufferSource::signalEndOfInputStream() { Mutex::Autolock autoLock(mMutex); - ALOGV("signalEndOfInputStream: exec=%d avail=%d", - mExecuting, mNumFramesAvailable); + ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d", + mExecuting, mNumFramesAvailable, mEndOfStream); + + if (mEndOfStream) { + ALOGE("EOS was already signaled"); + return INVALID_OPERATION; + } // Set the end-of-stream flag. If no frames are pending from the // BufferQueue, and a codec buffer is available, and we're executing, @@ -300,6 +303,8 @@ void GraphicBufferSource::signalEndOfInputStream() { if (mExecuting && mNumFramesAvailable == 0) { submitEndOfInputStream_l(); } + + return OK; } status_t GraphicBufferSource::submitBuffer_l(sp& graphicBuffer, diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 6d49f96..6a34bc5 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -67,10 +67,9 @@ public: // sitting in the BufferQueue, this will send them to the codec. void omxExecuting(); - // This is called when OMX transitions to OMX_StateIdle. If we were - // previously executing, this means we're about to be shut down. (We - // also enter Idle on the way up.) - void omxIdling(); + // This is called when OMX transitions to OMX_StateLoaded, indicating that + // we are shutting down. + void omxLoaded(); // A "codec buffer", i.e. a buffer that can be used to pass data into // the encoder, has been allocated. (This call does not call back into @@ -84,7 +83,7 @@ public: // This is called after the last input frame has been submitted. We // need to submit an empty buffer with the EOS flag set. If we don't // have a codec buffer ready, we just set the mEndOfStream flag. - void signalEndOfInputStream(); + status_t signalEndOfInputStream(); protected: // BufferQueue::ConsumerListener interface, called when a new frame of diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 6c2c33b..f3d8d14 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -584,6 +584,11 @@ status_t OMXNodeInstance::createInputSurface( mHandle, OMX_IndexParamPortDefinition, &def); CHECK(oerr == OMX_ErrorNone); + if (def.format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque) { + ALOGE("createInputSurface requires AndroidOpaque color format"); + return INVALID_OPERATION; + } + GraphicBufferSource* bufferSource = new GraphicBufferSource( this, def.format.video.nFrameWidth, def.format.video.nFrameHeight); if ((err = bufferSource->initCheck()) != OK) { @@ -602,11 +607,10 @@ status_t OMXNodeInstance::signalEndOfInputStream() { // flag set). Seems easier than doing the equivalent from here. sp bufferSource(getGraphicBufferSource()); if (bufferSource == NULL) { - ALOGW("signalEndOfInputStream should only be used with Surface input"); + ALOGW("signalEndOfInputStream can only be used with Surface input"); return INVALID_OPERATION; }; - bufferSource->signalEndOfInputStream(); - return OK; + return bufferSource->signalEndOfInputStream(); } status_t OMXNodeInstance::allocateBuffer( @@ -801,8 +805,11 @@ void OMXNodeInstance::onEvent( arg1 == OMX_CommandStateSet) { if (arg2 == OMX_StateExecuting) { bufferSource->omxExecuting(); - } else if (arg2 == OMX_StateIdle) { - bufferSource->omxIdling(); + } else if (arg2 == OMX_StateLoaded) { + // Must be shutting down -- won't have a GraphicBufferSource + // on the way up. + bufferSource->omxLoaded(); + setGraphicBufferSource(NULL); } } } -- cgit v1.1 From bfd79f2a8e795f304062e22756c72d995af7a0e6 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 7 Mar 2013 10:33:20 -0800 Subject: The transport stream HDCP descriptor belongs in the program_info section instead of being included in the per-stream descriptors Change-Id: If5251c0c02456646e2fdbb5e62acf66c356cf13e --- media/libstagefright/wifi-display/MediaSender.cpp | 28 ++++++---- media/libstagefright/wifi-display/MediaSender.h | 2 + .../wifi-display/source/TSPacketizer.cpp | 61 +++++++++++++++------- .../wifi-display/source/TSPacketizer.h | 9 +++- 4 files changed, 70 insertions(+), 30 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 900aa82..105c642 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -41,10 +41,16 @@ MediaSender::MediaSender( mMode(MODE_UNDEFINED), mGeneration(0), mPrevTimeUs(-1ll), - mInitDoneCount(0) { + mInitDoneCount(0), + mLogFile(NULL) { + // mLogFile = fopen("/data/misc/log.ts", "wb"); } MediaSender::~MediaSender() { + if (mLogFile != NULL) { + fclose(mLogFile); + mLogFile = NULL; + } } status_t MediaSender::setHDCP(const sp &hdcp) { @@ -89,21 +95,19 @@ status_t MediaSender::initAsync( return INVALID_OPERATION; } - mTSPacketizer = new TSPacketizer; + uint32_t flags = 0; + if (mHDCP != NULL) { + // XXX Determine proper HDCP version. + flags |= TSPacketizer::EMIT_HDCP20_DESCRIPTOR; + } + mTSPacketizer = new TSPacketizer(flags); status_t err = OK; for (size_t i = 0; i < mTrackInfos.size(); ++i) { TrackInfo *info = &mTrackInfos.editItemAt(i); - sp trackFormat = info->mFormat; - if (mHDCP != NULL && !info->mIsAudio) { - // HDCP2.0 _and_ HDCP 2.1 specs say to set the version - // inside the HDCP descriptor to 0x20!!! - trackFormat->setInt32("hdcp-version", 0x20); - } - ssize_t packetizerTrackIndex = - mTSPacketizer->addTrack(trackFormat); + mTSPacketizer->addTrack(info->mFormat); if (packetizerTrackIndex < 0) { err = packetizerTrackIndex; @@ -244,6 +248,10 @@ status_t MediaSender::queueAccessUnit( minTrackIndex, accessUnit, &tsPackets); if (err == OK) { + if (mLogFile != NULL) { + fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile); + } + err = mTSSender->queueBuffer( tsPackets, 33 /* packetType */, diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h index 834780a..9a50f9a 100644 --- a/media/libstagefright/wifi-display/MediaSender.h +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -107,6 +107,8 @@ private: size_t mInitDoneCount; + FILE *mLogFile; + void onSenderNotify(const sp &msg); void notifyInitDone(status_t err); diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index 8420529..53b7187 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -325,12 +325,31 @@ void TSPacketizer::Track::finalize() { mDescriptors.push_back(descriptor); } - int32_t hdcpVersion; - if (mFormat->findInt32("hdcp-version", &hdcpVersion)) { - // HDCP descriptor + mFinalized = true; +} - CHECK(hdcpVersion == 0x20 || hdcpVersion == 0x21); +//////////////////////////////////////////////////////////////////////////////// + +TSPacketizer::TSPacketizer(uint32_t flags) + : mFlags(flags), + mPATContinuityCounter(0), + mPMTContinuityCounter(0) { + initCrcTable(); + if (flags & (EMIT_HDCP20_DESCRIPTOR | EMIT_HDCP21_DESCRIPTOR)) { + int32_t hdcpVersion; + if (flags & EMIT_HDCP20_DESCRIPTOR) { + CHECK(!(flags & EMIT_HDCP21_DESCRIPTOR)); + hdcpVersion = 0x20; + } else { + CHECK(!(flags & EMIT_HDCP20_DESCRIPTOR)); + + // HDCP2.0 _and_ HDCP 2.1 specs say to set the version + // inside the HDCP descriptor to 0x20!!! + hdcpVersion = 0x20; + } + + // HDCP descriptor sp descriptor = new ABuffer(7); uint8_t *data = descriptor->data(); data[0] = 0x05; // descriptor_tag @@ -341,18 +360,8 @@ void TSPacketizer::Track::finalize() { data[5] = 'P'; data[6] = hdcpVersion; - mDescriptors.push_back(descriptor); + mProgramInfoDescriptors.push_back(descriptor); } - - mFinalized = true; -} - -//////////////////////////////////////////////////////////////////////////////// - -TSPacketizer::TSPacketizer() - : mPATContinuityCounter(0), - mPMTContinuityCounter(0) { - initCrcTable(); } TSPacketizer::~TSPacketizer() { @@ -605,8 +614,9 @@ status_t TSPacketizer::packetize( // reserved = b111 // PCR_PID = kPCR_PID (13 bits) // reserved = b1111 - // program_info_length = 0x000 - // one or more elementary stream descriptions follow: + // program_info_length = 0x??? + // program_info_descriptors follow + // one or more elementary stream descriptions follow: // stream_type = 0x?? // reserved = b111 // elementary_PID = b? ???? ???? ???? (13 bits) @@ -638,8 +648,21 @@ status_t TSPacketizer::packetize( *ptr++ = 0x00; *ptr++ = 0xe0 | (kPID_PCR >> 8); *ptr++ = kPID_PCR & 0xff; - *ptr++ = 0xf0; - *ptr++ = 0x00; + + size_t program_info_length = 0; + for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) { + program_info_length += mProgramInfoDescriptors.itemAt(i)->size(); + } + + CHECK_LT(program_info_length, 0x400); + *ptr++ = 0xf0 | (program_info_length >> 8); + *ptr++ = (program_info_length & 0xff); + + for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) { + const sp &desc = mProgramInfoDescriptors.itemAt(i); + memcpy(ptr, desc->data(), desc->size()); + ptr += desc->size(); + } for (size_t i = 0; i < mTracks.size(); ++i) { const sp &track = mTracks.itemAt(i); diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h index 5d1d70e..4a664ee 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.h +++ b/media/libstagefright/wifi-display/source/TSPacketizer.h @@ -32,7 +32,11 @@ struct AMessage; // Emits metadata tables (PAT and PMT) and timestamp stream (PCR) based // on flags. struct TSPacketizer : public RefBase { - TSPacketizer(); + enum { + EMIT_HDCP20_DESCRIPTOR = 1, + EMIT_HDCP21_DESCRIPTOR = 2, + }; + TSPacketizer(uint32_t flags); // Returns trackIndex or error. ssize_t addTrack(const sp &format); @@ -68,8 +72,11 @@ private: struct Track; + uint32_t mFlags; Vector > mTracks; + Vector > mProgramInfoDescriptors; + unsigned mPATContinuityCounter; unsigned mPMTContinuityCounter; -- cgit v1.1 From 5abf87f9af48149972eeb851ecaea679911da040 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 7 Mar 2013 10:57:07 -0800 Subject: Disable our fancy logic to respect both sink and source's native formats since it isn't actually supported by anything in the field. Change-Id: I9cd038d7631105de26303312ca87c472d67034d4 --- media/libstagefright/wifi-display/VideoFormats.cpp | 9 +++++++-- media/libstagefright/wifi-display/VideoFormats.h | 2 +- media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp | 8 -------- .../libstagefright/wifi-display/source/WifiDisplaySource.cpp | 12 ++---------- 4 files changed, 10 insertions(+), 21 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp index 9ad8c3c..d171c6f 100644 --- a/media/libstagefright/wifi-display/VideoFormats.cpp +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -256,7 +256,7 @@ bool VideoFormats::parseFormatSpec(const char *spec) { return GetConfiguration(mNativeType, mNativeIndex, NULL, NULL, NULL, NULL); } -AString VideoFormats::getFormatSpec() const { +AString VideoFormats::getFormatSpec(bool forM4Message) const { CHECK_EQ(kNumResolutionTypes, 3); // wfd_video_formats: @@ -277,7 +277,7 @@ AString VideoFormats::getFormatSpec() const { return StringPrintf( "%02x 00 02 02 %08x %08x %08x 00 0000 0000 00 none none", - (mNativeIndex << 3) | mNativeType, + forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType), mResolutionEnabled[0], mResolutionEnabled[1], mResolutionEnabled[2]); @@ -289,6 +289,10 @@ bool VideoFormats::PickBestFormat( const VideoFormats &sourceSupported, ResolutionType *chosenType, size_t *chosenIndex) { +#if 0 + // Support for the native format is a great idea, the spec includes + // these features, but nobody supports it and the tests don't validate it. + ResolutionType nativeType; size_t nativeIndex; sinkSupported.getNativeResolution(&nativeType, &nativeIndex); @@ -316,6 +320,7 @@ bool VideoFormats::PickBestFormat( ALOGW("Source advertised native resolution that it doesn't " "actually support... ignoring"); } +#endif bool first = true; uint32_t bestScore = 0; diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h index a84407a..69e2197 100644 --- a/media/libstagefright/wifi-display/VideoFormats.h +++ b/media/libstagefright/wifi-display/VideoFormats.h @@ -60,7 +60,7 @@ struct VideoFormats { bool *interlaced); bool parseFormatSpec(const char *spec); - AString getFormatSpec() const; + AString getFormatSpec(bool forM4Message = false) const; static bool PickBestFormat( const VideoFormats &sinkSupported, diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index a6f58cd..158c2da 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -47,19 +47,11 @@ WifiDisplaySink::WifiDisplaySink( mSessionID(0), mNextCSeq(1), mIDRFrameRequestPending(false) { -#if 1 // We support any and all resolutions, but prefer 720p30 mSinkSupportedVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 mSinkSupportedVideoFormats.enableAll(); -#else - // We only support 640 x 360 p30. - mSinkSupportedVideoFormats.disableAll(); - - mSinkSupportedVideoFormats.setNativeResolution( - VideoFormats::RESOLUTION_HH, 6); // 640 x 360 p30 -#endif } WifiDisplaySink::~WifiDisplaySink() { diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 07eb237..b8524f6 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -59,18 +59,10 @@ WifiDisplaySource::WifiDisplaySource( mHDCPPort(0), mHDCPInitializationComplete(false), mSetupTriggerDeferred(false) { - mSupportedSourceVideoFormats.enableAll(); + mSupportedSourceVideoFormats.disableAll(); mSupportedSourceVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30 - - // Disable resolutions above 1080p since the encoder won't be able to - // handle them. - mSupportedSourceVideoFormats.setResolutionEnabled( - VideoFormats::RESOLUTION_VESA, 28, false); // 1920x1200 p30 - - mSupportedSourceVideoFormats.setResolutionEnabled( - VideoFormats::RESOLUTION_VESA, 29, false); // 1920x1200 p60 } WifiDisplaySource::~WifiDisplaySource() { @@ -607,7 +599,7 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { chosenVideoFormat.setNativeResolution( mChosenVideoResolutionType, mChosenVideoResolutionIndex); - body.append(chosenVideoFormat.getFormatSpec()); + body.append(chosenVideoFormat.getFormatSpec(true /* forM4Message */)); body.append("\r\n"); } -- cgit v1.1 From 3db62dfc5102247d415df4667bd9609e669fc022 Mon Sep 17 00:00:00 2001 From: ztenghui Date: Fri, 22 Feb 2013 14:32:59 -0800 Subject: Clean up the native code to match Java update 1. Add flags to match the java side change. 2. Update the interface. bug:7991013 Change-Id: I8ffe84c466b2a68e2e1e48b35b78db9e44640265 --- media/libstagefright/MediaMuxer.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index 30bed90..aefc270 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -35,14 +35,20 @@ namespace android { -MediaMuxer::MediaMuxer(const char* pathOut) - : mState(INITED) { - mWriter = new MPEG4Writer(pathOut); +MediaMuxer::MediaMuxer(const char *path, OutputFormat format) + : mState(UNINITED) { + if (format == OUTPUT_FORMAT_MPEG_4) { + mWriter = new MPEG4Writer(path); + mState = INITED; + } } -MediaMuxer::MediaMuxer(int fd) - : mState(INITED) { - mWriter = new MPEG4Writer(fd); +MediaMuxer::MediaMuxer(int fd, OutputFormat format) + : mState(UNINITED) { + if (format == OUTPUT_FORMAT_MPEG_4) { + mWriter = new MPEG4Writer(fd); + mState = INITED; + } } MediaMuxer::~MediaMuxer() { @@ -107,8 +113,6 @@ status_t MediaMuxer::writeSampleData(const sp &buffer, size_t trackInde int64_t timeUs, uint32_t flags) { Mutex::Autolock autoLock(mMuxerLock); - sp currentTrack = mTrackList[trackIndex]; - if (buffer.get() == NULL) { ALOGE("WriteSampleData() get an NULL buffer."); return -EINVAL; @@ -134,10 +138,11 @@ status_t MediaMuxer::writeSampleData(const sp &buffer, size_t trackInde // Just set the kKeyDecodingTime as the presentation time for now. metaData->setInt64(kKeyDecodingTime, timeUs); - if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) { + if (flags & SAMPLE_FLAG_SYNC) { metaData->setInt32(kKeyIsSyncFrame, true); } + sp currentTrack = mTrackList[trackIndex]; // This pushBuffer will wait until the mediaBuffer is consumed. return currentTrack->pushBuffer(mediaBuffer); } -- cgit v1.1 From b9f4140b374b56277f8aec47d1a31ec713ad8668 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Mon, 11 Mar 2013 15:09:32 -0700 Subject: GraphicBufferSource fixes Various fixes: - Set the maximum number of BQ buffers we're allowed to acquire equal to the actual number of codec buffers. That way we keep the codec as full as possible, and never try to acquire more than we're allowed from the BufferQueue. - Actually use "end of stream sent" flag. - Name the BufferQueue (for debug messages). Bug 8359403 Change-Id: I3b8c1f679bbebf6a89e623e13ca029eda7f657ba --- media/libstagefright/omx/GraphicBufferSource.cpp | 128 +++++++++++++---------- media/libstagefright/omx/GraphicBufferSource.h | 6 +- media/libstagefright/omx/OMXNodeInstance.cpp | 3 +- 3 files changed, 81 insertions(+), 56 deletions(-) (limited to 'media') diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index 211e1d1..3854e52 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -32,7 +32,7 @@ static const bool EXTRA_CHECK = true; GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, - uint32_t bufferWidth, uint32_t bufferHeight) : + uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount) : mInitCheck(UNKNOWN_ERROR), mNodeInstance(nodeInstance), mExecuting(false), @@ -40,20 +40,31 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, mEndOfStream(false), mEndOfStreamSent(false) { - ALOGV("GraphicBufferSource w=%u h=%u", bufferWidth, bufferHeight); + ALOGV("GraphicBufferSource w=%u h=%u c=%u", + bufferWidth, bufferHeight, bufferCount); if (bufferWidth == 0 || bufferHeight == 0) { - ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight); + ALOGE("Invalid dimensions %ux%u", bufferWidth, bufferHeight); mInitCheck = BAD_VALUE; return; } + String8 name("GraphicBufferSource"); + mBufferQueue = new BufferQueue(true); + mBufferQueue->setConsumerName(name); mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight); mBufferQueue->setSynchronousMode(true); mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_TEXTURE); + mInitCheck = mBufferQueue->setMaxAcquiredBufferCount(bufferCount); + if (mInitCheck != NO_ERROR) { + ALOGE("Unable to set BQ max acquired buffer count to %u: %d", + bufferCount, mInitCheck); + return; + } + // Note that we can't create an sp<...>(this) in a ctor that will not keep a // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> @@ -64,21 +75,23 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, sp proxy; proxy = new BufferQueue::ProxyConsumerListener(listener); - status_t err = mBufferQueue->consumerConnect(proxy); - if (err != NO_ERROR) { + mInitCheck = mBufferQueue->consumerConnect(proxy); + if (mInitCheck != NO_ERROR) { ALOGE("Error connecting to BufferQueue: %s (%d)", - strerror(-err), err); + strerror(-mInitCheck), mInitCheck); return; } - mInitCheck = OK; + CHECK(mInitCheck == NO_ERROR); } GraphicBufferSource::~GraphicBufferSource() { ALOGV("~GraphicBufferSource"); - status_t err = mBufferQueue->consumerDisconnect(); - if (err != NO_ERROR) { - ALOGW("consumerDisconnect failed: %d", err); + if (mBufferQueue != NULL) { + status_t err = mBufferQueue->consumerDisconnect(); + if (err != NO_ERROR) { + ALOGW("consumerDisconnect failed: %d", err); + } } } @@ -98,8 +111,12 @@ void GraphicBufferSource::omxExecuting() { // one codec buffer simultaneously. (We could instead try to submit // all BQ buffers whenever any codec buffer is freed, but if we get the // initial conditions right that will never be useful.) - while (mNumFramesAvailable && isCodecBufferAvailable_l()) { - fillCodecBuffer_l(); + while (mNumFramesAvailable) { + if (!fillCodecBuffer_l()) { + ALOGV("stop load with frames available (codecAvail=%d)", + isCodecBufferAvailable_l()); + break; + } } ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable); @@ -166,7 +183,7 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // see if the GraphicBuffer reference was null, which should only ever // happen for EOS. if (codecBuffer.mGraphicBuffer == NULL) { - CHECK(mEndOfStream); + CHECK(mEndOfStream && mEndOfStreamSent); // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. return; @@ -216,8 +233,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { if (mNumFramesAvailable) { // Fill this codec buffer. - CHECK(!mEndOfStream); - ALOGV("buffer freed, %d frames avail", mNumFramesAvailable); + CHECK(!mEndOfStreamSent); + ALOGV("buffer freed, %d frames avail (eos=%d)", + mNumFramesAvailable, mEndOfStream); fillCodecBuffer_l(); } else if (mEndOfStream) { // No frames available, but EOS is pending, so use this buffer to @@ -228,56 +246,58 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { return; } -status_t GraphicBufferSource::fillCodecBuffer_l() { +bool GraphicBufferSource::fillCodecBuffer_l() { CHECK(mExecuting && mNumFramesAvailable > 0); + int cbi = findAvailableCodecBuffer_l(); if (cbi < 0) { // No buffers available, bail. ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d", mNumFramesAvailable); - } else { - ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", - mNumFramesAvailable); - BufferQueue::BufferItem item; - status_t err = mBufferQueue->acquireBuffer(&item); - if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - // shouldn't happen - ALOGW("fillCodecBuffer_l: frame was not available"); - return err; - } else if (err != OK) { - // now what? fake end-of-stream? - ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err); - return err; - } + return false; + } - mNumFramesAvailable--; + ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", + mNumFramesAvailable); + BufferQueue::BufferItem item; + status_t err = mBufferQueue->acquireBuffer(&item); + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // shouldn't happen + ALOGW("fillCodecBuffer_l: frame was not available"); + return false; + } else if (err != OK) { + // now what? fake end-of-stream? + ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err); + return false; + } - // Wait for it to become available. - err = item.mFence->waitForever(1000, - "GraphicBufferSource::fillCodecBuffer_l"); - if (err != OK) { - ALOGW("failed to wait for buffer fence: %d", err); - // keep going - } + mNumFramesAvailable--; - // If this is the first time we're seeing this buffer, add it to our - // slot table. - if (item.mGraphicBuffer != NULL) { - ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); - mBufferSlot[item.mBuf] = item.mGraphicBuffer; - } + // Wait for it to become available. + err = item.mFence->waitForever(1000, + "GraphicBufferSource::fillCodecBuffer_l"); + if (err != OK) { + ALOGW("failed to wait for buffer fence: %d", err); + // keep going + } - err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi); - if (err != OK) { - ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); - mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, + // If this is the first time we're seeing this buffer, add it to our + // slot table. + if (item.mGraphicBuffer != NULL) { + ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); + mBufferSlot[item.mBuf] = item.mGraphicBuffer; + } + + err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi); + if (err != OK) { + ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); + mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); - } else { - ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); - } + } else { + ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); } - return OK; + return true; } status_t GraphicBufferSource::signalEndOfInputStream() { @@ -372,6 +392,7 @@ void GraphicBufferSource::submitEndOfInputStream_l() { } else { ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d", header, cbi); + mEndOfStreamSent = true; } } @@ -400,7 +421,8 @@ int GraphicBufferSource::findMatchingCodecBuffer_l( void GraphicBufferSource::onFrameAvailable() { Mutex::Autolock autoLock(mMutex); - ALOGV("onFrameAvailable exec=%d avail=%d", mExecuting, mNumFramesAvailable); + ALOGV("onFrameAvailable exec=%d avail=%d", + mExecuting, mNumFramesAvailable); if (mEndOfStream) { // This should only be possible if a new buffer was queued after diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 6a34bc5..7f1f22e 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -47,7 +47,7 @@ namespace android { class GraphicBufferSource : public BufferQueue::ConsumerListener { public: GraphicBufferSource(OMXNodeInstance* nodeInstance, - uint32_t bufferWidth, uint32_t bufferHeight); + uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount); virtual ~GraphicBufferSource(); // We can't throw an exception if the constructor fails, so we just set @@ -124,7 +124,9 @@ private: // in the onFrameAvailable callback, or if we're in codecBufferEmptied // and mNumFramesAvailable is nonzero). Returns without doing anything if // we don't have a codec buffer available. - status_t fillCodecBuffer_l(); + // + // Returns true if we successfully filled a codec buffer with a BQ buffer. + bool fillCodecBuffer_l(); // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer // reference into the codec buffer, and submits the data to the codec. diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index f3d8d14..46ff22f 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -590,7 +590,8 @@ status_t OMXNodeInstance::createInputSurface( } GraphicBufferSource* bufferSource = new GraphicBufferSource( - this, def.format.video.nFrameWidth, def.format.video.nFrameHeight); + this, def.format.video.nFrameWidth, def.format.video.nFrameHeight, + def.nBufferCountActual); if ((err = bufferSource->initCheck()) != OK) { delete bufferSource; return err; -- cgit v1.1 From d5e56231a598b180a1d898bb7dc61b75580e59a4 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 12 Mar 2013 11:01:43 -0700 Subject: Squashed commit of the following: commit f4edf442741886cdbe071e2d15f6e6247269f7c5 Author: Andreas Huber Date: Tue Mar 12 09:09:18 2013 -0700 Pass additional flags to the sink, use TCP by default in wolfiecast mode. Change-Id: I41e11a2375d4199656e45c4f149d8441d0016092 commit 6302602ed280a38287f507159abfb40a1da38c5a Author: Andreas Huber Date: Tue Mar 12 08:51:58 2013 -0700 tweaks Change-Id: Ie29e422d7258be522f4bb1f6c5afcf74c937e547 commit a38a860e4979ba563cadbaafa21b084439449d26 Author: Andreas Huber Date: Mon Mar 11 16:57:43 2013 -0700 Report average lateness all the way from NuPlayerRenderer... Change-Id: I2e7700703ae656515e44b9c25610d26c75778111 commit a7d49b11675ea88be4029dd8451d1649db94571d Author: Andreas Huber Date: Mon Mar 11 14:54:19 2013 -0700 Make TimeSyncer smarter, enable TunnelRenderer Change-Id: I27377a60cd8feb01589da456967fddd34532c20e commit 0f214c8ef68179f7b61512c37040939554013151 Author: Andreas Huber Date: Thu Mar 7 15:57:56 2013 -0800 convert source timestamps to sink timestamps, report lateness. Change-Id: I051a60fbbceca2f7b508ae3dac6e01e402bae39e commit 04a4f8e16bad09157b5615a5fa45310438955832 Author: Andreas Huber Date: Thu Mar 7 09:00:28 2013 -0800 Sync time between sink and source. Change-Id: Ie8b4d75c957aa48310e7c81d1279761b9f821efe commit aebe20e6184e3636a99082f8ece08e708015cb8d Author: Andreas Huber Date: Wed Mar 6 09:03:12 2013 -0800 play with back pressure Change-Id: I51eb69257e6a79e76f5f9c75ff99d8adbd083947 Change-Id: Ifdf57228667fed7fc71c5090a2c3f7cea1037c5c --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 9 +- .../nuplayer/NuPlayerDriver.cpp | 1 + .../nuplayer/NuPlayerRenderer.cpp | 22 +- .../nuplayer/NuPlayerRenderer.h | 7 +- .../nuplayer/NuPlayerSource.h | 4 + .../nuplayer/StreamingSource.cpp | 4 + .../nuplayer/StreamingSource.h | 2 + media/libstagefright/mpeg2ts/ATSParser.cpp | 13 + media/libstagefright/mpeg2ts/ATSParser.h | 4 + .../wifi-display/ANetworkSession.cpp | 9 + media/libstagefright/wifi-display/Android.mk | 1 + .../libstagefright/wifi-display/MediaReceiver.cpp | 14 +- media/libstagefright/wifi-display/MediaReceiver.h | 2 + media/libstagefright/wifi-display/TimeSyncer.cpp | 332 +++++++++++++++++++++ media/libstagefright/wifi-display/TimeSyncer.h | 109 +++++++ .../wifi-display/rtp/RTPAssembler.cpp | 5 + .../wifi-display/rtp/RTPReceiver.cpp | 44 ++- .../libstagefright/wifi-display/rtp/RTPReceiver.h | 2 + .../libstagefright/wifi-display/rtp/RTPSender.cpp | 2 + .../wifi-display/sink/DirectRenderer.cpp | 53 +++- .../wifi-display/sink/DirectRenderer.h | 14 +- .../wifi-display/sink/TunnelRenderer.cpp | 40 ++- .../wifi-display/sink/TunnelRenderer.h | 6 + .../wifi-display/sink/WifiDisplaySink.cpp | 105 ++++++- .../wifi-display/sink/WifiDisplaySink.h | 19 ++ .../wifi-display/source/WifiDisplaySource.cpp | 12 + .../wifi-display/source/WifiDisplaySource.h | 3 + media/libstagefright/wifi-display/udptest.cpp | 283 +----------------- media/libstagefright/wifi-display/wfd.cpp | 5 +- 29 files changed, 799 insertions(+), 327 deletions(-) create mode 100644 media/libstagefright/wifi-display/TimeSyncer.cpp create mode 100644 media/libstagefright/wifi-display/TimeSyncer.h (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 2ba6c22..5387e1a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -381,9 +381,16 @@ void NuPlayer::onMessageReceived(const sp &msg) { mSource->start(); + uint32_t flags = 0; + + if (mSource->isRealTime()) { + flags |= Renderer::FLAG_REAL_TIME; + } + mRenderer = new Renderer( mAudioSink, - new AMessage(kWhatRendererNotify, id())); + new AMessage(kWhatRendererNotify, id()), + flags); looper()->registerHandler(mRenderer); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 3c63e80..723af09 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -378,6 +378,7 @@ status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { int mode = request.readInt32(); return mPlayer->setVideoScalingMode(mode); } + default: { return INVALID_OPERATION; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 1ba76a5..404b56f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -31,9 +31,11 @@ const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; NuPlayer::Renderer::Renderer( const sp &sink, - const sp ¬ify) + const sp ¬ify, + uint32_t flags) : mAudioSink(sink), mNotify(notify), + mFlags(flags), mNumFramesWritten(0), mDrainAudioQueuePending(false), mDrainVideoQueuePending(false), @@ -323,6 +325,11 @@ void NuPlayer::Renderer::postDrainVideoQueue() { if (entry.mBuffer == NULL) { // EOS doesn't carry a timestamp. delayUs = 0; + } else if (mFlags & FLAG_REAL_TIME) { + int64_t mediaTimeUs; + CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); + + delayUs = mediaTimeUs - ALooper::GetNowUs(); } else { int64_t mediaTimeUs; CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); @@ -368,12 +375,17 @@ void NuPlayer::Renderer::onDrainVideoQueue() { return; } - int64_t mediaTimeUs; - CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); + int64_t realTimeUs; + if (mFlags & FLAG_REAL_TIME) { + CHECK(entry->mBuffer->meta()->findInt64("timeUs", &realTimeUs)); + } else { + int64_t mediaTimeUs; + CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); + + realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs; + } - int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs; mVideoLateByUs = ALooper::GetNowUs() - realTimeUs; - bool tooLate = (mVideoLateByUs > 40000); if (tooLate) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index e4368c7..c9796e2 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -25,8 +25,12 @@ namespace android { struct ABuffer; struct NuPlayer::Renderer : public AHandler { + enum Flags { + FLAG_REAL_TIME = 1, + }; Renderer(const sp &sink, - const sp ¬ify); + const sp ¬ify, + uint32_t flags = 0); void queueBuffer( bool audio, @@ -79,6 +83,7 @@ private: sp mAudioSink; sp mNotify; + uint32_t mFlags; List mAudioQueue; List mVideoQueue; uint32_t mNumFramesWritten; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 8622abe..1cbf575 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -74,6 +74,10 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } + virtual bool isRealTime() const { + return false; + } + protected: virtual ~Source() {} diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index df03f86..28f0d50 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -182,5 +182,9 @@ status_t NuPlayer::StreamingSource::dequeueAccessUnit( return err; } +bool NuPlayer::StreamingSource::isRealTime() const { + return mSource->flags() & IStreamSource::kFlagIsRealTimeData; +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h index 80b061c..412b6c4 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.h +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h @@ -38,6 +38,8 @@ struct NuPlayer::StreamingSource : public NuPlayer::Source { virtual status_t dequeueAccessUnit(bool audio, sp *accessUnit); + virtual bool isRealTime() const; + protected: virtual ~StreamingSource(); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index a167b5a..c12572f 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -452,6 +452,10 @@ int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) { timeUs += mParser->mAbsoluteTimeAnchorUs; } + if (mParser->mTimeOffsetValid) { + timeUs += mParser->mTimeOffsetUs; + } + return timeUs; } @@ -930,6 +934,8 @@ sp ATSParser::Stream::getSource(SourceType type) { ATSParser::ATSParser(uint32_t flags) : mFlags(flags), mAbsoluteTimeAnchorUs(-1ll), + mTimeOffsetValid(false), + mTimeOffsetUs(0ll), mNumTSPacketsParsed(0), mNumPCRs(0) { mPSISections.add(0 /* PID */, new PSISection); @@ -960,6 +966,13 @@ void ATSParser::signalDiscontinuity( CHECK(mPrograms.empty()); mAbsoluteTimeAnchorUs = timeUs; return; + } else if (type == DISCONTINUITY_TIME_OFFSET) { + int64_t offset; + CHECK(extra->findInt64("offset", &offset)); + + mTimeOffsetValid = true; + mTimeOffsetUs = offset; + return; } for (size_t i = 0; i < mPrograms.size(); ++i) { diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 46edc45..a10edc9 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -39,6 +39,7 @@ struct ATSParser : public RefBase { DISCONTINUITY_AUDIO_FORMAT = 2, DISCONTINUITY_VIDEO_FORMAT = 4, DISCONTINUITY_ABSOLUTE_TIME = 8, + DISCONTINUITY_TIME_OFFSET = 16, DISCONTINUITY_SEEK = DISCONTINUITY_TIME, @@ -106,6 +107,9 @@ private: int64_t mAbsoluteTimeAnchorUs; + bool mTimeOffsetValid; + int64_t mTimeOffsetUs; + size_t mNumTSPacketsParsed; void parseProgramAssociationTable(ABitReader *br); diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index cb6011c..465f4c4 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -507,6 +508,14 @@ status_t ANetworkSession::Session::writeMore() { mSawSendFailure = true; } +#if 0 + int numBytesQueued; + int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); + if (res == 0 && numBytesQueued > 102400) { + ALOGI("numBytesQueued = %d", numBytesQueued); + } +#endif + return err; } diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 19f560c..f81929c 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -15,6 +15,7 @@ LOCAL_SRC_FILES:= \ sink/TunnelRenderer.cpp \ sink/WifiDisplaySink.cpp \ SNTPClient.cpp \ + TimeSyncer.cpp \ source/Converter.cpp \ source/MediaPuller.cpp \ source/PlaybackSession.cpp \ diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp index 3c92d41..10a2dff 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.cpp +++ b/media/libstagefright/wifi-display/MediaReceiver.cpp @@ -127,7 +127,10 @@ void MediaReceiver::onMessageReceived(const sp &msg) { notifyInitDone(mInitStatus); } - mTSParser = new ATSParser(ATSParser::ALIGNED_VIDEO_DATA); + mTSParser = new ATSParser( + ATSParser::ALIGNED_VIDEO_DATA + | ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE); + mFormatKnownMask = 0; break; } @@ -306,6 +309,15 @@ void MediaReceiver::postAccessUnit( notify->post(); } +status_t MediaReceiver::notifyLateness(size_t trackIndex, int64_t latenessUs) { + if (trackIndex >= mTrackInfos.size()) { + return -ERANGE; + } + + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + return info->mReceiver->notifyLateness(latenessUs); +} + } // namespace android diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h index 7adc3c4..cdfde99 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.h +++ b/media/libstagefright/wifi-display/MediaReceiver.h @@ -60,6 +60,8 @@ struct MediaReceiver : public AHandler { }; status_t initAsync(Mode mode); + status_t notifyLateness(size_t trackIndex, int64_t latenessUs); + protected: virtual void onMessageReceived(const sp &msg); virtual ~MediaReceiver(); diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp new file mode 100644 index 0000000..64e182e --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSyncer.cpp @@ -0,0 +1,332 @@ +/* + * Copyright 2013, 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_NEBUG 0 +#define LOG_TAG "TimeSyncer" +#include + +#include "TimeSyncer.h" + +#include "ANetworkSession.h" + +#include +#include +#include +#include +#include +#include + +namespace android { + +TimeSyncer::TimeSyncer( + const sp &netSession, const sp ¬ify) + : mNetSession(netSession), + mNotify(notify), + mIsServer(false), + mConnected(false), + mUDPSession(0), + mSeqNo(0), + mTotalTimeUs(0.0), + mPendingT1(0ll), + mTimeoutGeneration(0) { +} + +TimeSyncer::~TimeSyncer() { +} + +void TimeSyncer::startServer(unsigned localPort) { + sp msg = new AMessage(kWhatStartServer, id()); + msg->setInt32("localPort", localPort); + msg->post(); +} + +void TimeSyncer::startClient(const char *remoteHost, unsigned remotePort) { + sp msg = new AMessage(kWhatStartClient, id()); + msg->setString("remoteHost", remoteHost); + msg->setInt32("remotePort", remotePort); + msg->post(); +} + +void TimeSyncer::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatStartClient: + { + AString remoteHost; + CHECK(msg->findString("remoteHost", &remoteHost)); + + int32_t remotePort; + CHECK(msg->findInt32("remotePort", &remotePort)); + + sp notify = new AMessage(kWhatUDPNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createUDPSession( + 0 /* localPort */, + remoteHost.c_str(), + remotePort, + notify, + &mUDPSession)); + + postSendPacket(); + break; + } + + case kWhatStartServer: + { + mIsServer = true; + + int32_t localPort; + CHECK(msg->findInt32("localPort", &localPort)); + + sp notify = new AMessage(kWhatUDPNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createUDPSession( + localPort, notify, &mUDPSession)); + + break; + } + + case kWhatSendPacket: + { + TimeInfo ti; + memset(&ti, 0, sizeof(ti)); + + ti.mT1 = ALooper::GetNowUs(); + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mUDPSession, &ti, sizeof(ti))); + + mPendingT1 = ti.mT1; + postTimeout(); + break; + } + + case kWhatTimedOut: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mTimeoutGeneration) { + break; + } + + ALOGI("timed out, sending another request"); + postSendPacket(); + break; + } + + case kWhatUDPNotify: + { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred in session %d (%d, '%s/%s').", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + mNetSession->destroySession(sessionID); + + cancelTimeout(); + + notifyError(err); + break; + } + + case ANetworkSession::kWhatDatagram: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + sp packet; + CHECK(msg->findBuffer("data", &packet)); + + int64_t arrivalTimeUs; + CHECK(packet->meta()->findInt64( + "arrivalTimeUs", &arrivalTimeUs)); + + CHECK_EQ(packet->size(), sizeof(TimeInfo)); + + TimeInfo *ti = (TimeInfo *)packet->data(); + + if (mIsServer) { + if (!mConnected) { + AString fromAddr; + CHECK(msg->findString("fromAddr", &fromAddr)); + + int32_t fromPort; + CHECK(msg->findInt32("fromPort", &fromPort)); + + CHECK_EQ((status_t)OK, + mNetSession->connectUDPSession( + mUDPSession, fromAddr.c_str(), fromPort)); + + mConnected = true; + } + + ti->mT2 = arrivalTimeUs; + ti->mT3 = ALooper::GetNowUs(); + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mUDPSession, ti, sizeof(*ti))); + } else { + if (ti->mT1 != mPendingT1) { + break; + } + + cancelTimeout(); + mPendingT1 = 0; + + ti->mT4 = arrivalTimeUs; + + // One way delay for a packet to travel from client + // to server or back (assumed to be the same either way). + int64_t delay = + (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; + + // Offset between the client clock (T1, T4) and the + // server clock (T2, T3) timestamps. + int64_t offset = + (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; + + mHistory.push_back(*ti); + + ALOGV("delay = %lld us,\toffset %lld us", + delay, + offset); + + if (mHistory.size() < kNumPacketsPerBatch) { + postSendPacket(1000000ll / 30); + } else { + notifyOffset(); + + mHistory.clear(); + postSendPacket(kBatchDelayUs); + } + } + break; + } + + default: + TRESPASS(); + } + + break; + } + + default: + TRESPASS(); + } +} + +void TimeSyncer::postSendPacket(int64_t delayUs) { + (new AMessage(kWhatSendPacket, id()))->post(delayUs); +} + +void TimeSyncer::postTimeout() { + sp msg = new AMessage(kWhatTimedOut, id()); + msg->setInt32("generation", mTimeoutGeneration); + msg->post(kTimeoutDelayUs); +} + +void TimeSyncer::cancelTimeout() { + ++mTimeoutGeneration; +} + +void TimeSyncer::notifyError(status_t err) { + if (mNotify == NULL) { + looper()->stop(); + return; + } + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +// static +int TimeSyncer::CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2) { + int64_t rt1 = ti1->mT4 - ti1->mT1; + int64_t rt2 = ti2->mT4 - ti2->mT1; + + if (rt1 < rt2) { + return -1; + } else if (rt1 > rt2) { + return 1; + } + + return 0; +} + +void TimeSyncer::notifyOffset() { + mHistory.sort(CompareRountripTime); + + int64_t sum = 0ll; + size_t count = 0; + + // Only consider the third of the information associated with the best + // (smallest) roundtrip times. + for (size_t i = 0; i < mHistory.size() / 3; ++i) { + const TimeInfo *ti = &mHistory[i]; + +#if 0 + // One way delay for a packet to travel from client + // to server or back (assumed to be the same either way). + int64_t delay = + (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; +#endif + + // Offset between the client clock (T1, T4) and the + // server clock (T2, T3) timestamps. + int64_t offset = + (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; + + ALOGV("(%d) RT: %lld us, offset: %lld us", + i, ti->mT4 - ti->mT1, offset); + + sum += offset; + ++count; + } + + if (mNotify == NULL) { + ALOGI("avg. offset is %lld", sum / count); + return; + } + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatTimeOffset); + notify->setInt64("offset", sum / count); + notify->post(); +} + +} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h new file mode 100644 index 0000000..0e3aed7 --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSyncer.h @@ -0,0 +1,109 @@ +/* + * Copyright 2013, 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 TIME_SYNCER_H_ + +#define TIME_SYNCER_H_ + +#include + +namespace android { + +struct ANetworkSession; + +/* + TimeSyncer allows us to synchronize time between a client and a server. + The client sends a UDP packet containing its send-time to the server, + the server sends that packet back to the client amended with information + about when it was received as well as the time the reply was sent back. + Finally the client receives the reply and has now enough information to + compute the clock offset between client and server assuming that packet + exchange is symmetric, i.e. time for a packet client->server and + server->client is roughly equal. + This exchange is repeated a number of times and the average offset computed + over the 30% of packets that had the lowest roundtrip times. + The offset is determined every 10 secs to account for slight differences in + clock frequency. +*/ +struct TimeSyncer : public AHandler { + enum { + kWhatError, + kWhatTimeOffset, + }; + TimeSyncer( + const sp &netSession, + const sp ¬ify); + + void startServer(unsigned localPort); + void startClient(const char *remoteHost, unsigned remotePort); + +protected: + virtual ~TimeSyncer(); + + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatStartServer, + kWhatStartClient, + kWhatUDPNotify, + kWhatSendPacket, + kWhatTimedOut, + }; + + struct TimeInfo { + int64_t mT1; // client timestamp at send + int64_t mT2; // server timestamp at receive + int64_t mT3; // server timestamp at send + int64_t mT4; // client timestamp at receive + }; + + enum { + kNumPacketsPerBatch = 30, + }; + static const int64_t kTimeoutDelayUs = 500000ll; + static const int64_t kBatchDelayUs = 10000000ll; // every 10 secs + + sp mNetSession; + sp mNotify; + + bool mIsServer; + bool mConnected; + int32_t mUDPSession; + uint32_t mSeqNo; + double mTotalTimeUs; + + Vector mHistory; + + int64_t mPendingT1; + int32_t mTimeoutGeneration; + + void postSendPacket(int64_t delayUs = 0ll); + + void postTimeout(); + void cancelTimeout(); + + void notifyError(status_t err); + void notifyOffset(); + + static int CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2); + + DISALLOW_EVIL_CONSTRUCTORS(TimeSyncer); +}; + +} // namespace android + +#endif // TIME_SYNCER_H_ diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp index d0ab60d..5f189e7 100644 --- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp @@ -53,6 +53,11 @@ void RTPReceiver::TSAssembler::signalDiscontinuity() { } status_t RTPReceiver::TSAssembler::processPacket(const sp &packet) { + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + + packet->meta()->setInt64("timeUs", (rtpTime * 100ll) / 9); + postAccessUnit(packet, mSawDiscontinuity); if (mSawDiscontinuity) { diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp index 29482af..8711b08 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -221,10 +221,12 @@ void RTPReceiver::Source::dequeueMore() { mNumDeclaredLostPrior = mNumDeclaredLost; - ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", - lostInterval, - 100.0f * lostInterval / expectedInterval, - declaredLostInterval); + if (declaredLostInterval > 0) { + ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", + lostInterval, + 100.0f * lostInterval / expectedInterval, + declaredLostInterval); + } } mNextReportTimeUs = nowUs + kReportIntervalUs; @@ -530,6 +532,40 @@ status_t RTPReceiver::connect( return OK; } +status_t RTPReceiver::notifyLateness(int64_t latenessUs) { + sp buf = new ABuffer(20); + + uint8_t *ptr = buf->data(); + ptr[0] = 0x80 | 0; + ptr[1] = 204; // APP + ptr[2] = 0; + + CHECK((buf->size() % 4) == 0u); + ptr[3] = (buf->size() / 4) - 1; + + ptr[4] = kSourceID >> 24; // SSRC + ptr[5] = (kSourceID >> 16) & 0xff; + ptr[6] = (kSourceID >> 8) & 0xff; + ptr[7] = kSourceID & 0xff; + ptr[8] = 'l'; + ptr[9] = 'a'; + ptr[10] = 't'; + ptr[11] = 'e'; + + ptr[12] = latenessUs >> 56; + ptr[13] = (latenessUs >> 48) & 0xff; + ptr[14] = (latenessUs >> 40) & 0xff; + ptr[15] = (latenessUs >> 32) & 0xff; + ptr[16] = (latenessUs >> 24) & 0xff; + ptr[17] = (latenessUs >> 16) & 0xff; + ptr[18] = (latenessUs >> 8) & 0xff; + ptr[19] = latenessUs & 0xff; + + mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); + + return OK; +} + void RTPReceiver::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatRTPNotify: diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h index 2ae864a..ec4671d 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h @@ -53,6 +53,8 @@ struct RTPReceiver : public RTPBase, public AHandler { int32_t remoteRTPPort, int32_t remoteRTCPPort); + status_t notifyLateness(int64_t latenessUs); + protected: virtual ~RTPReceiver(); virtual void onMessageReceived(const sp &msg); diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 85c5933..b60853d 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -577,6 +577,8 @@ status_t RTPSender::onRTCPData(const sp &buffer) { case 202: // SDES case 203: + break; + case 204: // APP break; diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index b53252d..5efcd17 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -39,8 +39,11 @@ DirectRenderer::DirectRenderer( : mSurfaceTex(bufferProducer), mVideoDecoderNotificationPending(false), mRenderPending(false), - mFirstRenderTimeUs(-1ll), - mFirstRenderRealUs(-1ll) { + mTimeOffsetUs(0ll), + mLatencySum(0ll), + mLatencyCount(0), + mNumFramesLate(0), + mNumFrames(0) { } DirectRenderer::~DirectRenderer() { @@ -53,6 +56,29 @@ DirectRenderer::~DirectRenderer() { } } +void DirectRenderer::setTimeOffset(int64_t offset) { + mTimeOffsetUs = offset; +} + +int64_t DirectRenderer::getAvgLatenessUs() { + if (mLatencyCount == 0) { + return 0ll; + } + + int64_t avgLatencyUs = mLatencySum / mLatencyCount; + + mLatencySum = 0ll; + mLatencyCount = 0; + + if (mNumFrames > 0) { + ALOGI("%d / %d frames late", mNumFramesLate, mNumFrames); + mNumFramesLate = 0; + mNumFrames = 0; + } + + return avgLatencyUs; +} + void DirectRenderer::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatVideoDecoderNotify: @@ -224,14 +250,17 @@ void DirectRenderer::onVideoDecoderNotify() { } void DirectRenderer::queueOutputBuffer(size_t index, int64_t timeUs) { -#if 0 +#if 1 OutputInfo info; info.mIndex = index; - info.mTimeUs = timeUs; + info.mTimeUs = timeUs + mTimeOffsetUs; mOutputBuffers.push_back(info); scheduleRenderIfNecessary(); #else + mLatencySum += ALooper::GetNowUs() - (timeUs + mTimeOffsetUs); + ++mLatencyCount; + status_t err = mVideoDecoder->renderOutputBufferAndRelease(index); CHECK_EQ(err, (status_t)OK); #endif @@ -247,13 +276,7 @@ void DirectRenderer::scheduleRenderIfNecessary() { int64_t timeUs = (*mOutputBuffers.begin()).mTimeUs; int64_t nowUs = ALooper::GetNowUs(); - if (mFirstRenderTimeUs < 0ll) { - mFirstRenderTimeUs = timeUs; - mFirstRenderRealUs = nowUs; - } - - int64_t whenUs = timeUs - mFirstRenderTimeUs + mFirstRenderRealUs; - int64_t delayUs = whenUs - nowUs; + int64_t delayUs = timeUs - nowUs; (new AMessage(kWhatRender, id()))->post(delayUs); } @@ -270,6 +293,14 @@ void DirectRenderer::onRender() { break; } + if (info.mTimeUs + 15000ll < nowUs) { + ++mNumFramesLate; + } + ++mNumFrames; + + mLatencySum += nowUs - info.mTimeUs; + ++mLatencyCount; + status_t err = mVideoDecoder->renderOutputBufferAndRelease(info.mIndex); CHECK_EQ(err, (status_t)OK); diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h index 7219080..44be8f8 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -36,6 +36,10 @@ struct DirectRenderer : public AHandler { void setFormat(size_t trackIndex, const sp &format); void queueAccessUnit(size_t trackIndex, const sp &accessUnit); + void setTimeOffset(int64_t offset); + + int64_t getAvgLatenessUs(); + protected: virtual void onMessageReceived(const sp &msg); virtual ~DirectRenderer(); @@ -63,8 +67,14 @@ private: List mOutputBuffers; bool mRenderPending; - int64_t mFirstRenderTimeUs; - int64_t mFirstRenderRealUs; + + int64_t mTimeOffsetUs; + + int64_t mLatencySum; + size_t mLatencyCount; + + int32_t mNumFramesLate; + int32_t mNumFrames; void onVideoDecoderNotify(); void onRender(); diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index d9d8a76..6b185db 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,8 @@ struct TunnelRenderer::StreamSource : public BnStreamSource { void doSomeWork(); + void setTimeOffset(int64_t offset); + protected: virtual ~StreamSource(); @@ -75,6 +78,9 @@ private: size_t mNumDeqeued; + int64_t mTimeOffsetUs; + bool mTimeOffsetChanged; + DISALLOW_EVIL_CONSTRUCTORS(StreamSource); }; @@ -82,7 +88,9 @@ private: TunnelRenderer::StreamSource::StreamSource(TunnelRenderer *owner) : mOwner(owner), - mNumDeqeued(0) { + mNumDeqeued(0), + mTimeOffsetUs(0ll), + mTimeOffsetChanged(false) { } TunnelRenderer::StreamSource::~StreamSource() { @@ -110,7 +118,7 @@ void TunnelRenderer::StreamSource::onBufferAvailable(size_t index) { } uint32_t TunnelRenderer::StreamSource::flags() const { - return kFlagAlignedVideoData; + return kFlagAlignedVideoData | kFlagIsRealTimeData; } void TunnelRenderer::StreamSource::doSomeWork() { @@ -124,21 +132,21 @@ void TunnelRenderer::StreamSource::doSomeWork() { ++mNumDeqeued; - if (mNumDeqeued == 1) { - ALOGI("fixing real time now."); - + if (mTimeOffsetChanged) { sp extra = new AMessage; extra->setInt32( IStreamListener::kKeyDiscontinuityMask, - ATSParser::DISCONTINUITY_ABSOLUTE_TIME); + ATSParser::DISCONTINUITY_TIME_OFFSET); - extra->setInt64("timeUs", ALooper::GetNowUs()); + extra->setInt64("offset", mTimeOffsetUs); mListener->issueCommand( IStreamListener::DISCONTINUITY, false /* synchronous */, extra); + + mTimeOffsetChanged = false; } ALOGV("dequeue TS packet of size %d", srcBuffer->size()); @@ -155,18 +163,32 @@ void TunnelRenderer::StreamSource::doSomeWork() { } } +void TunnelRenderer::StreamSource::setTimeOffset(int64_t offset) { + Mutex::Autolock autoLock(mLock); + + if (offset != mTimeOffsetUs) { + mTimeOffsetUs = offset; + mTimeOffsetChanged = true; + } +} + //////////////////////////////////////////////////////////////////////////////// TunnelRenderer::TunnelRenderer( const sp &bufferProducer) : mSurfaceTex(bufferProducer), mStartup(true) { + mStreamSource = new StreamSource(this); } TunnelRenderer::~TunnelRenderer() { destroyPlayer(); } +void TunnelRenderer::setTimeOffset(int64_t offset) { + mStreamSource->setTimeOffset(offset); +} + void TunnelRenderer::onMessageReceived(const sp &msg) { switch (msg->what()) { default: @@ -209,8 +231,6 @@ void TunnelRenderer::initPlayer() { sp service = interface_cast(binder); CHECK(service.get() != NULL); - mStreamSource = new StreamSource(this); - mPlayerClient = new PlayerClient; mPlayer = service->create(mPlayerClient, 0); @@ -226,6 +246,8 @@ void TunnelRenderer::initPlayer() { void TunnelRenderer::destroyPlayer() { mStreamSource.clear(); + mPlayer->setVideoSurfaceTexture(NULL); + mPlayer->stop(); mPlayer.clear(); diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h index 8e96665..479e73c 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.h @@ -39,6 +39,12 @@ struct TunnelRenderer : public AHandler { void queueBuffer(const sp &buffer); sp dequeueBuffer(); + void setTimeOffset(int64_t offset); + + int64_t getAvgLatenessUs() { + return 0ll; + } + protected: virtual void onMessageReceived(const sp &msg); virtual ~TunnelRenderer(); diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 158c2da..0d2e347 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -23,22 +23,24 @@ #include "DirectRenderer.h" #include "MediaReceiver.h" #include "ParsedMessage.h" +#include "TimeSyncer.h" #include "TunnelRenderer.h" +#include #include #include #include #include -#include - namespace android { WifiDisplaySink::WifiDisplaySink( + uint32_t flags, const sp &netSession, const sp &bufferProducer, const sp ¬ify) : mState(UNDEFINED), + mFlags(flags), mNetSession(netSession), mSurfaceTex(bufferProducer), mNotify(notify), @@ -46,7 +48,11 @@ WifiDisplaySink::WifiDisplaySink( mUsingTCPInterleaving(false), mSessionID(0), mNextCSeq(1), - mIDRFrameRequestPending(false) { + mIDRFrameRequestPending(false), + mTimeOffsetUs(0ll), + mTimeOffsetValid(false), + mTargetLatencyUs(-1ll), + mSetupDeferred(false) { // We support any and all resolutions, but prefer 720p30 mSinkSupportedVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 @@ -199,6 +205,16 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { { ALOGI("We're now connected."); mState = CONNECTED; + + if (mFlags & FLAG_SPECIAL_MODE) { + sp notify = new AMessage( + kWhatTimeSyncerNotify, id()); + + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + mTimeSyncer->startClient(mRTSPHost.c_str(), 8123); + } break; } @@ -226,6 +242,41 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { break; } + case kWhatTimeSyncerNotify: + { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + if (what == TimeSyncer::kWhatTimeOffset) { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + + if (mSetupDeferred) { + CHECK_EQ((status_t)OK, + sendSetup( + mSessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0")); + + mSetupDeferred = false; + } + } + break; + } + + case kWhatReportLateness: + { + int64_t latenessUs = mRenderer->getAvgLatenessUs(); + + ALOGI("avg. lateness = %lld ms", + (latenessUs + mTargetLatencyUs) / 1000ll); + + mMediaReceiver->notifyLateness( + 0 /* trackIndex */, latenessUs); + + msg->post(kReportLatenessEveryUs); + break; + } + default: TRESPASS(); } @@ -266,15 +317,39 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { looper()->registerHandler(mRenderer); } + CHECK(mTimeOffsetValid); + + int64_t latencyUs = 300000ll; // 300ms by default + + char val[PROPERTY_VALUE_MAX]; + if (property_get("media.wfd-sink.latency", val, NULL)) { + char *end; + int64_t x = strtoll(val, &end, 10); + + if (end > val && *end == '\0' && x >= 0ll) { + latencyUs = x; + } + } + + if (latencyUs != mTargetLatencyUs) { + mTargetLatencyUs = latencyUs; + + ALOGI("Assuming %lld ms of latency.", latencyUs / 1000ll); + } + + // We are the timesync _client_, + // client time = server time - time offset. + mRenderer->setTimeOffset(-mTimeOffsetUs + mTargetLatencyUs); + sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); -#if USE_TUNNEL_RENDERER - mRenderer->queueBuffer(accessUnit); -#else size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); +#if USE_TUNNEL_RENDERER + mRenderer->queueBuffer(accessUnit); +#else sp format; if (msg->findMessage("format", &format)) { mRenderer->setFormat(trackIndex, format); @@ -445,6 +520,8 @@ status_t WifiDisplaySink::onReceivePlayResponse( mState = PLAYING; + (new AMessage(kWhatReportLateness, id()))->post(kReportLatenessEveryUs); + return OK; } @@ -555,6 +632,8 @@ void WifiDisplaySink::onGetParameterRequest( mUsingTCPTransport = true; mUsingTCPInterleaving = true; } + } else if (mFlags & FLAG_SPECIAL_MODE) { + mUsingTCPTransport = true; } body = "wfd_video_formats: "; @@ -735,12 +814,16 @@ void WifiDisplaySink::onSetParameterRequest( const char *content = data->getContent(); if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) { - status_t err = - sendSetup( - sessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); + if ((mFlags & FLAG_SPECIAL_MODE) && !mTimeOffsetValid) { + mSetupDeferred = true; + } else { + status_t err = + sendSetup( + sessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); - CHECK_EQ(err, (status_t)OK); + CHECK_EQ(err, (status_t)OK); + } } AString response = "RTSP/1.0 200 OK\r\n"; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index 01af58b..2b8c6f7 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -31,6 +31,7 @@ struct AMessage; struct DirectRenderer; struct MediaReceiver; struct ParsedMessage; +struct TimeSyncer; struct TunnelRenderer; #define USE_TUNNEL_RENDERER 0 @@ -43,11 +44,16 @@ struct WifiDisplaySink : public AHandler { kWhatDisconnected, }; + enum Flags { + FLAG_SPECIAL_MODE = 1, + }; + // If no notification message is specified (notify == NULL) // the sink will stop its looper() once the session ends, // otherwise it will post an appropriate notification but leave // the looper() running. WifiDisplaySink( + uint32_t flags, const sp &netSession, const sp &bufferProducer = NULL, const sp ¬ify = NULL); @@ -73,6 +79,8 @@ private: kWhatRTSPNotify, kWhatStop, kWhatMediaReceiverNotify, + kWhatTimeSyncerNotify, + kWhatReportLateness, }; struct ResponseID { @@ -89,11 +97,15 @@ private: typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)( int32_t sessionID, const sp &msg); + static const int64_t kReportLatenessEveryUs = 1000000ll; + State mState; + uint32_t mFlags; VideoFormats mSinkSupportedVideoFormats; sp mNetSession; sp mSurfaceTex; sp mNotify; + sp mTimeSyncer; bool mUsingTCPTransport; bool mUsingTCPInterleaving; AString mRTSPHost; @@ -117,6 +129,13 @@ private: bool mIDRFrameRequestPending; + int64_t mTimeOffsetUs; + bool mTimeOffsetValid; + + int64_t mTargetLatencyUs; + + bool mSetupDeferred; + status_t sendM2(int32_t sessionID); status_t sendSetup(int32_t sessionID, const char *uri); status_t sendPlay(int32_t sessionID, const char *uri); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index b8524f6..de66bde 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -23,6 +23,7 @@ #include "Parameters.h" #include "ParsedMessage.h" #include "rtp/RTPSender.h" +#include "TimeSyncer.h" #include #include @@ -157,6 +158,12 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { } if (err == OK) { + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + mTimeSyncer->startServer(8123); + mState = AWAITING_CLIENT_CONNECTION; } @@ -520,6 +527,11 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { break; } + case kWhatTimeSyncerNotify: + { + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 724462c..9e72682 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -30,6 +30,7 @@ namespace android { struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; +struct TimeSyncer; // Represents the RTSP server acting as a wifi display source. // Manages incoming connections, sets up Playback sessions as necessary. @@ -81,6 +82,7 @@ private: kWhatHDCPNotify, kWhatFinishStop2, kWhatTeardownTriggerTimedOut, + kWhatTimeSyncerNotify, }; struct ResponseID { @@ -114,6 +116,7 @@ private: VideoFormats mSupportedSourceVideoFormats; sp mNetSession; sp mClient; + sp mTimeSyncer; struct in_addr mInterfaceAddr; int32_t mSessionID; diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp index 86437e0..111846d 100644 --- a/media/libstagefright/wifi-display/udptest.cpp +++ b/media/libstagefright/wifi-display/udptest.cpp @@ -19,292 +19,13 @@ #include #include "ANetworkSession.h" +#include "TimeSyncer.h" #include -#include -#include -#include -#include #include -#include namespace android { -struct TestHandler : public AHandler { - TestHandler(const sp &netSession); - - void startServer(unsigned localPort); - void startClient(const char *remoteHost, unsigned remotePort); - -protected: - virtual ~TestHandler(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatStartServer, - kWhatStartClient, - kWhatUDPNotify, - kWhatSendPacket, - kWhatTimedOut, - }; - - struct TimeInfo { - int64_t mT1; // client timestamp at send - int64_t mT2; // server timestamp at receive - int64_t mT3; // server timestamp at send - int64_t mT4; // client timestamp at receive - }; - - static const int64_t kTimeoutDelayUs = 1000000ll; - - sp mNetSession; - - bool mIsServer; - bool mConnected; - int32_t mUDPSession; - uint32_t mSeqNo; - double mTotalTimeUs; - int32_t mCount; - int64_t mSumOffsets; - - int64_t mPendingT1; - int32_t mTimeoutGeneration; - - void postSendPacket(int64_t delayUs = 0ll); - - void postTimeout(); - void cancelTimeout(); - - DISALLOW_EVIL_CONSTRUCTORS(TestHandler); -}; - -TestHandler::TestHandler(const sp &netSession) - : mNetSession(netSession), - mIsServer(false), - mConnected(false), - mUDPSession(0), - mSeqNo(0), - mTotalTimeUs(0.0), - mCount(0), - mSumOffsets(0ll), - mPendingT1(0ll), - mTimeoutGeneration(0) { -} - -TestHandler::~TestHandler() { -} - -void TestHandler::startServer(unsigned localPort) { - sp msg = new AMessage(kWhatStartServer, id()); - msg->setInt32("localPort", localPort); - msg->post(); -} - -void TestHandler::startClient(const char *remoteHost, unsigned remotePort) { - sp msg = new AMessage(kWhatStartClient, id()); - msg->setString("remoteHost", remoteHost); - msg->setInt32("remotePort", remotePort); - msg->post(); -} - -void TestHandler::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatStartClient: - { - AString remoteHost; - CHECK(msg->findString("remoteHost", &remoteHost)); - - int32_t remotePort; - CHECK(msg->findInt32("remotePort", &remotePort)); - - sp notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - 0 /* localPort */, - remoteHost.c_str(), - remotePort, - notify, - &mUDPSession)); - - postSendPacket(); - break; - } - - case kWhatStartServer: - { - mIsServer = true; - - int32_t localPort; - CHECK(msg->findInt32("localPort", &localPort)); - - sp notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - localPort, notify, &mUDPSession)); - - break; - } - - case kWhatSendPacket: - { - TimeInfo ti; - memset(&ti, 0, sizeof(ti)); - - ti.mT1 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, &ti, sizeof(ti))); - - mPendingT1 = ti.mT1; - postTimeout(); - break; - } - - case kWhatTimedOut: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mTimeoutGeneration) { - break; - } - - ALOGI("timed out, sending another request"); - postSendPacket(); - break; - } - - case kWhatUDPNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred in session %d (%d, '%s/%s').", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - cancelTimeout(); - looper()->stop(); - break; - } - - case ANetworkSession::kWhatDatagram: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp packet; - CHECK(msg->findBuffer("data", &packet)); - - int64_t arrivalTimeUs; - CHECK(packet->meta()->findInt64( - "arrivalTimeUs", &arrivalTimeUs)); - - CHECK_EQ(packet->size(), sizeof(TimeInfo)); - - TimeInfo *ti = (TimeInfo *)packet->data(); - - if (mIsServer) { - if (!mConnected) { - AString fromAddr; - CHECK(msg->findString("fromAddr", &fromAddr)); - - int32_t fromPort; - CHECK(msg->findInt32("fromPort", &fromPort)); - - CHECK_EQ((status_t)OK, - mNetSession->connectUDPSession( - mUDPSession, fromAddr.c_str(), fromPort)); - - mConnected = true; - } - - ti->mT2 = arrivalTimeUs; - ti->mT3 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, ti, sizeof(*ti))); - } else { - if (ti->mT1 != mPendingT1) { - break; - } - - cancelTimeout(); - mPendingT1 = 0; - - ti->mT4 = arrivalTimeUs; - - // One way delay for a packet to travel from client - // to server or back (assumed to be the same either way). - int64_t delay = - (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; - - // Offset between the client clock (T1, T4) and the - // server clock (T2, T3) timestamps. - int64_t offset = - (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; - - mSumOffsets += offset; - ++mCount; - - printf("delay = %lld us,\toffset %lld us\n", - delay, - offset); - fflush(stdout); - - postSendPacket(1000000ll / 30); - } - break; - } - - default: - TRESPASS(); - } - - break; - } - - default: - TRESPASS(); - } -} - -void TestHandler::postSendPacket(int64_t delayUs) { - (new AMessage(kWhatSendPacket, id()))->post(delayUs); -} - -void TestHandler::postTimeout() { - sp msg = new AMessage(kWhatTimedOut, id()); - msg->setInt32("generation", mTimeoutGeneration); - msg->post(kTimeoutDelayUs); -} - -void TestHandler::cancelTimeout() { - ++mTimeoutGeneration; -} - } // namespace android static void usage(const char *me) { @@ -379,7 +100,7 @@ int main(int argc, char **argv) { sp looper = new ALooper; - sp handler = new TestHandler(netSession); + sp handler = new TimeSyncer(netSession, NULL /* notify */); looper->registerHandler(handler); if (localPort >= 0) { diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 3f4216a..0b18484 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -321,7 +321,10 @@ int main(int argc, char **argv) { sp looper = new ALooper; sp sink = new WifiDisplaySink( - session, surface->getIGraphicBufferProducer()); + 0 /* flags */, + session, + surface->getIGraphicBufferProducer()); + looper->registerHandler(sink); if (connectToPort >= 0) { -- cgit v1.1 From 99617adda9bc46c43f511f0940bc735c73de61de Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 12 Mar 2013 18:42:23 -0700 Subject: remove uses of Surface in favor or IGraphicBufferProducer Change-Id: I13d7a9553aa335bca790a3a59d389d7533c83d57 --- media/libmedia/IMediaRecorder.cpp | 6 +++--- media/libmedia/mediarecorder.cpp | 2 +- media/libmediaplayerservice/MediaRecorderClient.cpp | 2 +- media/libmediaplayerservice/MediaRecorderClient.h | 2 +- media/libmediaplayerservice/StagefrightRecorder.cpp | 2 +- media/libmediaplayerservice/StagefrightRecorder.h | 4 ++-- media/libstagefright/CameraSource.cpp | 6 +++--- media/libstagefright/CameraSourceTimeLapse.cpp | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) (limited to 'media') diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index c935d97..8e58162 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -87,12 +87,12 @@ public: return interface_cast(reply.readStrongBinder()); } - status_t setPreviewSurface(const sp& surface) + status_t setPreviewSurface(const sp& surface) { ALOGV("setPreviewSurface(%p)", surface.get()); Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); - Surface::writeToParcel(surface, &data); + data.writeStrongBinder(surface->asBinder()); remote()->transact(SET_PREVIEW_SURFACE, data, &reply); return reply.readInt32(); } @@ -443,7 +443,7 @@ status_t BnMediaRecorder::onTransact( case SET_PREVIEW_SURFACE: { ALOGV("SET_PREVIEW_SURFACE"); CHECK_INTERFACE(IMediaRecorder, data, reply); - sp surface = Surface::readFromParcel(data); + sp surface = interface_cast(data.readStrongBinder()); reply->writeInt32(setPreviewSurface(surface)); return NO_ERROR; } break; diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 3ac98cc..3710e46 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -49,7 +49,7 @@ status_t MediaRecorder::setCamera(const sp& camera, const sp& surface) +status_t MediaRecorder::setPreviewSurface(const sp& surface) { ALOGV("setPreviewSurface(%p)", surface.get()); if (mMediaRecorder == NULL) { diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index a52b238..a9820e0 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -81,7 +81,7 @@ status_t MediaRecorderClient::setCamera(const sp& camera, return mRecorder->setCamera(camera, proxy); } -status_t MediaRecorderClient::setPreviewSurface(const sp& surface) +status_t MediaRecorderClient::setPreviewSurface(const sp& surface) { ALOGV("setPreviewSurface"); Mutex::Autolock lock(mLock); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index bd0eaf1..a65ec9f 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -32,7 +32,7 @@ class MediaRecorderClient : public BnMediaRecorder public: virtual status_t setCamera(const sp& camera, const sp& proxy); - virtual status_t setPreviewSurface(const sp& surface); + virtual status_t setPreviewSurface(const sp& surface); virtual status_t setVideoSource(int vs); virtual status_t setAudioSource(int as); virtual status_t setOutputFormat(int of); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index f570856..c2c9985 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -224,7 +224,7 @@ status_t StagefrightRecorder::setCamera(const sp &camera, return OK; } -status_t StagefrightRecorder::setPreviewSurface(const sp &surface) { +status_t StagefrightRecorder::setPreviewSurface(const sp &surface) { ALOGV("setPreviewSurface: %p", surface.get()); mPreviewSurface = surface; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index fbe6fa6..c864207 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -51,7 +51,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t setVideoSize(int width, int height); virtual status_t setVideoFrameRate(int frames_per_second); virtual status_t setCamera(const sp& camera, const sp& proxy); - virtual status_t setPreviewSurface(const sp& surface); + virtual status_t setPreviewSurface(const sp& surface); virtual status_t setOutputFile(const char *path); virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setParameters(const String8& params); @@ -71,7 +71,7 @@ struct StagefrightRecorder : public MediaRecorderBase { private: sp mCamera; sp mCameraProxy; - sp mPreviewSurface; + sp mPreviewSurface; sp mListener; String16 mClientName; uid_t mClientUid; diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index f8557d0..5a26b06 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -140,7 +140,7 @@ CameraSource *CameraSource::CreateFromCamera( uid_t clientUid, Size videoSize, int32_t frameRate, - const sp& surface, + const sp& surface, bool storeMetaDataInVideoBuffers) { CameraSource *source = new CameraSource(camera, proxy, cameraId, @@ -157,7 +157,7 @@ CameraSource::CameraSource( uid_t clientUid, Size videoSize, int32_t frameRate, - const sp& surface, + const sp& surface, bool storeMetaDataInVideoBuffers) : mCameraFlags(0), mNumInputBuffers(0), @@ -536,7 +536,7 @@ status_t CameraSource::initWithCameraAccess( 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)); + CHECK_EQ((status_t)OK, mCamera->setPreviewTexture(mSurface)); } // By default, do not store metadata in video buffers diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 2ed2223..20214e8 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -40,7 +40,7 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera( uid_t clientUid, Size videoSize, int32_t videoFrameRate, - const sp& surface, + const sp& surface, int64_t timeBetweenFrameCaptureUs) { CameraSourceTimeLapse *source = new @@ -66,7 +66,7 @@ CameraSourceTimeLapse::CameraSourceTimeLapse( uid_t clientUid, Size videoSize, int32_t videoFrameRate, - const sp& surface, + const sp& surface, int64_t timeBetweenFrameCaptureUs) : CameraSource(camera, proxy, cameraId, clientName, clientUid, videoSize, videoFrameRate, surface, true), -- cgit v1.1 From 126568c7aeeb5570789e70a310477f44dbdbd885 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 12 Mar 2013 15:55:43 -0700 Subject: Attempt to recover from network stalls by dropping frames on the source side. Change-Id: I5f9eb4f5acb624a9e5bc0087801fb5a4a9ade35c --- .../wifi-display/ANetworkSession.cpp | 26 ++++++++++++++++++---- .../libstagefright/wifi-display/ANetworkSession.h | 1 + media/libstagefright/wifi-display/MediaSender.cpp | 16 +++++++++++++ media/libstagefright/wifi-display/MediaSender.h | 2 ++ .../libstagefright/wifi-display/rtp/RTPSender.cpp | 19 ++++++++++++++++ media/libstagefright/wifi-display/rtp/RTPSender.h | 2 ++ .../wifi-display/sink/WifiDisplaySink.cpp | 2 +- .../wifi-display/source/Converter.cpp | 18 +++++++++++++++ .../libstagefright/wifi-display/source/Converter.h | 5 +++++ .../wifi-display/source/PlaybackSession.cpp | 10 +++++++++ .../wifi-display/source/WifiDisplaySource.cpp | 5 +++++ 11 files changed, 101 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 465f4c4..23bb04e 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -104,6 +104,8 @@ private: AString mInBuffer; + int64_t mLastStallReportUs; + void notifyError(bool send, status_t err, const char *detail); void notify(NotificationReason reason); @@ -137,7 +139,8 @@ ANetworkSession::Session::Session( mSocket(s), mNotify(notify), mSawReceiveFailure(false), - mSawSendFailure(false) { + mSawSendFailure(false), + mLastStallReportUs(-1ll) { if (mState == CONNECTED) { struct sockaddr_in localAddr; socklen_t localAddrLen = sizeof(localAddr); @@ -508,11 +511,26 @@ status_t ANetworkSession::Session::writeMore() { mSawSendFailure = true; } -#if 0 +#if 1 int numBytesQueued; int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); - if (res == 0 && numBytesQueued > 102400) { - ALOGI("numBytesQueued = %d", numBytesQueued); + if (res == 0 && numBytesQueued > 50 * 1024) { + if (numBytesQueued > 409600) { + ALOGW("!!! numBytesQueued = %d", numBytesQueued); + } + + int64_t nowUs = ALooper::GetNowUs(); + + if (mLastStallReportUs < 0ll + || nowUs > mLastStallReportUs + 500000ll) { + sp msg = mNotify->dup(); + msg->setInt32("sessionID", mSessionID); + msg->setInt32("reason", kWhatNetworkStall); + msg->setSize("numBytesQueued", numBytesQueued); + msg->post(); + + mLastStallReportUs = nowUs; + } } #endif diff --git a/media/libstagefright/wifi-display/ANetworkSession.h b/media/libstagefright/wifi-display/ANetworkSession.h index c1acdcc..0d7cbd6 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.h +++ b/media/libstagefright/wifi-display/ANetworkSession.h @@ -83,6 +83,7 @@ struct ANetworkSession : public RefBase { kWhatData, kWhatDatagram, kWhatBinaryData, + kWhatNetworkStall, }; protected: diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 105c642..e1e957a 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -325,6 +325,15 @@ void MediaSender::onSenderNotify(const sp &msg) { break; } + case kWhatNetworkStall: + { + size_t numBytesQueued; + CHECK(msg->findSize("numBytesQueued", &numBytesQueued)); + + notifyNetworkStall(numBytesQueued); + break; + } + default: TRESPASS(); } @@ -344,6 +353,13 @@ void MediaSender::notifyError(status_t err) { notify->post(); } +void MediaSender::notifyNetworkStall(size_t numBytesQueued) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatNetworkStall); + notify->setSize("numBytesQueued", numBytesQueued); + notify->post(); +} + status_t MediaSender::packetizeAccessUnit( size_t trackIndex, sp accessUnit, diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h index 9a50f9a..447abf7 100644 --- a/media/libstagefright/wifi-display/MediaSender.h +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -42,6 +42,7 @@ struct MediaSender : public AHandler { enum { kWhatInitDone, kWhatError, + kWhatNetworkStall, }; MediaSender( @@ -113,6 +114,7 @@ private: void notifyInitDone(status_t err); void notifyError(status_t err); + void notifyNetworkStall(size_t numBytesQueued); status_t packetizeAccessUnit( size_t trackIndex, diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index b60853d..8cd712d 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -530,6 +530,18 @@ void RTPSender::onNetNotify(bool isRTP, const sp &msg) { } break; } + + case ANetworkSession::kWhatNetworkStall: + { + size_t numBytesQueued; + CHECK(msg->findSize("numBytesQueued", &numBytesQueued)); + + notifyNetworkStall(numBytesQueued); + break; + } + + default: + TRESPASS(); } } @@ -699,5 +711,12 @@ void RTPSender::notifyError(status_t err) { notify->post(); } +void RTPSender::notifyNetworkStall(size_t numBytesQueued) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatNetworkStall); + notify->setSize("numBytesQueued", numBytesQueued); + notify->post(); +} + } // namespace android diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 2b683a4..83c6223 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -36,6 +36,7 @@ struct RTPSender : public RTPBase, public AHandler { enum { kWhatInitDone, kWhatError, + kWhatNetworkStall, }; RTPSender( const sp &netSession, @@ -103,6 +104,7 @@ private: void notifyInitDone(status_t err); void notifyError(status_t err); + void notifyNetworkStall(size_t numBytesQueued); DISALLOW_EVIL_CONSTRUCTORS(RTPSender); }; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 0d2e347..d635c3a 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -319,7 +319,7 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { CHECK(mTimeOffsetValid); - int64_t latencyUs = 300000ll; // 300ms by default + int64_t latencyUs = 200000ll; // 200ms by default char val[PROPERTY_VALUE_MAX]; if (property_get("media.wfd-sink.latency", val, NULL)) { diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 2861aa9..bb8c387 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -55,6 +55,7 @@ Converter::Converter( ,mInSilentMode(false) #endif ,mPrevVideoBitrate(-1) + ,mNumFramesToDrop(0) { AString mime; CHECK(mInputFormat->findString("mime", &mime)); @@ -327,6 +328,13 @@ void Converter::onMessageReceived(const sp &msg) { sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); + if (mIsVideo && mNumFramesToDrop) { + --mNumFramesToDrop; + ALOGI("dropping frame."); + ReleaseMediaBufferReference(accessUnit); + break; + } + #if 0 void *mbuf; if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) @@ -422,6 +430,12 @@ void Converter::onMessageReceived(const sp &msg) { break; } + case kWhatDropAFrame: + { + ++mNumFramesToDrop; + break; + } + default: TRESPASS(); } @@ -690,4 +704,8 @@ void Converter::requestIDRFrame() { (new AMessage(kWhatRequestIDRFrame, id()))->post(); } +void Converter::dropAFrame() { + (new AMessage(kWhatDropAFrame, id()))->post(); +} + } // namespace android diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 57802bd..a418f69 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -51,6 +51,8 @@ struct Converter : public AHandler { void requestIDRFrame(); + void dropAFrame(); + enum { kWhatAccessUnit, kWhatEOS, @@ -63,6 +65,7 @@ struct Converter : public AHandler { kWhatShutdown, kWhatMediaPullerNotify, kWhatEncoderActivity, + kWhatDropAFrame, }; void shutdownAsync(); @@ -102,6 +105,8 @@ private: int32_t mPrevVideoBitrate; + int32_t mNumFramesToDrop; + status_t initEncoder(); void releaseEncoder(); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index ea195b3..94cb2a4 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -515,6 +515,16 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } else if (what == MediaSender::kWhatError) { notifySessionDead(); + } else if (what == MediaSender::kWhatNetworkStall) { + size_t numBytesQueued; + CHECK(msg->findSize("numBytesQueued", &numBytesQueued)); + + if (mVideoTrackIndex >= 0) { + const sp &videoTrack = + mTracks.valueFor(mVideoTrackIndex); + + videoTrack->converter()->dropAFrame(); + } } else { TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index de66bde..c8798c6 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -272,6 +272,11 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { break; } + case ANetworkSession::kWhatNetworkStall: + { + break; + } + default: TRESPASS(); } -- cgit v1.1 From 441a78d5e224e0d67f9b52fa9adc795c6944159b Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Fri, 8 Feb 2013 10:18:35 -0800 Subject: Implementing MediaDrm APIs Change-Id: I9ff8eeb7d0c383b5c0c68cd54eb54ce7d2d22fe6 --- media/libmedia/Android.mk | 1 + media/libmedia/IDrm.cpp | 551 +++++++++++++++++++++ media/libmedia/IMediaPlayerService.cpp | 15 + media/libmediaplayerservice/Android.mk | 2 + media/libmediaplayerservice/Drm.cpp | 423 ++++++++++++++++ media/libmediaplayerservice/Drm.h | 100 ++++ media/libmediaplayerservice/MediaPlayerService.cpp | 5 + media/libmediaplayerservice/MediaPlayerService.h | 1 + media/libmediaplayerservice/SharedLibrary.cpp | 49 ++ media/libmediaplayerservice/SharedLibrary.h | 39 ++ 10 files changed, 1186 insertions(+) create mode 100644 media/libmedia/IDrm.cpp create mode 100644 media/libmediaplayerservice/Drm.cpp create mode 100644 media/libmediaplayerservice/Drm.h create mode 100644 media/libmediaplayerservice/SharedLibrary.cpp create mode 100644 media/libmediaplayerservice/SharedLibrary.h (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 6b48991..1ada9c3 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -19,6 +19,7 @@ LOCAL_SRC_FILES:= \ IAudioTrack.cpp \ IAudioRecord.cpp \ ICrypto.cpp \ + IDrm.cpp \ IHDCP.cpp \ AudioRecord.cpp \ AudioSystem.cpp \ diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp new file mode 100644 index 0000000..3b13ec6 --- /dev/null +++ b/media/libmedia/IDrm.cpp @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2013 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 "IDrm" +#include + +#include +#include +#include +#include +#include + +namespace android { + +enum { + INIT_CHECK = IBinder::FIRST_CALL_TRANSACTION, + IS_CRYPTO_SUPPORTED, + CREATE_PLUGIN, + DESTROY_PLUGIN, + OPEN_SESSION, + CLOSE_SESSION, + GET_LICENSE_REQUEST, + PROVIDE_LICENSE_RESPONSE, + REMOVE_LICENSE, + QUERY_LICENSE_STATUS, + GET_PROVISION_REQUEST, + PROVIDE_PROVISION_RESPONSE, + GET_SECURE_STOPS, + RELEASE_SECURE_STOPS, + GET_PROPERTY_STRING, + GET_PROPERTY_BYTE_ARRAY, + SET_PROPERTY_STRING, + SET_PROPERTY_BYTE_ARRAY +}; + +struct BpDrm : public BpInterface { + BpDrm(const sp &impl) + : BpInterface(impl) { + } + + virtual status_t initCheck() const { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + remote()->transact(INIT_CHECK, data, &reply); + + return reply.readInt32(); + } + + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + data.write(uuid, 16); + remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); + + return reply.readInt32() != 0; + } + + virtual status_t createPlugin(const uint8_t uuid[16]) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + data.write(uuid, 16); + + remote()->transact(CREATE_PLUGIN, data, &reply); + + return reply.readInt32(); + } + + virtual status_t destroyPlugin() { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + remote()->transact(DESTROY_PLUGIN, data, &reply); + + return reply.readInt32(); + } + + virtual status_t openSession(Vector &sessionId) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + remote()->transact(OPEN_SESSION, data, &reply); + uint32_t size = reply.readInt32(); + sessionId.insertAt((size_t)0, size); + reply.read(sessionId.editArray(), size); + + return reply.readInt32(); + } + + virtual status_t closeSession(Vector const &sessionId) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeInt32(sessionId.size()); + data.write(sessionId.array(), sessionId.size()); + remote()->transact(CLOSE_SESSION, data, &reply); + + return reply.readInt32(); + } + + virtual status_t + getLicenseRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeInt32(sessionId.size()); + data.write(sessionId.array(), sessionId.size()); + + data.writeInt32(initData.size()); + data.write(initData.array(), initData.size()); + + data.writeString8(mimeType); + data.writeInt32((uint32_t)licenseType); + + data.writeInt32(optionalParameters.size()); + for (size_t i = 0; i < optionalParameters.size(); ++i) { + data.writeString8(optionalParameters.keyAt(i)); + data.writeString8(optionalParameters.valueAt(i)); + } + remote()->transact(GET_LICENSE_REQUEST, data, &reply); + + uint32_t len = reply.readInt32(); + request.insertAt((size_t)0, len); + reply.read(request.editArray(), len); + defaultUrl = reply.readString8(); + + return reply.readInt32(); + } + + virtual status_t provideLicenseResponse(Vector const &sessionId, + Vector const &response) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeInt32(sessionId.size()); + data.write(sessionId.array(), sessionId.size()); + data.writeInt32(response.size()); + data.write(response.array(), response.size()); + remote()->transact(PROVIDE_LICENSE_RESPONSE, data, &reply); + + return reply.readInt32(); + } + + virtual status_t removeLicense(Vector const &sessionId) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeInt32(sessionId.size()); + data.write(sessionId.array(), sessionId.size()); + remote()->transact(REMOVE_LICENSE, data, &reply); + + return reply.readInt32(); + } + + virtual status_t queryLicenseStatus(Vector const &sessionId, + KeyedVector &infoMap) const { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeInt32(sessionId.size()); + data.write(sessionId.array(), sessionId.size()); + + remote()->transact(QUERY_LICENSE_STATUS, data, &reply); + + infoMap.clear(); + size_t count = reply.readInt32(); + for (size_t i = 0; i < count; i++) { + String8 key = reply.readString8(); + String8 value = reply.readString8(); + infoMap.add(key, value); + } + return reply.readInt32(); + } + + virtual status_t getProvisionRequest(Vector &request, + String8 &defaultUrl) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + remote()->transact(GET_PROVISION_REQUEST, data, &reply); + + uint32_t len = reply.readInt32(); + request.insertAt((size_t)0, len); + reply.read(request.editArray(), len); + defaultUrl = reply.readString8(); + + return reply.readInt32(); + } + + virtual status_t provideProvisionResponse(Vector const &response) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeInt32(response.size()); + data.write(response.array(), response.size()); + remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply); + + return reply.readInt32(); + } + + virtual status_t getSecureStops(List > &secureStops) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + remote()->transact(GET_SECURE_STOPS, data, &reply); + + secureStops.clear(); + uint32_t count = reply.readInt32(); + for (size_t i = 0; i < count; i++) { + Vector secureStop; + uint32_t len = reply.readInt32(); + secureStop.insertAt((size_t)0, len); + reply.read(secureStop.editArray(), len); + secureStops.push_back(secureStop); + } + return reply.readInt32(); + } + + virtual status_t releaseSecureStops(Vector const &ssRelease) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeInt32(ssRelease.size()); + data.write(ssRelease.array(), ssRelease.size()); + remote()->transact(RELEASE_SECURE_STOPS, data, &reply); + + return reply.readInt32(); + } + + virtual status_t getPropertyString(String8 const &name, String8 &value) const { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeString8(name); + remote()->transact(GET_PROPERTY_STRING, data, &reply); + + value = reply.readString8(); + return reply.readInt32(); + } + + virtual status_t getPropertyByteArray(String8 const &name, Vector &value) const { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeString8(name); + remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply); + + uint32_t len = reply.readInt32(); + value.insertAt((size_t)0, len); + reply.read(value.editArray(), len); + + return reply.readInt32(); + } + + virtual status_t setPropertyString(String8 const &name, String8 const &value) const { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeString8(name); + data.writeString8(value); + remote()->transact(SET_PROPERTY_STRING, data, &reply); + + return reply.readInt32(); + } + + virtual status_t setPropertyByteArray(String8 const &name, + Vector const &value) const { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + data.writeString8(name); + data.writeInt32(value.size()); + data.write(value.array(), value.size()); + remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply); + + return reply.readInt32(); + } + + +private: + DISALLOW_EVIL_CONSTRUCTORS(BpDrm); +}; + +IMPLEMENT_META_INTERFACE(Drm, "android.drm.IDrm"); + +//////////////////////////////////////////////////////////////////////////////// + +status_t BnDrm::onTransact( + uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { + switch (code) { + case INIT_CHECK: + { + CHECK_INTERFACE(IDrm, data, reply); + reply->writeInt32(initCheck()); + return OK; + } + + case IS_CRYPTO_SUPPORTED: + { + CHECK_INTERFACE(IDrm, data, reply); + uint8_t uuid[16]; + data.read(uuid, sizeof(uuid)); + reply->writeInt32(isCryptoSchemeSupported(uuid)); + return OK; + } + + case CREATE_PLUGIN: + { + CHECK_INTERFACE(IDrm, data, reply); + uint8_t uuid[16]; + data.read(uuid, sizeof(uuid)); + reply->writeInt32(createPlugin(uuid)); + return OK; + } + + case DESTROY_PLUGIN: + { + CHECK_INTERFACE(IDrm, data, reply); + reply->writeInt32(destroyPlugin()); + return OK; + } + + case OPEN_SESSION: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + status_t result = openSession(sessionId); + reply->writeInt32(sessionId.size()); + reply->write(sessionId.array(), sessionId.size()); + reply->writeInt32(result); + return OK; + } + + case CLOSE_SESSION: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + reply->writeInt32(closeSession(sessionId)); + return OK; + } + + case GET_LICENSE_REQUEST: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + + Vector initData; + size = data.readInt32(); + initData.insertAt((size_t)0, size); + data.read(initData.editArray(), size); + + String8 mimeType = data.readString8(); + DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)data.readInt32(); + + KeyedVector optionalParameters; + uint32_t count = data.readInt32(); + for (size_t i = 0; i < count; ++i) { + String8 key, value; + key = data.readString8(); + value = data.readString8(); + optionalParameters.add(key, value); + } + + Vector request; + String8 defaultUrl; + + status_t result = getLicenseRequest(sessionId, initData, + mimeType, licenseType, + optionalParameters, + request, defaultUrl); + reply->writeInt32(request.size()); + reply->write(request.array(), request.size()); + reply->writeString8(defaultUrl); + reply->writeInt32(result); + return OK; + } + + case PROVIDE_LICENSE_RESPONSE: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + Vector response; + size = data.readInt32(); + response.insertAt((size_t)0, size); + data.read(response.editArray(), size); + + reply->writeInt32(provideLicenseResponse(sessionId, response)); + return OK; + } + + case REMOVE_LICENSE: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + reply->writeInt32(removeLicense(sessionId)); + return OK; + } + + case QUERY_LICENSE_STATUS: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + uint32_t size = data.readInt32(); + sessionId.insertAt((size_t)0, size); + data.read(sessionId.editArray(), size); + KeyedVector infoMap; + + status_t result = queryLicenseStatus(sessionId, infoMap); + + size_t count = infoMap.size(); + reply->writeInt32(count); + for (size_t i = 0; i < count; ++i) { + reply->writeString8(infoMap.keyAt(i)); + reply->writeString8(infoMap.valueAt(i)); + } + reply->writeInt32(result); + return OK; + } + + case GET_PROVISION_REQUEST: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector request; + String8 defaultUrl; + status_t result = getProvisionRequest(request, defaultUrl); + reply->writeInt32(request.size()); + reply->write(request.array(), request.size()); + reply->writeString8(defaultUrl); + reply->writeInt32(result); + return OK; + } + + case PROVIDE_PROVISION_RESPONSE: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector response; + uint32_t size = data.readInt32(); + response.insertAt((size_t)0, size); + data.read(response.editArray(), size); + reply->writeInt32(provideProvisionResponse(response)); + + return OK; + } + + case GET_SECURE_STOPS: + { + CHECK_INTERFACE(IDrm, data, reply); + List > secureStops; + status_t result = getSecureStops(secureStops); + size_t count = secureStops.size(); + reply->writeInt32(count); + List >::iterator iter = secureStops.begin(); + while(iter != secureStops.end()) { + size_t size = iter->size(); + reply->writeInt32(size); + reply->write(iter->array(), iter->size()); + } + reply->writeInt32(result); + return OK; + } + + case RELEASE_SECURE_STOPS: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector ssRelease; + uint32_t size = data.readInt32(); + ssRelease.insertAt((size_t)0, size); + data.read(ssRelease.editArray(), size); + reply->writeInt32(releaseSecureStops(ssRelease)); + return OK; + } + + case GET_PROPERTY_STRING: + { + CHECK_INTERFACE(IDrm, data, reply); + String8 name = data.readString8(); + String8 value; + status_t result = getPropertyString(name, value); + reply->writeString8(value); + reply->writeInt32(result); + return OK; + } + + case GET_PROPERTY_BYTE_ARRAY: + { + CHECK_INTERFACE(IDrm, data, reply); + String8 name = data.readString8(); + Vector value; + status_t result = getPropertyByteArray(name, value); + reply->writeInt32(value.size()); + reply->write(value.array(), value.size()); + reply->writeInt32(result); + return OK; + } + + case SET_PROPERTY_STRING: + { + CHECK_INTERFACE(IDrm, data, reply); + String8 name = data.readString8(); + String8 value = data.readString8(); + reply->writeInt32(setPropertyString(name, value)); + return OK; + } + + case SET_PROPERTY_BYTE_ARRAY: + { + CHECK_INTERFACE(IDrm, data, reply); + String8 name = data.readString8(); + Vector value; + size_t count = data.readInt32(); + value.insertAt((size_t)0, count); + data.read(value.editArray(), count); + reply->writeInt32(setPropertyByteArray(name, value)); + return OK; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +} // namespace android + diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index a95f4c9..e1ce5a9 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ enum { CREATE_METADATA_RETRIEVER, GET_OMX, MAKE_CRYPTO, + MAKE_DRM, MAKE_HDCP, ADD_BATTERY_DATA, PULL_BATTERY_DATA, @@ -123,6 +125,13 @@ public: return interface_cast(reply.readStrongBinder()); } + virtual sp makeDrm() { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + remote()->transact(MAKE_DRM, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + virtual sp makeHDCP(bool createEncryptionModule) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -225,6 +234,12 @@ status_t BnMediaPlayerService::onTransact( reply->writeStrongBinder(crypto->asBinder()); return NO_ERROR; } break; + case MAKE_DRM: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + sp drm = makeDrm(); + reply->writeStrongBinder(drm->asBinder()); + return NO_ERROR; + } break; case MAKE_HDCP: { CHECK_INTERFACE(IMediaPlayerService, data, reply); bool createEncryptionModule = data.readInt32(); diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 48f48e4..2a6f3c7 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -9,6 +9,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ActivityManager.cpp \ Crypto.cpp \ + Drm.cpp \ HDCP.cpp \ MediaPlayerFactory.cpp \ MediaPlayerService.cpp \ @@ -17,6 +18,7 @@ LOCAL_SRC_FILES:= \ MidiFile.cpp \ MidiMetadataRetriever.cpp \ RemoteDisplay.cpp \ + SharedLibrary.cpp \ StagefrightPlayer.cpp \ StagefrightRecorder.cpp \ TestPlayerStub.cpp \ diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp new file mode 100644 index 0000000..6ac7530 --- /dev/null +++ b/media/libmediaplayerservice/Drm.cpp @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2013 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 "Drm" +#include + +#include +#include + +#include "Drm.h" + +#include +#include +#include +#include +#include + +namespace android { + +KeyedVector, String8> Drm::mUUIDToLibraryPathMap; +KeyedVector > Drm::mLibraryPathToOpenLibraryMap; +Mutex Drm::mMapLock; + +static bool operator<(const Vector &lhs, const Vector &rhs) { + if (lhs.size() < rhs.size()) { + return true; + } else if (lhs.size() > rhs.size()) { + return false; + } + + return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0; +} + +Drm::Drm() + : mInitCheck(NO_INIT), + mFactory(NULL), + mPlugin(NULL) { +} + +Drm::~Drm() { + delete mPlugin; + mPlugin = NULL; + closeFactory(); +} + +void Drm::closeFactory() { + delete mFactory; + mFactory = NULL; + mLibrary.clear(); +} + +status_t Drm::initCheck() const { + return mInitCheck; +} + + +/* + * Search the plugins directory for a plugin that supports the scheme + * specified by uuid + * + * If found: + * mLibrary holds a strong pointer to the dlopen'd library + * mFactory is set to the library's factory method + * mInitCheck is set to OK + * + * If not found: + * mLibrary is cleared and mFactory are set to NULL + * mInitCheck is set to an error (!OK) + */ +void Drm::findFactoryForScheme(const uint8_t uuid[16]) { + + closeFactory(); + + // lock static maps + Mutex::Autolock autoLock(mMapLock); + + // first check cache + Vector uuidVector; + uuidVector.appendArray(uuid, sizeof(uuid)); + ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector); + if (index >= 0) { + if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) { + mInitCheck = OK; + return; + } else { + ALOGE("Failed to load from cached library path!"); + mInitCheck = ERROR_UNSUPPORTED; + return; + } + } + + // no luck, have to search + String8 dirPath("/vendor/lib/mediadrm"); + DIR* pDir = opendir(dirPath.string()); + + if (pDir == NULL) { + mInitCheck = ERROR_UNSUPPORTED; + ALOGE("Failed to open plugin directory %s", dirPath.string()); + return; + } + + + struct dirent* pEntry; + while ((pEntry = readdir(pDir))) { + + String8 pluginPath = dirPath + "/" + pEntry->d_name; + + if (pluginPath.getPathExtension() == ".so") { + + if (loadLibraryForScheme(pluginPath, uuid)) { + mUUIDToLibraryPathMap.add(uuidVector, pluginPath); + mInitCheck = OK; + closedir(pDir); + return; + } + } + } + + closedir(pDir); + + ALOGE("Failed to find drm plugin"); + mInitCheck = ERROR_UNSUPPORTED; +} + +bool Drm::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) { + + // get strong pointer to open shared library + ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path); + if (index >= 0) { + mLibrary = mLibraryPathToOpenLibraryMap[index].promote(); + } else { + index = mLibraryPathToOpenLibraryMap.add(path, NULL); + } + + if (!mLibrary.get()) { + mLibrary = new SharedLibrary(path); + if (!*mLibrary) { + return false; + } + + mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary); + } + + typedef DrmFactory *(*CreateDrmFactoryFunc)(); + + CreateDrmFactoryFunc createDrmFactory = + (CreateDrmFactoryFunc)mLibrary->lookup("createDrmFactory"); + + if (createDrmFactory == NULL || + (mFactory = createDrmFactory()) == NULL || + !mFactory->isCryptoSchemeSupported(uuid)) { + closeFactory(); + return false; + } + return true; +} + +bool Drm::isCryptoSchemeSupported(const uint8_t uuid[16]) { + Mutex::Autolock autoLock(mLock); + + if (mFactory && mFactory->isCryptoSchemeSupported(uuid)) { + return true; + } + + findFactoryForScheme(uuid); + return (mInitCheck == OK); +} + +status_t Drm::createPlugin(const uint8_t uuid[16]) { + Mutex::Autolock autoLock(mLock); + + if (mPlugin != NULL) { + return -EINVAL; + } + + if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) { + findFactoryForScheme(uuid); + } + + if (mInitCheck != OK) { + return mInitCheck; + } + + return mFactory->createDrmPlugin(uuid, &mPlugin); +} + +status_t Drm::destroyPlugin() { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + delete mPlugin; + mPlugin = NULL; + + return OK; +} + +status_t Drm::openSession(Vector &sessionId) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->openSession(sessionId); +} + +status_t Drm::closeSession(Vector const &sessionId) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->closeSession(sessionId); +} + +status_t Drm::getLicenseRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->getLicenseRequest(sessionId, initData, mimeType, licenseType, + optionalParameters, request, defaultUrl); +} + +status_t Drm::provideLicenseResponse(Vector const &sessionId, + Vector const &response) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->provideLicenseResponse(sessionId, response); +} + +status_t Drm::removeLicense(Vector const &sessionId) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->removeLicense(sessionId); +} + +status_t Drm::queryLicenseStatus(Vector const &sessionId, + KeyedVector &infoMap) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->queryLicenseStatus(sessionId, infoMap); +} + +status_t Drm::getProvisionRequest(Vector &request, String8 &defaultUrl) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->getProvisionRequest(request, defaultUrl); +} + +status_t Drm::provideProvisionResponse(Vector const &response) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->provideProvisionResponse(response); +} + + +status_t Drm::getSecureStops(List > &secureStops) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->getSecureStops(secureStops); +} + +status_t Drm::releaseSecureStops(Vector const &ssRelease) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->releaseSecureStops(ssRelease); +} + +status_t Drm::getPropertyString(String8 const &name, String8 &value ) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->getPropertyString(name, value); +} + +status_t Drm::getPropertyByteArray(String8 const &name, Vector &value ) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->getPropertyByteArray(name, value); +} + +status_t Drm::setPropertyString(String8 const &name, String8 const &value ) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->setPropertyString(name, value); +} + +status_t Drm::setPropertyByteArray(String8 const &name, + Vector const &value ) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->setPropertyByteArray(name, value); +} + +} // namespace android diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h new file mode 100644 index 0000000..1b10958 --- /dev/null +++ b/media/libmediaplayerservice/Drm.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013 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 DRM_H_ + +#define DRM_H_ + +#include "SharedLibrary.h" + +#include +#include + +namespace android { + +struct DrmFactory; +struct DrmPlugin; + +struct Drm : public BnDrm { + Drm(); + virtual ~Drm(); + + virtual status_t initCheck() const; + + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); + + virtual status_t createPlugin(const uint8_t uuid[16]); + + virtual status_t destroyPlugin(); + + virtual status_t openSession(Vector &sessionId); + + virtual status_t closeSession(Vector const &sessionId); + + virtual status_t + getLicenseRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::LicenseType licenseType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl); + + virtual status_t provideLicenseResponse(Vector const &sessionId, + Vector const &response); + + virtual status_t removeLicense(Vector const &sessionId); + + virtual status_t queryLicenseStatus(Vector const &sessionId, + KeyedVector &infoMap) const; + + virtual status_t getProvisionRequest(Vector &request, + String8 &defaulUrl); + + virtual status_t provideProvisionResponse(Vector const &response); + + virtual status_t getSecureStops(List > &secureStops); + + virtual status_t releaseSecureStops(Vector const &ssRelease); + + virtual status_t getPropertyString(String8 const &name, String8 &value ) const; + virtual status_t getPropertyByteArray(String8 const &name, + Vector &value ) const; + virtual status_t setPropertyString(String8 const &name, String8 const &value ) const; + virtual status_t setPropertyByteArray(String8 const &name, + Vector const &value ) const; + +private: + mutable Mutex mLock; + + status_t mInitCheck; + sp mLibrary; + DrmFactory *mFactory; + DrmPlugin *mPlugin; + + static KeyedVector, String8> mUUIDToLibraryPathMap; + static KeyedVector > mLibraryPathToOpenLibraryMap; + static Mutex mMapLock; + + void findFactoryForScheme(const uint8_t uuid[16]); + bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]); + void closeFactory(); + + + DISALLOW_EVIL_CONSTRUCTORS(Drm); +}; + +} // namespace android + +#endif // CRYPTO_H_ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 16f1317..ec6ace1 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -72,6 +72,7 @@ #include #include "Crypto.h" +#include "Drm.h" #include "HDCP.h" #include "RemoteDisplay.h" @@ -285,6 +286,10 @@ sp MediaPlayerService::makeCrypto() { return new Crypto; } +sp MediaPlayerService::makeDrm() { + return new Drm; +} + sp MediaPlayerService::makeHDCP(bool createEncryptionModule) { return new HDCP(createEncryptionModule); } diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 2d2a09d..82dc29b 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -249,6 +249,7 @@ public: virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); virtual sp getOMX(); virtual sp makeCrypto(); + virtual sp makeDrm(); virtual sp makeHDCP(bool createEncryptionModule); virtual sp listenForRemoteDisplay(const sp& client, diff --git a/media/libmediaplayerservice/SharedLibrary.cpp b/media/libmediaplayerservice/SharedLibrary.cpp new file mode 100644 index 0000000..178e15d --- /dev/null +++ b/media/libmediaplayerservice/SharedLibrary.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 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 "Drm" +#include +#include + +#include + +#include "SharedLibrary.h" + +namespace android { + + SharedLibrary::SharedLibrary(const String8 &path) { + mLibHandle = dlopen(path.string(), RTLD_NOW); + } + + SharedLibrary::~SharedLibrary() { + if (mLibHandle != NULL) { + dlclose(mLibHandle); + mLibHandle = NULL; + } + } + + bool SharedLibrary::operator!() const { + return mLibHandle == NULL; + } + + void *SharedLibrary::lookup(const char *symbol) const { + if (!mLibHandle) { + return NULL; + } + return dlsym(mLibHandle, symbol); + } +}; diff --git a/media/libmediaplayerservice/SharedLibrary.h b/media/libmediaplayerservice/SharedLibrary.h new file mode 100644 index 0000000..5353642 --- /dev/null +++ b/media/libmediaplayerservice/SharedLibrary.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 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 SHARED_LIBRARY_H_ +#define SHARED_LIBRARY_H_ + +#include +#include +#include + +namespace android { + class SharedLibrary : public RefBase { + public: + SharedLibrary(const String8 &path); + ~SharedLibrary(); + + bool operator!() const; + void *lookup(const char *symbol) const; + + private: + void *mLibHandle; + DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary); + }; +}; + +#endif // SHARED_LIBRARY_H_ -- cgit v1.1 From c66f8788871b2ae2d240e0e16dd3bdc5c277ce31 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 13 Mar 2013 10:22:46 -0700 Subject: Adds audio support to DirectRenderer. Change-Id: Ibf4df90aca29d638215e2da9b39e78bf3a2c4d08 --- .../wifi-display/sink/DirectRenderer.cpp | 607 ++++++++++++++++----- .../wifi-display/sink/DirectRenderer.h | 42 +- .../wifi-display/sink/WifiDisplaySink.cpp | 11 +- 3 files changed, 491 insertions(+), 169 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index 5efcd17..12338e9 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -34,159 +35,208 @@ namespace android { -DirectRenderer::DirectRenderer( - const sp &bufferProducer) - : mSurfaceTex(bufferProducer), - mVideoDecoderNotificationPending(false), - mRenderPending(false), - mTimeOffsetUs(0ll), - mLatencySum(0ll), - mLatencyCount(0), - mNumFramesLate(0), - mNumFrames(0) { -} +/* + Drives the decoding process using a MediaCodec instance. Input buffers + queued by calls to "queueInputBuffer" are fed to the decoder as soon + as the decoder is ready for them, the client is notified about output + buffers as the decoder spits them out. +*/ +struct DirectRenderer::DecoderContext : public AHandler { + enum { + kWhatOutputBufferReady, + }; + DecoderContext(const sp ¬ify); -DirectRenderer::~DirectRenderer() { - if (mVideoDecoder != NULL) { - mVideoDecoder->release(); - mVideoDecoder.clear(); + status_t init( + const sp &format, + const sp &surfaceTex); - mVideoDecoderLooper->stop(); - mVideoDecoderLooper.clear(); - } -} + void queueInputBuffer(const sp &accessUnit); -void DirectRenderer::setTimeOffset(int64_t offset) { - mTimeOffsetUs = offset; -} + status_t renderOutputBufferAndRelease(size_t index); + status_t releaseOutputBuffer(size_t index); -int64_t DirectRenderer::getAvgLatenessUs() { - if (mLatencyCount == 0) { - return 0ll; - } +protected: + virtual ~DecoderContext(); - int64_t avgLatencyUs = mLatencySum / mLatencyCount; + virtual void onMessageReceived(const sp &msg); - mLatencySum = 0ll; - mLatencyCount = 0; +private: + enum { + kWhatDecoderNotify, + }; - if (mNumFrames > 0) { - ALOGI("%d / %d frames late", mNumFramesLate, mNumFrames); - mNumFramesLate = 0; - mNumFrames = 0; - } + sp mNotify; + sp mDecoderLooper; + sp mDecoder; + Vector > mDecoderInputBuffers; + Vector > mDecoderOutputBuffers; + List mDecoderInputBuffersAvailable; + bool mDecoderNotificationPending; - return avgLatencyUs; -} + List > mAccessUnits; -void DirectRenderer::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatVideoDecoderNotify: - { - onVideoDecoderNotify(); - break; - } + void onDecoderNotify(); + void scheduleDecoderNotification(); + void queueDecoderInputBuffers(); - case kWhatRender: - { - onRender(); - break; - } + void queueOutputBuffer( + size_t index, int64_t timeUs, const sp &buffer); - default: - TRESPASS(); - } + DISALLOW_EVIL_CONSTRUCTORS(DecoderContext); +}; + +//////////////////////////////////////////////////////////////////////////////// + +/* + A "push" audio renderer. The primary function of this renderer is to use + an AudioTrack in push mode and making sure not to block the event loop + be ensuring that calls to AudioTrack::write never block. This is done by + estimating an upper bound of data that can be written to the AudioTrack + buffer without delay. +*/ +struct DirectRenderer::AudioRenderer : public AHandler { + AudioRenderer(const sp &decoderContext); + + void queueInputBuffer( + size_t index, int64_t timeUs, const sp &buffer); + +protected: + virtual ~AudioRenderer(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatPushAudio, + }; + + struct BufferInfo { + size_t mIndex; + int64_t mTimeUs; + sp mBuffer; + }; + + sp mDecoderContext; + sp mAudioTrack; + + List mInputBuffers; + bool mPushPending; + + size_t mNumFramesWritten; + + void schedulePushIfNecessary(); + void onPushAudio(); + + ssize_t writeNonBlocking(const uint8_t *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(AudioRenderer); +}; + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::DecoderContext::DecoderContext(const sp ¬ify) + : mNotify(notify), + mDecoderNotificationPending(false) { } -void DirectRenderer::setFormat( - size_t trackIndex, const sp &format) { - if (trackIndex == 1) { - // Ignore audio for now. - return; +DirectRenderer::DecoderContext::~DecoderContext() { + if (mDecoder != NULL) { + mDecoder->release(); + mDecoder.clear(); + + mDecoderLooper->stop(); + mDecoderLooper.clear(); } +} - CHECK(mVideoDecoder == NULL); +status_t DirectRenderer::DecoderContext::init( + const sp &format, + const sp &surfaceTex) { + CHECK(mDecoder == NULL); AString mime; CHECK(format->findString("mime", &mime)); - mVideoDecoderLooper = new ALooper; - mVideoDecoderLooper->setName("video codec looper"); + mDecoderLooper = new ALooper; + mDecoderLooper->setName("video codec looper"); - mVideoDecoderLooper->start( + mDecoderLooper->start( false /* runOnCallingThread */, false /* canCallJava */, PRIORITY_DEFAULT); - mVideoDecoder = MediaCodec::CreateByType( - mVideoDecoderLooper, mime.c_str(), false /* encoder */); + mDecoder = MediaCodec::CreateByType( + mDecoderLooper, mime.c_str(), false /* encoder */); - CHECK(mVideoDecoder != NULL); + CHECK(mDecoder != NULL); - status_t err = mVideoDecoder->configure( + status_t err = mDecoder->configure( format, - mSurfaceTex == NULL - ? NULL : new Surface(mSurfaceTex), + surfaceTex == NULL + ? NULL : new Surface(surfaceTex), NULL /* crypto */, 0 /* flags */); CHECK_EQ(err, (status_t)OK); - err = mVideoDecoder->start(); + err = mDecoder->start(); CHECK_EQ(err, (status_t)OK); - err = mVideoDecoder->getInputBuffers( - &mVideoDecoderInputBuffers); + err = mDecoder->getInputBuffers( + &mDecoderInputBuffers); CHECK_EQ(err, (status_t)OK); - scheduleVideoDecoderNotification(); + err = mDecoder->getOutputBuffers( + &mDecoderOutputBuffers); + CHECK_EQ(err, (status_t)OK); + + scheduleDecoderNotification(); + + return OK; } -void DirectRenderer::queueAccessUnit( - size_t trackIndex, const sp &accessUnit) { - if (trackIndex == 1) { - // Ignore audio for now. - return; - } +void DirectRenderer::DecoderContext::queueInputBuffer( + const sp &accessUnit) { + CHECK(mDecoder != NULL); - if (mVideoDecoder == NULL) { - sp format = new AMessage; - format->setString("mime", "video/avc"); - format->setInt32("width", 640); - format->setInt32("height", 360); + mAccessUnits.push_back(accessUnit); + queueDecoderInputBuffers(); +} - setFormat(0, format); - } +status_t DirectRenderer::DecoderContext::renderOutputBufferAndRelease( + size_t index) { + return mDecoder->renderOutputBufferAndRelease(index); +} - mVideoAccessUnits.push_back(accessUnit); - queueVideoDecoderInputBuffers(); +status_t DirectRenderer::DecoderContext::releaseOutputBuffer(size_t index) { + return mDecoder->releaseOutputBuffer(index); } -void DirectRenderer::queueVideoDecoderInputBuffers() { - if (mVideoDecoder == NULL) { +void DirectRenderer::DecoderContext::queueDecoderInputBuffers() { + if (mDecoder == NULL) { return; } bool submittedMore = false; - while (!mVideoAccessUnits.empty() - && !mVideoDecoderInputBuffersAvailable.empty()) { - size_t index = *mVideoDecoderInputBuffersAvailable.begin(); + while (!mAccessUnits.empty() + && !mDecoderInputBuffersAvailable.empty()) { + size_t index = *mDecoderInputBuffersAvailable.begin(); - mVideoDecoderInputBuffersAvailable.erase( - mVideoDecoderInputBuffersAvailable.begin()); + mDecoderInputBuffersAvailable.erase( + mDecoderInputBuffersAvailable.begin()); - sp srcBuffer = *mVideoAccessUnits.begin(); - mVideoAccessUnits.erase(mVideoAccessUnits.begin()); + sp srcBuffer = *mAccessUnits.begin(); + mAccessUnits.erase(mAccessUnits.begin()); const sp &dstBuffer = - mVideoDecoderInputBuffers.itemAt(index); + mDecoderInputBuffers.itemAt(index); memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size()); int64_t timeUs; CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs)); - status_t err = mVideoDecoder->queueInputBuffer( + status_t err = mDecoder->queueInputBuffer( index, 0 /* offset */, srcBuffer->size(), @@ -198,19 +248,33 @@ void DirectRenderer::queueVideoDecoderInputBuffers() { } if (submittedMore) { - scheduleVideoDecoderNotification(); + scheduleDecoderNotification(); + } +} + +void DirectRenderer::DecoderContext::onMessageReceived( + const sp &msg) { + switch (msg->what()) { + case kWhatDecoderNotify: + { + onDecoderNotify(); + break; + } + + default: + TRESPASS(); } } -void DirectRenderer::onVideoDecoderNotify() { - mVideoDecoderNotificationPending = false; +void DirectRenderer::DecoderContext::onDecoderNotify() { + mDecoderNotificationPending = false; for (;;) { size_t index; - status_t err = mVideoDecoder->dequeueInputBuffer(&index); + status_t err = mDecoder->dequeueInputBuffer(&index); if (err == OK) { - mVideoDecoderInputBuffersAvailable.push_back(index); + mDecoderInputBuffersAvailable.push_back(index); } else if (err == -EAGAIN) { break; } else { @@ -218,7 +282,7 @@ void DirectRenderer::onVideoDecoderNotify() { } } - queueVideoDecoderInputBuffers(); + queueDecoderInputBuffers(); for (;;) { size_t index; @@ -226,7 +290,7 @@ void DirectRenderer::onVideoDecoderNotify() { size_t size; int64_t timeUs; uint32_t flags; - status_t err = mVideoDecoder->dequeueOutputBuffer( + status_t err = mDecoder->dequeueOutputBuffer( &index, &offset, &size, @@ -234,9 +298,12 @@ void DirectRenderer::onVideoDecoderNotify() { &flags); if (err == OK) { - queueOutputBuffer(index, timeUs); + queueOutputBuffer( + index, timeUs, mDecoderOutputBuffers.itemAt(index)); } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { - // We don't care. + err = mDecoder->getOutputBuffers( + &mDecoderOutputBuffers); + CHECK_EQ(err, (status_t)OK); } else if (err == INFO_FORMAT_CHANGED) { // We don't care. } else if (err == -EAGAIN) { @@ -246,48 +313,315 @@ void DirectRenderer::onVideoDecoderNotify() { } } - scheduleVideoDecoderNotification(); + scheduleDecoderNotification(); } -void DirectRenderer::queueOutputBuffer(size_t index, int64_t timeUs) { -#if 1 - OutputInfo info; +void DirectRenderer::DecoderContext::scheduleDecoderNotification() { + if (mDecoderNotificationPending) { + return; + } + + sp notify = + new AMessage(kWhatDecoderNotify, id()); + + mDecoder->requestActivityNotification(notify); + mDecoderNotificationPending = true; +} + +void DirectRenderer::DecoderContext::queueOutputBuffer( + size_t index, int64_t timeUs, const sp &buffer) { + sp msg = mNotify->dup(); + msg->setInt32("what", kWhatOutputBufferReady); + msg->setSize("index", index); + msg->setInt64("timeUs", timeUs); + msg->setBuffer("buffer", buffer); + msg->post(); +} + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::AudioRenderer::AudioRenderer( + const sp &decoderContext) + : mDecoderContext(decoderContext), + mPushPending(false), + mNumFramesWritten(0) { + mAudioTrack = new AudioTrack( + AUDIO_STREAM_DEFAULT, + 48000.0f, + AUDIO_FORMAT_PCM, + AUDIO_CHANNEL_OUT_STEREO, + (int)0 /* frameCount */); + + CHECK_EQ((status_t)OK, mAudioTrack->initCheck()); + + mAudioTrack->start(); +} + +DirectRenderer::AudioRenderer::~AudioRenderer() { +} + +void DirectRenderer::AudioRenderer::queueInputBuffer( + size_t index, int64_t timeUs, const sp &buffer) { + BufferInfo info; info.mIndex = index; - info.mTimeUs = timeUs + mTimeOffsetUs; - mOutputBuffers.push_back(info); + info.mTimeUs = timeUs; + info.mBuffer = buffer; - scheduleRenderIfNecessary(); -#else - mLatencySum += ALooper::GetNowUs() - (timeUs + mTimeOffsetUs); - ++mLatencyCount; + mInputBuffers.push_back(info); + schedulePushIfNecessary(); +} - status_t err = mVideoDecoder->renderOutputBufferAndRelease(index); - CHECK_EQ(err, (status_t)OK); -#endif +void DirectRenderer::AudioRenderer::onMessageReceived( + const sp &msg) { + switch (msg->what()) { + case kWhatPushAudio: + { + onPushAudio(); + break; + } + + default: + break; + } } -void DirectRenderer::scheduleRenderIfNecessary() { - if (mRenderPending || mOutputBuffers.empty()) { +void DirectRenderer::AudioRenderer::schedulePushIfNecessary() { + if (mPushPending || mInputBuffers.empty()) { return; } - mRenderPending = true; + mPushPending = true; + + uint32_t numFramesPlayed; + CHECK_EQ(mAudioTrack->getPosition(&numFramesPlayed), + (status_t)OK); + + uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed; + + // This is how long the audio sink will have data to + // play back. + const float msecsPerFrame = 1000.0f / mAudioTrack->getSampleRate(); + + int64_t delayUs = + msecsPerFrame * numFramesPendingPlayout * 1000ll; - int64_t timeUs = (*mOutputBuffers.begin()).mTimeUs; + // Let's give it more data after about half that time + // has elapsed. + (new AMessage(kWhatPushAudio, id()))->post(delayUs / 2); +} + +void DirectRenderer::AudioRenderer::onPushAudio() { + mPushPending = false; + + while (!mInputBuffers.empty()) { + const BufferInfo &info = *mInputBuffers.begin(); + + ssize_t n = writeNonBlocking( + info.mBuffer->data(), info.mBuffer->size()); + + if (n < (ssize_t)info.mBuffer->size()) { + CHECK_GE(n, 0); + + info.mBuffer->setRange( + info.mBuffer->offset() + n, info.mBuffer->size() - n); + break; + } + + mDecoderContext->releaseOutputBuffer(info.mIndex); + + mInputBuffers.erase(mInputBuffers.begin()); + } + + schedulePushIfNecessary(); +} + +ssize_t DirectRenderer::AudioRenderer::writeNonBlocking( + const uint8_t *data, size_t size) { + uint32_t numFramesPlayed; + status_t err = mAudioTrack->getPosition(&numFramesPlayed); + if (err != OK) { + return err; + } + + ssize_t numFramesAvailableToWrite = + mAudioTrack->frameCount() - (mNumFramesWritten - numFramesPlayed); + + size_t numBytesAvailableToWrite = + numFramesAvailableToWrite * mAudioTrack->frameSize(); + + if (size > numBytesAvailableToWrite) { + size = numBytesAvailableToWrite; + } + + CHECK_EQ(mAudioTrack->write(data, size), (ssize_t)size); + + size_t numFramesWritten = size / mAudioTrack->frameSize(); + mNumFramesWritten += numFramesWritten; + + return size; +} + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::DirectRenderer( + const sp &bufferProducer) + : mSurfaceTex(bufferProducer), + mVideoRenderPending(false), + mLatencySum(0ll), + mLatencyCount(0), + mNumFramesLate(0), + mNumFrames(0) { +} + +DirectRenderer::~DirectRenderer() { +} + +int64_t DirectRenderer::getAvgLatenessUs() { + if (mLatencyCount == 0) { + return 0ll; + } + + int64_t avgLatencyUs = mLatencySum / mLatencyCount; + + mLatencySum = 0ll; + mLatencyCount = 0; + + if (mNumFrames > 0) { + ALOGI("%d / %d frames late", mNumFramesLate, mNumFrames); + mNumFramesLate = 0; + mNumFrames = 0; + } + + return avgLatencyUs; +} + +void DirectRenderer::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatDecoderNotify: + { + onDecoderNotify(msg); + break; + } + + case kWhatRenderVideo: + { + onRenderVideo(); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::setFormat(size_t trackIndex, const sp &format) { + CHECK_LT(trackIndex, 2u); + + CHECK(mDecoderContext[trackIndex] == NULL); + + sp notify = new AMessage(kWhatDecoderNotify, id()); + notify->setSize("trackIndex", trackIndex); + + mDecoderContext[trackIndex] = new DecoderContext(notify); + looper()->registerHandler(mDecoderContext[trackIndex]); + + CHECK_EQ((status_t)OK, + mDecoderContext[trackIndex]->init( + format, trackIndex == 0 ? mSurfaceTex : NULL)); + + if (trackIndex == 1) { + // Audio + mAudioRenderer = new AudioRenderer(mDecoderContext[1]); + looper()->registerHandler(mAudioRenderer); + } +} + +void DirectRenderer::queueAccessUnit( + size_t trackIndex, const sp &accessUnit) { + CHECK_LT(trackIndex, 2u); + + if (mDecoderContext[trackIndex] == NULL) { + CHECK_EQ(trackIndex, 0u); + + sp format = new AMessage; + format->setString("mime", "video/avc"); + format->setInt32("width", 640); + format->setInt32("height", 360); + + setFormat(trackIndex, format); + } + + mDecoderContext[trackIndex]->queueInputBuffer(accessUnit); +} + +void DirectRenderer::onDecoderNotify(const sp &msg) { + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case DecoderContext::kWhatOutputBufferReady: + { + size_t index; + CHECK(msg->findSize("index", &index)); + + int64_t timeUs; + CHECK(msg->findInt64("timeUs", &timeUs)); + + sp buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + queueOutputBuffer(trackIndex, index, timeUs, buffer); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::queueOutputBuffer( + size_t trackIndex, + size_t index, int64_t timeUs, const sp &buffer) { + if (trackIndex == 1) { + // Audio + mAudioRenderer->queueInputBuffer(index, timeUs, buffer); + return; + } + + OutputInfo info; + info.mIndex = index; + info.mTimeUs = timeUs; + info.mBuffer = buffer; + mVideoOutputBuffers.push_back(info); + + scheduleVideoRenderIfNecessary(); +} + +void DirectRenderer::scheduleVideoRenderIfNecessary() { + if (mVideoRenderPending || mVideoOutputBuffers.empty()) { + return; + } + + mVideoRenderPending = true; + + int64_t timeUs = (*mVideoOutputBuffers.begin()).mTimeUs; int64_t nowUs = ALooper::GetNowUs(); int64_t delayUs = timeUs - nowUs; - (new AMessage(kWhatRender, id()))->post(delayUs); + (new AMessage(kWhatRenderVideo, id()))->post(delayUs); } -void DirectRenderer::onRender() { - mRenderPending = false; +void DirectRenderer::onRenderVideo() { + mVideoRenderPending = false; int64_t nowUs = ALooper::GetNowUs(); - while (!mOutputBuffers.empty()) { - const OutputInfo &info = *mOutputBuffers.begin(); + while (!mVideoOutputBuffers.empty()) { + const OutputInfo &info = *mVideoOutputBuffers.begin(); if (info.mTimeUs > nowUs) { break; @@ -301,25 +635,14 @@ void DirectRenderer::onRender() { mLatencySum += nowUs - info.mTimeUs; ++mLatencyCount; - status_t err = mVideoDecoder->renderOutputBufferAndRelease(info.mIndex); + status_t err = + mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex); CHECK_EQ(err, (status_t)OK); - mOutputBuffers.erase(mOutputBuffers.begin()); + mVideoOutputBuffers.erase(mVideoOutputBuffers.begin()); } - scheduleRenderIfNecessary(); -} - -void DirectRenderer::scheduleVideoDecoderNotification() { - if (mVideoDecoderNotificationPending) { - return; - } - - sp notify = - new AMessage(kWhatVideoDecoderNotify, id()); - - mVideoDecoder->requestActivityNotification(notify); - mVideoDecoderNotificationPending = true; + scheduleVideoRenderIfNecessary(); } } // namespace android diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h index 44be8f8..92c176a 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -23,21 +23,17 @@ namespace android { struct ABuffer; +struct AudioTrack; struct IGraphicBufferProducer; struct MediaCodec; -// An experimental renderer that only supports video and decodes video data -// as soon as it arrives using a MediaCodec instance, rendering it without -// delay. Primarily meant to finetune packet loss discovery and minimize -// latency. +// Renders audio and video data queued by calls to "queueAccessUnit". struct DirectRenderer : public AHandler { DirectRenderer(const sp &bufferProducer); void setFormat(size_t trackIndex, const sp &format); void queueAccessUnit(size_t trackIndex, const sp &accessUnit); - void setTimeOffset(int64_t offset); - int64_t getAvgLatenessUs(); protected: @@ -45,30 +41,28 @@ protected: virtual ~DirectRenderer(); private: + struct DecoderContext; + struct AudioRenderer; + enum { - kWhatVideoDecoderNotify, - kWhatRender, + kWhatDecoderNotify, + kWhatRenderVideo, }; struct OutputInfo { size_t mIndex; int64_t mTimeUs; + sp mBuffer; }; sp mSurfaceTex; - sp mVideoDecoderLooper; - sp mVideoDecoder; - Vector > mVideoDecoderInputBuffers; - List mVideoDecoderInputBuffersAvailable; - bool mVideoDecoderNotificationPending; - - List > mVideoAccessUnits; + sp mDecoderContext[2]; + List mVideoOutputBuffers; - List mOutputBuffers; - bool mRenderPending; + bool mVideoRenderPending; - int64_t mTimeOffsetUs; + sp mAudioRenderer; int64_t mLatencySum; size_t mLatencyCount; @@ -76,14 +70,14 @@ private: int32_t mNumFramesLate; int32_t mNumFrames; - void onVideoDecoderNotify(); - void onRender(); + void onDecoderNotify(const sp &msg); - void queueVideoDecoderInputBuffers(); - void scheduleVideoDecoderNotification(); - void scheduleRenderIfNecessary(); + void queueOutputBuffer( + size_t trackIndex, + size_t index, int64_t timeUs, const sp &buffer); - void queueOutputBuffer(size_t index, int64_t timeUs); + void scheduleVideoRenderIfNecessary(); + void onRenderVideo(); DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); }; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index d635c3a..62021c0 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -337,12 +337,17 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { ALOGI("Assuming %lld ms of latency.", latencyUs / 1000ll); } + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + // We are the timesync _client_, // client time = server time - time offset. - mRenderer->setTimeOffset(-mTimeOffsetUs + mTargetLatencyUs); + timeUs += mTargetLatencyUs - mTimeOffsetUs; - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); + accessUnit->meta()->setInt64("timeUs", timeUs); size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); -- cgit v1.1 From acc47642e0f5d962f6289e6ba687fabf68f8312b Mon Sep 17 00:00:00 2001 From: James Dong Date: Tue, 12 Mar 2013 10:40:20 -0700 Subject: Make limitations of MPEG4Writer explicit o No more than 2 tracks will be supported o No more than one video and/or one audio tracks will be supported o Only take video and/or audio track (for instance, no text tracks) o If there is no track before start() is called, bail out. At the same time, make sure the errors from addSource() report to addTrack(), not to start(). Bug: 7991013 Change-Id: I1ca35aaeb75b5448d75ed2c6c10dd12ecea720ab --- media/libstagefright/MPEG4Writer.cpp | 41 ++++++++++++++++++++++++++++++++++++ media/libstagefright/MediaMuxer.cpp | 14 ++++++------ 2 files changed, 49 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 056b47a..316f669 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -428,6 +428,42 @@ status_t MPEG4Writer::addSource(const sp &source) { ALOGE("Attempt to add source AFTER recording is started"); return UNKNOWN_ERROR; } + + // At most 2 tracks can be supported. + if (mTracks.size() >= 2) { + ALOGE("Too many tracks (%d) to add", mTracks.size()); + return ERROR_UNSUPPORTED; + } + + CHECK(source.get() != NULL); + + // A track of type other than video or audio is not supported. + const char *mime; + source->getFormat()->findCString(kKeyMIMEType, &mime); + bool isAudio = !strncasecmp(mime, "audio/", 6); + bool isVideo = !strncasecmp(mime, "video/", 6); + if (!isAudio && !isVideo) { + ALOGE("Track (%s) other than video or audio is not supported", + mime); + return ERROR_UNSUPPORTED; + } + + // At this point, we know the track to be added is either + // video or audio. Thus, we only need to check whether it + // is an audio track or not (if it is not, then it must be + // a video track). + + // No more than one video or one audio track is supported. + for (List::iterator it = mTracks.begin(); + it != mTracks.end(); ++it) { + if ((*it)->isAudio() == isAudio) { + ALOGE("%s track already exists", isAudio? "Audio": "Video"); + return ERROR_UNSUPPORTED; + } + } + + // This is the first track of either audio or video. + // Go ahead to add the track. Track *track = new Track(this, source, 1 + mTracks.size()); mTracks.push_back(track); @@ -435,6 +471,11 @@ status_t MPEG4Writer::addSource(const sp &source) { } status_t MPEG4Writer::startTracks(MetaData *params) { + if (mTracks.empty()) { + ALOGE("No source added"); + return INVALID_OPERATION; + } + for (List::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { status_t err = (*it)->start(params); diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index aefc270..21841b3 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -76,17 +76,17 @@ ssize_t MediaMuxer::addTrack(const sp &format) { convertMessageToMetaData(format, meta); sp newTrack = new MediaAdapter(meta); - return mTrackList.add(newTrack); + status_t result = mWriter->addSource(newTrack); + if (result == OK) { + return mTrackList.add(newTrack); + } + return -1; } status_t MediaMuxer::start() { Mutex::Autolock autoLock(mMuxerLock); - if (mState == INITED) { mState = STARTED; - for (size_t i = 0 ; i < mTrackList.size(); i++) { - mWriter->addSource(mTrackList[i]); - } return mWriter->start(); } else { ALOGE("start() is called in invalid state %d", mState); @@ -100,7 +100,9 @@ status_t MediaMuxer::stop() { if (mState == STARTED) { mState = STOPPED; for (size_t i = 0; i < mTrackList.size(); i++) { - mTrackList[i]->stop(); + if (mTrackList[i]->stop() != OK) { + return INVALID_OPERATION; + } } return mWriter->stop(); } else { -- cgit v1.1 From 595ee7ba5f988ff34527226d0142a109ca3b049e Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 14 Mar 2013 14:50:47 -0700 Subject: Parse SBR extension sample rate Change-Id: Ib6f6994228a279ee10b389515fba04516c7c42ba --- media/libstagefright/MPEG4Extractor.cpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index b2e60be..56fad60 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -2067,17 +2067,30 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( sampleRate = br.getBits(24); numChannels = br.getBits(4); } else { - static uint32_t kSamplingRate[] = { - 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000, 7350 - }; - - if (freqIndex == 13 || freqIndex == 14) { - return ERROR_MALFORMED; + numChannels = br.getBits(4); + if (objectType == 5) { + // SBR specific config per 14496-3 table 1.13 + freqIndex = br.getBits(4); + if (freqIndex == 15) { + if (csd_size < 8) { + return ERROR_MALFORMED; + } + sampleRate = br.getBits(24); + } } - sampleRate = kSamplingRate[freqIndex]; - numChannels = br.getBits(4); + if (sampleRate == 0) { + static uint32_t kSamplingRate[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350 + }; + + if (freqIndex == 13 || freqIndex == 14) { + return ERROR_MALFORMED; + } + + sampleRate = kSamplingRate[freqIndex]; + } } if (numChannels == 0) { -- cgit v1.1 From e0fb528f8e3bbab04620c8534177168b358e837b Mon Sep 17 00:00:00 2001 From: ztenghui Date: Tue, 12 Mar 2013 15:43:56 -0700 Subject: Add the presentation rotation support bug:7991013 Change-Id: I10cb034b432876c724baa4974efcb3d67b8a99b6 --- media/libstagefright/MediaMuxer.cpp | 48 ++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 14 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index 21841b3..b948fe2 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -36,18 +36,21 @@ namespace android { MediaMuxer::MediaMuxer(const char *path, OutputFormat format) - : mState(UNINITED) { + : mState(UNINITIALIZED) { if (format == OUTPUT_FORMAT_MPEG_4) { mWriter = new MPEG4Writer(path); - mState = INITED; + mFileMeta = new MetaData; + mState = INITIALIZED; } + } MediaMuxer::MediaMuxer(int fd, OutputFormat format) - : mState(UNINITED) { + : mState(UNINITIALIZED) { if (format == OUTPUT_FORMAT_MPEG_4) { mWriter = new MPEG4Writer(fd); - mState = INITED; + mFileMeta = new MetaData; + mState = INITIALIZED; } } @@ -55,6 +58,7 @@ MediaMuxer::~MediaMuxer() { Mutex::Autolock autoLock(mMuxerLock); // Clean up all the internal resources. + mFileMeta.clear(); mWriter.clear(); mTrackList.clear(); } @@ -67,15 +71,15 @@ ssize_t MediaMuxer::addTrack(const sp &format) { return -EINVAL; } - if (mState != INITED) { + if (mState != INITIALIZED) { ALOGE("addTrack() must be called after constructor and before start()."); return INVALID_OPERATION; } - sp meta = new MetaData; - convertMessageToMetaData(format, meta); + sp trackMeta = new MetaData; + convertMessageToMetaData(format, trackMeta); - sp newTrack = new MediaAdapter(meta); + sp newTrack = new MediaAdapter(trackMeta); status_t result = mWriter->addSource(newTrack); if (result == OK) { return mTrackList.add(newTrack); @@ -83,11 +87,27 @@ ssize_t MediaMuxer::addTrack(const sp &format) { return -1; } +status_t MediaMuxer::setOrientationHint(int degrees) { + Mutex::Autolock autoLock(mMuxerLock); + if (mState != INITIALIZED) { + ALOGE("setOrientationHint() must be called before start()."); + return INVALID_OPERATION; + } + + if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) { + ALOGE("setOrientationHint() get invalid degrees"); + return -EINVAL; + } + + mFileMeta->setInt32(kKeyRotation, degrees); + return OK; +} + status_t MediaMuxer::start() { Mutex::Autolock autoLock(mMuxerLock); - if (mState == INITED) { + if (mState == INITIALIZED) { mState = STARTED; - return mWriter->start(); + return mWriter->start(mFileMeta.get()); } else { ALOGE("start() is called in invalid state %d", mState); return INVALID_OPERATION; @@ -135,13 +155,13 @@ status_t MediaMuxer::writeSampleData(const sp &buffer, size_t trackInde mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned(). mediaBuffer->set_range(buffer->offset(), buffer->size()); - sp metaData = mediaBuffer->meta_data(); - metaData->setInt64(kKeyTime, timeUs); + sp sampleMetaData = mediaBuffer->meta_data(); + sampleMetaData->setInt64(kKeyTime, timeUs); // Just set the kKeyDecodingTime as the presentation time for now. - metaData->setInt64(kKeyDecodingTime, timeUs); + sampleMetaData->setInt64(kKeyDecodingTime, timeUs); if (flags & SAMPLE_FLAG_SYNC) { - metaData->setInt32(kKeyIsSyncFrame, true); + sampleMetaData->setInt32(kKeyIsSyncFrame, true); } sp currentTrack = mTrackList[trackIndex]; -- cgit v1.1 From 0b530f1050150bb751ae642d5a9dce34141d9475 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 18 Mar 2013 11:09:22 -0700 Subject: Allow for streaming of media files (without recompression) Change-Id: I1de356cc37506ba986822d12a1a59e7b64069e02 --- media/libstagefright/wifi-display/MediaSender.cpp | 31 ++++ .../wifi-display/source/PlaybackSession.cpp | 196 ++++++++++++++++++++- .../wifi-display/source/PlaybackSession.h | 18 +- .../wifi-display/source/TSPacketizer.cpp | 2 +- .../wifi-display/source/WifiDisplaySource.cpp | 36 +++- .../wifi-display/source/WifiDisplaySource.h | 8 +- media/libstagefright/wifi-display/wfd.cpp | 62 +++++-- 7 files changed, 318 insertions(+), 35 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index e1e957a..a41f81b 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -256,6 +256,37 @@ status_t MediaSender::queueAccessUnit( tsPackets, 33 /* packetType */, RTPSender::PACKETIZATION_TRANSPORT_STREAM); + +#if 0 + { + int64_t nowUs = ALooper::GetNowUs(); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + int64_t delayMs = (nowUs - timeUs) / 1000ll; + + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("[%lld]: (%4lld ms) %s\n", + timeUs / 1000, + delayMs, + kPattern + kPatternSize - n); + } +#endif } if (err != OK) { diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 94cb2a4..a3b6542 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,8 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler { const sp &mediaPuller, const sp &converter); + Track(const sp ¬ify, const sp &format); + void setRepeaterSource(const sp &source); sp getFormat(); @@ -104,6 +107,7 @@ private: sp mCodecLooper; sp mMediaPuller; sp mConverter; + sp mFormat; bool mStarted; ssize_t mMediaSenderTrackIndex; bool mIsAudio; @@ -133,6 +137,15 @@ WifiDisplaySource::PlaybackSession::Track::Track( mLastOutputBufferQueuedTimeUs(-1ll) { } +WifiDisplaySource::PlaybackSession::Track::Track( + const sp ¬ify, const sp &format) + : mNotify(notify), + mFormat(format), + mStarted(false), + mIsAudio(IsAudioFormat(format)), + mLastOutputBufferQueuedTimeUs(-1ll) { +} + WifiDisplaySource::PlaybackSession::Track::~Track() { CHECK(!mStarted); } @@ -147,7 +160,7 @@ bool WifiDisplaySource::PlaybackSession::Track::IsAudioFormat( } sp WifiDisplaySource::PlaybackSession::Track::getFormat() { - return mConverter->getOutputFormat(); + return mFormat != NULL ? mFormat : mConverter->getOutputFormat(); } bool WifiDisplaySource::PlaybackSession::Track::isAudio() const { @@ -189,7 +202,9 @@ status_t WifiDisplaySource::PlaybackSession::Track::start() { void WifiDisplaySource::PlaybackSession::Track::stopAsync() { ALOGV("Track::stopAsync isAudio=%d", mIsAudio); - mConverter->shutdownAsync(); + if (mConverter != NULL) { + mConverter->shutdownAsync(); + } sp msg = new AMessage(kWhatMediaPullerStopped, id()); @@ -201,6 +216,7 @@ void WifiDisplaySource::PlaybackSession::Track::stopAsync() { mMediaPuller->stopAsync(msg); } else { + mStarted = false; msg->post(); } } @@ -324,7 +340,8 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( const sp &netSession, const sp ¬ify, const in_addr &interfaceAddr, - const sp &hdcp) + const sp &hdcp, + const char *path) : mNetSession(netSession), mNotify(notify), mInterfaceAddr(interfaceAddr), @@ -334,7 +351,14 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( mPaused(false), mLastLifesignUs(), mVideoTrackIndex(-1), - mPrevTimeUs(-1ll) { + mPrevTimeUs(-1ll), + mPullExtractorPending(false), + mPullExtractorGeneration(0), + mFirstSampleTimeRealUs(-1ll), + mFirstSampleTimeUs(-1ll) { + if (path != NULL) { + mMediaPath.setTo(path); + } } status_t WifiDisplaySource::PlaybackSession::init( @@ -405,10 +429,6 @@ status_t WifiDisplaySource::PlaybackSession::play() { return OK; } -status_t WifiDisplaySource::PlaybackSession::finishPlay() { - return OK; -} - status_t WifiDisplaySource::PlaybackSession::onMediaSenderInitialized() { for (size_t i = 0; i < mTracks.size(); ++i) { CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start()); @@ -523,7 +543,10 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( const sp &videoTrack = mTracks.valueFor(mVideoTrackIndex); - videoTrack->converter()->dropAFrame(); + sp converter = videoTrack->converter(); + if (converter != NULL) { + converter->dropAFrame(); + } } } else { TRESPASS(); @@ -564,6 +587,12 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( case kWhatPause: { + if (mExtractor != NULL) { + ++mPullExtractorGeneration; + mFirstSampleTimeRealUs = -1ll; + mFirstSampleTimeUs = -1ll; + } + if (mPaused) { break; } @@ -578,6 +607,10 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( case kWhatResume: { + if (mExtractor != NULL) { + schedulePullExtractor(); + } + if (!mPaused) { break; } @@ -590,11 +623,152 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } + case kWhatPullExtractorSample: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mPullExtractorGeneration) { + break; + } + + mPullExtractorPending = false; + + onPullExtractor(); + break; + } + default: TRESPASS(); } } +status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( + bool enableAudio, bool enableVideo) { + DataSource::RegisterDefaultSniffers(); + + mExtractor = new NuMediaExtractor; + + status_t err = mExtractor->setDataSource(mMediaPath.c_str()); + + if (err != OK) { + return err; + } + + size_t n = mExtractor->countTracks(); + bool haveAudio = false; + bool haveVideo = false; + for (size_t i = 0; i < n; ++i) { + sp format; + err = mExtractor->getTrackFormat(i, &format); + + if (err != OK) { + continue; + } + + AString mime; + CHECK(format->findString("mime", &mime)); + + bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6); + bool isVideo = !strncasecmp(mime.c_str(), "video/", 6); + + if (isAudio && enableAudio && !haveAudio) { + haveAudio = true; + } else if (isVideo && enableVideo && !haveVideo) { + haveVideo = true; + } else { + continue; + } + + err = mExtractor->selectTrack(i); + + size_t trackIndex = mTracks.size(); + + sp notify = new AMessage(kWhatTrackNotify, id()); + notify->setSize("trackIndex", trackIndex); + + sp track = new Track(notify, format); + looper()->registerHandler(track); + + mTracks.add(trackIndex, track); + + mExtractorTrackToInternalTrack.add(i, trackIndex); + + if (isVideo) { + mVideoTrackIndex = trackIndex; + } + + uint32_t flags = MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS; + + ssize_t mediaSenderTrackIndex = + mMediaSender->addTrack(format, flags); + CHECK_GE(mediaSenderTrackIndex, 0); + + track->setMediaSenderTrackIndex(mediaSenderTrackIndex); + + if ((haveAudio || !enableAudio) && (haveVideo || !enableVideo)) { + break; + } + } + + return OK; +} + +void WifiDisplaySource::PlaybackSession::schedulePullExtractor() { + if (mPullExtractorPending) { + return; + } + + int64_t sampleTimeUs; + status_t err = mExtractor->getSampleTime(&sampleTimeUs); + + int64_t nowUs = ALooper::GetNowUs(); + + if (mFirstSampleTimeRealUs < 0ll) { + mFirstSampleTimeRealUs = nowUs; + mFirstSampleTimeUs = sampleTimeUs; + } + + int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs; + + sp msg = new AMessage(kWhatPullExtractorSample, id()); + msg->setInt32("generation", mPullExtractorGeneration); + msg->post(whenUs - nowUs); + + mPullExtractorPending = true; +} + +void WifiDisplaySource::PlaybackSession::onPullExtractor() { + sp accessUnit = new ABuffer(1024 * 1024); + status_t err = mExtractor->readSampleData(accessUnit); + if (err != OK) { + // EOS. + return; + } + + int64_t timeUs; + CHECK_EQ((status_t)OK, mExtractor->getSampleTime(&timeUs)); + + accessUnit->meta()->setInt64( + "timeUs", mFirstSampleTimeRealUs + timeUs - mFirstSampleTimeUs); + + size_t trackIndex; + CHECK_EQ((status_t)OK, mExtractor->getSampleTrackIndex(&trackIndex)); + + sp msg = new AMessage(kWhatConverterNotify, id()); + + msg->setSize( + "trackIndex", mExtractorTrackToInternalTrack.valueFor(trackIndex)); + + msg->setInt32("what", Converter::kWhatAccessUnit); + msg->setBuffer("accessUnit", accessUnit); + msg->post(); + + mExtractor->advance(); + + schedulePullExtractor(); +} + status_t WifiDisplaySource::PlaybackSession::setupPacketizer( bool enableAudio, bool usePCMAudio, @@ -603,6 +777,10 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( size_t videoResolutionIndex) { CHECK(enableAudio || enableVideo); + if (!mMediaPath.empty()) { + return setupMediaPacketizer(enableAudio, enableVideo); + } + if (enableVideo) { status_t err = addVideoSource( videoResolutionType, videoResolutionIndex); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index cd6da85..da207e2 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -31,6 +31,7 @@ struct IGraphicBufferProducer; struct MediaPuller; struct MediaSource; struct MediaSender; +struct NuMediaExtractor; // Encapsulates the state of an RTP/RTCP session in the context of wifi // display. @@ -39,7 +40,8 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { const sp &netSession, const sp ¬ify, const struct in_addr &interfaceAddr, - const sp &hdcp); + const sp &hdcp, + const char *path = NULL); status_t init( const char *clientIP, int32_t clientRtp, int32_t clientRtcp, @@ -87,12 +89,14 @@ private: kWhatPause, kWhatResume, kWhatMediaSenderNotify, + kWhatPullExtractorSample, }; sp mNetSession; sp mNotify; in_addr mInterfaceAddr; sp mHDCP; + AString mMediaPath; sp mMediaSender; int32_t mLocalRTPPort; @@ -109,6 +113,15 @@ private: int64_t mPrevTimeUs; + sp mExtractor; + KeyedVector mExtractorTrackToInternalTrack; + bool mPullExtractorPending; + int32_t mPullExtractorGeneration; + int64_t mFirstSampleTimeRealUs; + int64_t mFirstSampleTimeUs; + + status_t setupMediaPacketizer(bool enableAudio, bool enableVideo); + status_t setupPacketizer( bool enableAudio, bool usePCMAudio, @@ -133,6 +146,9 @@ private: void notifySessionDead(); + void schedulePullExtractor(); + void onPullExtractor(); + DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession); }; diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index 53b7187..d993764 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -261,7 +261,7 @@ void TSPacketizer::Track::finalize() { data[0] = 40; // descriptor_tag data[1] = 4; // descriptor_length - CHECK_EQ(mCSD.size(), 1u); + CHECK_GE(mCSD.size(), 1u); const sp &sps = mCSD.itemAt(0); CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4)); CHECK_GE(sps->size(), 7u); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index c8798c6..5167cb3 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -44,7 +44,8 @@ namespace android { WifiDisplaySource::WifiDisplaySource( const sp &netSession, - const sp &client) + const sp &client, + const char *path) : mState(INITIALIZED), mNetSession(netSession), mClient(client), @@ -59,7 +60,12 @@ WifiDisplaySource::WifiDisplaySource( mIsHDCP2_0(false), mHDCPPort(0), mHDCPInitializationComplete(false), - mSetupTriggerDeferred(false) { + mSetupTriggerDeferred(false), + mPlaybackSessionEstablished(false) { + if (path != NULL) { + mMediaPath.setTo(path); + } + mSupportedSourceVideoFormats.disableAll(); mSupportedSourceVideoFormats.setNativeResolution( @@ -389,6 +395,8 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { mClient->onDisplayError( IRemoteDisplayClient::kDisplayErrorUnknown); } else if (what == PlaybackSession::kWhatSessionEstablished) { + mPlaybackSessionEstablished = true; + if (mClient != NULL) { if (!mSinkSupportsVideo) { mClient->onDisplayConnected( @@ -419,6 +427,8 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { } } + finishPlay(); + if (mState == ABOUT_TO_PLAY) { mState = PLAYING; } @@ -1222,7 +1232,7 @@ status_t WifiDisplaySource::onSetupRequest( sp playbackSession = new PlaybackSession( - mNetSession, notify, mInterfaceAddr, mHDCP); + mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str()); looper()->registerHandler(playbackSession); @@ -1332,16 +1342,18 @@ status_t WifiDisplaySource::onPlayRequest( } ALOGI("Received PLAY request."); - - status_t err = playbackSession->play(); - CHECK_EQ(err, (status_t)OK); + if (mPlaybackSessionEstablished) { + finishPlay(); + } else { + ALOGI("deferring PLAY request until session established."); + } AString response = "RTSP/1.0 200 OK\r\n"; AppendCommonResponse(&response, cseq, playbackSessionID); response.append("Range: npt=now-\r\n"); response.append("\r\n"); - err = mNetSession->sendRequest(sessionID, response.c_str()); + status_t err = mNetSession->sendRequest(sessionID, response.c_str()); if (err != OK) { return err; @@ -1352,14 +1364,20 @@ status_t WifiDisplaySource::onPlayRequest( return OK; } - playbackSession->finishPlay(); - CHECK_EQ(mState, AWAITING_CLIENT_PLAY); mState = ABOUT_TO_PLAY; return OK; } +void WifiDisplaySource::finishPlay() { + const sp &playbackSession = + mClientInfo.mPlaybackSession; + + status_t err = playbackSession->play(); + CHECK_EQ(err, (status_t)OK); +} + status_t WifiDisplaySource::onPauseRequest( int32_t sessionID, int32_t cseq, diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 9e72682..3a1b0f9 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -39,7 +39,8 @@ struct WifiDisplaySource : public AHandler { WifiDisplaySource( const sp &netSession, - const sp &client); + const sp &client, + const char *path = NULL); status_t start(const char *iface); status_t stop(); @@ -116,6 +117,7 @@ private: VideoFormats mSupportedSourceVideoFormats; sp mNetSession; sp mClient; + AString mMediaPath; sp mTimeSyncer; struct in_addr mInterfaceAddr; int32_t mSessionID; @@ -161,6 +163,8 @@ private: bool mHDCPInitializationComplete; bool mSetupTriggerDeferred; + bool mPlaybackSessionEstablished; + status_t makeHDCP(); // <<<< HDCP specific section @@ -257,6 +261,8 @@ private: void finishStopAfterDisconnectingClient(); void finishStop2(); + void finishPlay(); + DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySource); }; diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 0b18484..3a7a6e2 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -42,6 +42,7 @@ static void usage(const char *me) { " %s -c host[:port]\tconnect to wifi source\n" " -u uri \tconnect to an rtsp uri\n" " -l ip[:port] \tlisten on the specified port " + " -f(ilename) \tstream media " "(create a sink)\n", me); } @@ -93,22 +94,24 @@ void RemoteDisplayClient::onDisplayConnected( ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x", width, height, flags); - mSurfaceTexture = bufferProducer; - mDisplayBinder = mComposerClient->createDisplay( - String8("foo"), false /* secure */); + if (bufferProducer != NULL) { + mSurfaceTexture = bufferProducer; + mDisplayBinder = mComposerClient->createDisplay( + String8("foo"), false /* secure */); - SurfaceComposerClient::openGlobalTransaction(); - mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); + SurfaceComposerClient::openGlobalTransaction(); + mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); - Rect layerStackRect(1280, 720); // XXX fix this. - Rect displayRect(1280, 720); + Rect layerStackRect(1280, 720); // XXX fix this. + Rect displayRect(1280, 720); - mComposerClient->setDisplayProjection( - mDisplayBinder, 0 /* 0 degree rotation */, - layerStackRect, - displayRect); + mComposerClient->setDisplayProjection( + mDisplayBinder, 0 /* 0 degree rotation */, + layerStackRect, + displayRect); - SurfaceComposerClient::closeGlobalTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); + } } void RemoteDisplayClient::onDisplayDisconnected() { @@ -181,6 +184,24 @@ static void createSource(const AString &addr, int32_t port) { enableAudioSubmix(false /* enable */); } +static void createFileSource( + const AString &addr, int32_t port, const char *path) { + sp session = new ANetworkSession; + session->start(); + + sp looper = new ALooper; + looper->start(); + + sp client = new RemoteDisplayClient; + sp source = new WifiDisplaySource(session, client, path); + looper->registerHandler(source); + + AString iface = StringPrintf("%s:%d", addr.c_str(), port); + CHECK_EQ((status_t)OK, source->start(iface.c_str())); + + client->waitUntilDone(); +} + } // namespace android int main(int argc, char **argv) { @@ -197,8 +218,10 @@ int main(int argc, char **argv) { AString listenOnAddr; int32_t listenOnPort = -1; + AString path; + int res; - while ((res = getopt(argc, argv, "hc:l:u:")) >= 0) { + while ((res = getopt(argc, argv, "hc:l:u:f:")) >= 0) { switch (res) { case 'c': { @@ -228,6 +251,12 @@ int main(int argc, char **argv) { break; } + case 'f': + { + path = optarg; + break; + } + case 'l': { const char *colonPos = strrchr(optarg, ':'); @@ -266,7 +295,12 @@ int main(int argc, char **argv) { } if (listenOnPort >= 0) { - createSource(listenOnAddr, listenOnPort); + if (path.empty()) { + createSource(listenOnAddr, listenOnPort); + } else { + createFileSource(listenOnAddr, listenOnPort, path.c_str()); + } + exit(0); } -- cgit v1.1 From a239dd722e760fe4fd7379b454d7722e1f312928 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 18 Mar 2013 15:11:40 -0700 Subject: Change ANetworkSession implementation to optionally attach timestamps to fragments of data to be transferred and to log statistics when data is finally submitted to the POSIX layer. Change-Id: Icbfcac203cdc5c9eac1634e84d34bb380b316a01 --- .../wifi-display/ANetworkSession.cpp | 150 +++++++++++++++------ .../libstagefright/wifi-display/ANetworkSession.h | 3 +- media/libstagefright/wifi-display/MediaSender.cpp | 4 + .../libstagefright/wifi-display/rtp/RTPSender.cpp | 21 ++- media/libstagefright/wifi-display/rtp/RTPSender.h | 4 +- media/libstagefright/wifi-display/wfd.cpp | 2 + 6 files changed, 135 insertions(+), 49 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 23bb04e..df20ae2 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -81,7 +81,8 @@ struct ANetworkSession::Session : public RefBase { status_t readMore(); status_t writeMore(); - status_t sendRequest(const void *data, ssize_t size); + status_t sendRequest( + const void *data, ssize_t size, bool timeValid, int64_t timeUs); void setIsRTSPConnection(bool yesno); @@ -89,6 +90,15 @@ protected: virtual ~Session(); private: + enum { + FRAGMENT_FLAG_TIME_VALID = 1, + }; + struct Fragment { + uint32_t mFlags; + int64_t mTimeUs; + sp mBuffer; + }; + int32_t mSessionID; State mState; bool mIsRTSPConnection; @@ -96,11 +106,7 @@ private: sp mNotify; bool mSawReceiveFailure, mSawSendFailure; - // for TCP / stream data - AString mOutBuffer; - - // for UDP / datagrams - List > mOutDatagrams; + List mOutFragments; AString mInBuffer; @@ -109,6 +115,8 @@ private: void notifyError(bool send, status_t err, const char *detail); void notify(NotificationReason reason); + void dumpFragmentStats(const Fragment &frag); + DISALLOW_EVIL_CONSTRUCTORS(Session); }; //////////////////////////////////////////////////////////////////////////////// @@ -221,8 +229,8 @@ bool ANetworkSession::Session::wantsToRead() { bool ANetworkSession::Session::wantsToWrite() { return !mSawSendFailure && (mState == CONNECTING - || (mState == CONNECTED && !mOutBuffer.empty()) - || (mState == DATAGRAM && !mOutDatagrams.empty())); + || (mState == CONNECTED && !mOutFragments.empty()) + || (mState == DATAGRAM && !mOutFragments.empty())); } status_t ANetworkSession::Session::readMore() { @@ -407,13 +415,41 @@ status_t ANetworkSession::Session::readMore() { return err; } +void ANetworkSession::Session::dumpFragmentStats(const Fragment &frag) { +#if 0 + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll; + + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("[%lld]: (%4lld ms) %s\n", + frag.mTimeUs / 1000, + delayMs, + kPattern + kPatternSize - n); +#endif +} + status_t ANetworkSession::Session::writeMore() { if (mState == DATAGRAM) { - CHECK(!mOutDatagrams.empty()); + CHECK(!mOutFragments.empty()); status_t err; do { - const sp &datagram = *mOutDatagrams.begin(); + const Fragment &frag = *mOutFragments.begin(); + const sp &datagram = frag.mBuffer; uint8_t *data = datagram->data(); if (data[0] == 0x80 && (data[1] & 0x7f) == 33) { @@ -441,17 +477,21 @@ status_t ANetworkSession::Session::writeMore() { err = OK; if (n > 0) { - mOutDatagrams.erase(mOutDatagrams.begin()); + if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { + dumpFragmentStats(frag); + } + + mOutFragments.erase(mOutFragments.begin()); } else if (n < 0) { err = -errno; } else if (n == 0) { err = -ECONNRESET; } - } while (err == OK && !mOutDatagrams.empty()); + } while (err == OK && !mOutFragments.empty()); if (err == -EAGAIN) { - if (!mOutDatagrams.empty()) { - ALOGI("%d datagrams remain queued.", mOutDatagrams.size()); + if (!mOutFragments.empty()) { + ALOGI("%d datagrams remain queued.", mOutFragments.size()); } err = OK; } @@ -484,23 +524,37 @@ status_t ANetworkSession::Session::writeMore() { } CHECK_EQ(mState, CONNECTED); - CHECK(!mOutBuffer.empty()); + CHECK(!mOutFragments.empty()); ssize_t n; - do { - n = send(mSocket, mOutBuffer.c_str(), mOutBuffer.size(), 0); - } while (n < 0 && errno == EINTR); + while (!mOutFragments.empty()) { + const Fragment &frag = *mOutFragments.begin(); - status_t err = OK; + do { + n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0); + } while (n < 0 && errno == EINTR); - if (n > 0) { -#if 0 - ALOGI("out:"); - hexdump(mOutBuffer.c_str(), n); -#endif + if (n <= 0) { + break; + } - mOutBuffer.erase(0, n); - } else if (n < 0) { + frag.mBuffer->setRange( + frag.mBuffer->offset() + n, frag.mBuffer->size() - n); + + if (frag.mBuffer->size() > 0) { + break; + } + + if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { + dumpFragmentStats(frag); + } + + mOutFragments.erase(mOutFragments.begin()); + } + + status_t err = OK; + + if (n < 0) { err = -errno; } else if (n == 0) { err = -ECONNRESET; @@ -537,32 +591,43 @@ status_t ANetworkSession::Session::writeMore() { return err; } -status_t ANetworkSession::Session::sendRequest(const void *data, ssize_t size) { +status_t ANetworkSession::Session::sendRequest( + const void *data, ssize_t size, bool timeValid, int64_t timeUs) { CHECK(mState == CONNECTED || mState == DATAGRAM); - if (mState == DATAGRAM) { - CHECK_GE(size, 0); - - sp datagram = new ABuffer(size); - memcpy(datagram->data(), data, size); + if (size < 0) { + size = strlen((const char *)data); + } - mOutDatagrams.push_back(datagram); + if (size == 0) { return OK; } + sp buffer; + if (mState == CONNECTED && !mIsRTSPConnection) { CHECK_LE(size, 65535); - uint8_t prefix[2]; - prefix[0] = size >> 8; - prefix[1] = size & 0xff; + buffer = new ABuffer(size + 2); + buffer->data()[0] = size >> 8; + buffer->data()[1] = size & 0xff; + memcpy(buffer->data() + 2, data, size); + } else { + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + } + + Fragment frag; - mOutBuffer.append((const char *)prefix, sizeof(prefix)); + frag.mFlags = 0; + if (timeValid) { + frag.mFlags = FRAGMENT_FLAG_TIME_VALID; + frag.mTimeUs = timeUs; } - mOutBuffer.append( - (const char *)data, - (size >= 0) ? size : strlen((const char *)data)); + frag.mBuffer = buffer; + + mOutFragments.push_back(frag); return OK; } @@ -985,7 +1050,8 @@ status_t ANetworkSession::connectUDPSession( } status_t ANetworkSession::sendRequest( - int32_t sessionID, const void *data, ssize_t size) { + int32_t sessionID, const void *data, ssize_t size, + bool timeValid, int64_t timeUs) { Mutex::Autolock autoLock(mLock); ssize_t index = mSessions.indexOfKey(sessionID); @@ -996,7 +1062,7 @@ status_t ANetworkSession::sendRequest( const sp session = mSessions.valueAt(index); - status_t err = session->sendRequest(data, size); + status_t err = session->sendRequest(data, size, timeValid, timeUs); interrupt(); diff --git a/media/libstagefright/wifi-display/ANetworkSession.h b/media/libstagefright/wifi-display/ANetworkSession.h index 0d7cbd6..7c62b29 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.h +++ b/media/libstagefright/wifi-display/ANetworkSession.h @@ -74,7 +74,8 @@ struct ANetworkSession : public RefBase { status_t destroySession(int32_t sessionID); status_t sendRequest( - int32_t sessionID, const void *data, ssize_t size = -1); + int32_t sessionID, const void *data, ssize_t size = -1, + bool timeValid = false, int64_t timeUs = -1ll); enum NotificationReason { kWhatError, diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index a41f81b..d13a92e 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -252,6 +252,10 @@ status_t MediaSender::queueAccessUnit( fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile); } + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + tsPackets->meta()->setInt64("timeUs", timeUs); + err = mTSSender->queueBuffer( tsPackets, 33 /* packetType */, diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 8cd712d..c8e265c 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -194,6 +194,9 @@ status_t RTPSender::queueTSPackets( const sp &tsPackets, uint8_t packetType) { CHECK_EQ(0, tsPackets->size() % 188); + int64_t timeUs; + CHECK(tsPackets->meta()->findInt64("timeUs", &timeUs)); + const size_t numTSPackets = tsPackets->size() / 188; size_t srcOffset = 0; @@ -232,13 +235,19 @@ status_t RTPSender::queueTSPackets( memcpy(&rtp[12], tsPackets->data() + srcOffset, numTSPackets * 188); udpPacket->setRange(0, 12 + numTSPackets * 188); - status_t err = sendRTPPacket(udpPacket, true /* storeInHistory */); + + srcOffset += numTSPackets * 188; + bool isLastPacket = (srcOffset == tsPackets->size()); + + status_t err = sendRTPPacket( + udpPacket, + true /* storeInHistory */, + isLastPacket /* timeValid */, + timeUs); if (err != OK) { return err; } - - srcOffset += numTSPackets * 188; } return OK; @@ -395,11 +404,13 @@ status_t RTPSender::queueAVCBuffer( } status_t RTPSender::sendRTPPacket( - const sp &buffer, bool storeInHistory) { + const sp &buffer, bool storeInHistory, + bool timeValid, int64_t timeUs) { CHECK(mRTPConnected); status_t err = mNetSession->sendRequest( - mRTPSessionID, buffer->data(), buffer->size()); + mRTPSessionID, buffer->data(), buffer->size(), + timeValid, timeUs); if (err != OK) { return err; diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 83c6223..90b1796 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -94,7 +94,9 @@ private: status_t queueTSPackets(const sp &tsPackets, uint8_t packetType); status_t queueAVCBuffer(const sp &accessUnit, uint8_t packetType); - status_t sendRTPPacket(const sp &packet, bool storeInHistory); + status_t sendRTPPacket( + const sp &packet, bool storeInHistory, + bool timeValid = false, int64_t timeUs = -1ll); void onNetNotify(bool isRTP, const sp &msg); diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 3a7a6e2..4f7dcc8 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -200,6 +200,8 @@ static void createFileSource( CHECK_EQ((status_t)OK, source->start(iface.c_str())); client->waitUntilDone(); + + source->stop(); } } // namespace android -- cgit v1.1 From 0003b9b56e77764c77fd4e4e1a5d6e44a55e5b8a Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 19 Mar 2013 09:57:29 -0700 Subject: Fix valgrind error The volume member of the BundledEffectContext class was not being initialized, resulting in uninitialized data being used for calculations and control flow. Change-Id: I84bf9fd478e5d0479e781323b21c7c03dea958c5 --- media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'media') diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 94b9acf..54f8d9e 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -224,6 +224,7 @@ extern "C" int EffectCreate(const effect_uuid_t *uuid, pContext->pBundledContext->NumberEffectsEnabled = 0; pContext->pBundledContext->NumberEffectsCalled = 0; pContext->pBundledContext->firstVolume = LVM_TRUE; + pContext->pBundledContext->volume = 0; #ifdef LVM_PCM char fileName[256]; -- cgit v1.1 From 0e6858d6aea12fc585a8c7d217c1271878655081 Mon Sep 17 00:00:00 2001 From: Dan Morrill Date: Thu, 7 Mar 2013 14:40:40 -0800 Subject: Turn off debug tags in stagefright modules. LOCAL_MODULE_TAGS := debug causes the module to be included in every userdebug build, regardless of whether it's specified as a dep by the device config. This CL switches them all to optional (i.e. default behavior) so that we can do (userdebug) device builds without pulling these in. Change-Id: I4b7b65afea61865dd38b3af55550fb8f10edf66d --- media/libstagefright/codecs/aacenc/SampleCode/Android.mk | 2 +- media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk | 2 +- media/libstagefright/codecs/on2/h264dec/Android.mk | 2 +- media/libstagefright/id3/Android.mk | 2 +- media/libstagefright/rtsp/Android.mk | 2 +- media/libstagefright/wifi-display/Android.mk | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/aacenc/SampleCode/Android.mk b/media/libstagefright/codecs/aacenc/SampleCode/Android.mk index 01016e7..d06dcf6 100644 --- a/media/libstagefright/codecs/aacenc/SampleCode/Android.mk +++ b/media/libstagefright/codecs/aacenc/SampleCode/Android.mk @@ -5,7 +5,7 @@ LOCAL_SRC_FILES := \ AAC_E_SAMPLES.c \ ../../common/cmnMemory.c -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional LOCAL_MODULE := AACEncTest diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk index db34d08..c203f77 100644 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk @@ -5,7 +5,7 @@ LOCAL_SRC_FILES := \ AMRWB_E_SAMPLE.c \ ../../common/cmnMemory.c -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional LOCAL_MODULE := AMRWBEncTest LOCAL_ARM_MODE := arm diff --git a/media/libstagefright/codecs/on2/h264dec/Android.mk b/media/libstagefright/codecs/on2/h264dec/Android.mk index 772fd60..0a273e2 100644 --- a/media/libstagefright/codecs/on2/h264dec/Android.mk +++ b/media/libstagefright/codecs/on2/h264dec/Android.mk @@ -119,7 +119,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/inc LOCAL_SHARED_LIBRARIES := libstagefright_soft_h264dec -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional LOCAL_MODULE := decoder diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk index ff35d4a..995ab83 100644 --- a/media/libstagefright/id3/Android.mk +++ b/media/libstagefright/id3/Android.mk @@ -21,7 +21,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libstagefright_id3 -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional LOCAL_MODULE := testid3 diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index 9e2724d..e77c69c 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -51,7 +51,7 @@ LOCAL_C_INCLUDES:= \ LOCAL_CFLAGS += -Wno-multichar -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional LOCAL_MODULE:= rtp_test diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index f81929c..137ebe3 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -63,7 +63,7 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= wfd -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) @@ -85,7 +85,7 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= udptest -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) @@ -107,6 +107,6 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= rtptest -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) -- cgit v1.1 From 820ebf8d452165d9a7619e2667ffa3c0b638da39 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 21 Mar 2013 11:35:48 -0700 Subject: Ensure that the payload in each TS packet is an even multiple of 16 bytes long (except for the final TS packet) as specified by HDCP. Change-Id: I45d49d347c06f5daae310f196d9a8484be0f3ca0 related-to-bug: 7549145 --- .../wifi-display/source/TSPacketizer.cpp | 201 ++++++++++++++++----- 1 file changed, 158 insertions(+), 43 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index d993764..2c4a373 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -502,16 +502,121 @@ status_t TSPacketizer::packetize( // reserved = b1 // the first fragment of "buffer" follows + // Each transport packet (except for the last one contributing to the PES + // payload) must contain a multiple of 16 bytes of payload per HDCP spec. + bool alignPayload = + (mFlags & (EMIT_HDCP20_DESCRIPTOR | EMIT_HDCP21_DESCRIPTOR)); + + /* + a) The very first PES transport stream packet contains + + 4 bytes of TS header + ... padding + 14 bytes of static PES header + PES_private_data_len + 1 bytes (only if PES_private_data_len > 0) + numStuffingBytes bytes + + followed by the payload + + b) Subsequent PES transport stream packets contain + + 4 bytes of TS header + ... padding + + followed by the payload + */ + size_t PES_packet_length = accessUnit->size() + 8 + numStuffingBytes; if (PES_private_data_len > 0) { PES_packet_length += PES_private_data_len + 1; } - size_t numTSPackets; - if (PES_packet_length <= 178) { - numTSPackets = 1; - } else { - numTSPackets = 1 + ((PES_packet_length - 178) + 183) / 184; + size_t numTSPackets = 1; + + { + // Make sure the PES header fits into a single TS packet: + size_t PES_header_size = 14 + numStuffingBytes; + if (PES_private_data_len > 0) { + PES_header_size += PES_private_data_len + 1; + } + + CHECK_LE(PES_header_size, 188u - 4u); + + size_t sizeAvailableForPayload = 188 - 4 - PES_header_size; + size_t numBytesOfPayload = accessUnit->size(); + + if (numBytesOfPayload > sizeAvailableForPayload) { + numBytesOfPayload = sizeAvailableForPayload; + + if (alignPayload && numBytesOfPayload > 16) { + numBytesOfPayload -= (numBytesOfPayload % 16); + } + } + + // size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload; + ALOGV("packet 1 contains %zd padding bytes and %zd bytes of payload", + numPaddingBytes, numBytesOfPayload); + + size_t numBytesOfPayloadRemaining = accessUnit->size() - numBytesOfPayload; + +#if 0 + // The following hopefully illustrates the logic that led to the + // more efficient computation in the #else block... + + while (numBytesOfPayloadRemaining > 0) { + size_t sizeAvailableForPayload = 188 - 4; + + size_t numBytesOfPayload = numBytesOfPayloadRemaining; + + if (numBytesOfPayload > sizeAvailableForPayload) { + numBytesOfPayload = sizeAvailableForPayload; + + if (alignPayload && numBytesOfPayload > 16) { + numBytesOfPayload -= (numBytesOfPayload % 16); + } + } + + size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload; + ALOGI("packet %zd contains %zd padding bytes and %zd bytes of payload", + numTSPackets + 1, numPaddingBytes, numBytesOfPayload); + + numBytesOfPayloadRemaining -= numBytesOfPayload; + ++numTSPackets; + } +#else + // This is how many bytes of payload each subsequent TS packet + // can contain at most. + sizeAvailableForPayload = 188 - 4; + size_t sizeAvailableForAlignedPayload = sizeAvailableForPayload; + if (alignPayload) { + // We're only going to use a subset of the available space + // since we need to make each fragment a multiple of 16 in size. + sizeAvailableForAlignedPayload -= + (sizeAvailableForAlignedPayload % 16); + } + + size_t numFullTSPackets = + numBytesOfPayloadRemaining / sizeAvailableForAlignedPayload; + + numTSPackets += numFullTSPackets; + + numBytesOfPayloadRemaining -= + numFullTSPackets * sizeAvailableForAlignedPayload; + + // numBytesOfPayloadRemaining < sizeAvailableForAlignedPayload + if (numFullTSPackets == 0 && numBytesOfPayloadRemaining > 0) { + // There wasn't enough payload left to form a full aligned payload, + // the last packet doesn't have to be aligned. + ++numTSPackets; + } else if (numFullTSPackets > 0 + && numBytesOfPayloadRemaining + + sizeAvailableForAlignedPayload > sizeAvailableForPayload) { + // The last packet emitted had a full aligned payload and together + // with the bytes remaining does exceed the unaligned payload + // size, so we need another packet. + ++numTSPackets; + } +#endif } if (flags & EMIT_PAT_AND_PMT) { @@ -755,8 +860,6 @@ status_t TSPacketizer::packetize( uint64_t PTS = (timeUs * 9ll) / 100ll; - bool padding = (PES_packet_length < (188 - 10)); - if (PES_packet_length >= 65536) { // This really should only happen for video. CHECK(track->isVideo()); @@ -765,19 +868,37 @@ status_t TSPacketizer::packetize( PES_packet_length = 0; } + size_t sizeAvailableForPayload = 188 - 4 - 14 - numStuffingBytes; + if (PES_private_data_len > 0) { + sizeAvailableForPayload -= PES_private_data_len + 1; + } + + size_t copy = accessUnit->size(); + + if (copy > sizeAvailableForPayload) { + copy = sizeAvailableForPayload; + + if (alignPayload && copy > 16) { + copy -= (copy % 16); + } + } + + size_t numPaddingBytes = sizeAvailableForPayload - copy; + uint8_t *ptr = packetDataStart; *ptr++ = 0x47; *ptr++ = 0x40 | (track->PID() >> 8); *ptr++ = track->PID() & 0xff; - *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter(); - if (padding) { - size_t paddingSize = 188 - 10 - PES_packet_length; - *ptr++ = paddingSize - 1; - if (paddingSize >= 2) { + *ptr++ = (numPaddingBytes > 0 ? 0x30 : 0x10) + | track->incrementContinuityCounter(); + + if (numPaddingBytes > 0) { + *ptr++ = numPaddingBytes - 1; + if (numPaddingBytes >= 2) { *ptr++ = 0x00; - memset(ptr, 0xff, paddingSize - 2); - ptr += paddingSize - 2; + memset(ptr, 0xff, numPaddingBytes - 2); + ptr += numPaddingBytes - 2; } } @@ -813,25 +934,14 @@ status_t TSPacketizer::packetize( *ptr++ = 0xff; } - // 18 bytes of TS/PES header leave 188 - 18 = 170 bytes for the payload - - size_t sizeLeft = packetDataStart + 188 - ptr; - size_t copy = accessUnit->size(); - if (copy > sizeLeft) { - copy = sizeLeft; - } - memcpy(ptr, accessUnit->data(), copy); ptr += copy; - CHECK_EQ(sizeLeft, copy); - memset(ptr, 0xff, sizeLeft - copy); + CHECK_EQ(ptr, packetDataStart + 188); packetDataStart += 188; size_t offset = copy; while (offset < accessUnit->size()) { - bool padding = (accessUnit->size() - offset) < (188 - 4); - // for subsequent fragments of "buffer": // 0x47 // transport_error_indicator = b0 @@ -843,35 +953,40 @@ status_t TSPacketizer::packetize( // continuity_counter = b???? // the fragment of "buffer" follows. + size_t sizeAvailableForPayload = 188 - 4; + + size_t copy = accessUnit->size() - offset; + + if (copy > sizeAvailableForPayload) { + copy = sizeAvailableForPayload; + + if (alignPayload && copy > 16) { + copy -= (copy % 16); + } + } + + size_t numPaddingBytes = sizeAvailableForPayload - copy; + uint8_t *ptr = packetDataStart; *ptr++ = 0x47; *ptr++ = 0x00 | (track->PID() >> 8); *ptr++ = track->PID() & 0xff; - *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter(); + *ptr++ = (numPaddingBytes > 0 ? 0x30 : 0x10) + | track->incrementContinuityCounter(); - if (padding) { - size_t paddingSize = 188 - 4 - (accessUnit->size() - offset); - *ptr++ = paddingSize - 1; - if (paddingSize >= 2) { + if (numPaddingBytes > 0) { + *ptr++ = numPaddingBytes - 1; + if (numPaddingBytes >= 2) { *ptr++ = 0x00; - memset(ptr, 0xff, paddingSize - 2); - ptr += paddingSize - 2; + memset(ptr, 0xff, numPaddingBytes - 2); + ptr += numPaddingBytes - 2; } } - // 4 bytes of TS header leave 188 - 4 = 184 bytes for the payload - - size_t sizeLeft = packetDataStart + 188 - ptr; - size_t copy = accessUnit->size() - offset; - if (copy > sizeLeft) { - copy = sizeLeft; - } - memcpy(ptr, accessUnit->data() + offset, copy); ptr += copy; - CHECK_EQ(sizeLeft, copy); - memset(ptr, 0xff, sizeLeft - copy); + CHECK_EQ(ptr, packetDataStart + 188); offset += copy; packetDataStart += 188; -- cgit v1.1 From bafb682ec7f51486e751fea954169deb91846063 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Fri, 22 Mar 2013 15:26:39 -0700 Subject: Load crypto plugins from additional shared libraries Currently crypto plugins are expected to be in libdrmdecrypt.so. When there are multiple plugins supporting different schemes, this approach requires source code integration across vendors which is unmanagable. Also, for integration with MediaDrm where the crypto keys are obtained from a drm server, the MediaCrypto plugin needs to interoperate with the MediaDrm plugin. This change allows {MediaCrypto, MediaDrm} pairs that are logically related to be implemented in a common shared library. Change-Id: I7f6638f29171f91609fc2d944396365568630b56 --- media/libmedia/ICrypto.cpp | 2 +- media/libmediaplayerservice/Crypto.cpp | 161 ++++++++++++++++++++++++++------- media/libmediaplayerservice/Crypto.h | 15 ++- 3 files changed, 139 insertions(+), 39 deletions(-) (limited to 'media') diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp index 2defc2d..98b183a 100644 --- a/media/libmedia/ICrypto.cpp +++ b/media/libmedia/ICrypto.cpp @@ -48,7 +48,7 @@ struct BpCrypto : public BpInterface { return reply.readInt32(); } - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const { + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) { Parcel data, reply; data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); data.write(uuid, 16); diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp index 0e8f913..ae4d845 100644 --- a/media/libmediaplayerservice/Crypto.cpp +++ b/media/libmediaplayerservice/Crypto.cpp @@ -17,6 +17,8 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Crypto" #include +#include +#include #include "Crypto.h" @@ -26,87 +28,176 @@ #include #include -#include - namespace android { +KeyedVector, String8> Crypto::mUUIDToLibraryPathMap; +KeyedVector > Crypto::mLibraryPathToOpenLibraryMap; +Mutex Crypto::mMapLock; + +static bool operator<(const Vector &lhs, const Vector &rhs) { + if (lhs.size() < rhs.size()) { + return true; + } else if (lhs.size() > rhs.size()) { + return false; + } + + return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0; +} + Crypto::Crypto() : mInitCheck(NO_INIT), - mLibHandle(NULL), mFactory(NULL), mPlugin(NULL) { - mInitCheck = init(); } Crypto::~Crypto() { delete mPlugin; mPlugin = NULL; + closeFactory(); +} +void Crypto::closeFactory() { delete mFactory; mFactory = NULL; - - if (mLibHandle != NULL) { - dlclose(mLibHandle); - mLibHandle = NULL; - } + mLibrary.clear(); } status_t Crypto::initCheck() const { return mInitCheck; } -status_t Crypto::init() { - mLibHandle = dlopen("libdrmdecrypt.so", RTLD_NOW); +/* + * Search the plugins directory for a plugin that supports the scheme + * specified by uuid + * + * If found: + * mLibrary holds a strong pointer to the dlopen'd library + * mFactory is set to the library's factory method + * mInitCheck is set to OK + * + * If not found: + * mLibrary is cleared and mFactory are set to NULL + * mInitCheck is set to an error (!OK) + */ +void Crypto::findFactoryForScheme(const uint8_t uuid[16]) { - if (mLibHandle == NULL) { - ALOGE("Unable to locate libdrmdecrypt.so"); + closeFactory(); - return ERROR_UNSUPPORTED; + // lock static maps + Mutex::Autolock autoLock(mMapLock); + + // first check cache + Vector uuidVector; + uuidVector.appendArray(uuid, sizeof(uuid)); + ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector); + if (index >= 0) { + if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) { + mInitCheck = OK; + return; + } else { + ALOGE("Failed to load from cached library path!"); + mInitCheck = ERROR_UNSUPPORTED; + return; + } } - typedef CryptoFactory *(*CreateCryptoFactoryFunc)(); - CreateCryptoFactoryFunc createCryptoFactory = - (CreateCryptoFactoryFunc)dlsym(mLibHandle, "createCryptoFactory"); + // no luck, have to search + String8 dirPath("/vendor/lib/mediadrm"); + String8 pluginPath; - if (createCryptoFactory == NULL - || ((mFactory = createCryptoFactory()) == NULL)) { - if (createCryptoFactory == NULL) { - ALOGE("Unable to find symbol 'createCryptoFactory'."); - } else { - ALOGE("createCryptoFactory() failed."); + DIR* pDir = opendir(dirPath.string()); + if (pDir) { + struct dirent* pEntry; + while ((pEntry = readdir(pDir))) { + + pluginPath = dirPath + "/" + pEntry->d_name; + + if (pluginPath.getPathExtension() == ".so") { + + if (loadLibraryForScheme(pluginPath, uuid)) { + mUUIDToLibraryPathMap.add(uuidVector, pluginPath); + mInitCheck = OK; + closedir(pDir); + return; + } + } } - dlclose(mLibHandle); - mLibHandle = NULL; + closedir(pDir); + } - return ERROR_UNSUPPORTED; + // try the legacy libdrmdecrypt.so + pluginPath = "libdrmdecrypt.so"; + if (loadLibraryForScheme(pluginPath, uuid)) { + mUUIDToLibraryPathMap.add(uuidVector, pluginPath); + mInitCheck = OK; + return; } - return OK; + ALOGE("Failed to find crypto plugin"); + mInitCheck = ERROR_UNSUPPORTED; } -bool Crypto::isCryptoSchemeSupported(const uint8_t uuid[16]) const { - Mutex::Autolock autoLock(mLock); +bool Crypto::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) { - if (mInitCheck != OK) { + // get strong pointer to open shared library + ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path); + if (index >= 0) { + mLibrary = mLibraryPathToOpenLibraryMap[index].promote(); + } else { + index = mLibraryPathToOpenLibraryMap.add(path, NULL); + } + + if (!mLibrary.get()) { + mLibrary = new SharedLibrary(path); + if (!*mLibrary) { + return false; + } + + mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary); + } + + typedef CryptoFactory *(*CreateCryptoFactoryFunc)(); + + CreateCryptoFactoryFunc createCryptoFactory = + (CreateCryptoFactoryFunc)mLibrary->lookup("createCryptoFactory"); + + if (createCryptoFactory == NULL || + (mFactory = createCryptoFactory()) == NULL || + !mFactory->isCryptoSchemeSupported(uuid)) { + closeFactory(); return false; } + return true; +} - return mFactory->isCryptoSchemeSupported(uuid); +bool Crypto::isCryptoSchemeSupported(const uint8_t uuid[16]) { + Mutex::Autolock autoLock(mLock); + + if (mFactory && mFactory->isCryptoSchemeSupported(uuid)) { + return true; + } + + findFactoryForScheme(uuid); + return (mInitCheck == OK); } status_t Crypto::createPlugin( const uint8_t uuid[16], const void *data, size_t size) { Mutex::Autolock autoLock(mLock); - if (mInitCheck != OK) { - return mInitCheck; - } - if (mPlugin != NULL) { return -EINVAL; } + if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) { + findFactoryForScheme(uuid); + } + + if (mInitCheck != OK) { + return mInitCheck; + } + return mFactory->createPlugin(uuid, data, size, &mPlugin); } diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h index d066774..c44ae34 100644 --- a/media/libmediaplayerservice/Crypto.h +++ b/media/libmediaplayerservice/Crypto.h @@ -20,6 +20,9 @@ #include #include +#include + +#include "SharedLibrary.h" namespace android { @@ -32,7 +35,7 @@ struct Crypto : public BnCrypto { virtual status_t initCheck() const; - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const; + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); virtual status_t createPlugin( const uint8_t uuid[16], const void *data, size_t size); @@ -56,11 +59,17 @@ private: mutable Mutex mLock; status_t mInitCheck; - void *mLibHandle; + sp mLibrary; CryptoFactory *mFactory; CryptoPlugin *mPlugin; - status_t init(); + static KeyedVector, String8> mUUIDToLibraryPathMap; + static KeyedVector > mLibraryPathToOpenLibraryMap; + static Mutex mMapLock; + + void findFactoryForScheme(const uint8_t uuid[16]); + bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]); + void closeFactory(); DISALLOW_EVIL_CONSTRUCTORS(Crypto); }; -- cgit v1.1 From 6e98aba4d23d00cab236d993d895f57ea76ea0e5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 22 Mar 2013 09:56:29 -0700 Subject: Separate the mode of the RTP and RTCP channels. I now can use a TCP reliable data channel with a UDP back channel. Change-Id: Ieb0f0970e3a6da4cff250e9547e181c0c961b9fb --- .../libstagefright/wifi-display/MediaReceiver.cpp | 7 +- media/libstagefright/wifi-display/MediaSender.cpp | 11 +- media/libstagefright/wifi-display/rtp/RTPBase.h | 1 + .../wifi-display/rtp/RTPReceiver.cpp | 143 ++++++++++++++------- .../libstagefright/wifi-display/rtp/RTPReceiver.h | 11 +- .../libstagefright/wifi-display/rtp/RTPSender.cpp | 55 +++++--- media/libstagefright/wifi-display/rtp/RTPSender.h | 6 +- media/libstagefright/wifi-display/rtptest.cpp | 7 +- 8 files changed, 167 insertions(+), 74 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp index 10a2dff..e2e791d 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.cpp +++ b/media/libstagefright/wifi-display/MediaReceiver.cpp @@ -73,7 +73,12 @@ ssize_t MediaReceiver::addTrack( info.mReceiver->registerPacketType( 97, RTPReceiver::PACKETIZATION_H264); - status_t err = info.mReceiver->initAsync(transportMode, localRTPPort); + status_t err = info.mReceiver->initAsync( + transportMode, // rtpMode + transportMode == RTPReceiver::TRANSPORT_UDP + ? transportMode + : RTPReceiver::TRANSPORT_NONE, // rtcpMode + localRTPPort); if (err != OK) { looper()->unregisterHandler(info.mReceiver->id()); diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index d13a92e..6fc50f7 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -124,10 +124,14 @@ status_t MediaSender::initAsync( looper()->registerHandler(mTSSender); err = mTSSender->initAsync( - transportMode, remoteHost, remoteRTPPort, + transportMode, // rtpMode remoteRTCPPort, + (transportMode == RTPSender::TRANSPORT_UDP + && remoteRTCPPort >= 0) + ? transportMode + : RTPSender::TRANSPORT_NONE, // rtcpMode localRTPPort); if (err != OK) { @@ -174,10 +178,13 @@ status_t MediaSender::initAsync( looper()->registerHandler(info->mSender); status_t err = info->mSender->initAsync( - transportMode, remoteHost, remoteRTPPort, + transportMode, // rtpMode remoteRTCPPort, + (transportMode == RTPSender::TRANSPORT_UDP && remoteRTCPPort >= 0) + ? transportMode + : RTPSender::TRANSPORT_NONE, // rtcpMode localRTPPort); if (err != OK) { diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h index 6507a6f..e3fa845 100644 --- a/media/libstagefright/wifi-display/rtp/RTPBase.h +++ b/media/libstagefright/wifi-display/rtp/RTPBase.h @@ -29,6 +29,7 @@ struct RTPBase { enum TransportMode { TRANSPORT_UNDEFINED, + TRANSPORT_NONE, TRANSPORT_UDP, TRANSPORT_TCP, TRANSPORT_TCP_INTERLEAVED, diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp index 8711b08..c55e0be 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -407,13 +407,22 @@ RTPReceiver::RTPReceiver( const sp ¬ify) : mNetSession(netSession), mNotify(notify), - mMode(TRANSPORT_UNDEFINED), + mRTPMode(TRANSPORT_UNDEFINED), + mRTCPMode(TRANSPORT_UNDEFINED), mRTPSessionID(0), mRTCPSessionID(0), - mRTPClientSessionID(0) { + mRTPConnected(false), + mRTCPConnected(false), + mRTPClientSessionID(0), + mRTCPClientSessionID(0) { } RTPReceiver::~RTPReceiver() { + if (mRTCPClientSessionID != 0) { + mNetSession->destroySession(mRTCPClientSessionID); + mRTCPClientSessionID = 0; + } + if (mRTPClientSessionID != 0) { mNetSession->destroySession(mRTPClientSessionID); mRTPClientSessionID = 0; @@ -430,17 +439,24 @@ RTPReceiver::~RTPReceiver() { } } -status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) { - if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) { +status_t RTPReceiver::initAsync( + TransportMode rtpMode, + TransportMode rtcpMode, + int32_t *outLocalRTPPort) { + if (mRTPMode != TRANSPORT_UNDEFINED + || rtpMode == TRANSPORT_UNDEFINED + || rtpMode == TRANSPORT_NONE + || rtcpMode == TRANSPORT_UNDEFINED) { return INVALID_OPERATION; } - CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED); + CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED); + CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED); sp rtpNotify = new AMessage(kWhatRTPNotify, id()); sp rtcpNotify; - if (mode == TRANSPORT_UDP) { + if (rtcpMode != TRANSPORT_NONE) { rtcpNotify = new AMessage(kWhatRTCPNotify, id()); } @@ -456,13 +472,13 @@ status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) { localRTPPort = PickRandomRTPPort(); status_t err; - if (mode == TRANSPORT_UDP) { + if (rtpMode == TRANSPORT_UDP) { err = mNetSession->createUDPSession( localRTPPort, rtpNotify, &mRTPSessionID); } else { - CHECK_EQ(mode, TRANSPORT_TCP); + CHECK_EQ(rtpMode, TRANSPORT_TCP); err = mNetSession->createTCPDatagramSession( ifaceAddr, localRTPPort, @@ -474,15 +490,22 @@ status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) { continue; } - if (mode == TRANSPORT_TCP) { + if (rtcpMode == TRANSPORT_NONE) { break; + } else if (rtcpMode == TRANSPORT_UDP) { + err = mNetSession->createUDPSession( + localRTPPort + 1, + rtcpNotify, + &mRTCPSessionID); + } else { + CHECK_EQ(rtpMode, TRANSPORT_TCP); + err = mNetSession->createTCPDatagramSession( + ifaceAddr, + localRTPPort + 1, + rtcpNotify, + &mRTCPSessionID); } - err = mNetSession->createUDPSession( - localRTPPort + 1, - rtcpNotify, - &mRTCPSessionID); - if (err == OK) { break; } @@ -491,7 +514,8 @@ status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) { mRTPSessionID = 0; } - mMode = mode; + mRTPMode = rtpMode; + mRTCPMode = rtcpMode; *outLocalRTPPort = localRTPPort; return OK; @@ -499,35 +523,46 @@ status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) { status_t RTPReceiver::connect( const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) { - if (mMode == TRANSPORT_TCP) { - return OK; - } + status_t err; + + if (mRTPMode == TRANSPORT_UDP) { + CHECK(!mRTPConnected); + + err = mNetSession->connectUDPSession( + mRTPSessionID, remoteHost, remoteRTPPort); + + if (err != OK) { + notifyInitDone(err); + return err; + } - status_t err = mNetSession->connectUDPSession( - mRTPSessionID, remoteHost, remoteRTPPort); + ALOGI("connectUDPSession RTP successful."); - if (err != OK) { - notifyInitDone(err); - return err; + mRTPConnected = true; } - ALOGI("connectUDPSession RTP successful."); + if (mRTCPMode == TRANSPORT_UDP) { + CHECK(!mRTCPConnected); - if (remoteRTCPPort >= 0) { err = mNetSession->connectUDPSession( mRTCPSessionID, remoteHost, remoteRTCPPort); if (err != OK) { - ALOGI("connect failed w/ err %d", err); - notifyInitDone(err); return err; } scheduleSendRR(); + + ALOGI("connectUDPSession RTCP successful."); + + mRTCPConnected = true; } - notifyInitDone(OK); + if (mRTPConnected + && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { + notifyInitDone(OK); + } return OK; } @@ -615,15 +650,18 @@ void RTPReceiver::onNetNotify(bool isRTP, const sp &msg) { if (sessionID == mRTPSessionID) { mRTPSessionID = 0; - - if (mMode == TRANSPORT_TCP && mRTPClientSessionID == 0) { - notifyInitDone(err); - break; - } } else if (sessionID == mRTCPSessionID) { mRTCPSessionID = 0; } else if (sessionID == mRTPClientSessionID) { mRTPClientSessionID = 0; + } else if (sessionID == mRTCPClientSessionID) { + mRTCPClientSessionID = 0; + } + + if (!mRTPConnected + || (mRTCPMode != TRANSPORT_NONE && !mRTCPConnected)) { + notifyInitDone(err); + break; } notifyError(err); @@ -645,22 +683,39 @@ void RTPReceiver::onNetNotify(bool isRTP, const sp &msg) { case ANetworkSession::kWhatClientConnected: { - CHECK_EQ(mMode, TRANSPORT_TCP); - CHECK(isRTP); - int32_t sessionID; CHECK(msg->findInt32("sessionID", &sessionID)); - if (mRTPClientSessionID != 0) { - // We only allow a single client connection. - mNetSession->destroySession(sessionID); - sessionID = 0; - break; - } + if (isRTP) { + CHECK_EQ(mRTPMode, TRANSPORT_TCP); - mRTPClientSessionID = sessionID; + if (mRTPClientSessionID != 0) { + // We only allow a single client connection. + mNetSession->destroySession(sessionID); + sessionID = 0; + break; + } + + mRTPClientSessionID = sessionID; + mRTPConnected = true; + } else { + CHECK_EQ(mRTCPMode, TRANSPORT_TCP); + + if (mRTCPClientSessionID != 0) { + // We only allow a single client connection. + mNetSession->destroySession(sessionID); + sessionID = 0; + break; + } - notifyInitDone(OK); + mRTCPClientSessionID = sessionID; + mRTCPConnected = true; + } + + if (mRTPConnected + && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { + notifyInitDone(OK); + } break; } } diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h index ec4671d..abbe6a8 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h @@ -46,7 +46,10 @@ struct RTPReceiver : public RTPBase, public AHandler { status_t registerPacketType( uint8_t packetType, PacketizationMode mode); - status_t initAsync(TransportMode mode, int32_t *outLocalRTPPort); + status_t initAsync( + TransportMode rtpMode, + TransportMode rtcpMode, + int32_t *outLocalRTPPort); status_t connect( const char *remoteHost, @@ -79,11 +82,15 @@ private: sp mNetSession; sp mNotify; - TransportMode mMode; + TransportMode mRTPMode; + TransportMode mRTCPMode; int32_t mRTPSessionID; int32_t mRTCPSessionID; + bool mRTPConnected; + bool mRTCPConnected; int32_t mRTPClientSessionID; // in TRANSPORT_TCP mode. + int32_t mRTCPClientSessionID; // in TRANSPORT_TCP mode. KeyedVector mPacketTypes; KeyedVector > mSources; diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index c8e265c..c686e01 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -38,7 +38,8 @@ RTPSender::RTPSender( const sp ¬ify) : mNetSession(netSession), mNotify(notify), - mMode(TRANSPORT_UNDEFINED), + mRTPMode(TRANSPORT_UNDEFINED), + mRTCPMode(TRANSPORT_UNDEFINED), mRTPSessionID(0), mRTCPSessionID(0), mRTPConnected(false), @@ -74,18 +75,24 @@ int32_t RTPBase::PickRandomRTPPort() { } status_t RTPSender::initAsync( - TransportMode mode, const char *remoteHost, int32_t remoteRTPPort, + TransportMode rtpMode, int32_t remoteRTCPPort, + TransportMode rtcpMode, int32_t *outLocalRTPPort) { - if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) { + if (mRTPMode != TRANSPORT_UNDEFINED + || rtpMode == TRANSPORT_UNDEFINED + || rtpMode == TRANSPORT_NONE + || rtcpMode == TRANSPORT_UNDEFINED) { return INVALID_OPERATION; } - CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED); + CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED); + CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED); - if (mode == TRANSPORT_TCP && remoteRTCPPort >= 0) { + if (rtcpMode == TRANSPORT_NONE && remoteRTCPPort >= 0 + || rtcpMode != TRANSPORT_NONE && remoteRTCPPort < 0) { return INVALID_OPERATION; } @@ -105,7 +112,7 @@ status_t RTPSender::initAsync( localRTPPort = PickRandomRTPPort(); status_t err; - if (mode == TRANSPORT_UDP) { + if (rtpMode == TRANSPORT_UDP) { err = mNetSession->createUDPSession( localRTPPort, remoteHost, @@ -113,7 +120,7 @@ status_t RTPSender::initAsync( rtpNotify, &mRTPSessionID); } else { - CHECK_EQ(mode, TRANSPORT_TCP); + CHECK_EQ(rtpMode, TRANSPORT_TCP); err = mNetSession->createTCPDatagramSession( localRTPPort, remoteHost, @@ -130,7 +137,7 @@ status_t RTPSender::initAsync( break; } - if (mode == TRANSPORT_UDP) { + if (rtcpMode == TRANSPORT_UDP) { err = mNetSession->createUDPSession( localRTPPort + 1, remoteHost, @@ -138,7 +145,7 @@ status_t RTPSender::initAsync( rtcpNotify, &mRTCPSessionID); } else { - CHECK_EQ(mode, TRANSPORT_TCP); + CHECK_EQ(rtcpMode, TRANSPORT_TCP); err = mNetSession->createTCPDatagramSession( localRTPPort + 1, remoteHost, @@ -155,15 +162,20 @@ status_t RTPSender::initAsync( mRTPSessionID = 0; } - if (mode == TRANSPORT_UDP) { + if (rtpMode == TRANSPORT_UDP) { mRTPConnected = true; + } + + if (rtcpMode == TRANSPORT_UDP) { mRTCPConnected = true; } - mMode = mode; + mRTPMode = rtpMode; + mRTCPMode = rtcpMode; *outLocalRTPPort = localRTPPort; - if (mMode == TRANSPORT_UDP) { + if (mRTPMode == TRANSPORT_UDP + && (mRTCPMode == TRANSPORT_UDP || mRTCPMode == TRANSPORT_NONE)) { notifyInitDone(OK); } @@ -496,12 +508,12 @@ void RTPSender::onNetNotify(bool isRTP, const sp &msg) { mRTCPSessionID = 0; } - if (mMode == TRANSPORT_TCP) { - if (!mRTPConnected - || (mRTCPSessionID > 0 && !mRTCPConnected)) { - notifyInitDone(err); - break; - } + if (!mRTPConnected + || (mRTPMode != TRANSPORT_NONE && !mRTCPConnected)) { + // We haven't completed initialization, attach the error + // to the notification instead. + notifyInitDone(err); + break; } notifyError(err); @@ -523,20 +535,21 @@ void RTPSender::onNetNotify(bool isRTP, const sp &msg) { case ANetworkSession::kWhatConnected: { - CHECK_EQ(mMode, TRANSPORT_TCP); - int32_t sessionID; CHECK(msg->findInt32("sessionID", &sessionID)); if (isRTP) { + CHECK_EQ(mRTPMode, TRANSPORT_TCP); CHECK_EQ(sessionID, mRTPSessionID); mRTPConnected = true; } else { + CHECK_EQ(mRTCPMode, TRANSPORT_TCP); CHECK_EQ(sessionID, mRTCPSessionID); mRTCPConnected = true; } - if (mRTPConnected && (mRTCPSessionID == 0 || mRTCPConnected)) { + if (mRTPConnected + && (mRTCPMode == TRANSPORT_NONE || mRTCPConnected)) { notifyInitDone(OK); } break; diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 90b1796..8409b8d 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -43,10 +43,11 @@ struct RTPSender : public RTPBase, public AHandler { const sp ¬ify); status_t initAsync( - TransportMode mode, const char *remoteHost, int32_t remoteRTPPort, + TransportMode rtpMode, int32_t remoteRTCPPort, + TransportMode rtcpMode, int32_t *outLocalRTPPort); status_t queueBuffer( @@ -72,7 +73,8 @@ private: sp mNetSession; sp mNotify; - TransportMode mMode; + TransportMode mRTPMode; + TransportMode mRTCPMode; int32_t mRTPSessionID; int32_t mRTCPSessionID; bool mRTPConnected; diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp index 607d9d2..eade832 100644 --- a/media/libstagefright/wifi-display/rtptest.cpp +++ b/media/libstagefright/wifi-display/rtptest.cpp @@ -106,7 +106,9 @@ void TestHandler::onMessageReceived(const sp &msg) { int32_t receiverRTPPort; CHECK_EQ((status_t)OK, mReceiver->initAsync( - RTPReceiver::TRANSPORT_UDP, &receiverRTPPort)); + RTPReceiver::TRANSPORT_UDP, // rtpMode + RTPReceiver::TRANSPORT_UDP, // rtcpMode + &receiverRTPPort)); printf("picked receiverRTPPort %d\n", receiverRTPPort); @@ -155,10 +157,11 @@ void TestHandler::onMessageReceived(const sp &msg) { int32_t senderRTPPort; CHECK_EQ((status_t)OK, mSender->initAsync( - RTPSender::TRANSPORT_UDP, host.c_str(), receiverRTPPort, + RTPSender::TRANSPORT_UDP, // rtpMode receiverRTPPort + 1, + RTPSender::TRANSPORT_UDP, // rtcpMode &senderRTPPort)); printf("picked senderRTPPort %d\n", senderRTPPort); -- cgit v1.1 From cc8623a7af8c1f7f40dd7810e2b5cf24a008faf3 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 25 Mar 2013 13:25:39 -0700 Subject: Misc debugging support and handling of latency changes. Change-Id: I682944f793690842219cf1adbae5e61e061b6b62 --- media/libstagefright/wifi-display/MediaSender.cpp | 31 --------- .../wifi-display/sink/DirectRenderer.cpp | 24 ------- .../wifi-display/sink/DirectRenderer.h | 5 -- .../wifi-display/sink/WifiDisplaySink.cpp | 78 ++++++++++++++-------- .../wifi-display/sink/WifiDisplaySink.h | 6 +- media/libstagefright/wifi-display/wfd.cpp | 15 ++++- 6 files changed, 68 insertions(+), 91 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 6fc50f7..123bc1c 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -267,37 +267,6 @@ status_t MediaSender::queueAccessUnit( tsPackets, 33 /* packetType */, RTPSender::PACKETIZATION_TRANSPORT_STREAM); - -#if 0 - { - int64_t nowUs = ALooper::GetNowUs(); - - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - int64_t delayMs = (nowUs - timeUs) / 1000ll; - - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - ALOGI("[%lld]: (%4lld ms) %s\n", - timeUs / 1000, - delayMs, - kPattern + kPatternSize - n); - } -#endif } if (err != OK) { diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index 12338e9..15f9c88 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -467,8 +467,6 @@ DirectRenderer::DirectRenderer( const sp &bufferProducer) : mSurfaceTex(bufferProducer), mVideoRenderPending(false), - mLatencySum(0ll), - mLatencyCount(0), mNumFramesLate(0), mNumFrames(0) { } @@ -476,25 +474,6 @@ DirectRenderer::DirectRenderer( DirectRenderer::~DirectRenderer() { } -int64_t DirectRenderer::getAvgLatenessUs() { - if (mLatencyCount == 0) { - return 0ll; - } - - int64_t avgLatencyUs = mLatencySum / mLatencyCount; - - mLatencySum = 0ll; - mLatencyCount = 0; - - if (mNumFrames > 0) { - ALOGI("%d / %d frames late", mNumFramesLate, mNumFrames); - mNumFramesLate = 0; - mNumFrames = 0; - } - - return avgLatencyUs; -} - void DirectRenderer::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatDecoderNotify: @@ -632,9 +611,6 @@ void DirectRenderer::onRenderVideo() { } ++mNumFrames; - mLatencySum += nowUs - info.mTimeUs; - ++mLatencyCount; - status_t err = mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex); CHECK_EQ(err, (status_t)OK); diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h index 92c176a..c5a4a83 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -34,8 +34,6 @@ struct DirectRenderer : public AHandler { void setFormat(size_t trackIndex, const sp &format); void queueAccessUnit(size_t trackIndex, const sp &accessUnit); - int64_t getAvgLatenessUs(); - protected: virtual void onMessageReceived(const sp &msg); virtual ~DirectRenderer(); @@ -64,9 +62,6 @@ private: sp mAudioRenderer; - int64_t mLatencySum; - size_t mLatencyCount; - int32_t mNumFramesLate; int32_t mNumFrames; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 62021c0..bc98402 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -51,8 +51,10 @@ WifiDisplaySink::WifiDisplaySink( mIDRFrameRequestPending(false), mTimeOffsetUs(0ll), mTimeOffsetValid(false), - mTargetLatencyUs(-1ll), - mSetupDeferred(false) { + mSetupDeferred(false), + mLatencyCount(0), + mLatencySumUs(0ll), + mLatencyMaxUs(0ll) { // We support any and all resolutions, but prefer 720p30 mSinkSupportedVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 @@ -265,13 +267,20 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { case kWhatReportLateness: { - int64_t latenessUs = mRenderer->getAvgLatenessUs(); + if (mLatencyCount > 0) { + int64_t avgLatencyUs = mLatencySumUs / mLatencyCount; - ALOGI("avg. lateness = %lld ms", - (latenessUs + mTargetLatencyUs) / 1000ll); + ALOGI("avg. latency = %lld ms (max %lld ms)", + avgLatencyUs / 1000ll, + mLatencyMaxUs / 1000ll); - mMediaReceiver->notifyLateness( - 0 /* trackIndex */, latenessUs); + mMediaReceiver->notifyLateness( + 0 /* trackIndex */, avgLatencyUs); + } + + mLatencyCount = 0; + mLatencySumUs = 0ll; + mLatencyMaxUs = 0ll; msg->post(kReportLatenessEveryUs); break; @@ -282,6 +291,30 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { } } +static void dumpDelay(size_t trackIndex, int64_t timeUs) { + int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll; + + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("[%lld]: (%4lld ms) %s", + timeUs / 1000, + delayMs, + kPattern + kPatternSize - n); +} + void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { int32_t what; CHECK(msg->findInt32("what", &what)); @@ -319,24 +352,6 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { CHECK(mTimeOffsetValid); - int64_t latencyUs = 200000ll; // 200ms by default - - char val[PROPERTY_VALUE_MAX]; - if (property_get("media.wfd-sink.latency", val, NULL)) { - char *end; - int64_t x = strtoll(val, &end, 10); - - if (end > val && *end == '\0' && x >= 0ll) { - latencyUs = x; - } - } - - if (latencyUs != mTargetLatencyUs) { - mTargetLatencyUs = latencyUs; - - ALOGI("Assuming %lld ms of latency.", latencyUs / 1000ll); - } - sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); @@ -345,13 +360,24 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { // We are the timesync _client_, // client time = server time - time offset. - timeUs += mTargetLatencyUs - mTimeOffsetUs; + timeUs -= mTimeOffsetUs; accessUnit->meta()->setInt64("timeUs", timeUs); size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayUs = nowUs - timeUs; + + mLatencySumUs += delayUs; + if (mLatencyCount == 0 || delayUs > mLatencyMaxUs) { + mLatencyMaxUs = delayUs; + } + ++mLatencyCount; + + // dumpDelay(trackIndex, timeUs); + #if USE_TUNNEL_RENDERER mRenderer->queueBuffer(accessUnit); #else diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index 2b8c6f7..f515177 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -132,10 +132,12 @@ private: int64_t mTimeOffsetUs; bool mTimeOffsetValid; - int64_t mTargetLatencyUs; - bool mSetupDeferred; + size_t mLatencyCount; + int64_t mLatencySumUs; + int64_t mLatencyMaxUs; + status_t sendM2(int32_t sessionID); status_t sendSetup(int32_t sessionID, const char *uri); status_t sendPlay(int32_t sessionID, const char *uri); diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 4f7dcc8..9fee4d0 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -43,7 +43,8 @@ static void usage(const char *me) { " -u uri \tconnect to an rtsp uri\n" " -l ip[:port] \tlisten on the specified port " " -f(ilename) \tstream media " - "(create a sink)\n", + "(create a sink)\n" + " -s(pecial) \trun in 'special' mode\n", me); } @@ -222,8 +223,10 @@ int main(int argc, char **argv) { AString path; + bool specialMode = false; + int res; - while ((res = getopt(argc, argv, "hc:l:u:f:")) >= 0) { + while ((res = getopt(argc, argv, "hc:l:u:f:s")) >= 0) { switch (res) { case 'c': { @@ -281,6 +284,12 @@ int main(int argc, char **argv) { break; } + case 's': + { + specialMode = true; + break; + } + case '?': case 'h': default: @@ -357,7 +366,7 @@ int main(int argc, char **argv) { sp looper = new ALooper; sp sink = new WifiDisplaySink( - 0 /* flags */, + specialMode ? WifiDisplaySink::FLAG_SPECIAL_MODE : 0 /* flags */, session, surface->getIGraphicBufferProducer()); -- cgit v1.1 From f90debb467a0daf5288e7d8684642ef1119c4bad Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 25 Mar 2013 14:15:24 -0700 Subject: Get rid of TunnelRenderer Change-Id: I40dc00e2e689d7a6b8717ce524016c2948229807 --- media/libstagefright/wifi-display/Android.mk | 1 - .../wifi-display/sink/TunnelRenderer.cpp | 290 --------------------- .../wifi-display/sink/TunnelRenderer.h | 78 ------ .../wifi-display/sink/WifiDisplaySink.cpp | 18 +- .../wifi-display/sink/WifiDisplaySink.h | 8 - 5 files changed, 1 insertion(+), 394 deletions(-) delete mode 100644 media/libstagefright/wifi-display/sink/TunnelRenderer.cpp delete mode 100644 media/libstagefright/wifi-display/sink/TunnelRenderer.h (limited to 'media') diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index f81929c..f1f9f45 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -12,7 +12,6 @@ LOCAL_SRC_FILES:= \ rtp/RTPReceiver.cpp \ rtp/RTPSender.cpp \ sink/DirectRenderer.cpp \ - sink/TunnelRenderer.cpp \ sink/WifiDisplaySink.cpp \ SNTPClient.cpp \ TimeSyncer.cpp \ diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp deleted file mode 100644 index 6b185db..0000000 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright 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 "TunnelRenderer" -#include - -#include "TunnelRenderer.h" - -#include "ATSParser.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -struct TunnelRenderer::PlayerClient : public BnMediaPlayerClient { - PlayerClient() {} - - virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) { - ALOGI("notify %d, %d, %d", msg, ext1, ext2); - } - -protected: - virtual ~PlayerClient() {} - -private: - DISALLOW_EVIL_CONSTRUCTORS(PlayerClient); -}; - -struct TunnelRenderer::StreamSource : public BnStreamSource { - StreamSource(TunnelRenderer *owner); - - virtual void setListener(const sp &listener); - virtual void setBuffers(const Vector > &buffers); - - virtual void onBufferAvailable(size_t index); - - virtual uint32_t flags() const; - - void doSomeWork(); - - void setTimeOffset(int64_t offset); - -protected: - virtual ~StreamSource(); - -private: - mutable Mutex mLock; - - TunnelRenderer *mOwner; - - sp mListener; - - Vector > mBuffers; - List mIndicesAvailable; - - size_t mNumDeqeued; - - int64_t mTimeOffsetUs; - bool mTimeOffsetChanged; - - DISALLOW_EVIL_CONSTRUCTORS(StreamSource); -}; - -//////////////////////////////////////////////////////////////////////////////// - -TunnelRenderer::StreamSource::StreamSource(TunnelRenderer *owner) - : mOwner(owner), - mNumDeqeued(0), - mTimeOffsetUs(0ll), - mTimeOffsetChanged(false) { -} - -TunnelRenderer::StreamSource::~StreamSource() { -} - -void TunnelRenderer::StreamSource::setListener( - const sp &listener) { - mListener = listener; -} - -void TunnelRenderer::StreamSource::setBuffers( - const Vector > &buffers) { - mBuffers = buffers; -} - -void TunnelRenderer::StreamSource::onBufferAvailable(size_t index) { - CHECK_LT(index, mBuffers.size()); - - { - Mutex::Autolock autoLock(mLock); - mIndicesAvailable.push_back(index); - } - - doSomeWork(); -} - -uint32_t TunnelRenderer::StreamSource::flags() const { - return kFlagAlignedVideoData | kFlagIsRealTimeData; -} - -void TunnelRenderer::StreamSource::doSomeWork() { - Mutex::Autolock autoLock(mLock); - - while (!mIndicesAvailable.empty()) { - sp srcBuffer = mOwner->dequeueBuffer(); - if (srcBuffer == NULL) { - break; - } - - ++mNumDeqeued; - - if (mTimeOffsetChanged) { - sp extra = new AMessage; - - extra->setInt32( - IStreamListener::kKeyDiscontinuityMask, - ATSParser::DISCONTINUITY_TIME_OFFSET); - - extra->setInt64("offset", mTimeOffsetUs); - - mListener->issueCommand( - IStreamListener::DISCONTINUITY, - false /* synchronous */, - extra); - - mTimeOffsetChanged = false; - } - - ALOGV("dequeue TS packet of size %d", srcBuffer->size()); - - size_t index = *mIndicesAvailable.begin(); - mIndicesAvailable.erase(mIndicesAvailable.begin()); - - sp mem = mBuffers.itemAt(index); - CHECK_LE(srcBuffer->size(), mem->size()); - CHECK_EQ((srcBuffer->size() % 188), 0u); - - memcpy(mem->pointer(), srcBuffer->data(), srcBuffer->size()); - mListener->queueBuffer(index, srcBuffer->size()); - } -} - -void TunnelRenderer::StreamSource::setTimeOffset(int64_t offset) { - Mutex::Autolock autoLock(mLock); - - if (offset != mTimeOffsetUs) { - mTimeOffsetUs = offset; - mTimeOffsetChanged = true; - } -} - -//////////////////////////////////////////////////////////////////////////////// - -TunnelRenderer::TunnelRenderer( - const sp &bufferProducer) - : mSurfaceTex(bufferProducer), - mStartup(true) { - mStreamSource = new StreamSource(this); -} - -TunnelRenderer::~TunnelRenderer() { - destroyPlayer(); -} - -void TunnelRenderer::setTimeOffset(int64_t offset) { - mStreamSource->setTimeOffset(offset); -} - -void TunnelRenderer::onMessageReceived(const sp &msg) { - switch (msg->what()) { - default: - TRESPASS(); - } -} - -void TunnelRenderer::initPlayer() { - if (mSurfaceTex == NULL) { - mComposerClient = new SurfaceComposerClient; - CHECK_EQ(mComposerClient->initCheck(), (status_t)OK); - - DisplayInfo info; - SurfaceComposerClient::getDisplayInfo(0, &info); - ssize_t displayWidth = info.w; - ssize_t displayHeight = info.h; - - mSurfaceControl = - mComposerClient->createSurface( - String8("A Surface"), - displayWidth, - displayHeight, - PIXEL_FORMAT_RGB_565, - 0); - - CHECK(mSurfaceControl != NULL); - CHECK(mSurfaceControl->isValid()); - - SurfaceComposerClient::openGlobalTransaction(); - CHECK_EQ(mSurfaceControl->setLayer(INT_MAX), (status_t)OK); - CHECK_EQ(mSurfaceControl->show(), (status_t)OK); - SurfaceComposerClient::closeGlobalTransaction(); - - mSurface = mSurfaceControl->getSurface(); - CHECK(mSurface != NULL); - } - - sp sm = defaultServiceManager(); - sp binder = sm->getService(String16("media.player")); - sp service = interface_cast(binder); - CHECK(service.get() != NULL); - - mPlayerClient = new PlayerClient; - - mPlayer = service->create(mPlayerClient, 0); - CHECK(mPlayer != NULL); - CHECK_EQ(mPlayer->setDataSource(mStreamSource), (status_t)OK); - - mPlayer->setVideoSurfaceTexture( - mSurfaceTex != NULL ? mSurfaceTex : mSurface->getIGraphicBufferProducer()); - - mPlayer->start(); -} - -void TunnelRenderer::destroyPlayer() { - mStreamSource.clear(); - - mPlayer->setVideoSurfaceTexture(NULL); - - mPlayer->stop(); - mPlayer.clear(); - - if (mSurfaceTex == NULL) { - mSurface.clear(); - mSurfaceControl.clear(); - - mComposerClient->dispose(); - mComposerClient.clear(); - } -} - -void TunnelRenderer::queueBuffer(const sp &buffer) { - { - Mutex::Autolock autoLock(mLock); - mBuffers.push_back(buffer); - } - - if (mStartup) { - initPlayer(); - mStartup = false; - } - - mStreamSource->doSomeWork(); -} - -sp TunnelRenderer::dequeueBuffer() { - Mutex::Autolock autoLock(mLock); - if (mBuffers.empty()) { - return NULL; - } - - sp buf = *mBuffers.begin(); - mBuffers.erase(mBuffers.begin()); - - return buf; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h deleted file mode 100644 index 479e73c..0000000 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 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 TUNNEL_RENDERER_H_ - -#define TUNNEL_RENDERER_H_ - -#include -#include - -namespace android { - -struct ABuffer; -struct SurfaceComposerClient; -struct SurfaceControl; -struct Surface; -struct IMediaPlayer; -struct IStreamListener; - -// This class reassembles incoming RTP packets into the correct order -// and sends the resulting transport stream to a mediaplayer instance -// for playback. -struct TunnelRenderer : public AHandler { - TunnelRenderer(const sp &bufferProducer); - - void queueBuffer(const sp &buffer); - sp dequeueBuffer(); - - void setTimeOffset(int64_t offset); - - int64_t getAvgLatenessUs() { - return 0ll; - } - -protected: - virtual void onMessageReceived(const sp &msg); - virtual ~TunnelRenderer(); - -private: - struct PlayerClient; - struct StreamSource; - - mutable Mutex mLock; - - sp mSurfaceTex; - - bool mStartup; - List > mBuffers; - - sp mComposerClient; - sp mSurfaceControl; - sp mSurface; - sp mPlayerClient; - sp mPlayer; - sp mStreamSource; - - void initPlayer(); - void destroyPlayer(); - - DISALLOW_EVIL_CONSTRUCTORS(TunnelRenderer); -}; - -} // namespace android - -#endif // TUNNEL_RENDERER_H_ diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index bc98402..639634b 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -24,7 +24,6 @@ #include "MediaReceiver.h" #include "ParsedMessage.h" #include "TimeSyncer.h" -#include "TunnelRenderer.h" #include #include @@ -341,12 +340,7 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { case MediaReceiver::kWhatAccessUnit: { if (mRenderer == NULL) { -#if USE_TUNNEL_RENDERER - mRenderer = new TunnelRenderer(mSurfaceTex); -#else mRenderer = new DirectRenderer(mSurfaceTex); -#endif - looper()->registerHandler(mRenderer); } @@ -378,16 +372,12 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { // dumpDelay(trackIndex, timeUs); -#if USE_TUNNEL_RENDERER - mRenderer->queueBuffer(accessUnit); -#else sp format; if (msg->findMessage("format", &format)) { mRenderer->setFormat(trackIndex, format); } mRenderer->queueAccessUnit(trackIndex, accessUnit); -#endif break; } @@ -726,13 +716,7 @@ status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { status_t err = mMediaReceiver->addTrack(mode, &localRTPPort); if (err == OK) { - err = mMediaReceiver->initAsync( -#if USE_TUNNEL_RENDERER - MediaReceiver::MODE_TRANSPORT_STREAM_RAW -#else - MediaReceiver::MODE_TRANSPORT_STREAM -#endif - ); + err = mMediaReceiver->initAsync(MediaReceiver::MODE_TRANSPORT_STREAM); } if (err != OK) { diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index f515177..4587fb5 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -32,9 +32,6 @@ struct DirectRenderer; struct MediaReceiver; struct ParsedMessage; struct TimeSyncer; -struct TunnelRenderer; - -#define USE_TUNNEL_RENDERER 0 // Represents the RTSP client acting as a wifi display sink. // Connects to a wifi display source and renders the incoming @@ -117,12 +114,7 @@ private: sp mMediaReceiverLooper; sp mMediaReceiver; - -#if USE_TUNNEL_RENDERER - sp mRenderer; -#else sp mRenderer; -#endif AString mPlaybackSessionID; int32_t mPlaybackSessionTimeoutSecs; -- cgit v1.1 From 6eb954f54e4a92b3c4bfbee177a3259d1320500d Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 25 Mar 2013 14:38:10 -0700 Subject: ToneGenerator: optimize silent tone Do not create an AudioTrack and start playback when a silent tone is requested to ToneGenerator. Bug 7946399 Change-Id: Ib9282871a56f7a862af7d1504ce3fbd7c18e34e2 --- media/libmedia/ToneGenerator.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 42584fe..1c0268f 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -885,6 +885,11 @@ bool ToneGenerator::startTone(tone_type toneType, int durationMs) { if ((toneType < 0) || (toneType >= NUM_TONES)) return lResult; + toneType = getToneForRegion(toneType); + if (toneType == TONE_CDMA_SIGNAL_OFF) { + return true; + } + if (mState == TONE_IDLE) { ALOGV("startTone: try to re-init AudioTrack"); if (!initAudioTrack()) { @@ -897,7 +902,6 @@ bool ToneGenerator::startTone(tone_type toneType, int durationMs) { mLock.lock(); // Get descriptor for requested tone - toneType = getToneForRegion(toneType); mpNewToneDesc = &sToneDescriptors[toneType]; mDurationMs = durationMs; -- cgit v1.1 From 2aea9552aeba92bbaf9e56c666049ea2d14057b5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 25 Mar 2013 15:46:52 -0700 Subject: In "special" mode we now establish a UDP RTCP channel in addition to the TCP RTP channel and provide feedback on the latency of arriving packets from the sink back to the source. This information is then used to throttle video bitrate. Change-Id: Ic589a3cb65e4893a3ff67de947da6063d32a1c6e --- .../wifi-display/ANetworkSession.cpp | 4 +- .../libstagefright/wifi-display/MediaReceiver.cpp | 14 +++--- media/libstagefright/wifi-display/MediaReceiver.h | 5 ++- media/libstagefright/wifi-display/MediaSender.cpp | 32 ++++++++----- media/libstagefright/wifi-display/MediaSender.h | 4 +- .../wifi-display/rtp/RTPReceiver.cpp | 39 +++++++++++----- .../libstagefright/wifi-display/rtp/RTPReceiver.h | 2 +- .../libstagefright/wifi-display/rtp/RTPSender.cpp | 20 ++++++++- media/libstagefright/wifi-display/rtp/RTPSender.h | 2 + .../wifi-display/sink/WifiDisplaySink.cpp | 47 ++++++++++++------- .../wifi-display/source/Converter.cpp | 17 +++++++ .../libstagefright/wifi-display/source/Converter.h | 3 ++ .../wifi-display/source/PlaybackSession.cpp | 52 ++++++++++++++++++++-- .../wifi-display/source/PlaybackSession.h | 9 +++- .../wifi-display/source/WifiDisplaySource.cpp | 18 +++++--- 15 files changed, 206 insertions(+), 62 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index df20ae2..88ca1cc 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -565,7 +565,7 @@ status_t ANetworkSession::Session::writeMore() { mSawSendFailure = true; } -#if 1 +#if 0 int numBytesQueued; int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); if (res == 0 && numBytesQueued > 50 * 1024) { @@ -576,7 +576,7 @@ status_t ANetworkSession::Session::writeMore() { int64_t nowUs = ALooper::GetNowUs(); if (mLastStallReportUs < 0ll - || nowUs > mLastStallReportUs + 500000ll) { + || nowUs > mLastStallReportUs + 100000ll) { sp msg = mNotify->dup(); msg->setInt32("sessionID", mSessionID); msg->setInt32("reason", kWhatNetworkStall); diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp index e2e791d..364acb9 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.cpp +++ b/media/libstagefright/wifi-display/MediaReceiver.cpp @@ -47,7 +47,8 @@ MediaReceiver::~MediaReceiver() { } ssize_t MediaReceiver::addTrack( - RTPReceiver::TransportMode transportMode, + RTPReceiver::TransportMode rtpMode, + RTPReceiver::TransportMode rtcpMode, int32_t *localRTPPort) { if (mMode != MODE_UNDEFINED) { return INVALID_OPERATION; @@ -74,10 +75,8 @@ ssize_t MediaReceiver::addTrack( 97, RTPReceiver::PACKETIZATION_H264); status_t err = info.mReceiver->initAsync( - transportMode, // rtpMode - transportMode == RTPReceiver::TRANSPORT_UDP - ? transportMode - : RTPReceiver::TRANSPORT_NONE, // rtcpMode + rtpMode, + rtcpMode, localRTPPort); if (err != OK) { @@ -314,13 +313,14 @@ void MediaReceiver::postAccessUnit( notify->post(); } -status_t MediaReceiver::notifyLateness(size_t trackIndex, int64_t latenessUs) { +status_t MediaReceiver::informSender( + size_t trackIndex, const sp ¶ms) { if (trackIndex >= mTrackInfos.size()) { return -ERANGE; } TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); - return info->mReceiver->notifyLateness(latenessUs); + return info->mReceiver->informSender(params); } } // namespace android diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h index cdfde99..afbb407 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.h +++ b/media/libstagefright/wifi-display/MediaReceiver.h @@ -43,7 +43,8 @@ struct MediaReceiver : public AHandler { const sp ¬ify); ssize_t addTrack( - RTPReceiver::TransportMode transportMode, + RTPReceiver::TransportMode rtpMode, + RTPReceiver::TransportMode rtcpMode, int32_t *localRTPPort); status_t connectTrack( @@ -60,7 +61,7 @@ struct MediaReceiver : public AHandler { }; status_t initAsync(Mode mode); - status_t notifyLateness(size_t trackIndex, int64_t latenessUs); + status_t informSender(size_t trackIndex, const sp ¶ms); protected: virtual void onMessageReceived(const sp &msg); diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 123bc1c..33af66d 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -85,10 +85,11 @@ ssize_t MediaSender::addTrack(const sp &format, uint32_t flags) { status_t MediaSender::initAsync( ssize_t trackIndex, - RTPSender::TransportMode transportMode, const char *remoteHost, int32_t remoteRTPPort, + RTPSender::TransportMode rtpMode, int32_t remoteRTCPPort, + RTPSender::TransportMode rtcpMode, int32_t *localRTPPort) { if (trackIndex < 0) { if (mMode != MODE_UNDEFINED) { @@ -126,12 +127,9 @@ status_t MediaSender::initAsync( err = mTSSender->initAsync( remoteHost, remoteRTPPort, - transportMode, // rtpMode + rtpMode, remoteRTCPPort, - (transportMode == RTPSender::TRANSPORT_UDP - && remoteRTCPPort >= 0) - ? transportMode - : RTPSender::TRANSPORT_NONE, // rtcpMode + rtcpMode, localRTPPort); if (err != OK) { @@ -180,11 +178,9 @@ status_t MediaSender::initAsync( status_t err = info->mSender->initAsync( remoteHost, remoteRTPPort, - transportMode, // rtpMode + rtpMode, remoteRTCPPort, - (transportMode == RTPSender::TRANSPORT_UDP && remoteRTCPPort >= 0) - ? transportMode - : RTPSender::TRANSPORT_NONE, // rtcpMode + rtcpMode, localRTPPort); if (err != OK) { @@ -345,6 +341,22 @@ void MediaSender::onSenderNotify(const sp &msg) { break; } + case kWhatInformSender: + { + int64_t avgLatencyUs; + CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInformSender); + notify->setInt64("avgLatencyUs", avgLatencyUs); + notify->setInt64("maxLatencyUs", maxLatencyUs); + notify->post(); + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h index 447abf7..04538ea 100644 --- a/media/libstagefright/wifi-display/MediaSender.h +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -43,6 +43,7 @@ struct MediaSender : public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, + kWhatInformSender, }; MediaSender( @@ -59,10 +60,11 @@ struct MediaSender : public AHandler { // If trackIndex == -1, initialize for transport stream muxing. status_t initAsync( ssize_t trackIndex, - RTPSender::TransportMode transportMode, const char *remoteHost, int32_t remoteRTPPort, + RTPSender::TransportMode rtpMode, int32_t remoteRTCPPort, + RTPSender::TransportMode rtcpMode, int32_t *localRTPPort); status_t queueAccessUnit( diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp index c55e0be..238fb82 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -567,8 +567,18 @@ status_t RTPReceiver::connect( return OK; } -status_t RTPReceiver::notifyLateness(int64_t latenessUs) { - sp buf = new ABuffer(20); +status_t RTPReceiver::informSender(const sp ¶ms) { + if (!mRTCPConnected) { + return INVALID_OPERATION; + } + + int64_t avgLatencyUs; + CHECK(params->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(params->findInt64("maxLatencyUs", &maxLatencyUs)); + + sp buf = new ABuffer(28); uint8_t *ptr = buf->data(); ptr[0] = 0x80 | 0; @@ -587,14 +597,23 @@ status_t RTPReceiver::notifyLateness(int64_t latenessUs) { ptr[10] = 't'; ptr[11] = 'e'; - ptr[12] = latenessUs >> 56; - ptr[13] = (latenessUs >> 48) & 0xff; - ptr[14] = (latenessUs >> 40) & 0xff; - ptr[15] = (latenessUs >> 32) & 0xff; - ptr[16] = (latenessUs >> 24) & 0xff; - ptr[17] = (latenessUs >> 16) & 0xff; - ptr[18] = (latenessUs >> 8) & 0xff; - ptr[19] = latenessUs & 0xff; + ptr[12] = avgLatencyUs >> 56; + ptr[13] = (avgLatencyUs >> 48) & 0xff; + ptr[14] = (avgLatencyUs >> 40) & 0xff; + ptr[15] = (avgLatencyUs >> 32) & 0xff; + ptr[16] = (avgLatencyUs >> 24) & 0xff; + ptr[17] = (avgLatencyUs >> 16) & 0xff; + ptr[18] = (avgLatencyUs >> 8) & 0xff; + ptr[19] = avgLatencyUs & 0xff; + + ptr[20] = maxLatencyUs >> 56; + ptr[21] = (maxLatencyUs >> 48) & 0xff; + ptr[22] = (maxLatencyUs >> 40) & 0xff; + ptr[23] = (maxLatencyUs >> 32) & 0xff; + ptr[24] = (maxLatencyUs >> 24) & 0xff; + ptr[25] = (maxLatencyUs >> 16) & 0xff; + ptr[26] = (maxLatencyUs >> 8) & 0xff; + ptr[27] = maxLatencyUs & 0xff; mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h index abbe6a8..630bce9 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h @@ -56,7 +56,7 @@ struct RTPReceiver : public RTPBase, public AHandler { int32_t remoteRTPPort, int32_t remoteRTCPPort); - status_t notifyLateness(int64_t latenessUs); + status_t informSender(const sp ¶ms); protected: virtual ~RTPReceiver(); diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index c686e01..9eeeabd 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -91,8 +91,8 @@ status_t RTPSender::initAsync( CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED); CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED); - if (rtcpMode == TRANSPORT_NONE && remoteRTCPPort >= 0 - || rtcpMode != TRANSPORT_NONE && remoteRTCPPort < 0) { + if ((rtcpMode == TRANSPORT_NONE && remoteRTCPPort >= 0) + || (rtcpMode != TRANSPORT_NONE && remoteRTCPPort < 0)) { return INVALID_OPERATION; } @@ -616,6 +616,7 @@ status_t RTPSender::onRTCPData(const sp &buffer) { break; case 204: // APP + parseAPP(data, headerLength); break; case 205: // TSFB (transport layer specific feedback) @@ -721,6 +722,21 @@ status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) { return OK; } +status_t RTPSender::parseAPP(const uint8_t *data, size_t size) { + if (!memcmp("late", &data[8], 4)) { + int64_t avgLatencyUs = (int64_t)U64_AT(&data[12]); + int64_t maxLatencyUs = (int64_t)U64_AT(&data[20]); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInformSender); + notify->setInt64("avgLatencyUs", avgLatencyUs); + notify->setInt64("maxLatencyUs", maxLatencyUs); + notify->post(); + } + + return OK; +} + void RTPSender::notifyInitDone(status_t err) { sp notify = mNotify->dup(); notify->setInt32("what", kWhatInitDone); diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 8409b8d..3a926ea 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -37,6 +37,7 @@ struct RTPSender : public RTPBase, public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, + kWhatInformSender, }; RTPSender( const sp &netSession, @@ -105,6 +106,7 @@ private: status_t onRTCPData(const sp &data); status_t parseReceiverReport(const uint8_t *data, size_t size); status_t parseTSFB(const uint8_t *data, size_t size); + status_t parseAPP(const uint8_t *data, size_t size); void notifyInitDone(status_t err); void notifyError(status_t err); diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 639634b..f45a47f 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -269,12 +269,14 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { if (mLatencyCount > 0) { int64_t avgLatencyUs = mLatencySumUs / mLatencyCount; - ALOGI("avg. latency = %lld ms (max %lld ms)", + ALOGV("avg. latency = %lld ms (max %lld ms)", avgLatencyUs / 1000ll, mLatencyMaxUs / 1000ll); - mMediaReceiver->notifyLateness( - 0 /* trackIndex */, avgLatencyUs); + sp params = new AMessage; + params->setInt64("avgLatencyUs", avgLatencyUs); + params->setInt64("maxLatencyUs", mLatencyMaxUs); + mMediaReceiver->informSender(0 /* trackIndex */, params); } mLatencyCount = 0; @@ -356,8 +358,6 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { // client time = server time - time offset. timeUs -= mTimeOffsetUs; - accessUnit->meta()->setInt64("timeUs", timeUs); - size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); @@ -372,6 +372,9 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { // dumpDelay(trackIndex, timeUs); + timeUs += 220000ll; // Assume 220 ms of latency + accessUnit->meta()->setInt64("timeUs", timeUs); + sp format; if (msg->findMessage("format", &format)) { mRenderer->setFormat(trackIndex, format); @@ -486,7 +489,9 @@ status_t WifiDisplaySink::onReceiveSetupResponse( } status_t WifiDisplaySink::configureTransport(const sp &msg) { - if (mUsingTCPTransport) { + if (mUsingTCPTransport && !(mFlags & FLAG_SPECIAL_MODE)) { + // In "special" mode we still use a UDP RTCP back-channel that + // needs connecting. return OK; } @@ -703,17 +708,18 @@ status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { mMediaReceiver = new MediaReceiver(mNetSession, notify); mMediaReceiverLooper->registerHandler(mMediaReceiver); - RTPReceiver::TransportMode mode = RTPReceiver::TRANSPORT_UDP; + RTPReceiver::TransportMode rtpMode = RTPReceiver::TRANSPORT_UDP; if (mUsingTCPTransport) { if (mUsingTCPInterleaving) { - mode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED; + rtpMode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED; } else { - mode = RTPReceiver::TRANSPORT_TCP; + rtpMode = RTPReceiver::TRANSPORT_TCP; } } int32_t localRTPPort; - status_t err = mMediaReceiver->addTrack(mode, &localRTPPort); + status_t err = mMediaReceiver->addTrack( + rtpMode, RTPReceiver::TRANSPORT_UDP /* rtcpMode */, &localRTPPort); if (err == OK) { err = mMediaReceiver->initAsync(MediaReceiver::MODE_TRANSPORT_STREAM); @@ -733,13 +739,22 @@ status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { AppendCommonResponse(&request, mNextCSeq); - if (mode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) { + if (rtpMode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) { request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n"); - } else if (mode == RTPReceiver::TRANSPORT_TCP) { - request.append( - StringPrintf( - "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n", - localRTPPort)); + } else if (rtpMode == RTPReceiver::TRANSPORT_TCP) { + if (mFlags & FLAG_SPECIAL_MODE) { + // This isn't quite true, since the RTP connection is through TCP + // and the RTCP connection through UDP... + request.append( + StringPrintf( + "Transport: RTP/AVP/TCP;unicast;client_port=%d-%d\r\n", + localRTPPort, localRTPPort + 1)); + } else { + request.append( + StringPrintf( + "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n", + localRTPPort)); + } } else { request.append( StringPrintf( diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index bb8c387..d41e1e6 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -622,6 +622,7 @@ status_t Converter::feedEncoderInputBuffers() { } status_t Converter::doMoreWork() { +#if 0 if (mIsVideo) { int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000); if (videoBitrate != mPrevVideoBitrate) { @@ -633,6 +634,7 @@ status_t Converter::doMoreWork() { mPrevVideoBitrate = videoBitrate; } } +#endif status_t err; @@ -708,4 +710,19 @@ void Converter::dropAFrame() { (new AMessage(kWhatDropAFrame, id()))->post(); } +int32_t Converter::getVideoBitrate() const { + return mPrevVideoBitrate; +} + +void Converter::setVideoBitrate(int32_t bitRate) { + if (mIsVideo && mEncoder != NULL && bitRate != mPrevVideoBitrate) { + sp params = new AMessage; + params->setInt32("videoBitrate", bitRate); + + mEncoder->setParameters(params); + + mPrevVideoBitrate = bitRate; + } +} + } // namespace android diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index a418f69..538f10a 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -70,6 +70,9 @@ struct Converter : public AHandler { void shutdownAsync(); + int32_t getVideoBitrate() const; + void setVideoBitrate(int32_t bitrate); + protected: virtual ~Converter(); virtual void onMessageReceived(const sp &msg); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index a3b6542..68aa9cb 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -362,8 +362,11 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( } status_t WifiDisplaySource::PlaybackSession::init( - const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - RTPSender::TransportMode transportMode, + const char *clientIP, + int32_t clientRtp, + RTPSender::TransportMode rtpMode, + int32_t clientRtcp, + RTPSender::TransportMode rtcpMode, bool enableAudio, bool usePCMAudio, bool enableVideo, @@ -385,10 +388,11 @@ status_t WifiDisplaySource::PlaybackSession::init( if (err == OK) { err = mMediaSender->initAsync( -1 /* trackIndex */, - transportMode, clientIP, clientRtp, + rtpMode, clientRtcp, + rtcpMode, &mLocalRTPPort); } @@ -548,6 +552,8 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( converter->dropAFrame(); } } + } else if (what == MediaSender::kWhatInformSender) { + onSinkFeedback(msg); } else { TRESPASS(); } @@ -643,6 +649,46 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } +void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp &msg) { + int64_t avgLatencyUs; + CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); + + ALOGI("sink reports avg. latency of %lld ms (max %lld ms)", + avgLatencyUs / 1000ll, + maxLatencyUs / 1000ll); + + if (mVideoTrackIndex >= 0) { + const sp &videoTrack = mTracks.valueFor(mVideoTrackIndex); + sp converter = videoTrack->converter(); + if (converter != NULL) { + int32_t videoBitrate = converter->getVideoBitrate(); + + if (avgLatencyUs > 300000ll) { + videoBitrate *= 0.6; + + if (videoBitrate < 500000) { + videoBitrate = 500000; // cap at 500kbit/sec + } + } else if (avgLatencyUs < 100000ll) { + videoBitrate *= 1.1; + + if (videoBitrate > 10000000) { + videoBitrate = 10000000; // cap at 10Mbit/sec + } + } + + if (videoBitrate != converter->getVideoBitrate()) { + ALOGI("setting video bitrate to %d bps", videoBitrate); + + converter->setVideoBitrate(videoBitrate); + } + } + } +} + status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( bool enableAudio, bool enableVideo) { DataSource::RegisterDefaultSniffers(); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index da207e2..39086a1 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -44,8 +44,11 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { const char *path = NULL); status_t init( - const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - RTPSender::TransportMode transportMode, + const char *clientIP, + int32_t clientRtp, + RTPSender::TransportMode rtpMode, + int32_t clientRtcp, + RTPSender::TransportMode rtcpMode, bool enableAudio, bool usePCMAudio, bool enableVideo, @@ -149,6 +152,8 @@ private: void schedulePullExtractor(); void onPullExtractor(); + void onSinkFeedback(const sp &msg); + DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession); }; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 5167cb3..f2e659a 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -1159,7 +1159,7 @@ status_t WifiDisplaySource::onSetupRequest( return ERROR_MALFORMED; } - RTPSender::TransportMode transportMode = RTPSender::TRANSPORT_UDP; + RTPSender::TransportMode rtpMode = RTPSender::TRANSPORT_UDP; int clientRtp, clientRtcp; if (transport.startsWith("RTP/AVP/TCP;")) { @@ -1168,7 +1168,7 @@ status_t WifiDisplaySource::onSetupRequest( transport.c_str(), "interleaved", &interleaved) && sscanf(interleaved.c_str(), "%d-%d", &clientRtp, &clientRtcp) == 2) { - transportMode = RTPSender::TRANSPORT_TCP_INTERLEAVED; + rtpMode = RTPSender::TRANSPORT_TCP_INTERLEAVED; } else { bool badRequest = false; @@ -1190,7 +1190,7 @@ status_t WifiDisplaySource::onSetupRequest( return ERROR_MALFORMED; } - transportMode = RTPSender::TRANSPORT_TCP; + rtpMode = RTPSender::TRANSPORT_TCP; } } else if (transport.startsWith("RTP/AVP;unicast;") || transport.startsWith("RTP/AVP/UDP;unicast;")) { @@ -1249,11 +1249,17 @@ status_t WifiDisplaySource::onSetupRequest( return ERROR_MALFORMED; } + RTPSender::TransportMode rtcpMode = RTPSender::TRANSPORT_UDP; + if (clientRtcp < 0) { + rtcpMode = RTPSender::TRANSPORT_NONE; + } + status_t err = playbackSession->init( mClientInfo.mRemoteIP.c_str(), clientRtp, + rtpMode, clientRtcp, - transportMode, + rtcpMode, mSinkSupportsAudio, mUsingPCMAudio, mSinkSupportsVideo, @@ -1282,7 +1288,7 @@ status_t WifiDisplaySource::onSetupRequest( AString response = "RTSP/1.0 200 OK\r\n"; AppendCommonResponse(&response, cseq, playbackSessionID); - if (transportMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) { + if (rtpMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) { response.append( StringPrintf( "Transport: RTP/AVP/TCP;interleaved=%d-%d;", @@ -1291,7 +1297,7 @@ status_t WifiDisplaySource::onSetupRequest( int32_t serverRtp = playbackSession->getRTPPort(); AString transportString = "UDP"; - if (transportMode == RTPSender::TRANSPORT_TCP) { + if (rtpMode == RTPSender::TRANSPORT_TCP) { transportString = "TCP"; } -- cgit v1.1 From cd77d4a1d38b7609a03f6826a1ff5fa7c98aa34f Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 26 Mar 2013 10:19:24 -0700 Subject: Identify network servers and clients with a OS version related string and put the logic to create that string in one location instead of many... Change-Id: I1f729f2e7376cd3b45eea0e48f7bd10084b41b39 --- media/libstagefright/Utils.cpp | 17 ++++++++++++++- media/libstagefright/chromium_http/support.cpp | 16 ++------------ media/libstagefright/rtsp/ARTSPConnection.cpp | 25 ++++++---------------- media/libstagefright/rtsp/ARTSPConnection.h | 6 ++---- media/libstagefright/rtsp/MyHandler.h | 18 ++-------------- .../wifi-display/sink/WifiDisplaySink.cpp | 6 +++++- .../wifi-display/sink/WifiDisplaySink.h | 2 ++ .../wifi-display/source/WifiDisplaySource.cpp | 6 +++++- .../wifi-display/source/WifiDisplaySource.h | 2 ++ 9 files changed, 42 insertions(+), 56 deletions(-) (limited to 'media') diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 8ed07bf..b0df379 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -21,7 +21,7 @@ #include "include/ESDS.h" #include - +#include #include #include #include @@ -455,6 +455,21 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { #endif } +AString MakeUserAgent() { + AString ua; + ua.append("stagefright/1.2 (Linux;Android "); + +#if (PROPERTY_VALUE_MAX < 8) +#error "PROPERTY_VALUE_MAX must be at least 8" +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.release", value, "Unknown"); + ua.append(value); + ua.append(")"); + + return ua; +} } // namespace android diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp index 13ae3df..832e86d 100644 --- a/media/libstagefright/chromium_http/support.cpp +++ b/media/libstagefright/chromium_http/support.cpp @@ -36,8 +36,8 @@ #include "include/ChromiumHTTPDataSource.h" #include -#include #include +#include #include namespace android { @@ -156,19 +156,7 @@ net::NetLog::LogLevel SfNetLog::GetLogLevel() const { //////////////////////////////////////////////////////////////////////////////// SfRequestContext::SfRequestContext() { - AString ua; - ua.append("stagefright/1.2 (Linux;Android "); - -#if (PROPERTY_VALUE_MAX < 8) -#error "PROPERTY_VALUE_MAX must be at least 8" -#endif - - char value[PROPERTY_VALUE_MAX]; - property_get("ro.build.version.release", value, "Unknown"); - ua.append(value); - ua.append(")"); - - mUserAgent = ua.c_str(); + mUserAgent = MakeUserAgent().c_str(); set_net_log(new SfNetLog()); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 161bd4f..3068541 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -20,13 +20,12 @@ #include "ARTSPConnection.h" -#include - #include #include #include #include #include +#include #include #include @@ -41,6 +40,10 @@ namespace android { // static const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll; +// static +const AString ARTSPConnection::sUserAgent = + StringPrintf("User-Agent: %s\r\n", MakeUserAgent().c_str()); + ARTSPConnection::ARTSPConnection(bool uidValid, uid_t uid) : mUIDValid(uidValid), mUID(uid), @@ -50,7 +53,6 @@ ARTSPConnection::ARTSPConnection(bool uidValid, uid_t uid) mConnectionID(0), mNextCSeq(0), mReceiveResponseEventPending(false) { - MakeUserAgent(&mUserAgent); } ARTSPConnection::~ARTSPConnection() { @@ -1032,27 +1034,12 @@ void ARTSPConnection::addAuthentication(AString *request) { #endif } -// static -void ARTSPConnection::MakeUserAgent(AString *userAgent) { - userAgent->clear(); - userAgent->setTo("User-Agent: stagefright/1.1 (Linux;Android "); - -#if (PROPERTY_VALUE_MAX < 8) -#error "PROPERTY_VALUE_MAX must be at least 8" -#endif - - char value[PROPERTY_VALUE_MAX]; - property_get("ro.build.version.release", value, "Unknown"); - userAgent->append(value); - userAgent->append(")\r\n"); -} - void ARTSPConnection::addUserAgent(AString *request) const { // Find the boundary between headers and the body. ssize_t i = request->find("\r\n\r\n"); CHECK_GE(i, 0); - request->insert(mUserAgent, i + 2); + request->insert(sUserAgent, i + 2); } } // namespace android diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h index 68f2d59..1fe9c99 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.h +++ b/media/libstagefright/rtsp/ARTSPConnection.h @@ -74,6 +74,8 @@ private: static const int64_t kSelectTimeoutUs; + static const AString sUserAgent; + bool mUIDValid; uid_t mUID; State mState; @@ -89,8 +91,6 @@ private: sp mObserveBinaryMessage; - AString mUserAgent; - void performDisconnect(); void onConnect(const sp &msg); @@ -122,8 +122,6 @@ private: static bool ParseSingleUnsignedLong( const char *from, unsigned long *x); - static void MakeUserAgent(AString *userAgent); - DISALLOW_EVIL_CONSTRUCTORS(ARTSPConnection); }; diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 95ed43a..e067e20 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -28,13 +28,13 @@ #include "ASessionDescription.h" #include -#include #include #include #include #include #include +#include #include #include @@ -56,19 +56,6 @@ static int64_t kPauseDelayUs = 3000000ll; namespace android { -static void MakeUserAgentString(AString *s) { - s->setTo("stagefright/1.1 (Linux;Android "); - -#if (PROPERTY_VALUE_MAX < 8) -#error "PROPERTY_VALUE_MAX must be at least 8" -#endif - - char value[PROPERTY_VALUE_MAX]; - property_get("ro.build.version.release", value, "Unknown"); - s->append(value); - s->append(")"); -} - static bool GetAttribute(const char *s, const char *key, AString *value) { value->clear(); @@ -279,8 +266,7 @@ struct MyHandler : public AHandler { data[offset++] = 6; // TOOL - AString tool; - MakeUserAgentString(&tool); + AString tool = MakeUserAgent(); data[offset++] = tool.size(); diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index f45a47f..1a08bf5 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -30,9 +30,13 @@ #include #include #include +#include namespace android { +// static +const AString WifiDisplaySink::sUserAgent = MakeUserAgent(); + WifiDisplaySink::WifiDisplaySink( uint32_t flags, const sp &netSession, @@ -892,7 +896,7 @@ void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) { response->append(buf); response->append("\r\n"); - response->append("User-Agent: stagefright/1.1 (Linux;Android 4.1)\r\n"); + response->append(StringPrintf("User-Agent: %s\r\n", sUserAgent.c_str())); if (cseq >= 0) { response->append(StringPrintf("CSeq: %d\r\n", cseq)); diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index 4587fb5..7c62057 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -96,6 +96,8 @@ private: static const int64_t kReportLatenessEveryUs = 1000000ll; + static const AString sUserAgent; + State mState; uint32_t mFlags; VideoFormats mSinkSupportedVideoFormats; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index f2e659a..792a9c5 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,9 @@ namespace android { +// static +const AString WifiDisplaySource::sUserAgent = MakeUserAgent(); + WifiDisplaySource::WifiDisplaySource( const sp &netSession, const sp &client, @@ -1559,7 +1563,7 @@ void WifiDisplaySource::AppendCommonResponse( response->append(buf); response->append("\r\n"); - response->append("Server: Mine/1.0\r\n"); + response->append(StringPrintf("Server: %s\r\n", sUserAgent.c_str())); if (cseq >= 0) { response->append(StringPrintf("CSeq: %d\r\n", cseq)); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 3a1b0f9..3efa0b4 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -113,6 +113,8 @@ private: static const int64_t kPlaybackSessionTimeoutUs = kPlaybackSessionTimeoutSecs * 1000000ll; + static const AString sUserAgent; + State mState; VideoFormats mSupportedSourceVideoFormats; sp mNetSession; -- cgit v1.1 From 6386b50b67185a966d43ee761acdfe7add569d10 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 26 Mar 2013 12:25:30 -0700 Subject: ToneGenerator: fix AudioTrack pointer init The pointer to AudioTrack should be initialized before early return from ToneGenerator constructor because it is tested by the destructor. Bug 8140963 Change-Id: I9a7dfb60ba162b75dfaa5630ab7fc9485afd0074 --- media/libmedia/ToneGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 1c0268f..58d495e 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -803,6 +803,7 @@ ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool ALOGV("ToneGenerator constructor: streamType=%d, volume=%f", streamType, volume); mState = TONE_IDLE; + mpAudioTrack = NULL; if (AudioSystem::getOutputSamplingRate(&mSamplingRate, streamType) != NO_ERROR) { ALOGE("Unable to marshal AudioFlinger"); @@ -811,7 +812,6 @@ ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool mThreadCanCallJava = threadCanCallJava; mStreamType = streamType; mVolume = volume; - mpAudioTrack = NULL; mpToneDesc = NULL; mpNewToneDesc = NULL; // Generate tone by chunks of 20 ms to keep cadencing precision -- cgit v1.1 From eaf5381f38bf6c3ecb5fe32a8351c26a447549f5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 26 Mar 2013 13:52:43 -0700 Subject: Adapt frame rate instead of keeping that constant and tweaking bitrate Change-Id: I889abbbe1237e1a8fdd7135cdc91a2e9728ff39b --- .../wifi-display/source/Converter.cpp | 22 ++----- .../libstagefright/wifi-display/source/Converter.h | 2 + .../wifi-display/source/PlaybackSession.cpp | 71 ++++++++++++++++++---- .../wifi-display/source/RepeaterSource.cpp | 19 ++++++ .../wifi-display/source/RepeaterSource.h | 3 + 5 files changed, 88 insertions(+), 29 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index d41e1e6..0a8462c 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -135,7 +135,9 @@ bool Converter::needToManuallyPrependSPSPPS() const { return mNeedToManuallyPrependSPSPPS; } -static int32_t getBitrate(const char *propName, int32_t defaultValue) { +// static +int32_t Converter::GetInt32Property( + const char *propName, int32_t defaultValue) { char val[PROPERTY_VALUE_MAX]; if (property_get(propName, val, NULL)) { char *end; @@ -185,8 +187,8 @@ status_t Converter::initEncoder() { mOutputFormat->setString("mime", outputMIME.c_str()); - int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 128000); - int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000); + int32_t audioBitrate = GetInt32Property("media.wfd.audio-bitrate", 128000); + int32_t videoBitrate = GetInt32Property("media.wfd.video-bitrate", 5000000); mPrevVideoBitrate = videoBitrate; ALOGI("using audio bitrate of %d bps, video bitrate of %d bps", @@ -622,20 +624,6 @@ status_t Converter::feedEncoderInputBuffers() { } status_t Converter::doMoreWork() { -#if 0 - if (mIsVideo) { - int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000); - if (videoBitrate != mPrevVideoBitrate) { - sp params = new AMessage; - - params->setInt32("videoBitrate", videoBitrate); - mEncoder->setParameters(params); - - mPrevVideoBitrate = videoBitrate; - } - } -#endif - status_t err; for (;;) { diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 538f10a..ba297c4 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -73,6 +73,8 @@ struct Converter : public AHandler { int32_t getVideoBitrate() const; void setVideoBitrate(int32_t bitrate); + static int32_t GetInt32Property(const char *propName, int32_t defaultValue); + protected: virtual ~Converter(); virtual void onMessageReceived(const sp &msg); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 68aa9cb..715d0b5 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -27,6 +27,7 @@ #include "WifiDisplaySource.h" #include +#include #include #include #include @@ -66,6 +67,7 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler { bool isAudio() const; const sp &converter() const; + const sp &repeaterSource() const; ssize_t mediaSenderTrackIndex() const; void setMediaSenderTrackIndex(size_t index); @@ -171,6 +173,11 @@ const sp &WifiDisplaySource::PlaybackSession::Track::converter() cons return mConverter; } +const sp & +WifiDisplaySource::PlaybackSession::Track::repeaterSource() const { + return mRepeaterSource; +} + ssize_t WifiDisplaySource::PlaybackSession::Track::mediaSenderTrackIndex() const { CHECK_GE(mMediaSenderTrackIndex, 0); return mMediaSenderTrackIndex; @@ -663,27 +670,67 @@ void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp &msg) if (mVideoTrackIndex >= 0) { const sp &videoTrack = mTracks.valueFor(mVideoTrackIndex); sp converter = videoTrack->converter(); - if (converter != NULL) { - int32_t videoBitrate = converter->getVideoBitrate(); - if (avgLatencyUs > 300000ll) { - videoBitrate *= 0.6; + if (converter != NULL) { + int32_t videoBitrate = + Converter::GetInt32Property("media.wfd.video-bitrate", -1); + + char val[PROPERTY_VALUE_MAX]; + if (videoBitrate < 0 + && property_get("media.wfd.video-bitrate", val, NULL) + && !strcasecmp("adaptive", val)) { + videoBitrate = converter->getVideoBitrate(); + + if (avgLatencyUs > 300000ll) { + videoBitrate *= 0.6; + } else if (avgLatencyUs < 100000ll) { + videoBitrate *= 1.1; + } + } + if (videoBitrate > 0) { if (videoBitrate < 500000) { - videoBitrate = 500000; // cap at 500kbit/sec + videoBitrate = 500000; + } else if (videoBitrate > 10000000) { + videoBitrate = 10000000; + } + + if (videoBitrate != converter->getVideoBitrate()) { + ALOGI("setting video bitrate to %d bps", videoBitrate); + + converter->setVideoBitrate(videoBitrate); } - } else if (avgLatencyUs < 100000ll) { - videoBitrate *= 1.1; + } + } + + sp repeaterSource = videoTrack->repeaterSource(); + if (repeaterSource != NULL) { + double rateHz = + Converter::GetInt32Property( + "media.wfd.video-framerate", -1); - if (videoBitrate > 10000000) { - videoBitrate = 10000000; // cap at 10Mbit/sec + if (rateHz < 0.0) { + rateHz = repeaterSource->getFrameRate(); + + if (avgLatencyUs > 300000ll) { + rateHz *= 0.9; + } else if (avgLatencyUs < 200000ll) { + rateHz *= 1.1; } } - if (videoBitrate != converter->getVideoBitrate()) { - ALOGI("setting video bitrate to %d bps", videoBitrate); + if (rateHz > 0) { + if (rateHz < 5.0) { + rateHz = 5.0; + } else if (rateHz > 30.0) { + rateHz = 30.0; + } + + if (rateHz != repeaterSource->getFrameRate()) { + ALOGI("setting frame rate to %.2f Hz", rateHz); - converter->setVideoBitrate(videoBitrate); + repeaterSource->setFrameRate(rateHz); + } } } } diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp index 72be927..cc8dee3 100644 --- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp +++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp @@ -27,6 +27,25 @@ RepeaterSource::~RepeaterSource() { CHECK(!mStarted); } +double RepeaterSource::getFrameRate() const { + return mRateHz; +} + +void RepeaterSource::setFrameRate(double rateHz) { + Mutex::Autolock autoLock(mLock); + + if (rateHz == mRateHz) { + return; + } + + if (mStartTimeUs >= 0ll) { + int64_t nextTimeUs = mStartTimeUs + (mFrameCount * 1000000ll) / mRateHz; + mStartTimeUs = nextTimeUs; + mFrameCount = 0; + } + mRateHz = rateHz; +} + status_t RepeaterSource::start(MetaData *params) { CHECK(!mStarted); diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h index 146af32..8d414fd 100644 --- a/media/libstagefright/wifi-display/source/RepeaterSource.h +++ b/media/libstagefright/wifi-display/source/RepeaterSource.h @@ -28,6 +28,9 @@ struct RepeaterSource : public MediaSource { // send updates in a while, this is its wakeup call. void wakeUp(); + double getFrameRate() const; + void setFrameRate(double rateHz); + protected: virtual ~RepeaterSource(); -- cgit v1.1 From 09108adeca8cbbf3fbb21f8aea2a2ff250db9531 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 26 Mar 2013 16:37:19 -0700 Subject: ToneGenerator: fix stop/destroy concurrency There is a problem if the stopTone() method is called from two different threads (for instance if the destructor is called while stopTone() is waiting for the audio callback to finish). In this case, the second call to stopTone() will not wait for the condition to be signaled and call clearWaveGens() while the callback can still be active, thus causing a crash. There is a similar problem in case of concurrent calls to startTone() and stopTone(). The fix consists in making sure that stopTone() always waits for call back completion or timeout and exits before calling clearWaveGens() if a concurrent start request is detected. Bug 8163071 Change-Id: I9ddb4390407701dcad5bf83660fd9903f0d72268 --- media/libmedia/ToneGenerator.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 58d495e..3554608 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -976,21 +976,26 @@ void ToneGenerator::stopTone() { ALOGV("stopTone"); mLock.lock(); - if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) { - mState = TONE_STOPPING; + if (mState != TONE_IDLE && mState != TONE_INIT) { + if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) { + mState = TONE_STOPPING; + } ALOGV("waiting cond"); status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3)); if (lStatus == NO_ERROR) { + // If the tone was restarted exit now before calling clearWaveGens(); + if (mState != TONE_INIT) { + return; + } ALOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); } else { ALOGE("--- Stop timed out"); mState = TONE_IDLE; mpAudioTrack->stop(); } + clearWaveGens(); } - clearWaveGens(); - mLock.unlock(); } @@ -1299,7 +1304,7 @@ audioCallback_EndLoop: } if (lSignal) - lpToneGen->mWaitCbkCond.signal(); + lpToneGen->mWaitCbkCond.broadcast(); lpToneGen->mLock.unlock(); } } -- cgit v1.1 From 59ca8040a0cfb0324c6be7dded74d02e9f5cf6e8 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 27 Mar 2013 13:48:36 -0700 Subject: Fix valgrind error. The constructor calls reset(), which in turn calls stop(), which then accesses mCaptureTimeLapse before it has been initialized. Change-Id: Ia94ac740b9bd1a0389c72647a5639dd25320d92c --- media/libmediaplayerservice/StagefrightRecorder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index c2c9985..095d5ca 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -70,7 +70,8 @@ StagefrightRecorder::StagefrightRecorder() mOutputFd(-1), mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), - mStarted(false), mSurfaceMediaSource(NULL) { + mStarted(false), mSurfaceMediaSource(NULL), + mCaptureTimeLapse(false) { ALOGV("Constructor"); reset(); -- cgit v1.1 From 4a7fe9cd333c4cf533f78a074c8c2c820b94c6d2 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 27 Mar 2013 16:44:54 -0700 Subject: Fix valgrind error in software h264 decoder h264bsdNextMbAddress could read past the end of an allocation, which could conceivably result in a segfault if the allocation was at the very end of a page. Change-Id: Id7a0c5733d66e609f36feb0e15b2d67b9bbc0b4d --- media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c index 53b2fd8..cc838fd 100755 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c @@ -220,7 +220,7 @@ u32 h264bsdNextMbAddress(u32 *pSliceGroupMap, u32 picSizeInMbs, u32 currMbAddr) /* Variables */ - u32 i, sliceGroup, tmp; + u32 i, sliceGroup; /* Code */ @@ -231,11 +231,9 @@ u32 h264bsdNextMbAddress(u32 *pSliceGroupMap, u32 picSizeInMbs, u32 currMbAddr) sliceGroup = pSliceGroupMap[currMbAddr]; i = currMbAddr + 1; - tmp = pSliceGroupMap[i]; - while ((i < picSizeInMbs) && (tmp != sliceGroup)) + while ((i < picSizeInMbs) && (pSliceGroupMap[i] != sliceGroup)) { i++; - tmp = pSliceGroupMap[i]; } if (i == picSizeInMbs) -- cgit v1.1 From 681be0398a06da7e24db4ed934a92af64d1409b1 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 27 Mar 2013 12:05:40 -0700 Subject: ToneGenerator: fix truncated tones The first DTMF tone after a silent period is truncated. This is because the phone app starts and stops the tone when the user presses and releases the key. This combined to the fact that the tones use the low latency path and that when the output stream exists standby there is a period of several milliseconds during which no audio is mixed until the stream is "warmed up". The result is that much less audio is generated than the actual key press duration. The fix consists in storing the tone start time and making sure that the number of samples generated corresponds at least to the time difference between the tone start and stop commands. Bug 6607077 Change-Id: I070d20dd8600c25a9e5d5a60c1d3313b7917b00d --- media/libmedia/ToneGenerator.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 3554608..9ea3ea7 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -922,6 +922,9 @@ bool ToneGenerator::startTone(tone_type toneType, int durationMs) { ALOGV("Immediate start, time %d", (unsigned int)(systemTime()/1000000)); lResult = true; mState = TONE_STARTING; + if (clock_gettime(CLOCK_MONOTONIC, &mStartTime) != 0) { + mStartTime.tv_sec = 0; + } mLock.unlock(); mpAudioTrack->start(); mLock.lock(); @@ -940,6 +943,7 @@ bool ToneGenerator::startTone(tone_type toneType, int durationMs) { } else { ALOGV("Delayed start"); mState = TONE_RESTARTING; + mStartTime.tv_sec = 0; lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3)); if (lStatus == NO_ERROR) { if (mState != TONE_IDLE) { @@ -978,7 +982,30 @@ void ToneGenerator::stopTone() { mLock.lock(); if (mState != TONE_IDLE && mState != TONE_INIT) { if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) { - mState = TONE_STOPPING; + struct timespec stopTime; + // If the start time is valid, make sure that the number of audio samples produced + // corresponds at least to the time between the start and stop commands. + // This is needed in case of cold start of the output stream. + if ((mStartTime. tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) { + time_t sec = stopTime.tv_sec - mStartTime.tv_sec; + long nsec = stopTime.tv_nsec - mStartTime.tv_nsec; + long durationMs; + if (nsec < 0) { + --sec; + nsec += 1000000000; + } + + if ((sec + 1) > ((long)(INT_MAX / mSamplingRate))) { + mMaxSmp = sec * mSamplingRate; + } else { + // mSamplingRate is always > 1000 + sec = sec * 1000 + nsec / 1000000; // duration in milliseconds + mMaxSmp = (sec * mSamplingRate) / 1000; + } + ALOGV("stopTone() forcing mMaxSmp to %d, total for far %d", mMaxSmp, mTotalSmp); + } else { + mState = TONE_STOPPING; + } } ALOGV("waiting cond"); status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3)); @@ -1263,6 +1290,9 @@ audioCallback_EndLoop: ALOGV("Cbk restarting track"); if (lpToneGen->prepareWave()) { lpToneGen->mState = TONE_STARTING; + if (clock_gettime(CLOCK_MONOTONIC, &lpToneGen->mStartTime) != 0) { + lpToneGen->mStartTime.tv_sec = 0; + } // must reload lpToneDesc as prepareWave() may change mpToneDesc lpToneDesc = lpToneGen->mpToneDesc; } else { -- cgit v1.1 From e467ef084b75b074d0081616080b54212a7024c8 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 27 Mar 2013 19:04:12 -0700 Subject: stagefright: matroska: don't crash on parsing error MatroskaExtractor crashed with an assertion if mkvparser::ParseNext() returned a negative number. Now handle all error values the same way as the EOF. Change-Id: I173c4f878d692a0cbdb915ad1118d0686249d625 Signed-off-by: Lajos Molnar Bug: 8433794 --- media/libstagefright/matroska/MatroskaExtractor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 7fc7037..b304749 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -263,8 +263,8 @@ void BlockIterator::advance_l() { mCluster, nextCluster, pos, len); ALOGV("ParseNext returned %ld", res); - if (res > 0) { - // EOF + if (res != 0) { + // EOF or error mCluster = NULL; break; -- cgit v1.1 From d477b8d071826c0768620f7ac302f31d8b12b1ca Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 28 Mar 2013 11:16:43 -0700 Subject: Make sure resume() and flush() are handled appropriately even if the codec is in Idle->Executing state. Change-Id: I16a10791fae0e062a19299732c472cc93e4ed971 related-to-bug: 8347958 --- media/libstagefright/ACodec.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 1a2eeb1..c9f8741 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3461,6 +3461,22 @@ bool ACodec::IdleToExecutingState::onMessageReceived(const sp &msg) { return true; } + case kWhatResume: + { + // We'll be active soon enough. + return true; + } + + case kWhatFlush: + { + // We haven't even started yet, so we're flushed alright... + sp notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatFlushCompleted); + notify->post(); + + return true; + } + case kWhatSignalEndOfInputStream: { mCodec->onSignalEndOfInputStream(); -- cgit v1.1 From a8eccec73a40d4afcff505eb463a016c89aeed42 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 28 Mar 2013 11:58:45 -0700 Subject: ToneGenerator: fix overflow in stopTone Fix overflow in tone duration calculation introduced in commit 681be039. Bug 6607077 Change-Id: Ie12f13701345c2b2d3be0b3c4d71cbfa2394a29b --- media/libmedia/ToneGenerator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 9ea3ea7..f09ce75 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -986,7 +986,7 @@ void ToneGenerator::stopTone() { // If the start time is valid, make sure that the number of audio samples produced // corresponds at least to the time between the start and stop commands. // This is needed in case of cold start of the output stream. - if ((mStartTime. tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) { + if ((mStartTime.tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) { time_t sec = stopTime.tv_sec - mStartTime.tv_sec; long nsec = stopTime.tv_nsec - mStartTime.tv_nsec; long durationMs; @@ -1000,7 +1000,7 @@ void ToneGenerator::stopTone() { } else { // mSamplingRate is always > 1000 sec = sec * 1000 + nsec / 1000000; // duration in milliseconds - mMaxSmp = (sec * mSamplingRate) / 1000; + mMaxSmp = (unsigned int)(((int64_t)sec * mSamplingRate) / 1000); } ALOGV("stopTone() forcing mMaxSmp to %d, total for far %d", mMaxSmp, mTotalSmp); } else { -- cgit v1.1 From ff9297ac908aa01e44fda4ab9ca7a4bb514c00fd Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 28 Mar 2013 13:34:10 -0700 Subject: Fix SHUTDOWN after SETUP and before PLAY in wifi display. Change-Id: Ieb8ce1ac3130254839975a3677162b64156735bc related-to-bug: 8499893 --- .../wifi-display/source/WifiDisplaySource.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 792a9c5..4a49811 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -273,7 +273,8 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { if (!strcasecmp(val, "pause") && mState == PLAYING) { mState = PLAYING_TO_PAUSED; sendTrigger(mClientSessionID, TRIGGER_PAUSE); - } else if (!strcasecmp(val, "play") && mState == PAUSED) { + } else if (!strcasecmp(val, "play") + && mState == PAUSED) { mState = PAUSED_TO_PLAYING; sendTrigger(mClientSessionID, TRIGGER_PLAY); } @@ -422,7 +423,8 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { NULL /* interlaced */)); mClient->onDisplayConnected( - mClientInfo.mPlaybackSession->getSurfaceTexture(), + mClientInfo.mPlaybackSession + ->getSurfaceTexture(), width, height, mUsingHDCP @@ -1351,6 +1353,15 @@ status_t WifiDisplaySource::onPlayRequest( return ERROR_MALFORMED; } + if (mState != AWAITING_CLIENT_PLAY) { + ALOGW("Received PLAY request but we're in state %d", mState); + + sendErrorResponse( + sessionID, "455 Method Not Valid in This State", cseq); + + return INVALID_OPERATION; + } + ALOGI("Received PLAY request."); if (mPlaybackSessionEstablished) { finishPlay(); @@ -1673,7 +1684,10 @@ void WifiDisplaySource::HDCPObserver::notify( status_t WifiDisplaySource::makeHDCP() { sp sm = defaultServiceManager(); sp binder = sm->getService(String16("media.player")); - sp service = interface_cast(binder); + + sp service = + interface_cast(binder); + CHECK(service != NULL); mHDCP = service->makeHDCP(true /* createEncryptionModule */); -- cgit v1.1 From ec24fa46443634cd29627182c5812ccf43682692 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 1 Apr 2013 10:51:35 -0700 Subject: MTP: Implement date created field in GetObjectInfo Bug: 8293874 Change-Id: I6b74fe73362bd4fac34ca8a54e127ae91f82aef9 --- media/mtp/MtpServer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 8568dfc..df87db4 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -704,7 +704,8 @@ MtpResponseCode MtpServer::doGetObjectInfo() { mData.putUInt32(info.mAssociationDesc); mData.putUInt32(info.mSequenceNumber); mData.putString(info.mName); - mData.putEmptyString(); // date created + formatDateTime(info.mDateCreated, date, sizeof(date)); + mData.putString(date); // date created formatDateTime(info.mDateModified, date, sizeof(date)); mData.putString(date); // date modified mData.putEmptyString(); // keywords -- cgit v1.1 From 997594088164cfb33c1cb8c376884346fbf1e7ae Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 1 Apr 2013 14:28:31 -0700 Subject: Fix seek in response to OnPrepared() for HLS content Change-Id: I0e52352845398a4db074e939487f6f6de94bd523 related-to-bug: 8225122 --- media/libmediaplayerservice/MediaPlayerFactory.cpp | 3 ++- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 19 +++++++++-------- media/libstagefright/httplive/LiveSession.cpp | 24 ++++++++++------------ 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index 1fb8b1a..90aed39 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -206,7 +206,8 @@ class NuPlayerFactory : public MediaPlayerFactory::IFactory { return 0.0; if (!strncasecmp("http://", url, 7) - || !strncasecmp("https://", url, 8)) { + || !strncasecmp("https://", url, 8) + || !strncasecmp("file://", url, 7)) { size_t len = strlen(url); if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { return kOurScore; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 5387e1a..46d0a5a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -161,7 +161,8 @@ void NuPlayer::setDataSourceAsync(const sp &source) { static bool IsHTTPLiveURL(const char *url) { if (!strncasecmp("http://", url, 7) - || !strncasecmp("https://", url, 8)) { + || !strncasecmp("https://", url, 8) + || !strncasecmp("file://", url, 7)) { size_t len = strlen(url); if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { return true; @@ -833,14 +834,6 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp *decoder) { (*decoder)->configure(format); - int64_t durationUs; - if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) { - sp driver = mDriver.promote(); - if (driver != NULL) { - driver->notifyDuration(durationUs); - } - } - return OK; } @@ -1271,6 +1264,14 @@ void NuPlayer::onSourceNotify(const sp &msg) { if (driver != NULL) { driver->notifyPrepareCompleted(err); } + + int64_t durationUs; + if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) { + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifyDuration(durationUs); + } + } break; } diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 962b01c..505bdb3 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -631,22 +631,20 @@ rinse_repeat: if (index < mPlaylist->size()) { int32_t newSeqNumber = firstSeqNumberInPlaylist + index; - if (newSeqNumber != mSeqNumber) { - ALOGI("seeking to seq no %d", newSeqNumber); + ALOGI("seeking to seq no %d", newSeqNumber); - mSeqNumber = newSeqNumber; + mSeqNumber = newSeqNumber; - mDataSource->reset(); + mDataSource->reset(); - // reseting the data source will have had the - // side effect of discarding any previously queued - // bandwidth change discontinuity. - // Therefore we'll need to treat these seek - // discontinuities as involving a bandwidth change - // even if they aren't directly. - seekDiscontinuity = true; - bandwidthChanged = true; - } + // reseting the data source will have had the + // side effect of discarding any previously queued + // bandwidth change discontinuity. + // Therefore we'll need to treat these seek + // discontinuities as involving a bandwidth change + // even if they aren't directly. + seekDiscontinuity = true; + bandwidthChanged = true; } } -- cgit v1.1 From 4c63a239c404af1e055e5f9939939ab0fd09d98a Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Sat, 30 Mar 2013 16:19:44 -0700 Subject: MediaDrm API update Clarify offline usage of sessions and keys and implement implement CryptoSession to support additional crypto use cases. Change-Id: I5d8000ce7e1dd7eba08969fc50296c9e1456c4fc --- media/libmedia/IDrm.cpp | 408 +++++++++++++++++++++++++----------- media/libmediaplayerservice/Drm.cpp | 148 +++++++++++-- media/libmediaplayerservice/Drm.h | 53 ++++- 3 files changed, 465 insertions(+), 144 deletions(-) (limited to 'media') diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp index 3b13ec6..1641b56 100644 --- a/media/libmedia/IDrm.cpp +++ b/media/libmedia/IDrm.cpp @@ -33,10 +33,11 @@ enum { DESTROY_PLUGIN, OPEN_SESSION, CLOSE_SESSION, - GET_LICENSE_REQUEST, - PROVIDE_LICENSE_RESPONSE, - REMOVE_LICENSE, - QUERY_LICENSE_STATUS, + GET_KEY_REQUEST, + PROVIDE_KEY_RESPONSE, + REMOVE_KEYS, + RESTORE_KEYS, + QUERY_KEY_STATUS, GET_PROVISION_REQUEST, PROVIDE_PROVISION_RESPONSE, GET_SECURE_STOPS, @@ -44,7 +45,13 @@ enum { GET_PROPERTY_STRING, GET_PROPERTY_BYTE_ARRAY, SET_PROPERTY_STRING, - SET_PROPERTY_BYTE_ARRAY + SET_PROPERTY_BYTE_ARRAY, + SET_CIPHER_ALGORITHM, + SET_MAC_ALGORITHM, + ENCRYPT, + DECRYPT, + SIGN, + VERIFY }; struct BpDrm : public BpInterface { @@ -92,9 +99,7 @@ struct BpDrm : public BpInterface { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); remote()->transact(OPEN_SESSION, data, &reply); - uint32_t size = reply.readInt32(); - sessionId.insertAt((size_t)0, size); - reply.read(sessionId.editArray(), size); + readVector(reply, sessionId); return reply.readInt32(); } @@ -103,80 +108,81 @@ struct BpDrm : public BpInterface { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(sessionId.size()); - data.write(sessionId.array(), sessionId.size()); + writeVector(data, sessionId); remote()->transact(CLOSE_SESSION, data, &reply); return reply.readInt32(); } virtual status_t - getLicenseRequest(Vector const &sessionId, - Vector const &initData, - String8 const &mimeType, DrmPlugin::LicenseType licenseType, - KeyedVector const &optionalParameters, - Vector &request, String8 &defaultUrl) { + getKeyRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::KeyType keyType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(sessionId.size()); - data.write(sessionId.array(), sessionId.size()); - - data.writeInt32(initData.size()); - data.write(initData.array(), initData.size()); - + writeVector(data, sessionId); + writeVector(data, initData); data.writeString8(mimeType); - data.writeInt32((uint32_t)licenseType); + data.writeInt32((uint32_t)keyType); data.writeInt32(optionalParameters.size()); for (size_t i = 0; i < optionalParameters.size(); ++i) { data.writeString8(optionalParameters.keyAt(i)); data.writeString8(optionalParameters.valueAt(i)); } - remote()->transact(GET_LICENSE_REQUEST, data, &reply); + remote()->transact(GET_KEY_REQUEST, data, &reply); - uint32_t len = reply.readInt32(); - request.insertAt((size_t)0, len); - reply.read(request.editArray(), len); + readVector(reply, request); defaultUrl = reply.readString8(); return reply.readInt32(); } - virtual status_t provideLicenseResponse(Vector const &sessionId, - Vector const &response) { + virtual status_t provideKeyResponse(Vector const &sessionId, + Vector const &response, + Vector &keySetId) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + writeVector(data, sessionId); + writeVector(data, response); + remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply); + readVector(reply, keySetId); + + return reply.readInt32(); + } + + virtual status_t removeKeys(Vector const &keySetId) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(sessionId.size()); - data.write(sessionId.array(), sessionId.size()); - data.writeInt32(response.size()); - data.write(response.array(), response.size()); - remote()->transact(PROVIDE_LICENSE_RESPONSE, data, &reply); + writeVector(data, keySetId); + remote()->transact(REMOVE_KEYS, data, &reply); return reply.readInt32(); } - virtual status_t removeLicense(Vector const &sessionId) { + virtual status_t restoreKeys(Vector const &sessionId, + Vector const &keySetId) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(sessionId.size()); - data.write(sessionId.array(), sessionId.size()); - remote()->transact(REMOVE_LICENSE, data, &reply); + writeVector(data, sessionId); + writeVector(data, keySetId); + remote()->transact(RESTORE_KEYS, data, &reply); return reply.readInt32(); } - virtual status_t queryLicenseStatus(Vector const &sessionId, + virtual status_t queryKeyStatus(Vector const &sessionId, KeyedVector &infoMap) const { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(sessionId.size()); - data.write(sessionId.array(), sessionId.size()); - - remote()->transact(QUERY_LICENSE_STATUS, data, &reply); + writeVector(data, sessionId); + remote()->transact(QUERY_KEY_STATUS, data, &reply); infoMap.clear(); size_t count = reply.readInt32(); @@ -195,9 +201,7 @@ struct BpDrm : public BpInterface { remote()->transact(GET_PROVISION_REQUEST, data, &reply); - uint32_t len = reply.readInt32(); - request.insertAt((size_t)0, len); - reply.read(request.editArray(), len); + readVector(reply, request); defaultUrl = reply.readString8(); return reply.readInt32(); @@ -207,8 +211,7 @@ struct BpDrm : public BpInterface { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(response.size()); - data.write(response.array(), response.size()); + writeVector(data, response); remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply); return reply.readInt32(); @@ -224,9 +227,7 @@ struct BpDrm : public BpInterface { uint32_t count = reply.readInt32(); for (size_t i = 0; i < count; i++) { Vector secureStop; - uint32_t len = reply.readInt32(); - secureStop.insertAt((size_t)0, len); - reply.read(secureStop.editArray(), len); + readVector(reply, secureStop); secureStops.push_back(secureStop); } return reply.readInt32(); @@ -236,8 +237,7 @@ struct BpDrm : public BpInterface { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(ssRelease.size()); - data.write(ssRelease.array(), ssRelease.size()); + writeVector(data, ssRelease); remote()->transact(RELEASE_SECURE_STOPS, data, &reply); return reply.readInt32(); @@ -261,10 +261,7 @@ struct BpDrm : public BpInterface { data.writeString8(name); remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply); - uint32_t len = reply.readInt32(); - value.insertAt((size_t)0, len); - reply.read(value.editArray(), len); - + readVector(reply, value); return reply.readInt32(); } @@ -285,15 +282,120 @@ struct BpDrm : public BpInterface { data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.writeString8(name); - data.writeInt32(value.size()); - data.write(value.array(), value.size()); + writeVector(data, value); remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply); return reply.readInt32(); } + virtual status_t setCipherAlgorithm(Vector const &sessionId, + String8 const &algorithm) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + writeVector(data, sessionId); + data.writeString8(algorithm); + remote()->transact(SET_CIPHER_ALGORITHM, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMacAlgorithm(Vector const &sessionId, + String8 const &algorithm) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + writeVector(data, sessionId); + data.writeString8(algorithm); + remote()->transact(SET_MAC_ALGORITHM, data, &reply); + return reply.readInt32(); + } + + virtual status_t encrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + writeVector(data, sessionId); + writeVector(data, keyId); + writeVector(data, input); + writeVector(data, iv); + + remote()->transact(ENCRYPT, data, &reply); + readVector(reply, output); + + return reply.readInt32(); + } + + virtual status_t decrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + writeVector(data, sessionId); + writeVector(data, keyId); + writeVector(data, input); + writeVector(data, iv); + + remote()->transact(DECRYPT, data, &reply); + readVector(reply, output); + + return reply.readInt32(); + } + + virtual status_t sign(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector &signature) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + writeVector(data, sessionId); + writeVector(data, keyId); + writeVector(data, message); + + remote()->transact(SIGN, data, &reply); + readVector(reply, signature); + + return reply.readInt32(); + } + + virtual status_t verify(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector const &signature, + bool &match) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + writeVector(data, sessionId); + writeVector(data, keyId); + writeVector(data, message); + writeVector(data, signature); + + remote()->transact(VERIFY, data, &reply); + match = (bool)reply.readInt32(); + return reply.readInt32(); + } + private: + void readVector(Parcel &reply, Vector &vector) const { + uint32_t size = reply.readInt32(); + vector.insertAt((size_t)0, size); + reply.read(vector.editArray(), size); + } + + void writeVector(Parcel &data, Vector const &vector) const { + data.writeInt32(vector.size()); + data.write(vector.array(), vector.size()); + } + DISALLOW_EVIL_CONSTRUCTORS(BpDrm); }; @@ -301,6 +403,17 @@ IMPLEMENT_META_INTERFACE(Drm, "android.drm.IDrm"); //////////////////////////////////////////////////////////////////////////////// +void BnDrm::readVector(const Parcel &data, Vector &vector) const { + uint32_t size = data.readInt32(); + vector.insertAt((size_t)0, size); + data.read(vector.editArray(), size); +} + +void BnDrm::writeVector(Parcel *reply, Vector const &vector) const { + reply->writeInt32(vector.size()); + reply->write(vector.array(), vector.size()); +} + status_t BnDrm::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch (code) { @@ -341,8 +454,7 @@ status_t BnDrm::onTransact( CHECK_INTERFACE(IDrm, data, reply); Vector sessionId; status_t result = openSession(sessionId); - reply->writeInt32(sessionId.size()); - reply->write(sessionId.array(), sessionId.size()); + writeVector(reply, sessionId); reply->writeInt32(result); return OK; } @@ -351,28 +463,20 @@ status_t BnDrm::onTransact( { CHECK_INTERFACE(IDrm, data, reply); Vector sessionId; - uint32_t size = data.readInt32(); - sessionId.insertAt((size_t)0, size); - data.read(sessionId.editArray(), size); + readVector(data, sessionId); reply->writeInt32(closeSession(sessionId)); return OK; } - case GET_LICENSE_REQUEST: + case GET_KEY_REQUEST: { CHECK_INTERFACE(IDrm, data, reply); - Vector sessionId; - uint32_t size = data.readInt32(); - sessionId.insertAt((size_t)0, size); - data.read(sessionId.editArray(), size); - - Vector initData; - size = data.readInt32(); - initData.insertAt((size_t)0, size); - data.read(initData.editArray(), size); + Vector sessionId, initData; + readVector(data, sessionId); + readVector(data, initData); String8 mimeType = data.readString8(); - DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)data.readInt32(); + DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)data.readInt32(); KeyedVector optionalParameters; uint32_t count = data.readInt32(); @@ -386,55 +490,54 @@ status_t BnDrm::onTransact( Vector request; String8 defaultUrl; - status_t result = getLicenseRequest(sessionId, initData, - mimeType, licenseType, - optionalParameters, - request, defaultUrl); - reply->writeInt32(request.size()); - reply->write(request.array(), request.size()); + status_t result = getKeyRequest(sessionId, initData, + mimeType, keyType, + optionalParameters, + request, defaultUrl); + writeVector(reply, request); reply->writeString8(defaultUrl); reply->writeInt32(result); return OK; } - case PROVIDE_LICENSE_RESPONSE: + case PROVIDE_KEY_RESPONSE: { CHECK_INTERFACE(IDrm, data, reply); - Vector sessionId; - uint32_t size = data.readInt32(); - sessionId.insertAt((size_t)0, size); - data.read(sessionId.editArray(), size); - Vector response; - size = data.readInt32(); - response.insertAt((size_t)0, size); - data.read(response.editArray(), size); + Vector sessionId, response, keySetId; + readVector(data, sessionId); + readVector(data, response); + uint32_t result = provideKeyResponse(sessionId, response, keySetId); + writeVector(reply, keySetId); + reply->writeInt32(result); + return OK; + } - reply->writeInt32(provideLicenseResponse(sessionId, response)); + case REMOVE_KEYS: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector keySetId; + readVector(data, keySetId); + reply->writeInt32(removeKeys(keySetId)); return OK; } - case REMOVE_LICENSE: + case RESTORE_KEYS: { CHECK_INTERFACE(IDrm, data, reply); - Vector sessionId; - uint32_t size = data.readInt32(); - sessionId.insertAt((size_t)0, size); - data.read(sessionId.editArray(), size); - reply->writeInt32(removeLicense(sessionId)); + Vector sessionId, keySetId; + readVector(data, sessionId); + readVector(data, keySetId); + reply->writeInt32(restoreKeys(sessionId, keySetId)); return OK; } - case QUERY_LICENSE_STATUS: + case QUERY_KEY_STATUS: { CHECK_INTERFACE(IDrm, data, reply); Vector sessionId; - uint32_t size = data.readInt32(); - sessionId.insertAt((size_t)0, size); - data.read(sessionId.editArray(), size); + readVector(data, sessionId); KeyedVector infoMap; - - status_t result = queryLicenseStatus(sessionId, infoMap); - + status_t result = queryKeyStatus(sessionId, infoMap); size_t count = infoMap.size(); reply->writeInt32(count); for (size_t i = 0; i < count; ++i) { @@ -451,8 +554,7 @@ status_t BnDrm::onTransact( Vector request; String8 defaultUrl; status_t result = getProvisionRequest(request, defaultUrl); - reply->writeInt32(request.size()); - reply->write(request.array(), request.size()); + writeVector(reply, request); reply->writeString8(defaultUrl); reply->writeInt32(result); return OK; @@ -462,11 +564,8 @@ status_t BnDrm::onTransact( { CHECK_INTERFACE(IDrm, data, reply); Vector response; - uint32_t size = data.readInt32(); - response.insertAt((size_t)0, size); - data.read(response.editArray(), size); + readVector(data, response); reply->writeInt32(provideProvisionResponse(response)); - return OK; } @@ -491,9 +590,7 @@ status_t BnDrm::onTransact( { CHECK_INTERFACE(IDrm, data, reply); Vector ssRelease; - uint32_t size = data.readInt32(); - ssRelease.insertAt((size_t)0, size); - data.read(ssRelease.editArray(), size); + readVector(data, ssRelease); reply->writeInt32(releaseSecureStops(ssRelease)); return OK; } @@ -515,8 +612,7 @@ status_t BnDrm::onTransact( String8 name = data.readString8(); Vector value; status_t result = getPropertyByteArray(name, value); - reply->writeInt32(value.size()); - reply->write(value.array(), value.size()); + writeVector(reply, value); reply->writeInt32(result); return OK; } @@ -535,15 +631,89 @@ status_t BnDrm::onTransact( CHECK_INTERFACE(IDrm, data, reply); String8 name = data.readString8(); Vector value; - size_t count = data.readInt32(); - value.insertAt((size_t)0, count); - data.read(value.editArray(), count); + readVector(data, value); reply->writeInt32(setPropertyByteArray(name, value)); return OK; } - default: - return BBinder::onTransact(code, data, reply, flags); + case SET_CIPHER_ALGORITHM: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + readVector(data, sessionId); + String8 algorithm = data.readString8(); + reply->writeInt32(setCipherAlgorithm(sessionId, algorithm)); + return OK; + } + + case SET_MAC_ALGORITHM: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId; + readVector(data, sessionId); + String8 algorithm = data.readString8(); + reply->writeInt32(setMacAlgorithm(sessionId, algorithm)); + return OK; + } + + case ENCRYPT: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId, keyId, input, iv, output; + readVector(data, sessionId); + readVector(data, keyId); + readVector(data, input); + readVector(data, iv); + uint32_t result = encrypt(sessionId, keyId, input, iv, output); + writeVector(reply, output); + reply->writeInt32(result); + return OK; + } + + case DECRYPT: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId, keyId, input, iv, output; + readVector(data, sessionId); + readVector(data, keyId); + readVector(data, input); + readVector(data, iv); + uint32_t result = decrypt(sessionId, keyId, input, iv, output); + writeVector(reply, output); + reply->writeInt32(result); + return OK; + } + + case SIGN: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId, keyId, message, signature; + readVector(data, sessionId); + readVector(data, keyId); + readVector(data, message); + uint32_t result = sign(sessionId, keyId, message, signature); + writeVector(reply, signature); + reply->writeInt32(result); + return OK; + } + + case VERIFY: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector sessionId, keyId, message, signature; + readVector(data, sessionId); + readVector(data, keyId); + readVector(data, message); + readVector(data, signature); + bool match; + uint32_t result = verify(sessionId, keyId, message, signature, match); + reply->writeInt32(match); + reply->writeInt32(result); + return OK; + } + + default: + return BBinder::onTransact(code, data, reply, flags); } } diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp index 6ac7530..5fdb9f4 100644 --- a/media/libmediaplayerservice/Drm.cpp +++ b/media/libmediaplayerservice/Drm.cpp @@ -243,11 +243,11 @@ status_t Drm::closeSession(Vector const &sessionId) { return mPlugin->closeSession(sessionId); } -status_t Drm::getLicenseRequest(Vector const &sessionId, - Vector const &initData, - String8 const &mimeType, DrmPlugin::LicenseType licenseType, - KeyedVector const &optionalParameters, - Vector &request, String8 &defaultUrl) { +status_t Drm::getKeyRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::KeyType keyType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { @@ -258,12 +258,13 @@ status_t Drm::getLicenseRequest(Vector const &sessionId, return -EINVAL; } - return mPlugin->getLicenseRequest(sessionId, initData, mimeType, licenseType, - optionalParameters, request, defaultUrl); + return mPlugin->getKeyRequest(sessionId, initData, mimeType, keyType, + optionalParameters, request, defaultUrl); } -status_t Drm::provideLicenseResponse(Vector const &sessionId, - Vector const &response) { +status_t Drm::provideKeyResponse(Vector const &sessionId, + Vector const &response, + Vector &keySetId) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { @@ -274,10 +275,10 @@ status_t Drm::provideLicenseResponse(Vector const &sessionId, return -EINVAL; } - return mPlugin->provideLicenseResponse(sessionId, response); + return mPlugin->provideKeyResponse(sessionId, response, keySetId); } -status_t Drm::removeLicense(Vector const &sessionId) { +status_t Drm::removeKeys(Vector const &keySetId) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { @@ -288,11 +289,11 @@ status_t Drm::removeLicense(Vector const &sessionId) { return -EINVAL; } - return mPlugin->removeLicense(sessionId); + return mPlugin->removeKeys(keySetId); } -status_t Drm::queryLicenseStatus(Vector const &sessionId, - KeyedVector &infoMap) const { +status_t Drm::restoreKeys(Vector const &sessionId, + Vector const &keySetId) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { @@ -303,7 +304,22 @@ status_t Drm::queryLicenseStatus(Vector const &sessionId, return -EINVAL; } - return mPlugin->queryLicenseStatus(sessionId, infoMap); + return mPlugin->restoreKeys(sessionId, keySetId); +} + +status_t Drm::queryKeyStatus(Vector const &sessionId, + KeyedVector &infoMap) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->queryKeyStatus(sessionId, infoMap); } status_t Drm::getProvisionRequest(Vector &request, String8 &defaultUrl) { @@ -420,4 +436,106 @@ status_t Drm::setPropertyByteArray(String8 const &name, return mPlugin->setPropertyByteArray(name, value); } + +status_t Drm::setCipherAlgorithm(Vector const &sessionId, + String8 const &algorithm) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->setCipherAlgorithm(sessionId, algorithm); +} + +status_t Drm::setMacAlgorithm(Vector const &sessionId, + String8 const &algorithm) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->setMacAlgorithm(sessionId, algorithm); +} + +status_t Drm::encrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->encrypt(sessionId, keyId, input, iv, output); +} + +status_t Drm::decrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->decrypt(sessionId, keyId, input, iv, output); +} + +status_t Drm::sign(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector &signature) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->sign(sessionId, keyId, message, signature); +} + +status_t Drm::verify(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector const &signature, + bool &match) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPlugin == NULL) { + return -EINVAL; + } + + return mPlugin->verify(sessionId, keyId, message, signature, match); +} + } // namespace android diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h index 1b10958..f24921e 100644 --- a/media/libmediaplayerservice/Drm.h +++ b/media/libmediaplayerservice/Drm.h @@ -45,19 +45,23 @@ struct Drm : public BnDrm { virtual status_t closeSession(Vector const &sessionId); virtual status_t - getLicenseRequest(Vector const &sessionId, - Vector const &initData, - String8 const &mimeType, DrmPlugin::LicenseType licenseType, - KeyedVector const &optionalParameters, - Vector &request, String8 &defaultUrl); + getKeyRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::KeyType keyType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl); - virtual status_t provideLicenseResponse(Vector const &sessionId, - Vector const &response); + virtual status_t provideKeyResponse(Vector const &sessionId, + Vector const &response, + Vector &keySetId); - virtual status_t removeLicense(Vector const &sessionId); + virtual status_t removeKeys(Vector const &keySetId); - virtual status_t queryLicenseStatus(Vector const &sessionId, - KeyedVector &infoMap) const; + virtual status_t restoreKeys(Vector const &sessionId, + Vector const &keySetId); + + virtual status_t queryKeyStatus(Vector const &sessionId, + KeyedVector &infoMap) const; virtual status_t getProvisionRequest(Vector &request, String8 &defaulUrl); @@ -75,6 +79,35 @@ struct Drm : public BnDrm { virtual status_t setPropertyByteArray(String8 const &name, Vector const &value ) const; + virtual status_t setCipherAlgorithm(Vector const &sessionId, + String8 const &algorithm); + + virtual status_t setMacAlgorithm(Vector const &sessionId, + String8 const &algorithm); + + virtual status_t encrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output); + + virtual status_t decrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output); + + virtual status_t sign(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector &signature); + + virtual status_t verify(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector const &signature, + bool &match); + private: mutable Mutex mLock; -- cgit v1.1 From c0d5f1f8405de861ed6f1725f26cd6601e7103ab Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Tue, 2 Apr 2013 13:08:05 -0700 Subject: Implement async event callout from drm plugin to Java app Change-Id: I007f147d693664e777b8758be2bb8a4c7ec0236b --- media/libmedia/Android.mk | 1 + media/libmedia/IDrm.cpp | 19 ++++++++- media/libmedia/IDrmClient.cpp | 81 +++++++++++++++++++++++++++++++++++++ media/libmediaplayerservice/Drm.cpp | 40 +++++++++++++++++- media/libmediaplayerservice/Drm.h | 14 ++++++- 5 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 media/libmedia/IDrmClient.cpp (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 1ada9c3..fbe71ad 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -20,6 +20,7 @@ LOCAL_SRC_FILES:= \ IAudioRecord.cpp \ ICrypto.cpp \ IDrm.cpp \ + IDrmClient.cpp \ IHDCP.cpp \ AudioRecord.cpp \ AudioSystem.cpp \ diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp index 1641b56..1578846 100644 --- a/media/libmedia/IDrm.cpp +++ b/media/libmedia/IDrm.cpp @@ -51,7 +51,8 @@ enum { ENCRYPT, DECRYPT, SIGN, - VERIFY + VERIFY, + SET_LISTENER }; struct BpDrm : public BpInterface { @@ -384,6 +385,14 @@ struct BpDrm : public BpInterface { return reply.readInt32(); } + virtual status_t setListener(const sp& listener) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + remote()->transact(SET_LISTENER, data, &reply); + return reply.readInt32(); + } + private: void readVector(Parcel &reply, Vector &vector) const { uint32_t size = reply.readInt32(); @@ -712,6 +721,14 @@ status_t BnDrm::onTransact( return OK; } + case SET_LISTENER: { + CHECK_INTERFACE(IDrm, data, reply); + sp listener = + interface_cast(data.readStrongBinder()); + reply->writeInt32(setListener(listener)); + return NO_ERROR; + } break; + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IDrmClient.cpp b/media/libmedia/IDrmClient.cpp new file mode 100644 index 0000000..f50715e --- /dev/null +++ b/media/libmedia/IDrmClient.cpp @@ -0,0 +1,81 @@ +/* +** +** Copyright 2013, 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 "IDrmClient" +#include + +#include +#include +#include + +#include +#include + +namespace android { + +enum { + NOTIFY = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpDrmClient: public BpInterface +{ +public: + BpDrmClient(const sp& impl) + : BpInterface(impl) + { + } + + virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) + { + Parcel data, reply; + data.writeInterfaceToken(IDrmClient::getInterfaceDescriptor()); + data.writeInt32((int)eventType); + data.writeInt32(extra); + if (obj && obj->dataSize() > 0) { + data.appendFrom(const_cast(obj), 0, obj->dataSize()); + } + remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(DrmClient, "android.media.IDrmClient"); + +// ---------------------------------------------------------------------- + +status_t BnDrmClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case NOTIFY: { + CHECK_INTERFACE(IDrmClient, data, reply); + int eventType = data.readInt32(); + int extra = data.readInt32(); + Parcel obj; + if (data.dataAvail() > 0) { + obj.appendFrom(const_cast(&data), data.dataPosition(), data.dataAvail()); + } + + notify((DrmPlugin::EventType)eventType, extra, &obj); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp index 5fdb9f4..1e6cd94 100644 --- a/media/libmediaplayerservice/Drm.cpp +++ b/media/libmediaplayerservice/Drm.cpp @@ -47,6 +47,7 @@ static bool operator<(const Vector &lhs, const Vector &rhs) { Drm::Drm() : mInitCheck(NO_INIT), + mListener(NULL), mFactory(NULL), mPlugin(NULL) { } @@ -67,6 +68,41 @@ status_t Drm::initCheck() const { return mInitCheck; } +status_t Drm::setListener(const sp& listener) +{ + Mutex::Autolock lock(mEventLock); + mListener = listener; + return NO_ERROR; +} + +void Drm::sendEvent(DrmPlugin::EventType eventType, int extra, + Vector const *sessionId, + Vector const *data) +{ + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Parcel obj; + if (sessionId && sessionId->size()) { + obj.writeInt32(sessionId->size()); + obj.write(sessionId->array(), sessionId->size()); + } else { + obj.writeInt32(0); + } + + if (data && data->size()) { + obj.writeInt32(data->size()); + obj.write(data->array(), data->size()); + } else { + obj.writeInt32(0); + } + + Mutex::Autolock lock(mNotifyLock); + listener->notify(eventType, extra, &obj); + } +} /* * Search the plugins directory for a plugin that supports the scheme @@ -195,7 +231,9 @@ status_t Drm::createPlugin(const uint8_t uuid[16]) { return mInitCheck; } - return mFactory->createDrmPlugin(uuid, &mPlugin); + status_t result = mFactory->createDrmPlugin(uuid, &mPlugin); + mPlugin->setListener(this); + return result; } status_t Drm::destroyPlugin() { diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h index f24921e..3da8ad4 100644 --- a/media/libmediaplayerservice/Drm.h +++ b/media/libmediaplayerservice/Drm.h @@ -21,6 +21,7 @@ #include "SharedLibrary.h" #include +#include #include namespace android { @@ -28,7 +29,7 @@ namespace android { struct DrmFactory; struct DrmPlugin; -struct Drm : public BnDrm { +struct Drm : public BnDrm, public DrmPluginListener { Drm(); virtual ~Drm(); @@ -108,10 +109,21 @@ struct Drm : public BnDrm { Vector const &signature, bool &match); + virtual status_t setListener(const sp& listener); + + virtual void sendEvent(DrmPlugin::EventType eventType, int extra, + Vector const *sessionId, + Vector const *data); + private: mutable Mutex mLock; status_t mInitCheck; + + sp mListener; + mutable Mutex mEventLock; + mutable Mutex mNotifyLock; + sp mLibrary; DrmFactory *mFactory; DrmPlugin *mPlugin; -- cgit v1.1 From a5761dcb5f9f017ca9b83ec868565d7ce54a92ac Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 10:42:23 -0700 Subject: Fix a number of warnings in stagefright code. Change-Id: If3edd00d991851797aeccdfe795a4a405e3a2ea3 --- media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp | 2 +- media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp | 2 +- media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp index 7719435..5749733 100644 --- a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp +++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp @@ -481,7 +481,7 @@ void SoftAACEncoder2::onQueueFilled(OMX_U32 portIndex) { void* inBuffer[] = { (unsigned char *)mInputFrame }; INT inBufferIds[] = { IN_AUDIO_DATA }; - INT inBufferSize[] = { numBytesPerInputFrame }; + INT inBufferSize[] = { (INT)numBytesPerInputFrame }; INT inBufferElSize[] = { sizeof(int16_t) }; AACENC_BufDesc inBufDesc; diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp index 07f8b4f..50b739c 100644 --- a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp +++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp @@ -257,7 +257,7 @@ OMX_ERRORTYPE SoftAMRNBEncoder::internalSetParameter( } if (pcmParams->nChannels != 1 - || pcmParams->nSamplingRate != kSampleRate) { + || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) { return OMX_ErrorUndefined; } diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index cc38dc3..e25637a 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -655,7 +655,8 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { vpx_codec_iter_t encoded_packet_iterator = NULL; const vpx_codec_cx_pkt_t* encoded_packet; - while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) { + while ((encoded_packet = vpx_codec_get_cx_data( + mCodecContext, &encoded_packet_iterator))) { if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) { outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts; outputBufferHeader->nFlags = 0; -- cgit v1.1 From 5340cef8c137e7002ff196f2b88b508675bd5b24 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 10:48:50 -0700 Subject: Fix adaptive frame rate handling to be non-adaptive unless specified through adb shell setprop media.wfd.video-framerate adaptive Change-Id: I452576b62ad465680232b40464977e126616df18 --- media/libstagefright/wifi-display/source/PlaybackSession.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 715d0b5..cacfcca 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -709,8 +709,11 @@ void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp &msg) Converter::GetInt32Property( "media.wfd.video-framerate", -1); - if (rateHz < 0.0) { - rateHz = repeaterSource->getFrameRate(); + char val[PROPERTY_VALUE_MAX]; + if (rateHz < 0.0 + && property_get("media.wfd.video-framerate", val, NULL) + && !strcasecmp("adaptive", val)) { + rateHz = repeaterSource->getFrameRate(); if (avgLatencyUs > 300000ll) { rateHz *= 0.9; -- cgit v1.1 From 013673916b6badf0b6dfb65e7165cef84241ede3 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 10:50:18 -0700 Subject: Removed rtp timestamp patching hack in ANetworkSession, advertise differentiated service tags to help prioritize our traffic. Change-Id: Ibe42df64da272f4bb6b978e46a4ea2efdadfcbc7 --- .../wifi-display/ANetworkSession.cpp | 26 +++++++--------------- 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 88ca1cc..f074438 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -451,24 +451,6 @@ status_t ANetworkSession::Session::writeMore() { const Fragment &frag = *mOutFragments.begin(); const sp &datagram = frag.mBuffer; - uint8_t *data = datagram->data(); - if (data[0] == 0x80 && (data[1] & 0x7f) == 33) { - int64_t nowUs = ALooper::GetNowUs(); - - uint32_t prevRtpTime = U32_AT(&data[4]); - - // 90kHz time scale - uint32_t rtpTime = (nowUs * 9ll) / 100ll; - int32_t diffTime = (int32_t)rtpTime - (int32_t)prevRtpTime; - - ALOGV("correcting rtpTime by %.0f ms", diffTime / 90.0); - - data[4] = rtpTime >> 24; - data[5] = (rtpTime >> 16) & 0xff; - data[6] = (rtpTime >> 8) & 0xff; - data[7] = rtpTime & 0xff; - } - int n; do { n = send(mSocket, datagram->data(), datagram->size(), 0); @@ -874,6 +856,14 @@ status_t ANetworkSession::createClientOrServer( err = -errno; goto bail2; } + + int tos = 224; // VOICE + res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + + if (res < 0) { + err = -errno; + goto bail2; + } } err = MakeSocketNonBlocking(s); -- cgit v1.1 From 8f24c039fb3418c15f476988f12383b26c1201bc Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 10:51:52 -0700 Subject: Reduce TimeSyncer frequency to once every 60 secs, added some logs to indicate when syncing intervals start and end. Change-Id: I3b2b997d6723ff592af7c31082c6020cc1eca433 --- media/libstagefright/wifi-display/TimeSyncer.cpp | 6 ++++++ media/libstagefright/wifi-display/TimeSyncer.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp index 64e182e..cb429bc 100644 --- a/media/libstagefright/wifi-display/TimeSyncer.cpp +++ b/media/libstagefright/wifi-display/TimeSyncer.cpp @@ -102,6 +102,10 @@ void TimeSyncer::onMessageReceived(const sp &msg) { case kWhatSendPacket: { + if (mHistory.size() == 0) { + ALOGI("starting batch"); + } + TimeInfo ti; memset(&ti, 0, sizeof(ti)); @@ -229,6 +233,8 @@ void TimeSyncer::onMessageReceived(const sp &msg) { } else { notifyOffset(); + ALOGI("batch done"); + mHistory.clear(); postSendPacket(kBatchDelayUs); } diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h index 0e3aed7..4e7571f 100644 --- a/media/libstagefright/wifi-display/TimeSyncer.h +++ b/media/libstagefright/wifi-display/TimeSyncer.h @@ -75,7 +75,7 @@ private: kNumPacketsPerBatch = 30, }; static const int64_t kTimeoutDelayUs = 500000ll; - static const int64_t kBatchDelayUs = 10000000ll; // every 10 secs + static const int64_t kBatchDelayUs = 60000000ll; // every minute sp mNetSession; sp mNotify; -- cgit v1.1 From 30bf97b3665aa5a9024517697b8459d7662eb8b9 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 10:53:34 -0700 Subject: Track max latency in WifiDisplaySink also unbreak non-special-mode by choosing a proper time offset based on arrival time of the first access unit even when no time synchronization is present. Change-Id: I133050afc6f70d4639ca45de68a31d5bc3594e96 --- .../wifi-display/sink/WifiDisplaySink.cpp | 21 ++++++++++++++++----- .../wifi-display/sink/WifiDisplaySink.h | 4 ++++ 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 1a08bf5..5db2099 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -57,7 +57,8 @@ WifiDisplaySink::WifiDisplaySink( mSetupDeferred(false), mLatencyCount(0), mLatencySumUs(0ll), - mLatencyMaxUs(0ll) { + mLatencyMaxUs(0ll), + mMaxDelayMs(-1ll) { // We support any and all resolutions, but prefer 720p30 mSinkSupportedVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 @@ -296,9 +297,13 @@ void WifiDisplaySink::onMessageReceived(const sp &msg) { } } -static void dumpDelay(size_t trackIndex, int64_t timeUs) { +void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) { int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll; + if (delayMs > mMaxDelayMs) { + mMaxDelayMs = delayMs; + } + static const int64_t kMinDelayMs = 0; static const int64_t kMaxDelayMs = 300; @@ -314,9 +319,10 @@ static void dumpDelay(size_t trackIndex, int64_t timeUs) { n = kPatternSize; } - ALOGI("[%lld]: (%4lld ms) %s", + ALOGI("[%lld]: (%4lld ms / %4lld ms) %s", timeUs / 1000, delayMs, + mMaxDelayMs, kPattern + kPatternSize - n); } @@ -350,14 +356,19 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { looper()->registerHandler(mRenderer); } - CHECK(mTimeOffsetValid); - sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); int64_t timeUs; CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) { + mTimeOffsetUs = timeUs - ALooper::GetNowUs(); + mTimeOffsetValid = true; + } + + CHECK(mTimeOffsetValid); + // We are the timesync _client_, // client time = server time - time offset. timeUs -= mTimeOffsetUs; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index 7c62057..adb9d89 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -132,6 +132,8 @@ private: int64_t mLatencySumUs; int64_t mLatencyMaxUs; + int64_t mMaxDelayMs; + status_t sendM2(int32_t sessionID); status_t sendSetup(int32_t sessionID, const char *uri); status_t sendPlay(int32_t sessionID, const char *uri); @@ -184,6 +186,8 @@ private: const char *url, AString *host, int32_t *port, AString *path, AString *user, AString *pass); + void dumpDelay(size_t trackIndex, int64_t timeUs); + DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink); }; -- cgit v1.1 From 48c0addff1e943393272a5ed698d24afbf6b2471 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 11:08:21 -0700 Subject: RTPTest updated to allow for UDP/TCP transport and abstracted where the data is coming from, also added time synchronization. Change-Id: Iecc2201a2bd17be06f16690a28261bef5b4e439c --- media/libstagefright/wifi-display/rtptest.cpp | 277 +++++++++++++++++++++----- 1 file changed, 228 insertions(+), 49 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp index eade832..cf5199d 100644 --- a/media/libstagefright/wifi-display/rtptest.cpp +++ b/media/libstagefright/wifi-display/rtptest.cpp @@ -21,6 +21,7 @@ #include "ANetworkSession.h" #include "rtp/RTPSender.h" #include "rtp/RTPReceiver.h" +#include "TimeSyncer.h" #include #include @@ -28,12 +29,115 @@ #include #include #include +#include #include #include #include +#include namespace android { +struct PacketSource : public RefBase { + PacketSource() {} + + virtual sp getNextAccessUnit() = 0; + +protected: + virtual ~PacketSource() {} + +private: + DISALLOW_EVIL_CONSTRUCTORS(PacketSource); +}; + +struct MediaPacketSource : public PacketSource { + MediaPacketSource() + : mMaxSampleSize(1024 * 1024) { + mExtractor = new NuMediaExtractor; + CHECK_EQ((status_t)OK, + mExtractor->setDataSource( + "/sdcard/Frame Counter HD 30FPS_1080p.mp4")); + + bool haveVideo = false; + for (size_t i = 0; i < mExtractor->countTracks(); ++i) { + sp format; + CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format)); + + AString mime; + CHECK(format->findString("mime", &mime)); + + if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) { + mExtractor->selectTrack(i); + haveVideo = true; + break; + } + } + + CHECK(haveVideo); + } + + virtual sp getNextAccessUnit() { + int64_t timeUs; + status_t err = mExtractor->getSampleTime(&timeUs); + + if (err != OK) { + return NULL; + } + + sp accessUnit = new ABuffer(mMaxSampleSize); + CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit)); + + accessUnit->meta()->setInt64("timeUs", timeUs); + + CHECK_EQ((status_t)OK, mExtractor->advance()); + + return accessUnit; + } + +protected: + virtual ~MediaPacketSource() { + } + +private: + sp mExtractor; + size_t mMaxSampleSize; + + DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource); +}; + +struct SimplePacketSource : public PacketSource { + SimplePacketSource() + : mCounter(0) { + } + + virtual sp getNextAccessUnit() { + sp buffer = new ABuffer(4); + uint8_t *dst = buffer->data(); + dst[0] = mCounter >> 24; + dst[1] = (mCounter >> 16) & 0xff; + dst[2] = (mCounter >> 8) & 0xff; + dst[3] = mCounter & 0xff; + + buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate); + + ++mCounter; + + return buffer; + } + +protected: + virtual ~SimplePacketSource() { + } + +private: + enum { + kFrameRate = 30 + }; + + uint32_t mCounter; + + DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource); +}; + struct TestHandler : public AHandler { TestHandler(const sp &netSession); @@ -52,18 +156,39 @@ private: kWhatSenderNotify, kWhatSendMore, kWhatStop, + kWhatTimeSyncerNotify, }; +#if 1 + static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP; + static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP; +#else + static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP; + static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE; +#endif + +#if 1 + static const RTPBase::PacketizationMode kPacketizationMode + = RTPBase::PACKETIZATION_H264; +#else + static const RTPBase::PacketizationMode kPacketizationMode + = RTPBase::PACKETIZATION_NONE; +#endif + sp mNetSession; - sp mExtractor; + sp mSource; sp mSender; sp mReceiver; - size_t mMaxSampleSize; + sp mTimeSyncer; + bool mTimeSyncerStarted; int64_t mFirstTimeRealUs; int64_t mFirstTimeMediaUs; + int64_t mTimeOffsetUs; + bool mTimeOffsetValid; + status_t readMore(); DISALLOW_EVIL_CONSTRUCTORS(TestHandler); @@ -71,9 +196,11 @@ private: TestHandler::TestHandler(const sp &netSession) : mNetSession(netSession), - mMaxSampleSize(1024 * 1024), + mTimeSyncerStarted(false), mFirstTimeRealUs(-1ll), - mFirstTimeMediaUs(-1ll) { + mFirstTimeMediaUs(-1ll), + mTimeOffsetUs(-1ll), + mTimeOffsetValid(false) { } TestHandler::~TestHandler() { @@ -91,23 +218,48 @@ void TestHandler::connect(const char *host, int32_t port) { msg->post(); } +static void dumpDelay(int64_t delayMs) { + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("(%4lld ms) %s\n", + delayMs, + kPattern + kPatternSize - n); +} + void TestHandler::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatListen: { - sp notify = new AMessage(kWhatReceiverNotify, id()); - mReceiver = new RTPReceiver(mNetSession, notify); + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + notify = new AMessage(kWhatReceiverNotify, id()); + mReceiver = new RTPReceiver( + mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT); looper()->registerHandler(mReceiver); CHECK_EQ((status_t)OK, - mReceiver->registerPacketType( - 33, RTPReceiver::PACKETIZATION_H264)); + mReceiver->registerPacketType(33, kPacketizationMode)); int32_t receiverRTPPort; CHECK_EQ((status_t)OK, mReceiver->initAsync( - RTPReceiver::TRANSPORT_UDP, // rtpMode - RTPReceiver::TRANSPORT_UDP, // rtcpMode + kRTPMode, + kRTCPMode, &receiverRTPPort)); printf("picked receiverRTPPort %d\n", receiverRTPPort); @@ -125,33 +277,23 @@ void TestHandler::onMessageReceived(const sp &msg) { AString host; CHECK(msg->findString("host", &host)); + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + mTimeSyncer->startServer(8123); + int32_t receiverRTPPort; CHECK(msg->findInt32("port", &receiverRTPPort)); - mExtractor = new NuMediaExtractor; - CHECK_EQ((status_t)OK, - mExtractor->setDataSource( - "/sdcard/Frame Counter HD 30FPS_1080p.mp4")); - - bool haveVideo = false; - for (size_t i = 0; i < mExtractor->countTracks(); ++i) { - sp format; - CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format)); - - AString mime; - CHECK(format->findString("mime", &mime)); - - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) { - mExtractor->selectTrack(i); - haveVideo = true; - break; - } - } - - CHECK(haveVideo); +#if 1 + mSource = new MediaPacketSource; +#else + mSource = new SimplePacketSource; +#endif - sp notify = new AMessage(kWhatSenderNotify, id()); + notify = new AMessage(kWhatSenderNotify, id()); mSender = new RTPSender(mNetSession, notify); + looper()->registerHandler(mSender); int32_t senderRTPPort; @@ -159,9 +301,10 @@ void TestHandler::onMessageReceived(const sp &msg) { mSender->initAsync( host.c_str(), receiverRTPPort, - RTPSender::TRANSPORT_UDP, // rtpMode - receiverRTPPort + 1, - RTPSender::TRANSPORT_UDP, // rtcpMode + kRTPMode, + kRTCPMode == RTPBase::TRANSPORT_NONE + ? -1 : receiverRTPPort + 1, + kRTCPMode, &senderRTPPort)); printf("picked senderRTPPort %d\n", senderRTPPort); @@ -201,7 +344,7 @@ void TestHandler::onMessageReceived(const sp &msg) { case kWhatReceiverNotify: { - ALOGI("kWhatReceiverNotify"); + ALOGV("kWhatReceiverNotify"); int32_t what; CHECK(msg->findInt32("what", &what)); @@ -216,8 +359,40 @@ void TestHandler::onMessageReceived(const sp &msg) { break; } - case RTPSender::kWhatError: + case RTPReceiver::kWhatError: break; + + case RTPReceiver::kWhatAccessUnit: + { +#if 0 + if (!mTimeSyncerStarted) { + mTimeSyncer->startClient("172.18.41.216", 8123); + mTimeSyncerStarted = true; + } + + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + if (mTimeOffsetValid) { + timeUs -= mTimeOffsetUs; + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayMs = (nowUs - timeUs) / 1000ll; + + dumpDelay(delayMs); + } +#endif + break; + } + + case RTPReceiver::kWhatPacketLost: + ALOGV("kWhatPacketLost"); + break; + + default: + TRESPASS(); } break; } @@ -231,7 +406,7 @@ void TestHandler::onMessageReceived(const sp &msg) { mSender->queueBuffer( accessUnit, 33, - RTPSender::PACKETIZATION_H264)); + kPacketizationMode)); status_t err = readMore(); @@ -253,31 +428,33 @@ void TestHandler::onMessageReceived(const sp &msg) { mSender.clear(); } - mExtractor.clear(); + mSource.clear(); looper()->stop(); break; } + case kWhatTimeSyncerNotify: + { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + break; + } + default: TRESPASS(); } } status_t TestHandler::readMore() { - int64_t timeUs; - status_t err = mExtractor->getSampleTime(&timeUs); + sp accessUnit = mSource->getNextAccessUnit(); - if (err != OK) { - return err; + if (accessUnit == NULL) { + return ERROR_END_OF_STREAM; } - sp accessUnit = new ABuffer(mMaxSampleSize); - CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit)); - - accessUnit->meta()->setInt64("timeUs", timeUs); - - CHECK_EQ((status_t)OK, mExtractor->advance()); + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); int64_t nowUs = ALooper::GetNowUs(); int64_t whenUs; @@ -289,6 +466,8 @@ status_t TestHandler::readMore() { whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs; } + accessUnit->meta()->setInt64("timeUs", whenUs); + sp msg = new AMessage(kWhatSendMore, id()); msg->setBuffer("accessUnit", accessUnit); msg->post(whenUs - nowUs); -- cgit v1.1 From bd8319b282f39822d5f89bfb098c3317d2be6f03 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 11:12:24 -0700 Subject: New nettest, a simple TCP connection test to measure latency between two devices exchanging low-bitrate data. Change-Id: Iac138fa70d2a69bbc86c517c953011c80bed071f --- media/libstagefright/wifi-display/Android.mk | 22 ++ media/libstagefright/wifi-display/nettest.cpp | 396 ++++++++++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 media/libstagefright/wifi-display/nettest.cpp (limited to 'media') diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index f1f9f45..1578c21 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -109,3 +109,25 @@ LOCAL_MODULE:= rtptest LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + nettest.cpp \ + +LOCAL_SHARED_LIBRARIES:= \ + libbinder \ + libgui \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libstagefright_wfd \ + libutils \ + +LOCAL_MODULE:= nettest + +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp new file mode 100644 index 0000000..130016d --- /dev/null +++ b/media/libstagefright/wifi-display/nettest.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2013, 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_NEBUG 0 +#define LOG_TAG "nettest" +#include + +#include "ANetworkSession.h" +#include "TimeSyncer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +struct TestHandler : public AHandler { + TestHandler(const sp &netSession); + + void listen(int32_t port); + void connect(const char *host, int32_t port); + +protected: + virtual ~TestHandler(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatListen, + kWhatConnect, + kWhatTimeSyncerNotify, + kWhatNetNotify, + kWhatSendMore, + kWhatStop, + }; + + sp mNetSession; + sp mTimeSyncer; + + int32_t mServerSessionID; + int32_t mSessionID; + + int64_t mTimeOffsetUs; + bool mTimeOffsetValid; + + int32_t mCounter; + + int64_t mMaxDelayMs; + + void dumpDelay(int32_t counter, int64_t delayMs); + + DISALLOW_EVIL_CONSTRUCTORS(TestHandler); +}; + +TestHandler::TestHandler(const sp &netSession) + : mNetSession(netSession), + mServerSessionID(0), + mSessionID(0), + mTimeOffsetUs(-1ll), + mTimeOffsetValid(false), + mCounter(0), + mMaxDelayMs(-1ll) { +} + +TestHandler::~TestHandler() { +} + +void TestHandler::listen(int32_t port) { + sp msg = new AMessage(kWhatListen, id()); + msg->setInt32("port", port); + msg->post(); +} + +void TestHandler::connect(const char *host, int32_t port) { + sp msg = new AMessage(kWhatConnect, id()); + msg->setString("host", host); + msg->setInt32("port", port); + msg->post(); +} + +void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) { + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + if (delayMs > mMaxDelayMs) { + mMaxDelayMs = delayMs; + } + + ALOGI("[%d] (%4lld ms / %4lld ms) %s", + counter, + delayMs, + mMaxDelayMs, + kPattern + kPatternSize - n); +} + +void TestHandler::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatListen: + { + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + notify = new AMessage(kWhatNetNotify, id()); + + int32_t port; + CHECK(msg->findInt32("port", &port)); + + struct in_addr ifaceAddr; + ifaceAddr.s_addr = INADDR_ANY; + + CHECK_EQ((status_t)OK, + mNetSession->createTCPDatagramSession( + ifaceAddr, + port, + notify, + &mServerSessionID)); + break; + } + + case kWhatConnect: + { + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + mTimeSyncer->startServer(8123); + + AString host; + CHECK(msg->findString("host", &host)); + + int32_t port; + CHECK(msg->findInt32("port", &port)); + + notify = new AMessage(kWhatNetNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createTCPDatagramSession( + 0 /* localPort */, + host.c_str(), + port, + notify, + &mSessionID)); + break; + } + + case kWhatNetNotify: + { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatConnected: + { + ALOGI("kWhatConnected"); + + (new AMessage(kWhatSendMore, id()))->post(); + break; + } + + case ANetworkSession::kWhatClientConnected: + { + ALOGI("kWhatClientConnected"); + + CHECK_EQ(mSessionID, 0); + CHECK(msg->findInt32("sessionID", &mSessionID)); + + AString clientIP; + CHECK(msg->findString("client-ip", &clientIP)); + + mTimeSyncer->startClient(clientIP.c_str(), 8123); + break; + } + + case ANetworkSession::kWhatDatagram: + { + sp packet; + CHECK(msg->findBuffer("data", &packet)); + + CHECK_EQ(packet->size(), 12u); + + int32_t counter = U32_AT(packet->data()); + int64_t timeUs = U64_AT(packet->data() + 4); + + if (mTimeOffsetValid) { + timeUs -= mTimeOffsetUs; + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayMs = (nowUs - timeUs) / 1000ll; + + dumpDelay(counter, delayMs); + } else { + ALOGI("received %d", counter); + } + break; + } + + case ANetworkSession::kWhatError: + { + ALOGE("kWhatError"); + break; + } + + default: + TRESPASS(); + } + break; + } + + case kWhatTimeSyncerNotify: + { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + break; + } + + case kWhatSendMore: + { + uint8_t buffer[4 + 8]; + buffer[0] = mCounter >> 24; + buffer[1] = (mCounter >> 16) & 0xff; + buffer[2] = (mCounter >> 8) & 0xff; + buffer[3] = mCounter & 0xff; + + int64_t nowUs = ALooper::GetNowUs(); + + buffer[4] = nowUs >> 56; + buffer[5] = (nowUs >> 48) & 0xff; + buffer[6] = (nowUs >> 40) & 0xff; + buffer[7] = (nowUs >> 32) & 0xff; + buffer[8] = (nowUs >> 24) & 0xff; + buffer[9] = (nowUs >> 16) & 0xff; + buffer[10] = (nowUs >> 8) & 0xff; + buffer[11] = nowUs & 0xff; + + ++mCounter; + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mSessionID, + buffer, + sizeof(buffer), + true /* timeValid */, + nowUs)); + + msg->post(100000ll); + break; + } + + case kWhatStop: + { + if (mSessionID != 0) { + mNetSession->destroySession(mSessionID); + mSessionID = 0; + } + + if (mServerSessionID != 0) { + mNetSession->destroySession(mServerSessionID); + mServerSessionID = 0; + } + + looper()->stop(); + break; + } + + default: + TRESPASS(); + } +} + +} // namespace android + +static void usage(const char *me) { + fprintf(stderr, + "usage: %s -c host:port\tconnect to remote host\n" + " -l port \tlisten\n", + me); +} + +int main(int argc, char **argv) { + using namespace android; + + // srand(time(NULL)); + + ProcessState::self()->startThreadPool(); + + DataSource::RegisterDefaultSniffers(); + + int32_t connectToPort = -1; + AString connectToHost; + + int32_t listenOnPort = -1; + + int res; + while ((res = getopt(argc, argv, "hc:l:")) >= 0) { + switch (res) { + case 'c': + { + const char *colonPos = strrchr(optarg, ':'); + + if (colonPos == NULL) { + usage(argv[0]); + exit(1); + } + + connectToHost.setTo(optarg, colonPos - optarg); + + char *end; + connectToPort = strtol(colonPos + 1, &end, 10); + + if (*end != '\0' || end == colonPos + 1 + || connectToPort < 1 || connectToPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + break; + } + + case 'l': + { + char *end; + listenOnPort = strtol(optarg, &end, 10); + + if (*end != '\0' || end == optarg + 1 + || listenOnPort < 1 || listenOnPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + break; + } + + case '?': + case 'h': + usage(argv[0]); + exit(1); + } + } + + if ((listenOnPort < 0 && connectToPort < 0) + || (listenOnPort >= 0 && connectToPort >= 0)) { + fprintf(stderr, + "You need to select either client or server mode.\n"); + exit(1); + } + + sp netSession = new ANetworkSession; + netSession->start(); + + sp looper = new ALooper; + + sp handler = new TestHandler(netSession); + looper->registerHandler(handler); + + if (listenOnPort) { + handler->listen(listenOnPort); + } + + if (connectToPort >= 0) { + handler->connect(connectToHost.c_str(), connectToPort); + } + + looper->start(true /* runOnCallingThread */); + + return 0; +} -- cgit v1.1 From 8f1f6a4814403dd78539250c845f8326f6137a61 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 11:16:39 -0700 Subject: Support "raw" packetization in RTPSender. Change-Id: I14d59573ee0f57eccc104fea0fb46377476d213d --- .../wifi-display/rtp/RTPAssembler.cpp | 1 - media/libstagefright/wifi-display/rtp/RTPBase.h | 1 + .../libstagefright/wifi-display/rtp/RTPSender.cpp | 44 ++++++++++++++++++++++ media/libstagefright/wifi-display/rtp/RTPSender.h | 1 + 4 files changed, 46 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp index 5f189e7..7a96081 100644 --- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp @@ -40,7 +40,6 @@ void RTPReceiver::Assembler::postAccessUnit( notify->setInt32("followsDiscontinuity", followsDiscontinuity); notify->post(); } - //////////////////////////////////////////////////////////////////////////////// RTPReceiver::TSAssembler::TSAssembler(const sp ¬ify) diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h index e3fa845..6178f00 100644 --- a/media/libstagefright/wifi-display/rtp/RTPBase.h +++ b/media/libstagefright/wifi-display/rtp/RTPBase.h @@ -25,6 +25,7 @@ struct RTPBase { PACKETIZATION_TRANSPORT_STREAM, PACKETIZATION_H264, PACKETIZATION_AAC, + PACKETIZATION_NONE, }; enum TransportMode { diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 9eeeabd..ed5a50e 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -187,6 +187,10 @@ status_t RTPSender::queueBuffer( status_t err; switch (mode) { + case PACKETIZATION_NONE: + err = queueRawPacket(buffer, packetType); + break; + case PACKETIZATION_TRANSPORT_STREAM: err = queueTSPackets(buffer, packetType); break; @@ -202,6 +206,46 @@ status_t RTPSender::queueBuffer( return err; } +status_t RTPSender::queueRawPacket( + const sp &packet, uint8_t packetType) { + CHECK_LE(packet->size(), kMaxUDPPacketSize - 12); + + int64_t timeUs; + CHECK(packet->meta()->findInt64("timeUs", &timeUs)); + + sp udpPacket = new ABuffer(12 + packet->size()); + + udpPacket->setInt32Data(mRTPSeqNo); + + uint8_t *rtp = udpPacket->data(); + rtp[0] = 0x80; + rtp[1] = packetType; + + rtp[2] = (mRTPSeqNo >> 8) & 0xff; + rtp[3] = mRTPSeqNo & 0xff; + ++mRTPSeqNo; + + uint32_t rtpTime = (timeUs * 9) / 100ll; + + rtp[4] = rtpTime >> 24; + rtp[5] = (rtpTime >> 16) & 0xff; + rtp[6] = (rtpTime >> 8) & 0xff; + rtp[7] = rtpTime & 0xff; + + rtp[8] = kSourceID >> 24; + rtp[9] = (kSourceID >> 16) & 0xff; + rtp[10] = (kSourceID >> 8) & 0xff; + rtp[11] = kSourceID & 0xff; + + memcpy(&rtp[12], packet->data(), packet->size()); + + return sendRTPPacket( + udpPacket, + true /* storeInHistory */, + true /* timeValid */, + timeUs); +} + status_t RTPSender::queueTSPackets( const sp &tsPackets, uint8_t packetType) { CHECK_EQ(0, tsPackets->size() % 188); diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 3a926ea..fefcab7 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -94,6 +94,7 @@ private: static uint64_t GetNowNTP(); + status_t queueRawPacket(const sp &tsPackets, uint8_t packetType); status_t queueTSPackets(const sp &tsPackets, uint8_t packetType); status_t queueAVCBuffer(const sp &accessUnit, uint8_t packetType); -- cgit v1.1 From 2be6121a47d3df2a0efcb73afd214f2958eb9927 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 11:17:05 -0700 Subject: RTPReceiver can now track packet loss, account for late arrivals it also uses timers to trigger retransmission and packet loss declaration Change-Id: If1f9324783b3bef950076c2edf321f7c33ff9fea --- .../wifi-display/rtp/RTPReceiver.cpp | 272 ++++++++++++++++----- .../libstagefright/wifi-display/rtp/RTPReceiver.h | 8 +- 2 files changed, 215 insertions(+), 65 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp index 238fb82..8fa1dae 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -30,11 +30,13 @@ #include #include +#define TRACK_PACKET_LOSS 0 + namespace android { //////////////////////////////////////////////////////////////////////////////// -struct RTPReceiver::Source : public RefBase { +struct RTPReceiver::Source : public AHandler { Source(RTPReceiver *receiver, uint32_t ssrc); void onPacketReceived(uint16_t seq, const sp &buffer); @@ -44,7 +46,14 @@ struct RTPReceiver::Source : public RefBase { protected: virtual ~Source(); + virtual void onMessageReceived(const sp &msg); + private: + enum { + kWhatRetransmit, + kWhatDeclareLost, + }; + static const uint32_t kMinSequential = 2; static const uint32_t kMaxDropout = 3000; static const uint32_t kMaxMisorder = 100; @@ -67,6 +76,17 @@ private: // Ordered by extended seq number. List > mPackets; + enum StatusBits { + STATUS_DECLARED_LOST = 1, + STATUS_REQUESTED_RETRANSMISSION = 2, + STATUS_ARRIVED_LATE = 4, + }; +#if TRACK_PACKET_LOSS + KeyedVector mLostPackets; +#endif + + void modifyPacketStatus(int32_t extSeqNo, uint32_t mask); + int32_t mAwaitingExtSeqNo; bool mRequestedRetransmission; @@ -78,12 +98,20 @@ private: int32_t mNumDeclaredLost; int32_t mNumDeclaredLostPrior; + int32_t mRetransmitGeneration; + int32_t mDeclareLostGeneration; + bool mDeclareLostTimerPending; + void queuePacket(const sp &packet); void dequeueMore(); sp getNextPacket(); void resync(); + void postRetransmitTimer(int64_t delayUs); + void postDeclareLostTimer(int64_t delayUs); + void cancelTimers(); + DISALLOW_EVIL_CONSTRUCTORS(Source); }; @@ -106,12 +134,71 @@ RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc) mActivePacketType(-1), mNextReportTimeUs(-1ll), mNumDeclaredLost(0), - mNumDeclaredLostPrior(0) { + mNumDeclaredLostPrior(0), + mRetransmitGeneration(0), + mDeclareLostGeneration(0), + mDeclareLostTimerPending(false) { } RTPReceiver::Source::~Source() { } +void RTPReceiver::Source::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatRetransmit: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mRetransmitGeneration) { + break; + } + + mRequestedRetransmission = true; + mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo); + + modifyPacketStatus( + mAwaitingExtSeqNo, STATUS_REQUESTED_RETRANSMISSION); + break; + } + + case kWhatDeclareLost: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mDeclareLostGeneration) { + break; + } + + cancelTimers(); + + ALOGV("Lost packet extSeqNo %d %s", + mAwaitingExtSeqNo, + mRequestedRetransmission ? "*" : ""); + + mRequestedRetransmission = false; + if (mActiveAssembler != NULL) { + mActiveAssembler->signalDiscontinuity(); + } + + modifyPacketStatus(mAwaitingExtSeqNo, STATUS_DECLARED_LOST); + + // resync(); + ++mAwaitingExtSeqNo; + ++mNumDeclaredLost; + + mReceiver->notifyPacketLost(); + + dequeueMore(); + break; + } + + default: + TRESPASS(); + } +} + void RTPReceiver::Source::onPacketReceived( uint16_t seq, const sp &buffer) { if (mFirst) { @@ -164,6 +251,8 @@ void RTPReceiver::Source::queuePacket(const sp &packet) { if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) { // We're no longer interested in these. They're old. ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo); + + modifyPacketStatus(newExtendedSeqNo, STATUS_ARRIVED_LATE); return; } @@ -230,85 +319,89 @@ void RTPReceiver::Source::dequeueMore() { } mNextReportTimeUs = nowUs + kReportIntervalUs; - } - for (;;) { - sp packet = getNextPacket(); +#if TRACK_PACKET_LOSS + for (size_t i = 0; i < mLostPackets.size(); ++i) { + int32_t key = mLostPackets.keyAt(i); + uint32_t value = mLostPackets.valueAt(i); - if (packet == NULL) { - if (mPackets.empty()) { - break; + AString status; + if (value & STATUS_REQUESTED_RETRANSMISSION) { + status.append("retrans "); + } + if (value & STATUS_ARRIVED_LATE) { + status.append("arrived-late "); } + ALOGI("Packet %d declared lost %s", key, status.c_str()); + } +#endif + } + + sp packet; + while ((packet = getNextPacket()) != NULL) { + if (mDeclareLostTimerPending) { + cancelTimers(); + } + + CHECK_GE(mAwaitingExtSeqNo, 0); +#if TRACK_PACKET_LOSS + mLostPackets.removeItem(mAwaitingExtSeqNo); +#endif - CHECK_GE(mAwaitingExtSeqNo, 0); + int32_t packetType; + CHECK(packet->meta()->findInt32("PT", &packetType)); - const sp &firstPacket = *mPackets.begin(); + if (packetType != mActivePacketType) { + mActiveAssembler = mReceiver->makeAssembler(packetType); + mActivePacketType = packetType; + } - uint32_t rtpTime; - CHECK(firstPacket->meta()->findInt32( - "rtp-time", (int32_t *)&rtpTime)); + if (mActiveAssembler != NULL) { + status_t err = mActiveAssembler->processPacket(packet); + if (err != OK) { + ALOGV("assembler returned error %d", err); + } + } + ++mAwaitingExtSeqNo; + } - int64_t rtpUs = (rtpTime * 100ll) / 9ll; + if (mDeclareLostTimerPending) { + return; + } - int64_t maxArrivalTimeUs = - mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs; + if (mPackets.empty()) { + return; + } - int64_t nowUs = ALooper::GetNowUs(); + CHECK_GE(mAwaitingExtSeqNo, 0); - CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data()); + const sp &firstPacket = *mPackets.begin(); - ALOGV("waiting for %d, comparing against %d, %lld us left", - mAwaitingExtSeqNo, - firstPacket->int32Data(), - maxArrivalTimeUs - nowUs); + uint32_t rtpTime; + CHECK(firstPacket->meta()->findInt32( + "rtp-time", (int32_t *)&rtpTime)); - if (maxArrivalTimeUs + kPacketLostAfterUs <= nowUs) { - ALOGV("Lost packet extSeqNo %d %s", - mAwaitingExtSeqNo, - mRequestedRetransmission ? "*" : ""); - mRequestedRetransmission = false; - if (mActiveAssembler != NULL) { - mActiveAssembler->signalDiscontinuity(); - } + int64_t rtpUs = (rtpTime * 100ll) / 9ll; - // resync(); - ++mAwaitingExtSeqNo; - ++mNumDeclaredLost; - - mReceiver->notifyPacketLost(); - continue; - } else if (kRequestRetransmissionAfterUs > 0 - && maxArrivalTimeUs + kRequestRetransmissionAfterUs <= nowUs - && !mRequestedRetransmission - && mAwaitingExtSeqNo >= 0) { - mRequestedRetransmission = true; - mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo); - break; - } else { - break; - } - } + int64_t maxArrivalTimeUs = + mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs; - mRequestedRetransmission = false; + nowUs = ALooper::GetNowUs(); - int32_t packetType; - CHECK(packet->meta()->findInt32("PT", &packetType)); + CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data()); - if (packetType != mActivePacketType) { - mActiveAssembler = mReceiver->makeAssembler(packetType); - mActivePacketType = packetType; - } + ALOGV("waiting for %d, comparing against %d, %lld us left", + mAwaitingExtSeqNo, + firstPacket->int32Data(), + maxArrivalTimeUs - nowUs); - if (mActiveAssembler == NULL) { - continue; - } + postDeclareLostTimer(maxArrivalTimeUs + kPacketLostAfterUs); - status_t err = mActiveAssembler->processPacket(packet); - if (err != OK) { - ALOGV("assembler returned error %d", err); - } + if (kRequestRetransmissionAfterUs > 0ll) { + postRetransmitTimer( + maxArrivalTimeUs + kRequestRetransmissionAfterUs); } } @@ -328,8 +421,6 @@ sp RTPReceiver::Source::getNextPacket() { sp packet = *mPackets.begin(); mPackets.erase(mPackets.begin()); - ++mAwaitingExtSeqNo; - return packet; } @@ -404,9 +495,11 @@ void RTPReceiver::Source::addReportBlock( RTPReceiver::RTPReceiver( const sp &netSession, - const sp ¬ify) + const sp ¬ify, + uint32_t flags) : mNetSession(netSession), mNotify(notify), + mFlags(flags), mRTPMode(TRANSPORT_UNDEFINED), mRTCPMode(TRANSPORT_UNDEFINED), mRTPSessionID(0), @@ -693,6 +786,20 @@ void RTPReceiver::onNetNotify(bool isRTP, const sp &msg) { CHECK(msg->findBuffer("data", &data)); if (isRTP) { + if (mFlags & FLAG_AUTO_CONNECT) { + AString fromAddr; + CHECK(msg->findString("fromAddr", &fromAddr)); + + int32_t fromPort; + CHECK(msg->findInt32("fromPort", &fromPort)); + + CHECK_EQ((status_t)OK, + connect( + fromAddr.c_str(), fromPort, fromPort + 1)); + + mFlags &= ~FLAG_AUTO_CONNECT; + } + onRTPData(data); } else { onRTCPData(data); @@ -835,6 +942,8 @@ status_t RTPReceiver::onRTPData(const sp &buffer) { sp source; if (index < 0) { source = new Source(this, srcId); + looper()->registerHandler(source); + mSources.add(srcId, source); } else { source = mSources.valueAt(index); @@ -965,6 +1074,7 @@ sp RTPReceiver::makeAssembler(uint8_t packetType) { PacketizationMode mode = mPacketTypes.valueAt(index); switch (mode) { + case PACKETIZATION_NONE: case PACKETIZATION_TRANSPORT_STREAM: return new TSAssembler(mNotify); @@ -1005,5 +1115,39 @@ void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) { mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); } +void RTPReceiver::Source::modifyPacketStatus(int32_t extSeqNo, uint32_t mask) { +#if TRACK_PACKET_LOSS + ssize_t index = mLostPackets.indexOfKey(extSeqNo); + if (index < 0) { + mLostPackets.add(extSeqNo, mask); + } else { + mLostPackets.editValueAt(index) |= mask; + } +#endif +} + +void RTPReceiver::Source::postRetransmitTimer(int64_t timeUs) { + int64_t delayUs = timeUs - ALooper::GetNowUs(); + sp msg = new AMessage(kWhatRetransmit, id()); + msg->setInt32("generation", mRetransmitGeneration); + msg->post(delayUs); +} + +void RTPReceiver::Source::postDeclareLostTimer(int64_t timeUs) { + CHECK(!mDeclareLostTimerPending); + mDeclareLostTimerPending = true; + + int64_t delayUs = timeUs - ALooper::GetNowUs(); + sp msg = new AMessage(kWhatDeclareLost, id()); + msg->setInt32("generation", mDeclareLostGeneration); + msg->post(delayUs); +} + +void RTPReceiver::Source::cancelTimers() { + ++mRetransmitGeneration; + ++mDeclareLostGeneration; + mDeclareLostTimerPending = false; +} + } // namespace android diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h index 630bce9..240ab2e 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h @@ -39,9 +39,14 @@ struct RTPReceiver : public RTPBase, public AHandler { kWhatAccessUnit, kWhatPacketLost, }; + + enum Flags { + FLAG_AUTO_CONNECT = 1, + }; RTPReceiver( const sp &netSession, - const sp ¬ify); + const sp ¬ify, + uint32_t flags = 0); status_t registerPacketType( uint8_t packetType, PacketizationMode mode); @@ -82,6 +87,7 @@ private: sp mNetSession; sp mNotify; + uint32_t mFlags; TransportMode mRTPMode; TransportMode mRTCPMode; int32_t mRTPSessionID; -- cgit v1.1 From 4eac4e624f6930966d208d8e1ee99eefee077b50 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Apr 2013 13:03:03 -0700 Subject: Fix previous changes. Change-Id: I1cd3803b6507156174591c3252f1d89ef2e6140a --- media/libstagefright/wifi-display/nettest.cpp | 14 +++++++++----- media/libstagefright/wifi-display/rtp/RTPSender.cpp | 2 +- media/libstagefright/wifi-display/rtptest.cpp | 5 +++-- 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp index 130016d..0779bf5 100644 --- a/media/libstagefright/wifi-display/nettest.cpp +++ b/media/libstagefright/wifi-display/nettest.cpp @@ -47,6 +47,10 @@ protected: private: enum { + kTimeSyncerPort = 8123, + }; + + enum { kWhatListen, kWhatConnect, kWhatTimeSyncerNotify, @@ -156,7 +160,7 @@ void TestHandler::onMessageReceived(const sp &msg) { sp notify = new AMessage(kWhatTimeSyncerNotify, id()); mTimeSyncer = new TimeSyncer(mNetSession, notify); looper()->registerHandler(mTimeSyncer); - mTimeSyncer->startServer(8123); + mTimeSyncer->startServer(kTimeSyncerPort); AString host; CHECK(msg->findString("host", &host)); @@ -200,7 +204,7 @@ void TestHandler::onMessageReceived(const sp &msg) { AString clientIP; CHECK(msg->findString("client-ip", &clientIP)); - mTimeSyncer->startClient(clientIP.c_str(), 8123); + mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort); break; } @@ -340,7 +344,7 @@ int main(int argc, char **argv) { connectToPort = strtol(colonPos + 1, &end, 10); if (*end != '\0' || end == colonPos + 1 - || connectToPort < 1 || connectToPort > 65535) { + || connectToPort < 0 || connectToPort > 65535) { fprintf(stderr, "Illegal port specified.\n"); exit(1); } @@ -352,8 +356,8 @@ int main(int argc, char **argv) { char *end; listenOnPort = strtol(optarg, &end, 10); - if (*end != '\0' || end == optarg + 1 - || listenOnPort < 1 || listenOnPort > 65535) { + if (*end != '\0' || end == optarg + || listenOnPort < 0 || listenOnPort > 65535) { fprintf(stderr, "Illegal port specified.\n"); exit(1); } diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index ed5a50e..6bbe650 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -243,7 +243,7 @@ status_t RTPSender::queueRawPacket( udpPacket, true /* storeInHistory */, true /* timeValid */, - timeUs); + ALooper::GetNowUs()); } status_t RTPSender::queueTSPackets( diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp index cf5199d..764a38b 100644 --- a/media/libstagefright/wifi-display/rtptest.cpp +++ b/media/libstagefright/wifi-display/rtptest.cpp @@ -35,6 +35,8 @@ #include #include +#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4" + namespace android { struct PacketSource : public RefBase { @@ -54,8 +56,7 @@ struct MediaPacketSource : public PacketSource { : mMaxSampleSize(1024 * 1024) { mExtractor = new NuMediaExtractor; CHECK_EQ((status_t)OK, - mExtractor->setDataSource( - "/sdcard/Frame Counter HD 30FPS_1080p.mp4")); + mExtractor->setDataSource(MEDIA_FILENAME)); bool haveVideo = false; for (size_t i = 0; i < mExtractor->countTracks(); ++i) { -- cgit v1.1 From 6463e76d41430f9b03a79b221de84255f2475658 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 4 Apr 2013 15:37:21 -0700 Subject: Make sure resume() and flush() are handled appropriately even if the codec is in Loaded->Idle state. b/8347958 Change-Id: Ic14d29502a7effc636251379bb1bbc25739db98e --- media/libstagefright/ACodec.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index c9f8741..ff72b71 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3416,6 +3416,21 @@ bool ACodec::LoadedToIdleState::onMessageReceived(const sp &msg) { return true; } + case kWhatResume: + { + // We'll be active soon enough. + return true; + } + + case kWhatFlush: + { + // We haven't even started yet, so we're flushed alright... + sp notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatFlushCompleted); + notify->post(); + return true; + } + default: return BaseState::onMessageReceived(msg); } -- cgit v1.1 From a1cc7d579888554a59f35c6cdfae3e7f85645ae2 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 5 Apr 2013 09:27:29 -0700 Subject: In certain cases where AAC audio frames extended into the next PES payload (inside transport streams) timestamps would be miscalculated. This fixes it. Change-Id: I9d74eeea474d2b89e8a9cdc478ed6085282fb3be --- media/libstagefright/mpeg2ts/ESQueue.cpp | 61 +++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) (limited to 'media') diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 9499712..9f3b19c 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -147,9 +147,9 @@ status_t ElementaryStreamQueue::appendData( } if (startOffset > 0) { - ALOGI("found something resembling an H.264/MPEG syncword at " - "offset %ld", - startOffset); + ALOGI("found something resembling an H.264/MPEG syncword " + "at offset %d", + startOffset); } data = &ptr[startOffset]; @@ -180,9 +180,9 @@ status_t ElementaryStreamQueue::appendData( } if (startOffset > 0) { - ALOGI("found something resembling an H.264/MPEG syncword at " - "offset %ld", - startOffset); + ALOGI("found something resembling an H.264/MPEG syncword " + "at offset %d", + startOffset); } data = &ptr[startOffset]; @@ -213,8 +213,9 @@ status_t ElementaryStreamQueue::appendData( } if (startOffset > 0) { - ALOGI("found something resembling an AAC syncword at offset %ld", - startOffset); + ALOGI("found something resembling an AAC syncword at " + "offset %d", + startOffset); } data = &ptr[startOffset]; @@ -241,8 +242,8 @@ status_t ElementaryStreamQueue::appendData( if (startOffset > 0) { ALOGI("found something resembling an MPEG audio " - "syncword at offset %ld", - startOffset); + "syncword at offset %d", + startOffset); } data = &ptr[startOffset]; @@ -394,10 +395,30 @@ sp ElementaryStreamQueue::dequeueAccessUnitPCMAudio() { } sp ElementaryStreamQueue::dequeueAccessUnitAAC() { - int64_t timeUs; + if (mBuffer->size() == 0) { + return NULL; + } + + CHECK(!mRangeInfos.empty()); + const RangeInfo &info = *mRangeInfos.begin(); + if (mBuffer->size() < info.mLength) { + return NULL; + } + + CHECK_GE(info.mTimestampUs, 0ll); + + // The idea here is consume all AAC frames starting at offsets before + // info.mLength so we can assign a meaningful timestamp without + // having to interpolate. + // The final AAC frame may well extend into the next RangeInfo but + // that's ok. size_t offset = 0; - while (offset + 7 <= mBuffer->size()) { + while (offset < info.mLength) { + if (offset + 7 > mBuffer->size()) { + return NULL; + } + ABitReader bits(mBuffer->data() + offset, mBuffer->size() - offset); // adts_fixed_header @@ -450,24 +471,15 @@ sp ElementaryStreamQueue::dequeueAccessUnitAAC() { } if (offset + aac_frame_length > mBuffer->size()) { - break; + return NULL; } size_t headerSize = protection_absent ? 7 : 9; - int64_t tmpUs = fetchTimestamp(aac_frame_length); - CHECK_GE(tmpUs, 0ll); - - if (offset == 0) { - timeUs = tmpUs; - } - offset += aac_frame_length; } - if (offset == 0) { - return NULL; - } + int64_t timeUs = fetchTimestamp(offset); sp accessUnit = new ABuffer(offset); memcpy(accessUnit->data(), mBuffer->data(), offset); @@ -492,7 +504,6 @@ int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) { if (first) { timeUs = info->mTimestampUs; - first = false; } if (info->mLength > size) { @@ -509,6 +520,8 @@ int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) { mRangeInfos.erase(mRangeInfos.begin()); info = NULL; } + + first = false; } if (timeUs == 0ll) { -- cgit v1.1 From ec77122351b4e78c1fe5b60a208f76baf8c67591 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 8 Apr 2013 14:30:57 -0700 Subject: Add support for common encryption b/7465749 Change-Id: I5403b74a5ae428ad28b382863a09daafc400b137 --- media/libstagefright/DataSource.cpp | 13 + media/libstagefright/MPEG4Extractor.cpp | 364 +++++++++++++++++++++++++- media/libstagefright/NuMediaExtractor.cpp | 28 ++ media/libstagefright/include/MPEG4Extractor.h | 7 + 4 files changed, 404 insertions(+), 8 deletions(-) (limited to 'media') diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 19b38ee..fc6fd9c 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -58,6 +58,19 @@ bool DataSource::getUInt16(off64_t offset, uint16_t *x) { return true; } +bool DataSource::getUInt24(off64_t offset, uint32_t *x) { + *x = 0; + + uint8_t byte[3]; + if (readAt(offset, byte, 3) != 3) { + return false; + } + + *x = (byte[0] << 16) | (byte[1] << 8) | byte[2]; + + return true; +} + bool DataSource::getUInt32(off64_t offset, uint32_t *x) { *x = 0; diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 56fad60..3503aaf 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -78,6 +78,19 @@ private: int32_t mLastParsedTrackId; int32_t mTrackId; + int32_t mCryptoMode; // passed in from extractor + int32_t mDefaultIVSize; // passed in from extractor + uint8_t mCryptoKey[16]; // passed in from extractor + uint32_t mCurrentAuxInfoType; + uint32_t mCurrentAuxInfoTypeParameter; + uint32_t mCurrentDefaultSampleInfoSize; + uint32_t mCurrentSampleInfoCount; + uint32_t mCurrentSampleInfoAllocSize; + uint8_t* mCurrentSampleInfoSizes; + uint32_t mCurrentSampleInfoOffsetCount; + uint32_t mCurrentSampleInfoOffsetsAllocSize; + uint64_t* mCurrentSampleInfoOffsets; + bool mIsAVC; size_t mNALLengthSize; @@ -95,6 +108,8 @@ private: status_t parseChunk(off64_t *offset); status_t parseTrackFragmentHeader(off64_t offset, off64_t size); status_t parseTrackFragmentRun(off64_t offset, off64_t size); + status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size); + status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size); struct TrackFragmentHeaderInfo { enum Flags { @@ -122,6 +137,9 @@ private: off64_t offset; size_t size; uint32_t duration; + uint8_t iv[16]; + Vector clearsizes; + Vector encryptedsizes; }; Vector mCurrentSamples; @@ -333,6 +351,10 @@ MPEG4Extractor::~MPEG4Extractor() { sinf = next; } mFirstSINF = NULL; + + for (size_t i = 0; i < mPssh.size(); i++) { + delete [] mPssh[i].data; + } } uint32_t MPEG4Extractor::flags() const { @@ -353,6 +375,7 @@ sp MPEG4Extractor::getMetaData() { size_t MPEG4Extractor::countTracks() { status_t err; if ((err = readMetaData()) != OK) { + ALOGV("MPEG4Extractor::countTracks: no tracks"); return 0; } @@ -363,6 +386,7 @@ size_t MPEG4Extractor::countTracks() { track = track->next; } + ALOGV("MPEG4Extractor::countTracks: %d tracks", n); return n; } @@ -461,6 +485,23 @@ status_t MPEG4Extractor::readMetaData() { } CHECK_NE(err, (status_t)NO_INIT); + + // copy pssh data into file metadata + int psshsize = 0; + for (size_t i = 0; i < mPssh.size(); i++) { + psshsize += 20 + mPssh[i].datalen; + } + if (psshsize) { + char *buf = (char*)malloc(psshsize); + char *ptr = buf; + for (size_t i = 0; i < mPssh.size(); i++) { + memcpy(ptr, mPssh[i].uuid, 20); // uuid + length + memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen); + ptr += (20 + mPssh[i].datalen); + } + mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize); + free(buf); + } return mInitCheck; } @@ -759,6 +800,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('m', 'f', 'r', 'a'): case FOURCC('u', 'd', 't', 'a'): case FOURCC('i', 'l', 's', 't'): + case FOURCC('s', 'i', 'n', 'f'): + case FOURCC('s', 'c', 'h', 'i'): { if (chunk_type == FOURCC('s', 't', 'b', 'l')) { ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size); @@ -846,6 +889,69 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('f', 'r', 'm', 'a'): + { + int32_t original_fourcc; + if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) { + return ERROR_IO; + } + original_fourcc = ntohl(original_fourcc); + ALOGV("read original format: %d", original_fourcc); + mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc)); + *offset += chunk_size; + break; + } + + case FOURCC('t', 'e', 'n', 'c'): + { + if (chunk_size < 32) { + return ERROR_MALFORMED; + } + + // tenc box contains 1 byte version, 3 byte flags, 3 byte default algorithm id, one byte + // default IV size, 16 bytes default KeyID + // (ISO 23001-7) + char buf[4]; + memset(buf, 0, 4); + if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) { + return ERROR_IO; + } + uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf)); + if (defaultAlgorithmId > 1) { + // only 0 (clear) and 1 (AES-128) are valid + return ERROR_MALFORMED; + } + + memset(buf, 0, 4); + if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) { + return ERROR_IO; + } + uint32_t defaultIVSize = ntohl(*((int32_t*)buf)); + + if ((defaultAlgorithmId == 0 && defaultIVSize != 0) || + (defaultAlgorithmId != 0 && defaultIVSize == 0)) { + // only unencrypted data must have 0 IV size + return ERROR_MALFORMED; + } else if (defaultIVSize != 0 && + defaultIVSize != 8 && + defaultIVSize != 16) { + // only supported sizes are 0, 8 and 16 + return ERROR_MALFORMED; + } + + uint8_t defaultKeyId[16]; + + if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) { + return ERROR_IO; + } + + mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId); + mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize); + mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16); + *offset += chunk_size; + break; + } + case FOURCC('t', 'k', 'h', 'd'): { status_t err; @@ -857,6 +963,37 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('p', 's', 's', 'h'): + { + PsshInfo pssh; + + if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) { + return ERROR_IO; + } + + uint32_t psshdatalen = 0; + if (mDataSource->readAt(data_offset + 20, &psshdatalen, 4) < 4) { + return ERROR_IO; + } + pssh.datalen = ntohl(psshdatalen); + ALOGV("pssh data size: %d", pssh.datalen); + if (pssh.datalen + 20 > chunk_size) { + // pssh data length exceeds size of containing box + return ERROR_MALFORMED; + } + + pssh.data = new uint8_t[pssh.datalen]; + ALOGV("allocated pssh @ %p", pssh.data); + ssize_t requested = (ssize_t) pssh.datalen; + if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) { + return ERROR_IO; + } + mPssh.push_back(pssh); + + *offset += chunk_size; + break; + } + case FOURCC('m', 'd', 'h', 'd'): { if (chunk_data_size < 4) { @@ -970,16 +1107,17 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // For 3GPP timed text, there could be multiple tx3g boxes contain // multiple text display formats. These formats will be used to // display the timed text. + // For encrypted files, there may also be more than one entry. const char *mime; CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); - if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { + if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) && + strcasecmp(mime, "application/octet-stream")) { // For now we only support a single type of media per track. mLastTrack->skipTrack = true; *offset += chunk_size; break; } } - off64_t stop_offset = *offset + chunk_size; *offset = data_offset + 8; for (uint32_t i = 0; i < entry_count; ++i) { @@ -1053,6 +1191,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } case FOURCC('m', 'p', '4', 'v'): + case FOURCC('e', 'n', 'c', 'v'): case FOURCC('s', '2', '6', '3'): case FOURCC('H', '2', '6', '3'): case FOURCC('h', '2', '6', '3'): @@ -1075,7 +1214,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { uint16_t width = U16_AT(&buffer[6 + 18]); uint16_t height = U16_AT(&buffer[6 + 20]); - // The video sample is not stand-compliant if it has invalid dimension. + // The video sample is not standard-compliant if it has invalid dimension. // Use some default width and height value, and // let the decoder figure out the actual width and height (and thus // be prepared for INFO_FOMRAT_CHANGED event). @@ -1085,7 +1224,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // printf("*** coding='%s' width=%d height=%d\n", // chunk, width, height); - mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); + if (chunk_type != FOURCC('e', 'n', 'c', 'v')) { + // if the chunk type is encv, we'll get the type from the sinf/frma box later + mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); + } mLastTrack->meta->setInt32(kKeyWidth, width); mLastTrack->meta->setInt32(kKeyHeight, height); @@ -1442,6 +1584,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('m', 'd', 'a', 't'): { + ALOGV("mdat chunk, drm: %d", mIsDrm); if (!mIsDrm) { *offset += chunk_size; break; @@ -1968,6 +2111,8 @@ sp MPEG4Extractor::getTrack(size_t index) { return NULL; } + ALOGV("getTrack called, pssh: %d", mPssh.size()); + return new MPEG4Source( track->meta, mDataSource, track->timescale, track->sampleTable, mSidxEntries, mMoofOffset); @@ -2139,6 +2284,10 @@ MPEG4Source::MPEG4Source( mFirstMoofOffset(firstMoofOffset), mCurrentMoofOffset(firstMoofOffset), mCurrentTime(0), + mCurrentSampleInfoAllocSize(0), + mCurrentSampleInfoSizes(NULL), + mCurrentSampleInfoOffsetsAllocSize(0), + mCurrentSampleInfoOffsets(NULL), mIsAVC(false), mNALLengthSize(0), mStarted(false), @@ -2146,6 +2295,18 @@ MPEG4Source::MPEG4Source( mBuffer(NULL), mWantsNALFragments(false), mSrcBuffer(NULL) { + + mFormat->findInt32(kKeyCryptoMode, &mCryptoMode); + mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize); + uint32_t keytype; + const void *key; + size_t keysize; + if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) { + CHECK(keysize <= 16); + memset(mCryptoKey, 0, 16); + memcpy(mCryptoKey, key, keysize); + } + const char *mime; bool success = mFormat->findCString(kKeyMIMEType, &mime); CHECK(success); @@ -2179,6 +2340,8 @@ MPEG4Source::~MPEG4Source() { if (mStarted) { stop(); } + free(mCurrentSampleInfoSizes); + free(mCurrentSampleInfoOffsets); } status_t MPEG4Source::start(MetaData *params) { @@ -2274,7 +2437,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { } } if (chunk_type == FOURCC('m', 'o', 'o', 'f')) { - // *offset points to then mdat box following this moof + // *offset points to the mdat box following this moof parseChunk(offset); // doesn't actually parse it, just updates offset mNextMoofOffset = *offset; } @@ -2302,6 +2465,31 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { break; } + case FOURCC('s', 'a', 'i', 'z'): { + status_t err; + if ((err = parseSampleAuxiliaryInformationSizes(data_offset, chunk_data_size)) != OK) { + return err; + } + *offset += chunk_size; + break; + } + case FOURCC('s', 'a', 'i', 'o'): { + status_t err; + if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) { + return err; + } + *offset += chunk_size; + break; + } + + case FOURCC('m', 'd', 'a', 't'): { + // parse DRM info if present + ALOGV("MPEG4Source::parseChunk mdat"); + // if saiz/saoi was previously observed, do something with the sampleinfos + *offset += chunk_size; + break; + } + default: { *offset += chunk_size; break; @@ -2310,6 +2498,152 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { return OK; } +status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size) { + ALOGV("parseSampleAuxiliaryInformationSizes"); + // 14496-12 8.7.12 + uint8_t version; + if (mDataSource->readAt( + offset, &version, sizeof(version)) + < (ssize_t)sizeof(version)) { + return ERROR_IO; + } + + if (version != 0) { + return ERROR_UNSUPPORTED; + } + offset++; + + uint32_t flags; + if (!mDataSource->getUInt24(offset, &flags)) { + return ERROR_IO; + } + offset += 3; + + if (flags & 1) { + uint32_t tmp; + if (!mDataSource->getUInt32(offset, &tmp)) { + return ERROR_MALFORMED; + } + mCurrentAuxInfoType = tmp; + offset += 4; + if (!mDataSource->getUInt32(offset, &tmp)) { + return ERROR_MALFORMED; + } + mCurrentAuxInfoTypeParameter = tmp; + offset += 4; + } + + uint8_t defsize; + if (mDataSource->readAt(offset, &defsize, 1) != 1) { + return ERROR_MALFORMED; + } + mCurrentDefaultSampleInfoSize = defsize; + offset++; + + uint32_t smplcnt; + if (!mDataSource->getUInt32(offset, &smplcnt)) { + return ERROR_MALFORMED; + } + offset += 4; + + if (smplcnt > mCurrentSampleInfoAllocSize) { + mCurrentSampleInfoSizes = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt); + mCurrentSampleInfoAllocSize = smplcnt; + } + mCurrentSampleInfoCount = smplcnt; + + mDataSource->readAt(offset, mCurrentSampleInfoSizes, smplcnt); + return OK; +} + +status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size) { + ALOGV("parseSampleAuxiliaryInformationOffsets"); + // 14496-12 8.7.13 + uint8_t version; + if (mDataSource->readAt(offset, &version, sizeof(version)) != 1) { + return ERROR_IO; + } + offset++; + + uint32_t flags; + if (!mDataSource->getUInt24(offset, &flags)) { + return ERROR_IO; + } + offset += 3; + + uint32_t entrycount; + if (!mDataSource->getUInt32(offset, &entrycount)) { + return ERROR_IO; + } + offset += 4; + + if (entrycount > mCurrentSampleInfoOffsetsAllocSize) { + mCurrentSampleInfoOffsets = (uint64_t*) realloc(mCurrentSampleInfoOffsets, entrycount * 8); + mCurrentSampleInfoOffsetsAllocSize = entrycount; + } + mCurrentSampleInfoOffsetCount = entrycount; + + for (size_t i = 0; i < entrycount; i++) { + if (version == 0) { + uint32_t tmp; + if (!mDataSource->getUInt32(offset, &tmp)) { + return ERROR_IO; + } + mCurrentSampleInfoOffsets[i] = tmp; + offset += 4; + } else { + uint64_t tmp; + if (!mDataSource->getUInt64(offset, &tmp)) { + return ERROR_IO; + } + mCurrentSampleInfoOffsets[i] = tmp; + offset += 8; + } + } + + // parse clear/encrypted data + + off64_t drmoffset = mCurrentSampleInfoOffsets[0]; // from moof + + drmoffset += mCurrentMoofOffset; + int ivlength; + CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength)); + int foo = 1; + for (size_t i = 0; i < mCurrentSampleInfoCount; i++) { + Sample *smpl = &mCurrentSamples.editItemAt(i); + + memset(smpl->iv, 0, 16); + if (mDataSource->readAt(drmoffset, smpl->iv, ivlength) != ivlength) { + return ERROR_IO; + } + + drmoffset += ivlength; + + uint16_t numsubsamples; + if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) { + return ERROR_IO; + } + drmoffset += 2; + for (size_t j = 0; j < numsubsamples; j++) { + uint16_t numclear; + uint32_t numencrypted; + if (!mDataSource->getUInt16(drmoffset, &numclear)) { + return ERROR_IO; + } + drmoffset += 2; + if (!mDataSource->getUInt32(drmoffset, &numencrypted)) { + return ERROR_IO; + } + drmoffset += 4; + smpl->clearsizes.add(numclear); + smpl->encryptedsizes.add(numencrypted); + } + } + + + return OK; +} + status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) { if (size < 8) { @@ -2317,7 +2651,7 @@ status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) { } uint32_t flags; - if (!mDataSource->getUInt32(offset, &flags)) { + if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags return ERROR_MALFORMED; } @@ -2550,8 +2884,8 @@ status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) { offset += 4; } - ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, " - " flags 0x%08x", + ALOGV("adding sample %d at offset 0x%08llx, size %u, duration %u, " + " flags 0x%08x", i + 1, dataOffset, sampleSize, sampleDuration, (flags & kFirstSampleFlagsPresent) && i == 0 ? firstSampleFlags : sampleFlags); @@ -3111,6 +3445,20 @@ status_t MPEG4Source::fragmentedRead( mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); } + const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex]; + if (smpl->encryptedsizes.size()) { + // store clear/encrypted lengths in metadata + sp bufmeta = mBuffer->meta_data(); + bufmeta->setData(kKeyPlainSizes, 0, + smpl->clearsizes.array(), smpl->clearsizes.size() * 4); + bufmeta->setData(kKeyEncryptedSizes, 0, + smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4); + bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size? + bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize); + bufmeta->setInt32(kKeyCryptoMode, mCryptoMode); + bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16); + } + ++mCurrentSampleIndex; *out = mBuffer; diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index 404fa94..7bc7da2 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -228,6 +228,34 @@ status_t NuMediaExtractor::getTrackFormat( return convertMetaDataToMessage(meta, format); } +status_t NuMediaExtractor::getFileFormat(sp *format) const { + Mutex::Autolock autoLock(mLock); + + *format = NULL; + + if (mImpl == NULL) { + return -EINVAL; + } + + sp meta = mImpl->getMetaData(); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + *format = new AMessage(); + (*format)->setString("mime", mime); + + uint32_t type; + const void *pssh; + size_t psshsize; + if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) { + sp buf = new ABuffer(psshsize); + memcpy(buf->data(), pssh, psshsize); + (*format)->setBuffer("pssh", buf); + } + + return OK; +} + status_t NuMediaExtractor::selectTrack(size_t index) { Mutex::Autolock autoLock(mLock); diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index c68623a..35eff96 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -59,6 +59,11 @@ protected: private: + struct PsshInfo { + uint8_t uuid[16]; + uint32_t datalen; + uint8_t *data; + }; struct Track { Track *next; sp meta; @@ -72,6 +77,8 @@ private: uint64_t mSidxDuration; off64_t mMoofOffset; + Vector mPssh; + sp mDataSource; status_t mInitCheck; bool mHasVideo; -- cgit v1.1 From 63594e8f83d982e45bd454224f2d20739b662c40 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Tue, 9 Apr 2013 16:40:54 -0700 Subject: OMXNodeInstance: fix OMX_GetExtensionIndex logging This change fixes the logging of OMX_GetExtensionIndex errors. Under certain circumstances these errors are not harmful and should not be logged. Bug: 8538872 Change-Id: I19a13d29ca6263454a9a7a8be205e10363725f31 --- media/libstagefright/omx/OMXNodeInstance.cpp | 32 +++++++++++++--------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'media') diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 46ff22f..971875f 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -292,15 +292,14 @@ status_t OMXNodeInstance::getState(OMX_STATETYPE* state) { status_t OMXNodeInstance::enableGraphicBuffers( OMX_U32 portIndex, OMX_BOOL enable) { Mutex::Autolock autoLock(mLock); + OMX_STRING name = const_cast( + "OMX.google.android.index.enableAndroidNativeBuffers"); OMX_INDEXTYPE index; - OMX_ERRORTYPE err = OMX_GetExtensionIndex( - mHandle, - const_cast("OMX.google.android.index.enableAndroidNativeBuffers"), - &index); + OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex failed"); + ALOGE("OMX_GetExtensionIndex %s failed", name); return StatusFromOMXError(err); } @@ -331,14 +330,12 @@ status_t OMXNodeInstance::getGraphicBufferUsage( Mutex::Autolock autoLock(mLock); OMX_INDEXTYPE index; - OMX_ERRORTYPE err = OMX_GetExtensionIndex( - mHandle, - const_cast( - "OMX.google.android.index.getAndroidNativeBufferUsage"), - &index); + OMX_STRING name = const_cast( + "OMX.google.android.index.getAndroidNativeBufferUsage"); + OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex failed"); + ALOGE("OMX_GetExtensionIndex %s failed", name); return StatusFromOMXError(err); } @@ -381,7 +378,9 @@ status_t OMXNodeInstance::storeMetaDataInBuffers_l( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); + if (enable) { + ALOGE("OMX_GetExtensionIndex %s failed", name); + } return StatusFromOMXError(err); } @@ -507,13 +506,12 @@ status_t OMXNodeInstance::useGraphicBuffer( return useGraphicBuffer2_l(portIndex, graphicBuffer, buffer); } - OMX_ERRORTYPE err = OMX_GetExtensionIndex( - mHandle, - const_cast("OMX.google.android.index.useAndroidNativeBuffer"), - &index); + OMX_STRING name = const_cast( + "OMX.google.android.index.useAndroidNativeBuffer"); + OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex failed"); + ALOGE("OMX_GetExtensionIndex %s failed", name); return StatusFromOMXError(err); } -- cgit v1.1 From da0dc0af0effe9fbfb3ce3187c8472fca2baf3c6 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Tue, 9 Apr 2013 21:53:49 -0700 Subject: Add liblog Bug: 8580410 Change-Id: If493d87d60d71be664ad75b140c62acadb75b0d0 --- media/common_time/Android.mk | 3 ++- media/libeffects/downmix/Android.mk | 2 +- media/libeffects/factory/Android.mk | 2 +- media/libeffects/preprocessing/Android.mk | 3 ++- media/libeffects/visualizer/Android.mk | 1 + media/libmedia/Android.mk | 2 +- media/libmediaplayerservice/Android.mk | 1 + media/libnbaio/Android.mk | 3 ++- media/libstagefright/chromium_http/Android.mk | 1 + media/libstagefright/codecs/aacdec/Android.mk | 2 +- media/libstagefright/codecs/aacenc/Android.mk | 4 ++-- media/libstagefright/codecs/amrnb/dec/Android.mk | 2 +- media/libstagefright/codecs/amrnb/enc/Android.mk | 2 +- media/libstagefright/codecs/amrwbenc/Android.mk | 2 +- media/libstagefright/codecs/avc/enc/Android.mk | 1 + media/libstagefright/codecs/flac/enc/Android.mk | 2 +- media/libstagefright/codecs/g711/dec/Android.mk | 2 +- media/libstagefright/codecs/gsm/dec/Android.mk | 2 +- media/libstagefright/codecs/m4v_h263/dec/Android.mk | 2 +- media/libstagefright/codecs/m4v_h263/enc/Android.mk | 1 + media/libstagefright/codecs/mp3dec/Android.mk | 2 +- media/libstagefright/codecs/on2/dec/Android.mk | 2 +- media/libstagefright/codecs/on2/enc/Android.mk | 2 +- media/libstagefright/codecs/on2/h264dec/Android.mk | 3 +-- media/libstagefright/codecs/raw/Android.mk | 2 +- media/libstagefright/codecs/vorbis/dec/Android.mk | 3 +-- media/libstagefright/foundation/Android.mk | 1 + media/libstagefright/id3/Android.mk | 2 +- media/libstagefright/omx/Android.mk | 1 + media/libstagefright/omx/tests/Android.mk | 2 +- media/libstagefright/tests/Android.mk | 1 + media/libstagefright/wifi-display/Android.mk | 5 +++++ media/libstagefright/yuv/Android.mk | 3 ++- media/mediaserver/Android.mk | 1 + media/mtp/Android.mk | 2 +- 35 files changed, 44 insertions(+), 28 deletions(-) (limited to 'media') diff --git a/media/common_time/Android.mk b/media/common_time/Android.mk index 526f17b..632acbc 100644 --- a/media/common_time/Android.mk +++ b/media/common_time/Android.mk @@ -16,6 +16,7 @@ LOCAL_SRC_FILES := cc_helper.cpp \ utils.cpp LOCAL_SHARED_LIBRARIES := libbinder \ libhardware \ - libutils + libutils \ + liblog include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk index 3052ad9..5d0a87c 100644 --- a/media/libeffects/downmix/Android.mk +++ b/media/libeffects/downmix/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES:= \ EffectDownmix.c LOCAL_SHARED_LIBRARIES := \ - libcutils + libcutils liblog LOCAL_MODULE:= libdownmix diff --git a/media/libeffects/factory/Android.mk b/media/libeffects/factory/Android.mk index 6e69151..60a6ce5 100644 --- a/media/libeffects/factory/Android.mk +++ b/media/libeffects/factory/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES:= \ EffectsFactory.c LOCAL_SHARED_LIBRARIES := \ - libcutils + libcutils liblog LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) LOCAL_MODULE:= libeffects diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk index dfa1711..c344352 100644 --- a/media/libeffects/preprocessing/Android.mk +++ b/media/libeffects/preprocessing/Android.mk @@ -21,7 +21,8 @@ LOCAL_C_INCLUDES += $(call include-path-for, speex) LOCAL_SHARED_LIBRARIES := \ libwebrtc_audio_preprocessing \ libspeexresampler \ - libutils + libutils \ + liblog ifeq ($(TARGET_SIMULATOR),true) LOCAL_LDLIBS += -ldl diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk index 49cf4fa..e196eb2 100644 --- a/media/libeffects/visualizer/Android.mk +++ b/media/libeffects/visualizer/Android.mk @@ -10,6 +10,7 @@ LOCAL_CFLAGS+= -O2 -fvisibility=hidden LOCAL_SHARED_LIBRARIES := \ libcutils \ + liblog \ libdl LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index fbe71ad..2c0c3a5 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -63,7 +63,7 @@ LOCAL_SRC_FILES += SingleStateQueue.cpp LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"' LOCAL_SHARED_LIBRARIES := \ - libui libcutils libutils libbinder libsonivox libicuuc libexpat \ + libui liblog libcutils libutils libbinder libsonivox libicuuc libexpat \ libcamera_client libstagefright_foundation \ libgui libdl libaudioutils diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 2a6f3c7..d87bc7f 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -27,6 +27,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcamera_client \ libcutils \ + liblog \ libdl \ libgui \ libmedia \ diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk index d372d20..5d00d15 100644 --- a/media/libnbaio/Android.mk +++ b/media/libnbaio/Android.mk @@ -30,6 +30,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcommon_time_client \ libcutils \ - libutils + libutils \ + liblog include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk index 2c6d84c..f26f386 100644 --- a/media/libstagefright/chromium_http/Android.mk +++ b/media/libstagefright/chromium_http/Android.mk @@ -22,6 +22,7 @@ LOCAL_SHARED_LIBRARIES += \ libchromium_net \ libutils \ libcutils \ + liblog \ libstagefright_foundation \ libstagefright \ libdrmframework diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk index 4dc38a8..ffa64f9 100644 --- a/media/libstagefright/codecs/aacdec/Android.mk +++ b/media/libstagefright/codecs/aacdec/Android.mk @@ -20,7 +20,7 @@ LOCAL_CFLAGS := LOCAL_STATIC_LIBRARIES := libFraunhoferAAC LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils libcutils + libstagefright_omx libstagefright_foundation libutils libcutils liblog LOCAL_MODULE := libstagefright_soft_aacdec LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk index 820734d..057c69b 100644 --- a/media/libstagefright/codecs/aacenc/Android.mk +++ b/media/libstagefright/codecs/aacenc/Android.mk @@ -109,7 +109,7 @@ ifeq ($(AAC_LIBRARY), fraunhofer) LOCAL_STATIC_LIBRARIES := libFraunhoferAAC LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils + libstagefright_omx libstagefright_foundation libutils liblog LOCAL_MODULE := libstagefright_soft_aacenc LOCAL_MODULE_TAGS := optional @@ -132,7 +132,7 @@ else # visualon libstagefright_aacenc LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils \ + libstagefright_omx libstagefright_foundation libutils liblog \ libstagefright_enc_common LOCAL_MODULE := libstagefright_soft_aacenc diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk index b48a459..8d6c6f8 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.mk +++ b/media/libstagefright/codecs/amrnb/dec/Android.mk @@ -72,7 +72,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_amrnbdec libstagefright_amrwbdec LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils \ + libstagefright_omx libstagefright_foundation libutils liblog \ libstagefright_amrnb_common LOCAL_MODULE := libstagefright_soft_amrdec diff --git a/media/libstagefright/codecs/amrnb/enc/Android.mk b/media/libstagefright/codecs/amrnb/enc/Android.mk index 457656a..f4e467a 100644 --- a/media/libstagefright/codecs/amrnb/enc/Android.mk +++ b/media/libstagefright/codecs/amrnb/enc/Android.mk @@ -92,7 +92,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_amrnbenc LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils \ + libstagefright_omx libstagefright_foundation libutils liblog \ libstagefright_amrnb_common LOCAL_MODULE := libstagefright_soft_amrnbenc diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk index edfd7b7..c5b8e0c 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/Android.mk @@ -130,7 +130,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_amrwbenc LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils \ + libstagefright_omx libstagefright_foundation libutils liblog \ libstagefright_enc_common LOCAL_MODULE := libstagefright_soft_amrwbenc diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk index cffe469..7d17c2a 100644 --- a/media/libstagefright/codecs/avc/enc/Android.mk +++ b/media/libstagefright/codecs/avc/enc/Android.mk @@ -62,6 +62,7 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_foundation \ libstagefright_omx \ libutils \ + liblog \ libui diff --git a/media/libstagefright/codecs/flac/enc/Android.mk b/media/libstagefright/codecs/flac/enc/Android.mk index 546a357..f01d605 100644 --- a/media/libstagefright/codecs/flac/enc/Android.mk +++ b/media/libstagefright/codecs/flac/enc/Android.mk @@ -10,7 +10,7 @@ LOCAL_C_INCLUDES := \ external/flac/include LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils + libstagefright libstagefright_omx libstagefright_foundation libutils liblog LOCAL_STATIC_LIBRARIES := \ libFLAC \ diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk index 28be646..4c80da6 100644 --- a/media/libstagefright/codecs/g711/dec/Android.mk +++ b/media/libstagefright/codecs/g711/dec/Android.mk @@ -9,7 +9,7 @@ LOCAL_C_INCLUDES := \ frameworks/native/include/media/openmax LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils + libstagefright libstagefright_omx libstagefright_foundation libutils liblog LOCAL_MODULE := libstagefright_soft_g711dec LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/codecs/gsm/dec/Android.mk b/media/libstagefright/codecs/gsm/dec/Android.mk index 9c0c6ae..71613d2 100644 --- a/media/libstagefright/codecs/gsm/dec/Android.mk +++ b/media/libstagefright/codecs/gsm/dec/Android.mk @@ -10,7 +10,7 @@ LOCAL_C_INCLUDES := \ external/libgsm/inc LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils + libstagefright libstagefright_omx libstagefright_foundation libutils liblog LOCAL_STATIC_LIBRARIES := \ libgsm diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk index a6b1edc..a3d5779 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk @@ -67,7 +67,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_m4vh263dec LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils + libstagefright libstagefright_omx libstagefright_foundation libutils liblog LOCAL_MODULE := libstagefright_soft_mpeg4dec LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk index 865cc9c..83a2dd2 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk @@ -65,6 +65,7 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_foundation \ libstagefright_omx \ libutils \ + liblog \ libui diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk index ec8d7ec..135c715 100644 --- a/media/libstagefright/codecs/mp3dec/Android.mk +++ b/media/libstagefright/codecs/mp3dec/Android.mk @@ -70,7 +70,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils + libstagefright libstagefright_omx libstagefright_foundation libutils liblog LOCAL_STATIC_LIBRARIES := \ libstagefright_mp3dec diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk index 0082d7c..7f2c46d 100644 --- a/media/libstagefright/codecs/on2/dec/Android.mk +++ b/media/libstagefright/codecs/on2/dec/Android.mk @@ -15,7 +15,7 @@ LOCAL_STATIC_LIBRARIES := \ libvpx LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils + libstagefright libstagefright_omx libstagefright_foundation libutils liblog LOCAL_MODULE := libstagefright_soft_vpxdec LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk index 5d3317c..a92d376 100644 --- a/media/libstagefright/codecs/on2/enc/Android.mk +++ b/media/libstagefright/codecs/on2/enc/Android.mk @@ -16,7 +16,7 @@ LOCAL_STATIC_LIBRARIES := \ libvpx LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils \ + libstagefright libstagefright_omx libstagefright_foundation libutils liblog \ LOCAL_MODULE := libstagefright_soft_vpxenc LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/codecs/on2/h264dec/Android.mk b/media/libstagefright/codecs/on2/h264dec/Android.mk index 772fd60..2539f98 100644 --- a/media/libstagefright/codecs/on2/h264dec/Android.mk +++ b/media/libstagefright/codecs/on2/h264dec/Android.mk @@ -97,7 +97,7 @@ ifeq ($(ARCH_ARM_HAVE_NEON),true) endif LOCAL_SHARED_LIBRARIES := \ - libstagefright libstagefright_omx libstagefright_foundation libutils \ + libstagefright libstagefright_omx libstagefright_foundation libutils liblog \ LOCAL_MODULE := libstagefright_soft_h264dec @@ -124,4 +124,3 @@ LOCAL_MODULE_TAGS := debug LOCAL_MODULE := decoder include $(BUILD_EXECUTABLE) - diff --git a/media/libstagefright/codecs/raw/Android.mk b/media/libstagefright/codecs/raw/Android.mk index 285c747..fe90a03 100644 --- a/media/libstagefright/codecs/raw/Android.mk +++ b/media/libstagefright/codecs/raw/Android.mk @@ -9,7 +9,7 @@ LOCAL_C_INCLUDES := \ frameworks/native/include/media/openmax LOCAL_SHARED_LIBRARIES := \ - libstagefright_omx libstagefright_foundation libutils + libstagefright_omx libstagefright_foundation libutils liblog LOCAL_MODULE := libstagefright_soft_rawdec LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk index 395dd6b..2232353 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.mk +++ b/media/libstagefright/codecs/vorbis/dec/Android.mk @@ -11,10 +11,9 @@ LOCAL_C_INCLUDES := \ LOCAL_SHARED_LIBRARIES := \ libvorbisidec libstagefright libstagefright_omx \ - libstagefright_foundation libutils + libstagefright_foundation libutils liblog LOCAL_MODULE := libstagefright_soft_vorbisdec LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) - diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk index b7577d6..d65e213 100644 --- a/media/libstagefright/foundation/Android.mk +++ b/media/libstagefright/foundation/Android.mk @@ -20,6 +20,7 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES := \ libbinder \ libutils \ + liblog LOCAL_CFLAGS += -Wno-multichar diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk index ff35d4a..80a1a3a 100644 --- a/media/libstagefright/id3/Android.mk +++ b/media/libstagefright/id3/Android.mk @@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \ testid3.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libutils libbinder libstagefright_foundation + libstagefright libutils liblog libbinder libstagefright_foundation LOCAL_STATIC_LIBRARIES := \ libstagefright_id3 diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 9129f08..a8b4939 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -19,6 +19,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libmedia \ libutils \ + liblog \ libui \ libgui \ libcutils \ diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk index 04441ca..1061c39 100644 --- a/media/libstagefright/omx/tests/Android.mk +++ b/media/libstagefright/omx/tests/Android.mk @@ -5,7 +5,7 @@ LOCAL_SRC_FILES = \ OMXHarness.cpp \ LOCAL_SHARED_LIBRARIES := \ - libstagefright libbinder libmedia libutils libstagefright_foundation + libstagefright libbinder libmedia libutils liblog libstagefright_foundation LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/media/libstagefright \ diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk index 57fff0b..06ce16b 100644 --- a/media/libstagefright/tests/Android.mk +++ b/media/libstagefright/tests/Android.mk @@ -26,6 +26,7 @@ LOCAL_SHARED_LIBRARIES := \ libsync \ libui \ libutils \ + liblog LOCAL_STATIC_LIBRARIES := \ libgtest \ diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 1578c21..f99ef60 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -31,6 +31,7 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES:= \ libbinder \ libcutils \ + liblog \ libgui \ libmedia \ libstagefright \ @@ -59,6 +60,7 @@ LOCAL_SHARED_LIBRARIES:= \ libstagefright_foundation \ libstagefright_wfd \ libutils \ + liblog \ LOCAL_MODULE:= wfd @@ -81,6 +83,7 @@ LOCAL_SHARED_LIBRARIES:= \ libstagefright_foundation \ libstagefright_wfd \ libutils \ + liblog \ LOCAL_MODULE:= udptest @@ -103,6 +106,7 @@ LOCAL_SHARED_LIBRARIES:= \ libstagefright_foundation \ libstagefright_wfd \ libutils \ + liblog \ LOCAL_MODULE:= rtptest @@ -125,6 +129,7 @@ LOCAL_SHARED_LIBRARIES:= \ libstagefright_foundation \ libstagefright_wfd \ libutils \ + liblog \ LOCAL_MODULE:= nettest diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk index a4253f6..b3f7b1b 100644 --- a/media/libstagefright/yuv/Android.mk +++ b/media/libstagefright/yuv/Android.mk @@ -6,7 +6,8 @@ LOCAL_SRC_FILES:= \ YUVCanvas.cpp LOCAL_SHARED_LIBRARIES := \ - libcutils + libcutils \ + liblog LOCAL_MODULE:= libstagefright_yuv diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index a485646..1ac647a 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -22,6 +22,7 @@ LOCAL_SHARED_LIBRARIES := \ libmedia \ libmediaplayerservice \ libutils \ + liblog \ libbinder LOCAL_STATIC_LIBRARIES := \ diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk index bee28d4..ac608a1 100644 --- a/media/mtp/Android.mk +++ b/media/mtp/Android.mk @@ -42,6 +42,6 @@ LOCAL_CFLAGS := -DMTP_DEVICE -DMTP_HOST # Needed for LOCAL_C_INCLUDES := bionic/libc/private -LOCAL_SHARED_LIBRARIES := libutils libcutils libusbhost libbinder +LOCAL_SHARED_LIBRARIES := libutils libcutils liblog libusbhost libbinder include $(BUILD_SHARED_LIBRARY) -- cgit v1.1 From 2c65be2298f055d015c31dea9956855236a0b465 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Wed, 10 Apr 2013 16:58:15 -0700 Subject: Add support for OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m color format Change-Id: Ib862ee341ccf668445f0dff29c5a39e91e769244 --- media/libstagefright/ACodec.cpp | 1 + media/libstagefright/OMXCodec.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index ff72b71..9c4378e 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1438,6 +1438,7 @@ status_t ACodec::setSupportedOutputFormat() { || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka + || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m || format.eColorFormat == OMX_SEC_COLOR_FormatNV12Tiled); return mOMX->setParameter( diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 22aefcc..c537557 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1218,7 +1218,8 @@ status_t OMXCodec::setVideoOutputFormat( || format.eColorFormat == OMX_COLOR_FormatCbYCrY || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar - || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka); + || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka + || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m); int32_t colorFormat; if (meta->findInt32(kKeyColorFormat, &colorFormat) -- cgit v1.1 From d85929f6086e050d7cb33bfe0d29f339ad7279e5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 11 Apr 2013 11:07:55 -0700 Subject: Instead of returning an error, return an invalid duration (-1 ms) if no duration information was available. This prevents us from entering ERROR state, effectively rendering the player instance useless. Change-Id: I602d2661ae8b8633360306c0ea9208fb11e2bf17 related-to-bug: 8596285 --- media/libmedia/mediaplayer.cpp | 7 +++++++ media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 3defec3..ecae3d3 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -398,6 +398,13 @@ status_t MediaPlayer::getDuration_l(int *msec) if (mPlayer != 0 && isValidState) { int durationMs; status_t ret = mPlayer->getDuration(&durationMs); + + if (ret != OK) { + // Do not enter error state just because no duration was available. + durationMs = -1; + ret = OK; + } + if (msec) { *msec = durationMs; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 723af09..bdafb29 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -314,11 +314,11 @@ status_t NuPlayerDriver::getDuration(int *msec) { Mutex::Autolock autoLock(mLock); if (mDurationUs < 0) { - *msec = 0; - } else { - *msec = (mDurationUs + 500ll) / 1000; + return UNKNOWN_ERROR; } + *msec = (mDurationUs + 500ll) / 1000; + return OK; } -- cgit v1.1 From 76ab6df08a2069eac6317715dadccbb6041e7fab Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 11 Apr 2013 11:37:28 -0700 Subject: The framework really doesn't need to know the specifics of the codec color format, so stop trying to enforce a whitelist. Change-Id: I0ceb3bdd5bcc6c1bbd56740b0cd662a2b5820dfe related-to-bug: 8596546 --- media/libstagefright/ACodec.cpp | 8 -------- media/libstagefright/OMXCodec.cpp | 7 ------- 2 files changed, 15 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index ff72b71..d24bd64 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1432,14 +1432,6 @@ status_t ACodec::setSupportedOutputFormat() { CHECK_EQ(err, (status_t)OK); CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused); - CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar - || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar - || format.eColorFormat == OMX_COLOR_FormatCbYCrY - || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar - || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar - || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka - || format.eColorFormat == OMX_SEC_COLOR_FormatNV12Tiled); - return mOMX->setParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 22aefcc..6c0779d 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1213,13 +1213,6 @@ status_t OMXCodec::setVideoOutputFormat( CHECK_EQ(err, (status_t)OK); CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused); - CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar - || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar - || format.eColorFormat == OMX_COLOR_FormatCbYCrY - || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar - || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar - || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka); - int32_t colorFormat; if (meta->findInt32(kKeyColorFormat, &colorFormat) && colorFormat != OMX_COLOR_FormatUnused -- cgit v1.1 From ac0230da14a3d223c2144b165a3a163e8519d239 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 11 Apr 2013 16:06:14 -0700 Subject: ACodec now signals an error if the mediaserver died while it is in anything other than "uninitialized" state. Change-Id: Id133d897ac65b455b34e5de17ff9c39b47285630 related-to-bug: 8397711 --- media/libstagefright/ACodec.cpp | 57 ++++++++++++++++++++++++++++++++------ media/libstagefright/OMXClient.cpp | 2 +- 2 files changed, 50 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index ff72b71..01ff07e 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -166,6 +166,24 @@ private: //////////////////////////////////////////////////////////////////////////////// +struct ACodec::DeathNotifier : public IBinder::DeathRecipient { + DeathNotifier(const sp ¬ify) + : mNotify(notify) { + } + + virtual void binderDied(const wp &) { + mNotify->post(); + } + +protected: + virtual ~DeathNotifier() {} + +private: + sp mNotify; + + DISALLOW_EVIL_CONSTRUCTORS(DeathNotifier); +}; + struct ACodec::UninitializedState : public ACodec::BaseState { UninitializedState(ACodec *codec); @@ -177,6 +195,8 @@ private: void onSetup(const sp &msg); bool onAllocateComponent(const sp &msg); + sp mDeathNotifier; + DISALLOW_EVIL_CONSTRUCTORS(UninitializedState); }; @@ -2487,6 +2507,13 @@ bool ACodec::BaseState::onMessageReceived(const sp &msg) { return true; } + case ACodec::kWhatOMXDied: + { + ALOGE("OMX/mediaserver died, signalling error!"); + mCodec->signalError(OMX_ErrorResourcesLost, DEAD_OBJECT); + break; + } + default: return false; } @@ -3035,6 +3062,18 @@ ACodec::UninitializedState::UninitializedState(ACodec *codec) void ACodec::UninitializedState::stateEntered() { ALOGV("Now uninitialized"); + + if (mDeathNotifier != NULL) { + mCodec->mOMX->asBinder()->unlinkToDeath(mDeathNotifier); + mDeathNotifier.clear(); + } + + mCodec->mNativeWindow.clear(); + mCodec->mNode = NULL; + mCodec->mOMX.clear(); + mCodec->mQuirks = 0; + mCodec->mFlags = 0; + mCodec->mComponentName.clear(); } bool ACodec::UninitializedState::onMessageReceived(const sp &msg) { @@ -3106,6 +3145,15 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { sp omx = client.interface(); + sp notify = new AMessage(kWhatOMXDied, mCodec->id()); + + mDeathNotifier = new DeathNotifier(notify); + if (omx->asBinder()->linkToDeath(mDeathNotifier) != OK) { + // This was a local binder, if it dies so do we, we won't care + // about any notifications in the afterlife. + mDeathNotifier.clear(); + } + Vector matchingCodecs; AString mime; @@ -3170,7 +3218,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { return false; } - sp notify = new AMessage(kWhatOMXMessage, mCodec->id()); + notify = new AMessage(kWhatOMXMessage, mCodec->id()); observer->setNotificationMessage(notify); mCodec->mComponentName = componentName; @@ -3224,13 +3272,6 @@ void ACodec::LoadedState::onShutdown(bool keepComponentAllocated) { if (!keepComponentAllocated) { CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK); - mCodec->mNativeWindow.clear(); - mCodec->mNode = NULL; - mCodec->mOMX.clear(); - mCodec->mQuirks = 0; - mCodec->mFlags = 0; - mCodec->mComponentName.clear(); - mCodec->changeState(mCodec->mUninitializedState); } diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index ff72e0e..1822f07 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -32,7 +32,7 @@ struct MuxOMX : public IOMX { MuxOMX(const sp &remoteOMX); virtual ~MuxOMX(); - virtual IBinder *onAsBinder() { return NULL; } + virtual IBinder *onAsBinder() { return mRemoteOMX->asBinder().get(); } virtual bool livesLocally(node_id node, pid_t pid); -- cgit v1.1 From f3bd1972e039c6ded5154db715e5a32f1813a239 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 9 Apr 2013 14:57:38 -0700 Subject: Fix MediaCodec.flush() There were two problems here. One was that the skip/cut buffer wasn't cleared when it should be, and the second was that we were always sending the first buffer of encoded data to the AAC decoder twice. b/8543366 Change-Id: Ic040edabf16cccd1f6ef8c9e5c9cfbacbdd8a089 --- media/libstagefright/ACodec.cpp | 5 +- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 103 +++++++++++------------- media/libstagefright/codecs/aacdec/SoftAAC2.h | 3 +- 3 files changed, 55 insertions(+), 56 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index ff72b71..31a9490 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3626,7 +3626,6 @@ bool ACodec::ExecutingState::onMessageReceived(const sp &msg) { (status_t)OK); mCodec->changeState(mCodec->mFlushingState); - handled = true; break; } @@ -4141,6 +4140,10 @@ void ACodec::FlushingState::changeStateIfWeOwnAllBuffers() { mCodec->mInputEOSResult = OK; + if (mCodec->mSkipCutBuffer != NULL) { + mCodec->mSkipCutBuffer->clear(); + } + mCodec->changeState(mCodec->mExecutingState); } } diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index a8ab2ac..8ba2afb 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -118,7 +118,7 @@ status_t SoftAAC2::initDecoder() { status = OK; } } - mIsFirst = true; + mDecoderHasData = false; // for streams that contain metadata, use the mobile profile DRC settings unless overridden // by platform properties: @@ -327,6 +327,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); return; } + inQueue.erase(inQueue.begin()); info->mOwnedByUs = false; notifyEmptyBufferDone(header); @@ -358,7 +359,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); - if (!mIsFirst) { + if (mDecoderHasData) { // flush out the decoder's delayed data by calling DecodeFrame // one more time, with the AACDEC_FLUSH flag set INT_PCM *outBuffer = @@ -370,6 +371,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { outBuffer, outHeader->nAllocLen, AACDEC_FLUSH); + mDecoderHasData = false; if (decoderErr != AAC_DEC_OK) { mSignalledError = true; @@ -385,9 +387,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { * sizeof(int16_t) * mStreamInfo->numChannels; } else { - // Since we never discarded frames from the start, we won't have - // to add any padding at the end either. - + // we never submitted any data to the decoder, so there's nothing to flush out outHeader->nFilledLen = 0; } @@ -473,6 +473,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { inBuffer, inBufferLength, bytesValid); + mDecoderHasData = true; decoderErr = aacDecoder_DecodeFrame(mAACDecoder, outBuffer, @@ -484,6 +485,35 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { } } + size_t numOutBytes = + mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; + + if (decoderErr == AAC_DEC_OK) { + UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; + inHeader->nFilledLen -= inBufferUsedLength; + inHeader->nOffset += inBufferUsedLength; + } else { + ALOGW("AAC decoder returned error %d, substituting silence", + decoderErr); + + memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); + + // Discard input buffer. + inHeader->nFilledLen = 0; + + aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); + + // fall through + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + /* * AAC+/eAAC+ streams can be signalled in two ways: either explicitly * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual @@ -502,15 +532,9 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { if (mStreamInfo->sampleRate != prevSampleRate || mStreamInfo->numChannels != prevNumChannels) { maybeConfigureDownmix(); - ALOGI("Reconfiguring decoder: %d Hz, %d channels", - mStreamInfo->sampleRate, - mStreamInfo->numChannels); - - // We're going to want to revisit this input buffer, but - // may have already advanced the offset. Undo that if - // necessary. - inHeader->nOffset -= adtsHeaderSize; - inHeader->nFilledLen += adtsHeaderSize; + ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels", + prevSampleRate, mStreamInfo->sampleRate, + prevNumChannels, mStreamInfo->numChannels); notify(OMX_EventPortSettingsChanged, 1, 0, NULL); mOutputPortSettingsChange = AWAITING_DISABLED; @@ -523,38 +547,10 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { return; } - size_t numOutBytes = - mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; - - if (decoderErr == AAC_DEC_OK) { - UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; - inHeader->nFilledLen -= inBufferUsedLength; - inHeader->nOffset += inBufferUsedLength; - } else { - ALOGW("AAC decoder returned error %d, substituting silence", - decoderErr); - - memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); - - // Discard input buffer. - inHeader->nFilledLen = 0; - - aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - - // fall through - } - if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) { // We'll only output data if we successfully decoded it or // we've previously decoded valid data, in the latter case // (decode failed) we'll output a silent frame. - if (mIsFirst) { - mIsFirst = false; - // the first decoded frame should be discarded to account - // for decoder delay - numOutBytes = 0; - } - outHeader->nFilledLen = numOutBytes; outHeader->nFlags = 0; @@ -571,14 +567,6 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { outHeader = NULL; } - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; - } - if (decoderErr == AAC_DEC_OK) { ++mInputBufferCount; } @@ -589,14 +577,21 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { if (portIndex == 0) { // Make sure that the next buffer output does not still // depend on fragments from the last one decoded. - aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - mIsFirst = true; + // drain all existing data + drainDecoder(); } } -void SoftAAC2::onReset() { +void SoftAAC2::drainDecoder() { + short buf [2048]; + aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); + aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - mIsFirst = true; + mDecoderHasData = false; +} + +void SoftAAC2::onReset() { + drainDecoder(); } void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h index 6957ade..2d960ab 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -52,7 +52,7 @@ private: HANDLE_AACDECODER mAACDecoder; CStreamInfo *mStreamInfo; bool mIsADTS; - bool mIsFirst; + bool mDecoderHasData; size_t mInputBufferCount; bool mSignalledError; int64_t mAnchorTimeUs; @@ -68,6 +68,7 @@ private: status_t initDecoder(); bool isConfigured() const; void maybeConfigureDownmix() const; + void drainDecoder(); DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2); }; -- cgit v1.1 From de05c8eab188e98798f2b9c3dfac53dbc18ef584 Mon Sep 17 00:00:00 2001 From: ztenghui Date: Fri, 12 Apr 2013 13:50:38 -0700 Subject: MediaMuxer prefer not to use the MPEG4Writer in real time recording mode. By default, MPEG4Write will keep running in real time recording mode. bug:8598944 Change-Id: Idf7fbd4e0feb7763660a74279ba8817b79098aaf --- media/libstagefright/MPEG4Writer.cpp | 43 ++++++++++++++++++++++-------------- media/libstagefright/MediaMuxer.cpp | 1 + 2 files changed, 28 insertions(+), 16 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 316f669..a0f17b5 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -212,7 +212,6 @@ private: int64_t mTrackDurationUs; int64_t mMaxChunkDurationUs; - bool mIsRealTimeRecording; int64_t mEstimatedTrackSizeBytes; int64_t mMdatSizeBytes; int32_t mTimeScale; @@ -335,6 +334,7 @@ private: MPEG4Writer::MPEG4Writer(const char *filename) : mFd(-1), mInitCheck(NO_INIT), + mIsRealTimeRecording(true), mUse4ByteNalLength(true), mUse32BitOffset(true), mIsFileSizeLimitExplicitlyRequested(false), @@ -359,6 +359,7 @@ MPEG4Writer::MPEG4Writer(const char *filename) MPEG4Writer::MPEG4Writer(int fd) : mFd(dup(fd)), mInitCheck(mFd < 0? NO_INIT: OK), + mIsRealTimeRecording(true), mUse4ByteNalLength(true), mUse32BitOffset(true), mIsFileSizeLimitExplicitlyRequested(false), @@ -596,6 +597,11 @@ status_t MPEG4Writer::start(MetaData *param) { mUse4ByteNalLength = false; } + int32_t isRealTimeRecording; + if (param && param->findInt32(kKeyRealTimeRecording, &isRealTimeRecording)) { + mIsRealTimeRecording = isRealTimeRecording; + } + mStartTimestampUs = -1; if (mStarted) { @@ -1640,12 +1646,18 @@ void MPEG4Writer::threadFunc() { mChunkReadyCondition.wait(mLock); } - // Actual write without holding the lock in order to - // reduce the blocking time for media track threads. + // In real time recording mode, write without holding the lock in order + // to reduce the blocking time for media track threads. + // Otherwise, hold the lock until the existing chunks get written to the + // file. if (chunkFound) { - mLock.unlock(); + if (mIsRealTimeRecording) { + mLock.unlock(); + } writeChunkToFile(&chunk); - mLock.lock(); + if (mIsRealTimeRecording) { + mLock.lock(); + } } } @@ -1695,18 +1707,10 @@ status_t MPEG4Writer::Track::start(MetaData *params) { mRotation = rotationDegrees; } - mIsRealTimeRecording = true; - { - int32_t isNotRealTime; - if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) { - mIsRealTimeRecording = (isNotRealTime == 0); - } - } - initTrackingProgressStatus(params); sp meta = new MetaData; - if (mIsRealTimeRecording && mOwner->numTracks() > 1) { + if (mOwner->isRealTimeRecording() && mOwner->numTracks() > 1) { /* * This extra delay of accepting incoming audio/video signals * helps to align a/v start time at the beginning of a recording @@ -2084,7 +2088,10 @@ status_t MPEG4Writer::Track::threadEntry() { } else { prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0); } - androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); + + if (mOwner->isRealTimeRecording()) { + androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); + } sp meta_data; @@ -2245,7 +2252,7 @@ status_t MPEG4Writer::Track::threadEntry() { } - if (mIsRealTimeRecording) { + if (mOwner->isRealTimeRecording()) { if (mIsAudio) { updateDriftTime(meta_data); } @@ -2531,6 +2538,10 @@ int64_t MPEG4Writer::getDriftTimeUs() { return mDriftTimeUs; } +bool MPEG4Writer::isRealTimeRecording() const { + return mIsRealTimeRecording; +} + bool MPEG4Writer::useNalLengthFour() { return mUse4ByteNalLength; } diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index b948fe2..388c65b 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -107,6 +107,7 @@ status_t MediaMuxer::start() { Mutex::Autolock autoLock(mMuxerLock); if (mState == INITIALIZED) { mState = STARTED; + mFileMeta->setInt32(kKeyRealTimeRecording, false); return mWriter->start(mFileMeta.get()); } else { ALOGE("start() is called in invalid state %d", mState); -- cgit v1.1 From 7c5abbb0e1b20df4b265a08a8560899f637f9b44 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 15 Apr 2013 12:06:18 -0700 Subject: Fix AAC decoder reconfiguration The recent flush() changed made the codec behave differently after a reconfigure. Now we reset its state properly again. b/8543366 Change-Id: I8807b5ab02249b43fc1cf315d4e8d4ceb3f9b298 --- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'media') diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index 8ba2afb..536cfde 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -592,6 +592,12 @@ void SoftAAC2::drainDecoder() { void SoftAAC2::onReset() { drainDecoder(); + // reset the "configured" state + mInputBufferCount = 0; + mNumSamplesOutput = 0; + // To make the codec behave the same before and after a reset, we need to invalidate the + // streaminfo struct. This does that: + mStreamInfo->sampleRate = 0; } void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { -- cgit v1.1 From a68e7b98361692d4120bf99fa5dc18cd93673130 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 15 Apr 2013 16:18:56 -0700 Subject: Guard against mLooper == NULL. Change-Id: I01aa0e47b55d0dffe34525edf9f055a5cb4dc70f related-to-bug: 8620223 --- media/libmediaplayerservice/nuplayer/RTSPSource.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index a5ff0ca..50ebf9c 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -66,7 +66,9 @@ NuPlayer::RTSPSource::RTSPSource( } NuPlayer::RTSPSource::~RTSPSource() { - mLooper->stop(); + if (mLooper != NULL) { + mLooper->stop(); + } } void NuPlayer::RTSPSource::prepareAsync() { -- cgit v1.1 From 94b66227ff5a57dd810aafa7b3aa810b9a185b8d Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 15 Apr 2013 16:28:49 -0700 Subject: Fix Vorbis decoder reset b/8543366 Change-Id: I9f32e96fdfc355cf444259a7c40554e2de184728 --- media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index 922ac61..4115324 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -411,8 +411,19 @@ void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) { } void SoftVorbis::onReset() { + mInputBufferCount = 0; mNumFramesOutput = 0; - vorbis_dsp_restart(mState); + if (mState != NULL) { + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + } + + if (mVi != NULL) { + vorbis_info_clear(mVi); + delete mVi; + mVi = NULL; + } } void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { -- cgit v1.1 From 0e03e72e7bb2633f5a16c2fe7c164bc3ec8ca855 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Mon, 15 Apr 2013 18:40:51 -0700 Subject: Better default AAC DRC setting By default, use boost and attenuation together. bug 7140036 Change-Id: Ie0565f9e5cd24f7a59b5a218b4e6186661832601 --- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'media') diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index 8ba2afb..5eeda5c 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -29,6 +29,7 @@ #define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */ #define MAX_CHANNEL_COUNT 6 /* maximum number of audio channels that can be decoded */ // names of properties that can be used to override the default DRC settings #define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" @@ -146,6 +147,8 @@ status_t SoftAAC2::initDecoder() { unsigned boost = atoi(value); ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost); aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost); + } else { + aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST); } return status; -- cgit v1.1 From 1e0757e0a63d876acc65991ac73284227c522bdc Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Mon, 31 Dec 2012 17:48:20 +0900 Subject: Parse the last none-empty line of .m3u8 file If the last sentence of .m3u8 file is not processed, because it is not empty and has no line feed, M3UParser.isComplete() returns false even though it is completed. Change-Id: I01b9f900d44247a3ef40369a2f9198bb7eaf01b7 related-to-bug: 8405824 --- media/libstagefright/httplive/M3UParser.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 44e03dc..68bbca2 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -163,9 +163,6 @@ status_t M3UParser::parse(const void *_data, size_t size) { while (offsetLF < size && data[offsetLF] != '\n') { ++offsetLF; } - if (offsetLF >= size) { - break; - } AString line; if (offsetLF > offset && data[offsetLF - 1] == '\r') { -- cgit v1.1 From da9740e63a835e610519bd235be9137d74b6d409 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 16 Apr 2013 10:54:03 -0700 Subject: Fix a typo that would cause us not to shutdown/flush the decoders in some cases. related-to-bug: 8630032 Change-Id: I8e94b53b34e137e827e9630c65f3252ea91e4ebd --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 46d0a5a..607ec6a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -1158,7 +1158,7 @@ void NuPlayer::performSeek(int64_t seekTimeUs) { void NuPlayer::performDecoderFlush() { ALOGV("performDecoderFlush"); - if (mAudioDecoder != NULL && mVideoDecoder == NULL) { + if (mAudioDecoder == NULL && mVideoDecoder == NULL) { return; } @@ -1176,7 +1176,7 @@ void NuPlayer::performDecoderFlush() { void NuPlayer::performDecoderShutdown() { ALOGV("performDecoderShutdown"); - if (mAudioDecoder != NULL && mVideoDecoder == NULL) { + if (mAudioDecoder == NULL && mVideoDecoder == NULL) { return; } -- cgit v1.1 From e42f027d19b20cf581be11a89e26b2c96c50c335 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 16 Apr 2013 15:57:38 -0700 Subject: Make sure the drain buffer is big enough b/8614909 Change-Id: I9d973dcd74100b793791359c262b821207ff9ddd --- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index cf81c16..cf50dc9 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -586,9 +586,12 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { } void SoftAAC2::drainDecoder() { - short buf [2048]; - aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); - aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); + // a buffer big enough for 6 channels of decoded HE-AAC + short buf [2048*6]; + aacDecoder_DecodeFrame(mAACDecoder, + buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); + aacDecoder_DecodeFrame(mAACDecoder, + buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); mDecoderHasData = false; } -- cgit v1.1 From 423e33ce6569cb14ecf772e9670208517f7b30c4 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Mon, 8 Apr 2013 15:23:17 -0700 Subject: Added CTS test for secure stop APIs bug: 8604418 Change-Id: I173fa1ec904ba11dc4cff0343462b3f4bac0d365 --- media/libmedia/IDrm.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'media') diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp index 1578846..902aeb2 100644 --- a/media/libmedia/IDrm.cpp +++ b/media/libmedia/IDrm.cpp @@ -590,6 +590,7 @@ status_t BnDrm::onTransact( size_t size = iter->size(); reply->writeInt32(size); reply->write(iter->array(), iter->size()); + iter++; } reply->writeInt32(result); return OK; -- cgit v1.1 From 42392e49e167c6a0c573e55e1c1b4c7fa0ceb213 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 16 Apr 2013 16:35:19 -0700 Subject: Apparently the mp3 decoder glitches if it was configured with the wrong sample rate. Make sure we tell it about the one the extractor provides. Change-Id: Ice5b9cf55485ca1a8f099a14be0202be9540e781 related-to-bug: 8621639 --- media/libstagefright/ACodec.cpp | 13 +++++++++++++ media/libstagefright/OMXCodec.cpp | 11 +++++++++++ media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 15 +++++++++++++++ 3 files changed, 39 insertions(+) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index ee49033..b3bc6d8 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -960,6 +960,19 @@ status_t ACodec::configureCodec( err = setupVideoDecoder(mime, width, height); } } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { + int32_t numChannels, sampleRate; + if (!msg->findInt32("channel-count", &numChannels) + || !msg->findInt32("sample-rate", &sampleRate)) { + // Since we did not always check for these, leave them optional + // and have the decoder figure it all out. + err = OK; + } else { + err = setupRawAudioFormat( + encoder ? kPortIndexInput : kPortIndexOutput, + sampleRate, + numChannels); + } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { int32_t numChannels, sampleRate; if (!msg->findInt32("channel-count", &numChannels) diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 6c0779d..9d349a1 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -522,6 +522,17 @@ status_t OMXCodec::configureCodec(const sp &meta) { CODEC_LOGE("setAACFormat() failed (err = %d)", err); return err; } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG, mMIME)) { + int32_t numChannels, sampleRate; + if (meta->findInt32(kKeyChannelCount, &numChannels) + && meta->findInt32(kKeySampleRate, &sampleRate)) { + // Since we did not always check for these, leave them optional + // and have the decoder figure it all out. + setRawAudioFormat( + mIsEncoder ? kPortIndexInput : kPortIndexOutput, + sampleRate, + numChannels); + } } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME) || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) { // These are PCM-like formats with a fixed sample rate but diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index 849be87..9f25536 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -166,6 +166,21 @@ OMX_ERRORTYPE SoftMP3::internalSetParameter( return OMX_ErrorNone; } + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (const OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + mSamplingRate = pcmParams->nSamplingRate; + + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalSetParameter(index, params); } -- cgit v1.1 From 48186b6ec99aa71ec48338a55f2a2d8291681fe4 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 17 Apr 2013 11:49:11 -0700 Subject: Fix sample info parsing b/8626561 Change-Id: Ibd5168282eb33d1abdc423e15a0d9aeb5a1ad687 --- media/libstagefright/MPEG4Extractor.cpp | 141 +++++++++++++++++++------------- 1 file changed, 83 insertions(+), 58 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 3503aaf..145869e 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -83,7 +83,7 @@ private: uint8_t mCryptoKey[16]; // passed in from extractor uint32_t mCurrentAuxInfoType; uint32_t mCurrentAuxInfoTypeParameter; - uint32_t mCurrentDefaultSampleInfoSize; + int32_t mCurrentDefaultSampleInfoSize; uint32_t mCurrentSampleInfoCount; uint32_t mCurrentSampleInfoAllocSize; uint8_t* mCurrentSampleInfoSizes; @@ -320,6 +320,21 @@ static const char *FourCC2MIME(uint32_t fourcc) { } } +static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t *rate) { + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, FourCC2MIME(fourcc))) { + // AMR NB audio is always mono, 8kHz + *channels = 1; + *rate = 8000; + return true; + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, FourCC2MIME(fourcc))) { + // AMR WB audio is always mono, 16kHz + *channels = 1; + *rate = 16000; + return true; + } + return false; +} + MPEG4Extractor::MPEG4Extractor(const sp &source) : mSidxDuration(0), mMoofOffset(0), @@ -443,6 +458,14 @@ sp MPEG4Extractor::getTrackMetaData( return track->meta; } +static void MakeFourCCString(uint32_t x, char *s) { + s[0] = x >> 24; + s[1] = (x >> 16) & 0xff; + s[2] = (x >> 8) & 0xff; + s[3] = x & 0xff; + s[4] = '\0'; +} + status_t MPEG4Extractor::readMetaData() { if (mInitCheck != NO_INIT) { return mInitCheck; @@ -673,14 +696,6 @@ status_t MPEG4Extractor::parseDrmSINF(off64_t *offset, off64_t data_offset) { return UNKNOWN_ERROR; // Return a dummy error. } -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'; -} - struct PathAdder { PathAdder(Vector *path, uint32_t chunkType) : mPath(path) { @@ -891,13 +906,19 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('f', 'r', 'm', 'a'): { - int32_t original_fourcc; + uint32_t original_fourcc; if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) { return ERROR_IO; } original_fourcc = ntohl(original_fourcc); ALOGV("read original format: %d", original_fourcc); mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc)); + uint32_t num_channels = 0; + uint32_t sample_rate = 0; + if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) { + mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); + mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); + } *offset += chunk_size; break; } @@ -1134,6 +1155,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } case FOURCC('m', 'p', '4', 'a'): + case FOURCC('e', 'n', 'c', 'a'): case FOURCC('s', 'a', 'm', 'r'): case FOURCC('s', 'a', 'w', 'b'): { @@ -1149,29 +1171,18 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } uint16_t data_ref_index = U16_AT(&buffer[6]); - uint16_t num_channels = U16_AT(&buffer[16]); + uint32_t num_channels = U16_AT(&buffer[16]); uint16_t sample_size = U16_AT(&buffer[18]); uint32_t sample_rate = U32_AT(&buffer[24]) >> 16; - if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, - FourCC2MIME(chunk_type))) { - // AMR NB audio is always mono, 8kHz - num_channels = 1; - sample_rate = 8000; - } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, - FourCC2MIME(chunk_type))) { - // AMR WB audio is always mono, 16kHz - num_channels = 1; - sample_rate = 16000; + if (chunk_type != FOURCC('e', 'n', 'c', 'a')) { + // if the chunk type is enca, we'll get the type from the sinf/frma box later + mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); + AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate); } - -#if 0 - printf("*** coding='%s' %d channels, size %d, rate %d\n", + ALOGV("*** coding='%s' %d channels, size %d, rate %d\n", chunk, num_channels, sample_size, sample_rate); -#endif - - mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); @@ -2297,6 +2308,7 @@ MPEG4Source::MPEG4Source( mSrcBuffer(NULL) { mFormat->findInt32(kKeyCryptoMode, &mCryptoMode); + mDefaultIVSize = 0; mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize); uint32_t keytype; const void *key; @@ -2544,13 +2556,17 @@ status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64 if (!mDataSource->getUInt32(offset, &smplcnt)) { return ERROR_MALFORMED; } + mCurrentSampleInfoCount = smplcnt; offset += 4; + if (mCurrentDefaultSampleInfoSize != 0) { + ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize); + return OK; + } if (smplcnt > mCurrentSampleInfoAllocSize) { mCurrentSampleInfoSizes = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt); mCurrentSampleInfoAllocSize = smplcnt; } - mCurrentSampleInfoCount = smplcnt; mDataSource->readAt(offset, mCurrentSampleInfoSizes, smplcnt); return OK; @@ -2608,7 +2624,8 @@ status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off drmoffset += mCurrentMoofOffset; int ivlength; CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength)); - int foo = 1; + + // read CencSampleAuxiliaryDataFormats for (size_t i = 0; i < mCurrentSampleInfoCount; i++) { Sample *smpl = &mCurrentSamples.editItemAt(i); @@ -2619,24 +2636,33 @@ status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off drmoffset += ivlength; - uint16_t numsubsamples; - if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) { - return ERROR_IO; + int32_t smplinfosize = mCurrentDefaultSampleInfoSize; + if (smplinfosize == 0) { + smplinfosize = mCurrentSampleInfoSizes[i]; } - drmoffset += 2; - for (size_t j = 0; j < numsubsamples; j++) { - uint16_t numclear; - uint32_t numencrypted; - if (!mDataSource->getUInt16(drmoffset, &numclear)) { + if (smplinfosize > ivlength) { + uint16_t numsubsamples; + if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) { return ERROR_IO; } drmoffset += 2; - if (!mDataSource->getUInt32(drmoffset, &numencrypted)) { - return ERROR_IO; + for (size_t j = 0; j < numsubsamples; j++) { + uint16_t numclear; + uint32_t numencrypted; + if (!mDataSource->getUInt16(drmoffset, &numclear)) { + return ERROR_IO; + } + drmoffset += 2; + if (!mDataSource->getUInt32(drmoffset, &numencrypted)) { + return ERROR_IO; + } + drmoffset += 4; + smpl->clearsizes.add(numclear); + smpl->encryptedsizes.add(numencrypted); } - drmoffset += 4; - smpl->clearsizes.add(numclear); - smpl->encryptedsizes.add(numencrypted); + } else { + smpl->clearsizes.add(0); + smpl->encryptedsizes.add(smpl->size); } } @@ -3293,6 +3319,21 @@ status_t MPEG4Source::fragmentedRead( } } + const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex]; + const sp bufmeta = mBuffer->meta_data(); + bufmeta->clear(); + if (smpl->encryptedsizes.size()) { + // store clear/encrypted lengths in metadata + bufmeta->setData(kKeyPlainSizes, 0, + smpl->clearsizes.array(), smpl->clearsizes.size() * 4); + bufmeta->setData(kKeyEncryptedSizes, 0, + smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4); + bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size? + bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize); + bufmeta->setInt32(kKeyCryptoMode, mCryptoMode); + bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16); + } + if (!mIsAVC || mWantsNALFragments) { if (newBuffer) { ssize_t num_bytes_read = @@ -3308,7 +3349,6 @@ status_t MPEG4Source::fragmentedRead( CHECK(mBuffer != NULL); mBuffer->set_range(0, size); - mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( kKeyTime, ((int64_t)cts * 1000000) / mTimescale); @@ -3432,7 +3472,6 @@ status_t MPEG4Source::fragmentedRead( mBuffer->set_range(0, dstOffset); } - mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( kKeyTime, ((int64_t)cts * 1000000) / mTimescale); @@ -3445,20 +3484,6 @@ status_t MPEG4Source::fragmentedRead( mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); } - const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex]; - if (smpl->encryptedsizes.size()) { - // store clear/encrypted lengths in metadata - sp bufmeta = mBuffer->meta_data(); - bufmeta->setData(kKeyPlainSizes, 0, - smpl->clearsizes.array(), smpl->clearsizes.size() * 4); - bufmeta->setData(kKeyEncryptedSizes, 0, - smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4); - bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size? - bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize); - bufmeta->setInt32(kKeyCryptoMode, mCryptoMode); - bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16); - } - ++mCurrentSampleIndex; *out = mBuffer; -- cgit v1.1 From 03ddaec84b65157af1dbf022a72de778dc59a63e Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 18 Apr 2013 13:24:17 -0700 Subject: Make sure MediaCodec::stop() and MediaCodec::release() still return instead of blocking indefinitely if the mediaserver died while the call is pending. Change-Id: If2789b7fe99634d947ce4a3bb69c04baff5f8b10 related-to-bug: 8397711 --- media/libstagefright/MediaCodec.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 0d89c0f..e4e95d2 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -534,6 +534,20 @@ void MediaCodec::onMessageReceived(const sp &msg) { // the shutdown complete notification. sendErrorReponse = false; + + if (omxError == OMX_ErrorResourcesLost + && internalError == DEAD_OBJECT) { + // MediaServer died, there definitely won't + // be a shutdown complete notification after + // all. + + // note that we're directly going from + // STOPPING->UNINITIALIZED, instead of the + // usual STOPPING->INITIALIZED state. + setState(UNINITIALIZED); + + (new AMessage)->postReply(mReplyID); + } break; } @@ -1013,8 +1027,16 @@ void MediaCodec::onMessageReceived(const sp &msg) { if (mState != INITIALIZED && mState != CONFIGURED && mState != STARTED) { + // We may be in "UNINITIALIZED" state already without the + // client being aware of this if media server died while + // we were being stopped. The client would assume that + // after stop() returned, it would be safe to call release() + // and it should be in this case, no harm to allow a release() + // if we're already uninitialized. sp response = new AMessage; - response->setInt32("err", INVALID_OPERATION); + response->setInt32( + "err", + mState == UNINITIALIZED ? OK : INVALID_OPERATION); response->postReply(replyID); break; -- cgit v1.1 From 5ffabf0664b80b3dd94d40a3a63db25ecd993fd0 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 18 Apr 2013 16:34:44 -0700 Subject: OMXNodeInstance: actually fix OMX_GetExtIndex logging Bug: 8538872 Change-Id: I228746e8eb502af4bba4054caa4d8569fab35025 --- media/libstagefright/omx/OMXNodeInstance.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 971875f..e7d5e74 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -299,7 +299,9 @@ status_t OMXNodeInstance::enableGraphicBuffers( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - ALOGE("OMX_GetExtensionIndex %s failed", name); + if (enable) { + ALOGE("OMX_GetExtensionIndex %s failed", name); + } return StatusFromOMXError(err); } @@ -378,9 +380,8 @@ status_t OMXNodeInstance::storeMetaDataInBuffers_l( OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err != OMX_ErrorNone) { - if (enable) { - ALOGE("OMX_GetExtensionIndex %s failed", name); - } + ALOGE("OMX_GetExtensionIndex %s failed", name); + return StatusFromOMXError(err); } -- cgit v1.1 From 7e7013392e302a28364df1dcee79b82ad90978b4 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 19 Apr 2013 11:55:18 -0700 Subject: A flush of a video decoder connected to a native window must reclaim output buffers already queued for rendering before considering a flush completed. Otherwise the decoder may not have enough output buffers to continue decoding after the discontinuity and we'll never dequeue more from the native window. Change-Id: I42e275dc336568e180081c6d7c0dc05fc9637c79 related-to-bug: 8578467 --- media/libstagefright/ACodec.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index b3bc6d8..6d952c3 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2135,6 +2135,42 @@ size_t ACodec::countBuffersOwnedByComponent(OMX_U32 portIndex) const { return n; } +size_t ACodec::countBuffersOwnedByNativeWindow() const { + size_t n = 0; + + for (size_t i = 0; i < mBuffers[kPortIndexOutput].size(); ++i) { + const BufferInfo &info = mBuffers[kPortIndexOutput].itemAt(i); + + if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { + ++n; + } + } + + return n; +} + +void ACodec::waitUntilAllPossibleNativeWindowBuffersAreReturnedToUs() { + if (mNativeWindow == NULL) { + return; + } + + int minUndequeuedBufs = 0; + status_t err = mNativeWindow->query( + mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBufs); + + if (err != OK) { + ALOGE("[%s] NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)", + mComponentName.c_str(), strerror(-err), -err); + + minUndequeuedBufs = 0; + } + + while (countBuffersOwnedByNativeWindow() > (size_t)minUndequeuedBufs + && dequeueBufferFromNativeWindow() != NULL) { + } +} + bool ACodec::allYourBuffersAreBelongToUs( OMX_U32 portIndex) { for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) { @@ -4177,6 +4213,10 @@ void ACodec::FlushingState::changeStateIfWeOwnAllBuffers() { if (mFlushComplete[kPortIndexInput] && mFlushComplete[kPortIndexOutput] && mCodec->allYourBuffersAreBelongToUs()) { + // We now own all buffers except possibly those still queued with + // the native window for rendering. Let's get those back as well. + mCodec->waitUntilAllPossibleNativeWindowBuffersAreReturnedToUs(); + sp notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatFlushCompleted); notify->post(); -- cgit v1.1 From aa7f97bb9c70176245ffb7ed0ce52bee6c1a57d7 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 19 Apr 2013 14:33:45 -0700 Subject: 3rd time's the charm, right? Fix another instance where MediaCodec would not return from a stop() or release() call if mediaserver dies at just the right moment. Change-Id: I7728f8df82d62602d4d272f8023aa88678dd7d95 related-to-bug: 8397711 --- media/libstagefright/MediaCodec.cpp | 54 +++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index e4e95d2..ae7bb17 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -506,6 +506,11 @@ void MediaCodec::onMessageReceived(const sp &msg) { "(omx error 0x%08x, internalError %d)", omxError, internalError); + if (omxError == OMX_ErrorResourcesLost + && internalError == DEAD_OBJECT) { + mFlags |= kFlagSawMediaServerDie; + } + bool sendErrorReponse = true; switch (mState) { @@ -535,8 +540,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { sendErrorReponse = false; - if (omxError == OMX_ErrorResourcesLost - && internalError == DEAD_OBJECT) { + if (mFlags & kFlagSawMediaServerDie) { // MediaServer died, there definitely won't // be a shutdown complete notification after // all. @@ -999,29 +1003,11 @@ void MediaCodec::onMessageReceived(const sp &msg) { } case kWhatStop: - { - uint32_t replyID; - CHECK(msg->senderAwaitsResponse(&replyID)); - - if (mState != INITIALIZED - && mState != CONFIGURED && mState != STARTED) { - sp response = new AMessage; - response->setInt32("err", INVALID_OPERATION); - - response->postReply(replyID); - break; - } - - mReplyID = replyID; - setState(STOPPING); - - mCodec->initiateShutdown(true /* keepComponentAllocated */); - returnBuffersToCodec(); - break; - } - case kWhatRelease: { + State targetState = + (msg->what() == kWhatStop) ? INITIALIZED : UNINITIALIZED; + uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); @@ -1033,19 +1019,30 @@ void MediaCodec::onMessageReceived(const sp &msg) { // after stop() returned, it would be safe to call release() // and it should be in this case, no harm to allow a release() // if we're already uninitialized. + // Similarly stopping a stopped MediaCodec should be benign. sp response = new AMessage; response->setInt32( "err", - mState == UNINITIALIZED ? OK : INVALID_OPERATION); + mState == targetState ? OK : INVALID_OPERATION); response->postReply(replyID); break; } + if (mFlags & kFlagSawMediaServerDie) { + // It's dead, Jim. Don't expect initiateShutdown to yield + // any useful results now... + setState(UNINITIALIZED); + (new AMessage)->postReply(replyID); + break; + } + mReplyID = replyID; - setState(RELEASING); + setState(msg->what() == kWhatStop ? STOPPING : RELEASING); + + mCodec->initiateShutdown( + msg->what() == kWhatStop /* keepComponentAllocated */); - mCodec->initiateShutdown(); returnBuffersToCodec(); break; } @@ -1422,6 +1419,11 @@ void MediaCodec::setState(State newState) { if (newState == UNINITIALIZED) { mComponentName.clear(); + + // The component is gone, mediaserver's probably back up already + // but should definitely be back up should we try to instantiate + // another component.. and the cycle continues. + mFlags &= ~kFlagSawMediaServerDie; } mState = newState; -- cgit v1.1 From 3a9682a86ead84d6f60d3f3aa01b2b4d34af983d Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 23 Apr 2013 13:47:46 -0700 Subject: Remove all traces of wifi display sink implementation and supporting code. Change-Id: I64b681b7e3df1ef0dd80c0d261cacae293d5e684 related-to-bug: 8698812 --- media/libstagefright/wifi-display/Android.mk | 76 -- .../libstagefright/wifi-display/MediaReceiver.cpp | 328 ------ media/libstagefright/wifi-display/MediaReceiver.h | 111 -- media/libstagefright/wifi-display/MediaSender.cpp | 16 - media/libstagefright/wifi-display/MediaSender.h | 1 - media/libstagefright/wifi-display/SNTPClient.cpp | 174 --- media/libstagefright/wifi-display/SNTPClient.h | 62 -- media/libstagefright/wifi-display/TimeSyncer.cpp | 338 ------ media/libstagefright/wifi-display/TimeSyncer.h | 109 -- media/libstagefright/wifi-display/nettest.cpp | 400 ------- .../wifi-display/rtp/RTPAssembler.cpp | 328 ------ .../libstagefright/wifi-display/rtp/RTPAssembler.h | 92 -- .../wifi-display/rtp/RTPReceiver.cpp | 1153 -------------------- .../libstagefright/wifi-display/rtp/RTPReceiver.h | 125 --- .../libstagefright/wifi-display/rtp/RTPSender.cpp | 11 - media/libstagefright/wifi-display/rtp/RTPSender.h | 1 - media/libstagefright/wifi-display/rtptest.cpp | 565 ---------- .../wifi-display/sink/DirectRenderer.cpp | 625 ----------- .../wifi-display/sink/DirectRenderer.h | 82 -- .../wifi-display/sink/WifiDisplaySink.cpp | 917 ---------------- .../wifi-display/sink/WifiDisplaySink.h | 196 ---- .../wifi-display/source/PlaybackSession.cpp | 85 -- .../wifi-display/source/WifiDisplaySource.cpp | 14 - .../wifi-display/source/WifiDisplaySource.h | 3 - media/libstagefright/wifi-display/udptest.cpp | 116 -- media/libstagefright/wifi-display/wfd.cpp | 125 +-- 26 files changed, 4 insertions(+), 6049 deletions(-) delete mode 100644 media/libstagefright/wifi-display/MediaReceiver.cpp delete mode 100644 media/libstagefright/wifi-display/MediaReceiver.h delete mode 100644 media/libstagefright/wifi-display/SNTPClient.cpp delete mode 100644 media/libstagefright/wifi-display/SNTPClient.h delete mode 100644 media/libstagefright/wifi-display/TimeSyncer.cpp delete mode 100644 media/libstagefright/wifi-display/TimeSyncer.h delete mode 100644 media/libstagefright/wifi-display/nettest.cpp delete mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.cpp delete mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.h delete mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.cpp delete mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.h delete mode 100644 media/libstagefright/wifi-display/rtptest.cpp delete mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.cpp delete mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.h delete mode 100644 media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp delete mode 100644 media/libstagefright/wifi-display/sink/WifiDisplaySink.h delete mode 100644 media/libstagefright/wifi-display/udptest.cpp (limited to 'media') diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index f99ef60..061ae89 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -4,17 +4,10 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ANetworkSession.cpp \ - MediaReceiver.cpp \ MediaSender.cpp \ Parameters.cpp \ ParsedMessage.cpp \ - rtp/RTPAssembler.cpp \ - rtp/RTPReceiver.cpp \ rtp/RTPSender.cpp \ - sink/DirectRenderer.cpp \ - sink/WifiDisplaySink.cpp \ - SNTPClient.cpp \ - TimeSyncer.cpp \ source/Converter.cpp \ source/MediaPuller.cpp \ source/PlaybackSession.cpp \ @@ -67,72 +60,3 @@ LOCAL_MODULE:= wfd LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - udptest.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= udptest - -LOCAL_MODULE_TAGS := debug - -include $(BUILD_EXECUTABLE) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - rtptest.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= rtptest - -LOCAL_MODULE_TAGS := debug - -include $(BUILD_EXECUTABLE) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - nettest.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= nettest - -LOCAL_MODULE_TAGS := debug - -include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp deleted file mode 100644 index 364acb9..0000000 --- a/media/libstagefright/wifi-display/MediaReceiver.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2013, 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 "MediaReceiver" -#include - -#include "MediaReceiver.h" - -#include "ANetworkSession.h" -#include "AnotherPacketSource.h" -#include "rtp/RTPReceiver.h" - -#include -#include -#include -#include -#include - -namespace android { - -MediaReceiver::MediaReceiver( - const sp &netSession, - const sp ¬ify) - : mNetSession(netSession), - mNotify(notify), - mMode(MODE_UNDEFINED), - mGeneration(0), - mInitStatus(OK), - mInitDoneCount(0) { -} - -MediaReceiver::~MediaReceiver() { -} - -ssize_t MediaReceiver::addTrack( - RTPReceiver::TransportMode rtpMode, - RTPReceiver::TransportMode rtcpMode, - int32_t *localRTPPort) { - if (mMode != MODE_UNDEFINED) { - return INVALID_OPERATION; - } - - size_t trackIndex = mTrackInfos.size(); - - TrackInfo info; - - sp notify = new AMessage(kWhatReceiverNotify, id()); - notify->setInt32("generation", mGeneration); - notify->setSize("trackIndex", trackIndex); - - info.mReceiver = new RTPReceiver(mNetSession, notify); - looper()->registerHandler(info.mReceiver); - - info.mReceiver->registerPacketType( - 33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM); - - info.mReceiver->registerPacketType( - 96, RTPReceiver::PACKETIZATION_AAC); - - info.mReceiver->registerPacketType( - 97, RTPReceiver::PACKETIZATION_H264); - - status_t err = info.mReceiver->initAsync( - rtpMode, - rtcpMode, - localRTPPort); - - if (err != OK) { - looper()->unregisterHandler(info.mReceiver->id()); - info.mReceiver.clear(); - - return err; - } - - mTrackInfos.push_back(info); - - return trackIndex; -} - -status_t MediaReceiver::connectTrack( - size_t trackIndex, - const char *remoteHost, - int32_t remoteRTPPort, - int32_t remoteRTCPPort) { - if (trackIndex >= mTrackInfos.size()) { - return -ERANGE; - } - - TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); - return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort); -} - -status_t MediaReceiver::initAsync(Mode mode) { - if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW) - && mTrackInfos.size() > 1) { - return INVALID_OPERATION; - } - - sp msg = new AMessage(kWhatInit, id()); - msg->setInt32("mode", mode); - msg->post(); - - return OK; -} - -void MediaReceiver::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatInit: - { - int32_t mode; - CHECK(msg->findInt32("mode", &mode)); - - CHECK_EQ(mMode, MODE_UNDEFINED); - mMode = (Mode)mode; - - if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { - notifyInitDone(mInitStatus); - } - - mTSParser = new ATSParser( - ATSParser::ALIGNED_VIDEO_DATA - | ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE); - - mFormatKnownMask = 0; - break; - } - - case kWhatReceiverNotify: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - if (generation != mGeneration) { - break; - } - - onReceiverNotify(msg); - break; - } - - default: - TRESPASS(); - } -} - -void MediaReceiver::onReceiverNotify(const sp &msg) { - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case RTPReceiver::kWhatInitDone: - { - ++mInitDoneCount; - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - if (err != OK) { - mInitStatus = err; - ++mGeneration; - } - - if (mMode != MODE_UNDEFINED) { - if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { - notifyInitDone(mInitStatus); - } - } - break; - } - - case RTPReceiver::kWhatError: - { - int32_t err; - CHECK(msg->findInt32("err", &err)); - - notifyError(err); - break; - } - - case RTPReceiver::kWhatAccessUnit: - { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - int32_t followsDiscontinuity; - if (!msg->findInt32( - "followsDiscontinuity", &followsDiscontinuity)) { - followsDiscontinuity = 0; - } - - if (mMode == MODE_TRANSPORT_STREAM) { - if (followsDiscontinuity) { - mTSParser->signalDiscontinuity( - ATSParser::DISCONTINUITY_TIME, NULL /* extra */); - } - - for (size_t offset = 0; - offset < accessUnit->size(); offset += 188) { - status_t err = mTSParser->feedTSPacket( - accessUnit->data() + offset, 188); - - if (err != OK) { - notifyError(err); - break; - } - } - - drainPackets(0 /* trackIndex */, ATSParser::VIDEO); - drainPackets(1 /* trackIndex */, ATSParser::AUDIO); - } else { - postAccessUnit(trackIndex, accessUnit, NULL); - } - break; - } - - case RTPReceiver::kWhatPacketLost: - { - notifyPacketLost(); - break; - } - - default: - TRESPASS(); - } -} - -void MediaReceiver::drainPackets( - size_t trackIndex, ATSParser::SourceType type) { - sp source = - static_cast( - mTSParser->getSource(type).get()); - - if (source == NULL) { - return; - } - - sp format; - if (!(mFormatKnownMask & (1ul << trackIndex))) { - sp meta = source->getFormat(); - CHECK(meta != NULL); - - CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format)); - - mFormatKnownMask |= 1ul << trackIndex; - } - - status_t finalResult; - while (source->hasBufferAvailable(&finalResult)) { - sp accessUnit; - status_t err = source->dequeueAccessUnit(&accessUnit); - if (err == OK) { - postAccessUnit(trackIndex, accessUnit, format); - format.clear(); - } else if (err != INFO_DISCONTINUITY) { - notifyError(err); - } - } - - if (finalResult != OK) { - notifyError(finalResult); - } -} - -void MediaReceiver::notifyInitDone(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatInitDone); - notify->setInt32("err", err); - notify->post(); -} - -void MediaReceiver::notifyError(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatError); - notify->setInt32("err", err); - notify->post(); -} - -void MediaReceiver::notifyPacketLost() { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatPacketLost); - notify->post(); -} - -void MediaReceiver::postAccessUnit( - size_t trackIndex, - const sp &accessUnit, - const sp &format) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatAccessUnit); - notify->setSize("trackIndex", trackIndex); - notify->setBuffer("accessUnit", accessUnit); - - if (format != NULL) { - notify->setMessage("format", format); - } - - notify->post(); -} - -status_t MediaReceiver::informSender( - size_t trackIndex, const sp ¶ms) { - if (trackIndex >= mTrackInfos.size()) { - return -ERANGE; - } - - TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); - return info->mReceiver->informSender(params); -} - -} // namespace android - - diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h deleted file mode 100644 index afbb407..0000000 --- a/media/libstagefright/wifi-display/MediaReceiver.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "ATSParser.h" -#include "rtp/RTPReceiver.h" - -namespace android { - -struct ABuffer; -struct ANetworkSession; -struct AMessage; -struct ATSParser; - -// This class facilitates receiving of media data for one or more tracks -// over RTP. Either a 1:1 track to RTP channel mapping is used or a single -// RTP channel provides the data for a transport stream that is consequently -// demuxed and its track's data provided to the observer. -struct MediaReceiver : public AHandler { - enum { - kWhatInitDone, - kWhatError, - kWhatAccessUnit, - kWhatPacketLost, - }; - - MediaReceiver( - const sp &netSession, - const sp ¬ify); - - ssize_t addTrack( - RTPReceiver::TransportMode rtpMode, - RTPReceiver::TransportMode rtcpMode, - int32_t *localRTPPort); - - status_t connectTrack( - size_t trackIndex, - const char *remoteHost, - int32_t remoteRTPPort, - int32_t remoteRTCPPort); - - enum Mode { - MODE_UNDEFINED, - MODE_TRANSPORT_STREAM, - MODE_TRANSPORT_STREAM_RAW, - MODE_ELEMENTARY_STREAMS, - }; - status_t initAsync(Mode mode); - - status_t informSender(size_t trackIndex, const sp ¶ms); - -protected: - virtual void onMessageReceived(const sp &msg); - virtual ~MediaReceiver(); - -private: - enum { - kWhatInit, - kWhatReceiverNotify, - }; - - struct TrackInfo { - sp mReceiver; - }; - - sp mNetSession; - sp mNotify; - - Mode mMode; - int32_t mGeneration; - - Vector mTrackInfos; - - status_t mInitStatus; - size_t mInitDoneCount; - - sp mTSParser; - uint32_t mFormatKnownMask; - - void onReceiverNotify(const sp &msg); - - void drainPackets(size_t trackIndex, ATSParser::SourceType type); - - void notifyInitDone(status_t err); - void notifyError(status_t err); - void notifyPacketLost(); - - void postAccessUnit( - size_t trackIndex, - const sp &accessUnit, - const sp &format); - - DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver); -}; - -} // namespace android - diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 33af66d..8a3566f 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -341,22 +341,6 @@ void MediaSender::onSenderNotify(const sp &msg) { break; } - case kWhatInformSender: - { - int64_t avgLatencyUs; - CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); - - int64_t maxLatencyUs; - CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); - - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatInformSender); - notify->setInt64("avgLatencyUs", avgLatencyUs); - notify->setInt64("maxLatencyUs", maxLatencyUs); - notify->post(); - break; - } - default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h index 04538ea..64722c5 100644 --- a/media/libstagefright/wifi-display/MediaSender.h +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -43,7 +43,6 @@ struct MediaSender : public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, - kWhatInformSender, }; MediaSender( diff --git a/media/libstagefright/wifi-display/SNTPClient.cpp b/media/libstagefright/wifi-display/SNTPClient.cpp deleted file mode 100644 index 5c0af6a..0000000 --- a/media/libstagefright/wifi-display/SNTPClient.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2013, 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 "SNTPClient.h" - -#include -#include - -#include -#include -#include -#include -#include - -namespace android { - -SNTPClient::SNTPClient() { -} - -status_t SNTPClient::requestTime(const char *host) { - struct hostent *ent; - int64_t requestTimeNTP, requestTimeUs; - ssize_t n; - int64_t responseTimeUs, responseTimeNTP; - int64_t originateTimeNTP, receiveTimeNTP, transmitTimeNTP; - int64_t roundTripTimeNTP, clockOffsetNTP; - - status_t err = UNKNOWN_ERROR; - - int s = socket(AF_INET, SOCK_DGRAM, 0); - - if (s < 0) { - err = -errno; - - goto bail; - } - - ent = gethostbyname(host); - - if (ent == NULL) { - err = -ENOENT; - goto bail2; - } - - struct sockaddr_in hostAddr; - memset(hostAddr.sin_zero, 0, sizeof(hostAddr.sin_zero)); - hostAddr.sin_family = AF_INET; - hostAddr.sin_port = htons(kNTPPort); - hostAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - - uint8_t packet[kNTPPacketSize]; - memset(packet, 0, sizeof(packet)); - - packet[0] = kNTPModeClient | (kNTPVersion << 3); - - requestTimeNTP = getNowNTP(); - requestTimeUs = ALooper::GetNowUs(); - writeTimeStamp(&packet[kNTPTransmitTimeOffset], requestTimeNTP); - - n = sendto( - s, packet, sizeof(packet), 0, - (const struct sockaddr *)&hostAddr, sizeof(hostAddr)); - - if (n < 0) { - err = -errno; - goto bail2; - } - - memset(packet, 0, sizeof(packet)); - - do { - n = recv(s, packet, sizeof(packet), 0); - } while (n < 0 && errno == EINTR); - - if (n < 0) { - err = -errno; - goto bail2; - } - - responseTimeUs = ALooper::GetNowUs(); - - responseTimeNTP = requestTimeNTP + makeNTP(responseTimeUs - requestTimeUs); - - originateTimeNTP = readTimeStamp(&packet[kNTPOriginateTimeOffset]); - receiveTimeNTP = readTimeStamp(&packet[kNTPReceiveTimeOffset]); - transmitTimeNTP = readTimeStamp(&packet[kNTPTransmitTimeOffset]); - - roundTripTimeNTP = - makeNTP(responseTimeUs - requestTimeUs) - - (transmitTimeNTP - receiveTimeNTP); - - clockOffsetNTP = - ((receiveTimeNTP - originateTimeNTP) - + (transmitTimeNTP - responseTimeNTP)) / 2; - - mTimeReferenceNTP = responseTimeNTP + clockOffsetNTP; - mTimeReferenceUs = responseTimeUs; - mRoundTripTimeNTP = roundTripTimeNTP; - - err = OK; - -bail2: - close(s); - s = -1; - -bail: - return err; -} - -int64_t SNTPClient::adjustTimeUs(int64_t timeUs) const { - uint64_t nowNTP = - mTimeReferenceNTP + makeNTP(timeUs - mTimeReferenceUs); - - int64_t nowUs = - (nowNTP >> 32) * 1000000ll - + ((nowNTP & 0xffffffff) * 1000000ll) / (1ll << 32); - - nowUs -= ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; - - return nowUs; -} - -// static -void SNTPClient::writeTimeStamp(uint8_t *dst, uint64_t ntpTime) { - *dst++ = (ntpTime >> 56) & 0xff; - *dst++ = (ntpTime >> 48) & 0xff; - *dst++ = (ntpTime >> 40) & 0xff; - *dst++ = (ntpTime >> 32) & 0xff; - *dst++ = (ntpTime >> 24) & 0xff; - *dst++ = (ntpTime >> 16) & 0xff; - *dst++ = (ntpTime >> 8) & 0xff; - *dst++ = ntpTime & 0xff; -} - -// static -uint64_t SNTPClient::readTimeStamp(const uint8_t *dst) { - return U64_AT(dst); -} - -// static -uint64_t SNTPClient::getNowNTP() { - struct timeval tv; - gettimeofday(&tv, NULL /* time zone */); - - uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec; - - nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; - - return makeNTP(nowUs); -} - -// static -uint64_t SNTPClient::makeNTP(uint64_t deltaUs) { - uint64_t hi = deltaUs / 1000000ll; - uint64_t lo = ((1ll << 32) * (deltaUs % 1000000ll)) / 1000000ll; - - return (hi << 32) | lo; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/SNTPClient.h b/media/libstagefright/wifi-display/SNTPClient.h deleted file mode 100644 index 967d1fc..0000000 --- a/media/libstagefright/wifi-display/SNTPClient.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2013, 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 SNTP_CLIENT_H_ - -#define SNTP_CLIENT_H_ - -#include -#include - -namespace android { - -// Implementation of the SNTP (Simple Network Time Protocol) -struct SNTPClient { - SNTPClient(); - - status_t requestTime(const char *host); - - // given a time obtained from ALooper::GetNowUs() - // return the number of us elapsed since Jan 1 1970 00:00:00 (UTC). - int64_t adjustTimeUs(int64_t timeUs) const; - -private: - enum { - kNTPPort = 123, - kNTPPacketSize = 48, - kNTPModeClient = 3, - kNTPVersion = 3, - kNTPTransmitTimeOffset = 40, - kNTPOriginateTimeOffset = 24, - kNTPReceiveTimeOffset = 32, - }; - - uint64_t mTimeReferenceNTP; - int64_t mTimeReferenceUs; - int64_t mRoundTripTimeNTP; - - static void writeTimeStamp(uint8_t *dst, uint64_t ntpTime); - static uint64_t readTimeStamp(const uint8_t *dst); - - static uint64_t getNowNTP(); - static uint64_t makeNTP(uint64_t deltaUs); - - DISALLOW_EVIL_CONSTRUCTORS(SNTPClient); -}; - -} // namespace android - -#endif // SNTP_CLIENT_H_ diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp deleted file mode 100644 index cb429bc..0000000 --- a/media/libstagefright/wifi-display/TimeSyncer.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2013, 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_NEBUG 0 -#define LOG_TAG "TimeSyncer" -#include - -#include "TimeSyncer.h" - -#include "ANetworkSession.h" - -#include -#include -#include -#include -#include -#include - -namespace android { - -TimeSyncer::TimeSyncer( - const sp &netSession, const sp ¬ify) - : mNetSession(netSession), - mNotify(notify), - mIsServer(false), - mConnected(false), - mUDPSession(0), - mSeqNo(0), - mTotalTimeUs(0.0), - mPendingT1(0ll), - mTimeoutGeneration(0) { -} - -TimeSyncer::~TimeSyncer() { -} - -void TimeSyncer::startServer(unsigned localPort) { - sp msg = new AMessage(kWhatStartServer, id()); - msg->setInt32("localPort", localPort); - msg->post(); -} - -void TimeSyncer::startClient(const char *remoteHost, unsigned remotePort) { - sp msg = new AMessage(kWhatStartClient, id()); - msg->setString("remoteHost", remoteHost); - msg->setInt32("remotePort", remotePort); - msg->post(); -} - -void TimeSyncer::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatStartClient: - { - AString remoteHost; - CHECK(msg->findString("remoteHost", &remoteHost)); - - int32_t remotePort; - CHECK(msg->findInt32("remotePort", &remotePort)); - - sp notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - 0 /* localPort */, - remoteHost.c_str(), - remotePort, - notify, - &mUDPSession)); - - postSendPacket(); - break; - } - - case kWhatStartServer: - { - mIsServer = true; - - int32_t localPort; - CHECK(msg->findInt32("localPort", &localPort)); - - sp notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - localPort, notify, &mUDPSession)); - - break; - } - - case kWhatSendPacket: - { - if (mHistory.size() == 0) { - ALOGI("starting batch"); - } - - TimeInfo ti; - memset(&ti, 0, sizeof(ti)); - - ti.mT1 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, &ti, sizeof(ti))); - - mPendingT1 = ti.mT1; - postTimeout(); - break; - } - - case kWhatTimedOut: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mTimeoutGeneration) { - break; - } - - ALOGI("timed out, sending another request"); - postSendPacket(); - break; - } - - case kWhatUDPNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred in session %d (%d, '%s/%s').", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - cancelTimeout(); - - notifyError(err); - break; - } - - case ANetworkSession::kWhatDatagram: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp packet; - CHECK(msg->findBuffer("data", &packet)); - - int64_t arrivalTimeUs; - CHECK(packet->meta()->findInt64( - "arrivalTimeUs", &arrivalTimeUs)); - - CHECK_EQ(packet->size(), sizeof(TimeInfo)); - - TimeInfo *ti = (TimeInfo *)packet->data(); - - if (mIsServer) { - if (!mConnected) { - AString fromAddr; - CHECK(msg->findString("fromAddr", &fromAddr)); - - int32_t fromPort; - CHECK(msg->findInt32("fromPort", &fromPort)); - - CHECK_EQ((status_t)OK, - mNetSession->connectUDPSession( - mUDPSession, fromAddr.c_str(), fromPort)); - - mConnected = true; - } - - ti->mT2 = arrivalTimeUs; - ti->mT3 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, ti, sizeof(*ti))); - } else { - if (ti->mT1 != mPendingT1) { - break; - } - - cancelTimeout(); - mPendingT1 = 0; - - ti->mT4 = arrivalTimeUs; - - // One way delay for a packet to travel from client - // to server or back (assumed to be the same either way). - int64_t delay = - (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; - - // Offset between the client clock (T1, T4) and the - // server clock (T2, T3) timestamps. - int64_t offset = - (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; - - mHistory.push_back(*ti); - - ALOGV("delay = %lld us,\toffset %lld us", - delay, - offset); - - if (mHistory.size() < kNumPacketsPerBatch) { - postSendPacket(1000000ll / 30); - } else { - notifyOffset(); - - ALOGI("batch done"); - - mHistory.clear(); - postSendPacket(kBatchDelayUs); - } - } - break; - } - - default: - TRESPASS(); - } - - break; - } - - default: - TRESPASS(); - } -} - -void TimeSyncer::postSendPacket(int64_t delayUs) { - (new AMessage(kWhatSendPacket, id()))->post(delayUs); -} - -void TimeSyncer::postTimeout() { - sp msg = new AMessage(kWhatTimedOut, id()); - msg->setInt32("generation", mTimeoutGeneration); - msg->post(kTimeoutDelayUs); -} - -void TimeSyncer::cancelTimeout() { - ++mTimeoutGeneration; -} - -void TimeSyncer::notifyError(status_t err) { - if (mNotify == NULL) { - looper()->stop(); - return; - } - - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatError); - notify->setInt32("err", err); - notify->post(); -} - -// static -int TimeSyncer::CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2) { - int64_t rt1 = ti1->mT4 - ti1->mT1; - int64_t rt2 = ti2->mT4 - ti2->mT1; - - if (rt1 < rt2) { - return -1; - } else if (rt1 > rt2) { - return 1; - } - - return 0; -} - -void TimeSyncer::notifyOffset() { - mHistory.sort(CompareRountripTime); - - int64_t sum = 0ll; - size_t count = 0; - - // Only consider the third of the information associated with the best - // (smallest) roundtrip times. - for (size_t i = 0; i < mHistory.size() / 3; ++i) { - const TimeInfo *ti = &mHistory[i]; - -#if 0 - // One way delay for a packet to travel from client - // to server or back (assumed to be the same either way). - int64_t delay = - (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; -#endif - - // Offset between the client clock (T1, T4) and the - // server clock (T2, T3) timestamps. - int64_t offset = - (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; - - ALOGV("(%d) RT: %lld us, offset: %lld us", - i, ti->mT4 - ti->mT1, offset); - - sum += offset; - ++count; - } - - if (mNotify == NULL) { - ALOGI("avg. offset is %lld", sum / count); - return; - } - - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatTimeOffset); - notify->setInt64("offset", sum / count); - notify->post(); -} - -} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h deleted file mode 100644 index 4e7571f..0000000 --- a/media/libstagefright/wifi-display/TimeSyncer.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2013, 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 TIME_SYNCER_H_ - -#define TIME_SYNCER_H_ - -#include - -namespace android { - -struct ANetworkSession; - -/* - TimeSyncer allows us to synchronize time between a client and a server. - The client sends a UDP packet containing its send-time to the server, - the server sends that packet back to the client amended with information - about when it was received as well as the time the reply was sent back. - Finally the client receives the reply and has now enough information to - compute the clock offset between client and server assuming that packet - exchange is symmetric, i.e. time for a packet client->server and - server->client is roughly equal. - This exchange is repeated a number of times and the average offset computed - over the 30% of packets that had the lowest roundtrip times. - The offset is determined every 10 secs to account for slight differences in - clock frequency. -*/ -struct TimeSyncer : public AHandler { - enum { - kWhatError, - kWhatTimeOffset, - }; - TimeSyncer( - const sp &netSession, - const sp ¬ify); - - void startServer(unsigned localPort); - void startClient(const char *remoteHost, unsigned remotePort); - -protected: - virtual ~TimeSyncer(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatStartServer, - kWhatStartClient, - kWhatUDPNotify, - kWhatSendPacket, - kWhatTimedOut, - }; - - struct TimeInfo { - int64_t mT1; // client timestamp at send - int64_t mT2; // server timestamp at receive - int64_t mT3; // server timestamp at send - int64_t mT4; // client timestamp at receive - }; - - enum { - kNumPacketsPerBatch = 30, - }; - static const int64_t kTimeoutDelayUs = 500000ll; - static const int64_t kBatchDelayUs = 60000000ll; // every minute - - sp mNetSession; - sp mNotify; - - bool mIsServer; - bool mConnected; - int32_t mUDPSession; - uint32_t mSeqNo; - double mTotalTimeUs; - - Vector mHistory; - - int64_t mPendingT1; - int32_t mTimeoutGeneration; - - void postSendPacket(int64_t delayUs = 0ll); - - void postTimeout(); - void cancelTimeout(); - - void notifyError(status_t err); - void notifyOffset(); - - static int CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2); - - DISALLOW_EVIL_CONSTRUCTORS(TimeSyncer); -}; - -} // namespace android - -#endif // TIME_SYNCER_H_ diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp deleted file mode 100644 index 0779bf5..0000000 --- a/media/libstagefright/wifi-display/nettest.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright 2013, 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_NEBUG 0 -#define LOG_TAG "nettest" -#include - -#include "ANetworkSession.h" -#include "TimeSyncer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -struct TestHandler : public AHandler { - TestHandler(const sp &netSession); - - void listen(int32_t port); - void connect(const char *host, int32_t port); - -protected: - virtual ~TestHandler(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kTimeSyncerPort = 8123, - }; - - enum { - kWhatListen, - kWhatConnect, - kWhatTimeSyncerNotify, - kWhatNetNotify, - kWhatSendMore, - kWhatStop, - }; - - sp mNetSession; - sp mTimeSyncer; - - int32_t mServerSessionID; - int32_t mSessionID; - - int64_t mTimeOffsetUs; - bool mTimeOffsetValid; - - int32_t mCounter; - - int64_t mMaxDelayMs; - - void dumpDelay(int32_t counter, int64_t delayMs); - - DISALLOW_EVIL_CONSTRUCTORS(TestHandler); -}; - -TestHandler::TestHandler(const sp &netSession) - : mNetSession(netSession), - mServerSessionID(0), - mSessionID(0), - mTimeOffsetUs(-1ll), - mTimeOffsetValid(false), - mCounter(0), - mMaxDelayMs(-1ll) { -} - -TestHandler::~TestHandler() { -} - -void TestHandler::listen(int32_t port) { - sp msg = new AMessage(kWhatListen, id()); - msg->setInt32("port", port); - msg->post(); -} - -void TestHandler::connect(const char *host, int32_t port) { - sp msg = new AMessage(kWhatConnect, id()); - msg->setString("host", host); - msg->setInt32("port", port); - msg->post(); -} - -void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) { - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - if (delayMs > mMaxDelayMs) { - mMaxDelayMs = delayMs; - } - - ALOGI("[%d] (%4lld ms / %4lld ms) %s", - counter, - delayMs, - mMaxDelayMs, - kPattern + kPatternSize - n); -} - -void TestHandler::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatListen: - { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - notify = new AMessage(kWhatNetNotify, id()); - - int32_t port; - CHECK(msg->findInt32("port", &port)); - - struct in_addr ifaceAddr; - ifaceAddr.s_addr = INADDR_ANY; - - CHECK_EQ((status_t)OK, - mNetSession->createTCPDatagramSession( - ifaceAddr, - port, - notify, - &mServerSessionID)); - break; - } - - case kWhatConnect: - { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - mTimeSyncer->startServer(kTimeSyncerPort); - - AString host; - CHECK(msg->findString("host", &host)); - - int32_t port; - CHECK(msg->findInt32("port", &port)); - - notify = new AMessage(kWhatNetNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createTCPDatagramSession( - 0 /* localPort */, - host.c_str(), - port, - notify, - &mSessionID)); - break; - } - - case kWhatNetNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatConnected: - { - ALOGI("kWhatConnected"); - - (new AMessage(kWhatSendMore, id()))->post(); - break; - } - - case ANetworkSession::kWhatClientConnected: - { - ALOGI("kWhatClientConnected"); - - CHECK_EQ(mSessionID, 0); - CHECK(msg->findInt32("sessionID", &mSessionID)); - - AString clientIP; - CHECK(msg->findString("client-ip", &clientIP)); - - mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort); - break; - } - - case ANetworkSession::kWhatDatagram: - { - sp packet; - CHECK(msg->findBuffer("data", &packet)); - - CHECK_EQ(packet->size(), 12u); - - int32_t counter = U32_AT(packet->data()); - int64_t timeUs = U64_AT(packet->data() + 4); - - if (mTimeOffsetValid) { - timeUs -= mTimeOffsetUs; - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayMs = (nowUs - timeUs) / 1000ll; - - dumpDelay(counter, delayMs); - } else { - ALOGI("received %d", counter); - } - break; - } - - case ANetworkSession::kWhatError: - { - ALOGE("kWhatError"); - break; - } - - default: - TRESPASS(); - } - break; - } - - case kWhatTimeSyncerNotify: - { - CHECK(msg->findInt64("offset", &mTimeOffsetUs)); - mTimeOffsetValid = true; - break; - } - - case kWhatSendMore: - { - uint8_t buffer[4 + 8]; - buffer[0] = mCounter >> 24; - buffer[1] = (mCounter >> 16) & 0xff; - buffer[2] = (mCounter >> 8) & 0xff; - buffer[3] = mCounter & 0xff; - - int64_t nowUs = ALooper::GetNowUs(); - - buffer[4] = nowUs >> 56; - buffer[5] = (nowUs >> 48) & 0xff; - buffer[6] = (nowUs >> 40) & 0xff; - buffer[7] = (nowUs >> 32) & 0xff; - buffer[8] = (nowUs >> 24) & 0xff; - buffer[9] = (nowUs >> 16) & 0xff; - buffer[10] = (nowUs >> 8) & 0xff; - buffer[11] = nowUs & 0xff; - - ++mCounter; - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mSessionID, - buffer, - sizeof(buffer), - true /* timeValid */, - nowUs)); - - msg->post(100000ll); - break; - } - - case kWhatStop: - { - if (mSessionID != 0) { - mNetSession->destroySession(mSessionID); - mSessionID = 0; - } - - if (mServerSessionID != 0) { - mNetSession->destroySession(mServerSessionID); - mServerSessionID = 0; - } - - looper()->stop(); - break; - } - - default: - TRESPASS(); - } -} - -} // namespace android - -static void usage(const char *me) { - fprintf(stderr, - "usage: %s -c host:port\tconnect to remote host\n" - " -l port \tlisten\n", - me); -} - -int main(int argc, char **argv) { - using namespace android; - - // srand(time(NULL)); - - ProcessState::self()->startThreadPool(); - - DataSource::RegisterDefaultSniffers(); - - int32_t connectToPort = -1; - AString connectToHost; - - int32_t listenOnPort = -1; - - int res; - while ((res = getopt(argc, argv, "hc:l:")) >= 0) { - switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - usage(argv[0]); - exit(1); - } - - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 0 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case 'l': - { - char *end; - listenOnPort = strtol(optarg, &end, 10); - - if (*end != '\0' || end == optarg - || listenOnPort < 0 || listenOnPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case '?': - case 'h': - usage(argv[0]); - exit(1); - } - } - - if ((listenOnPort < 0 && connectToPort < 0) - || (listenOnPort >= 0 && connectToPort >= 0)) { - fprintf(stderr, - "You need to select either client or server mode.\n"); - exit(1); - } - - sp netSession = new ANetworkSession; - netSession->start(); - - sp looper = new ALooper; - - sp handler = new TestHandler(netSession); - looper->registerHandler(handler); - - if (listenOnPort) { - handler->listen(listenOnPort); - } - - if (connectToPort >= 0) { - handler->connect(connectToHost.c_str(), connectToPort); - } - - looper->start(true /* runOnCallingThread */); - - return 0; -} diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp deleted file mode 100644 index 7a96081..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2013, 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 "RTPAssembler" -#include - -#include "RTPAssembler.h" - -#include -#include -#include -#include -#include - -namespace android { - -RTPReceiver::Assembler::Assembler(const sp ¬ify) - : mNotify(notify) { -} - -void RTPReceiver::Assembler::postAccessUnit( - const sp &accessUnit, bool followsDiscontinuity) { - sp notify = mNotify->dup(); - notify->setInt32("what", RTPReceiver::kWhatAccessUnit); - notify->setBuffer("accessUnit", accessUnit); - notify->setInt32("followsDiscontinuity", followsDiscontinuity); - notify->post(); -} -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::TSAssembler::TSAssembler(const sp ¬ify) - : Assembler(notify), - mSawDiscontinuity(false) { -} - -void RTPReceiver::TSAssembler::signalDiscontinuity() { - mSawDiscontinuity = true; -} - -status_t RTPReceiver::TSAssembler::processPacket(const sp &packet) { - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - - packet->meta()->setInt64("timeUs", (rtpTime * 100ll) / 9); - - postAccessUnit(packet, mSawDiscontinuity); - - if (mSawDiscontinuity) { - mSawDiscontinuity = false; - } - - return OK; -} - -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::H264Assembler::H264Assembler(const sp ¬ify) - : Assembler(notify), - mState(0), - mIndicator(0), - mNALType(0), - mAccessUnitRTPTime(0) { -} - -void RTPReceiver::H264Assembler::signalDiscontinuity() { - reset(); -} - -status_t RTPReceiver::H264Assembler::processPacket(const sp &packet) { - status_t err = internalProcessPacket(packet); - - if (err != OK) { - reset(); - } - - return err; -} - -status_t RTPReceiver::H264Assembler::internalProcessPacket( - const sp &packet) { - const uint8_t *data = packet->data(); - size_t size = packet->size(); - - switch (mState) { - case 0: - { - if (size < 1 || (data[0] & 0x80)) { - ALOGV("Malformed H264 RTP packet (empty or F-bit set)"); - return ERROR_MALFORMED; - } - - unsigned nalType = data[0] & 0x1f; - if (nalType >= 1 && nalType <= 23) { - addSingleNALUnit(packet); - ALOGV("added single NAL packet"); - } else if (nalType == 28) { - // FU-A - unsigned indicator = data[0]; - CHECK((indicator & 0x1f) == 28); - - if (size < 2) { - ALOGV("Malformed H264 FU-A packet (single byte)"); - return ERROR_MALFORMED; - } - - if (!(data[1] & 0x80)) { - ALOGV("Malformed H264 FU-A packet (no start bit)"); - return ERROR_MALFORMED; - } - - mIndicator = data[0]; - mNALType = data[1] & 0x1f; - uint32_t nri = (data[0] >> 5) & 3; - - clearAccumulator(); - - uint8_t byte = mNALType | (nri << 5); - appendToAccumulator(&byte, 1); - appendToAccumulator(data + 2, size - 2); - - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - mAccumulator->meta()->setInt32("rtp-time", rtpTime); - - if (data[1] & 0x40) { - // Huh? End bit also set on the first buffer. - addSingleNALUnit(mAccumulator); - clearAccumulator(); - - ALOGV("added FU-A"); - break; - } - - mState = 1; - } else if (nalType == 24) { - // STAP-A - - status_t err = addSingleTimeAggregationPacket(packet); - if (err != OK) { - return err; - } - } else { - ALOGV("Malformed H264 packet (unknown type %d)", nalType); - return ERROR_UNSUPPORTED; - } - break; - } - - case 1: - { - if (size < 2 - || data[0] != mIndicator - || (data[1] & 0x1f) != mNALType - || (data[1] & 0x80)) { - ALOGV("Malformed H264 FU-A packet (indicator, " - "type or start bit mismatch)"); - - return ERROR_MALFORMED; - } - - appendToAccumulator(data + 2, size - 2); - - if (data[1] & 0x40) { - addSingleNALUnit(mAccumulator); - - clearAccumulator(); - mState = 0; - - ALOGV("added FU-A"); - } - break; - } - - default: - TRESPASS(); - } - - int32_t marker; - CHECK(packet->meta()->findInt32("M", &marker)); - - if (marker) { - flushAccessUnit(); - } - - return OK; -} - -void RTPReceiver::H264Assembler::reset() { - mNALUnits.clear(); - - clearAccumulator(); - mState = 0; -} - -void RTPReceiver::H264Assembler::clearAccumulator() { - if (mAccumulator != NULL) { - // XXX Too expensive. - mAccumulator.clear(); - } -} - -void RTPReceiver::H264Assembler::appendToAccumulator( - const void *data, size_t size) { - if (mAccumulator == NULL) { - mAccumulator = new ABuffer(size); - memcpy(mAccumulator->data(), data, size); - return; - } - - if (mAccumulator->size() + size > mAccumulator->capacity()) { - sp buf = new ABuffer(mAccumulator->size() + size); - memcpy(buf->data(), mAccumulator->data(), mAccumulator->size()); - buf->setRange(0, mAccumulator->size()); - - int32_t rtpTime; - if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) { - buf->meta()->setInt32("rtp-time", rtpTime); - } - - mAccumulator = buf; - } - - memcpy(mAccumulator->data() + mAccumulator->size(), data, size); - mAccumulator->setRange(0, mAccumulator->size() + size); -} - -void RTPReceiver::H264Assembler::addSingleNALUnit(const sp &packet) { - if (mNALUnits.empty()) { - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - - mAccessUnitRTPTime = rtpTime; - } - - mNALUnits.push_back(packet); -} - -void RTPReceiver::H264Assembler::flushAccessUnit() { - if (mNALUnits.empty()) { - return; - } - - size_t totalSize = 0; - for (List >::iterator it = mNALUnits.begin(); - it != mNALUnits.end(); ++it) { - totalSize += 4 + (*it)->size(); - } - - sp accessUnit = new ABuffer(totalSize); - size_t offset = 0; - for (List >::iterator it = mNALUnits.begin(); - it != mNALUnits.end(); ++it) { - const sp nalUnit = *it; - - memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4); - - memcpy(accessUnit->data() + offset + 4, - nalUnit->data(), - nalUnit->size()); - - offset += 4 + nalUnit->size(); - } - - mNALUnits.clear(); - - accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll); - postAccessUnit(accessUnit, false /* followsDiscontinuity */); -} - -status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket( - const sp &packet) { - const uint8_t *data = packet->data(); - size_t size = packet->size(); - - if (size < 3) { - ALOGV("Malformed H264 STAP-A packet (too small)"); - return ERROR_MALFORMED; - } - - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - - ++data; - --size; - while (size >= 2) { - size_t nalSize = (data[0] << 8) | data[1]; - - if (size < nalSize + 2) { - ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)"); - return ERROR_MALFORMED; - } - - sp unit = new ABuffer(nalSize); - memcpy(unit->data(), &data[2], nalSize); - - unit->meta()->setInt32("rtp-time", rtpTime); - - addSingleNALUnit(unit); - - data += 2 + nalSize; - size -= 2 + nalSize; - } - - if (size != 0) { - ALOGV("Unexpected padding at end of STAP-A packet."); - } - - ALOGV("added STAP-A"); - - return OK; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.h b/media/libstagefright/wifi-display/rtp/RTPAssembler.h deleted file mode 100644 index e456d32..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPAssembler.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2013, 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 RTP_ASSEMBLER_H_ - -#define RTP_ASSEMBLER_H_ - -#include "RTPReceiver.h" - -namespace android { - -// A helper class to reassemble the payload of RTP packets into access -// units depending on the packetization scheme. -struct RTPReceiver::Assembler : public RefBase { - Assembler(const sp ¬ify); - - virtual void signalDiscontinuity() = 0; - virtual status_t processPacket(const sp &packet) = 0; - -protected: - virtual ~Assembler() {} - - void postAccessUnit( - const sp &accessUnit, bool followsDiscontinuity); - -private: - sp mNotify; - - DISALLOW_EVIL_CONSTRUCTORS(Assembler); -}; - -struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler { - TSAssembler(const sp ¬ify); - - virtual void signalDiscontinuity(); - virtual status_t processPacket(const sp &packet); - -private: - bool mSawDiscontinuity; - - DISALLOW_EVIL_CONSTRUCTORS(TSAssembler); -}; - -struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler { - H264Assembler(const sp ¬ify); - - virtual void signalDiscontinuity(); - virtual status_t processPacket(const sp &packet); - -private: - int32_t mState; - - uint8_t mIndicator; - uint8_t mNALType; - - sp mAccumulator; - - List > mNALUnits; - int32_t mAccessUnitRTPTime; - - status_t internalProcessPacket(const sp &packet); - - void addSingleNALUnit(const sp &packet); - status_t addSingleTimeAggregationPacket(const sp &packet); - - void flushAccessUnit(); - - void clearAccumulator(); - void appendToAccumulator(const void *data, size_t size); - - void reset(); - - DISALLOW_EVIL_CONSTRUCTORS(H264Assembler); -}; - -} // namespace android - -#endif // RTP_ASSEMBLER_H_ - diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp deleted file mode 100644 index 8fa1dae..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * Copyright 2013, 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 "RTPReceiver" -#include - -#include "RTPAssembler.h" -#include "RTPReceiver.h" - -#include "ANetworkSession.h" - -#include -#include -#include -#include -#include -#include - -#define TRACK_PACKET_LOSS 0 - -namespace android { - -//////////////////////////////////////////////////////////////////////////////// - -struct RTPReceiver::Source : public AHandler { - Source(RTPReceiver *receiver, uint32_t ssrc); - - void onPacketReceived(uint16_t seq, const sp &buffer); - - void addReportBlock(uint32_t ssrc, const sp &buf); - -protected: - virtual ~Source(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatRetransmit, - kWhatDeclareLost, - }; - - static const uint32_t kMinSequential = 2; - static const uint32_t kMaxDropout = 3000; - static const uint32_t kMaxMisorder = 100; - static const uint32_t kRTPSeqMod = 1u << 16; - static const int64_t kReportIntervalUs = 10000000ll; - - RTPReceiver *mReceiver; - uint32_t mSSRC; - bool mFirst; - uint16_t mMaxSeq; - uint32_t mCycles; - uint32_t mBaseSeq; - uint32_t mReceived; - uint32_t mExpectedPrior; - uint32_t mReceivedPrior; - - int64_t mFirstArrivalTimeUs; - int64_t mFirstRTPTimeUs; - - // Ordered by extended seq number. - List > mPackets; - - enum StatusBits { - STATUS_DECLARED_LOST = 1, - STATUS_REQUESTED_RETRANSMISSION = 2, - STATUS_ARRIVED_LATE = 4, - }; -#if TRACK_PACKET_LOSS - KeyedVector mLostPackets; -#endif - - void modifyPacketStatus(int32_t extSeqNo, uint32_t mask); - - int32_t mAwaitingExtSeqNo; - bool mRequestedRetransmission; - - int32_t mActivePacketType; - sp mActiveAssembler; - - int64_t mNextReportTimeUs; - - int32_t mNumDeclaredLost; - int32_t mNumDeclaredLostPrior; - - int32_t mRetransmitGeneration; - int32_t mDeclareLostGeneration; - bool mDeclareLostTimerPending; - - void queuePacket(const sp &packet); - void dequeueMore(); - - sp getNextPacket(); - void resync(); - - void postRetransmitTimer(int64_t delayUs); - void postDeclareLostTimer(int64_t delayUs); - void cancelTimers(); - - DISALLOW_EVIL_CONSTRUCTORS(Source); -}; - -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc) - : mReceiver(receiver), - mSSRC(ssrc), - mFirst(true), - mMaxSeq(0), - mCycles(0), - mBaseSeq(0), - mReceived(0), - mExpectedPrior(0), - mReceivedPrior(0), - mFirstArrivalTimeUs(-1ll), - mFirstRTPTimeUs(-1ll), - mAwaitingExtSeqNo(-1), - mRequestedRetransmission(false), - mActivePacketType(-1), - mNextReportTimeUs(-1ll), - mNumDeclaredLost(0), - mNumDeclaredLostPrior(0), - mRetransmitGeneration(0), - mDeclareLostGeneration(0), - mDeclareLostTimerPending(false) { -} - -RTPReceiver::Source::~Source() { -} - -void RTPReceiver::Source::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatRetransmit: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mRetransmitGeneration) { - break; - } - - mRequestedRetransmission = true; - mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo); - - modifyPacketStatus( - mAwaitingExtSeqNo, STATUS_REQUESTED_RETRANSMISSION); - break; - } - - case kWhatDeclareLost: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mDeclareLostGeneration) { - break; - } - - cancelTimers(); - - ALOGV("Lost packet extSeqNo %d %s", - mAwaitingExtSeqNo, - mRequestedRetransmission ? "*" : ""); - - mRequestedRetransmission = false; - if (mActiveAssembler != NULL) { - mActiveAssembler->signalDiscontinuity(); - } - - modifyPacketStatus(mAwaitingExtSeqNo, STATUS_DECLARED_LOST); - - // resync(); - ++mAwaitingExtSeqNo; - ++mNumDeclaredLost; - - mReceiver->notifyPacketLost(); - - dequeueMore(); - break; - } - - default: - TRESPASS(); - } -} - -void RTPReceiver::Source::onPacketReceived( - uint16_t seq, const sp &buffer) { - if (mFirst) { - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); - - mFirst = false; - mBaseSeq = seq; - mMaxSeq = seq; - ++mReceived; - return; - } - - uint16_t udelta = seq - mMaxSeq; - - if (udelta < kMaxDropout) { - // In order, with permissible gap. - - if (seq < mMaxSeq) { - // Sequence number wrapped - count another 64K cycle - mCycles += kRTPSeqMod; - } - - mMaxSeq = seq; - - ++mReceived; - } else if (udelta <= kRTPSeqMod - kMaxMisorder) { - // The sequence number made a very large jump - return; - } else { - // Duplicate or reordered packet. - } - - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); -} - -void RTPReceiver::Source::queuePacket(const sp &packet) { - int32_t newExtendedSeqNo = packet->int32Data(); - - if (mFirstArrivalTimeUs < 0ll) { - mFirstArrivalTimeUs = ALooper::GetNowUs(); - - uint32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); - - mFirstRTPTimeUs = (rtpTime * 100ll) / 9ll; - } - - if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) { - // We're no longer interested in these. They're old. - ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo); - - modifyPacketStatus(newExtendedSeqNo, STATUS_ARRIVED_LATE); - return; - } - - if (mPackets.empty()) { - mPackets.push_back(packet); - dequeueMore(); - return; - } - - List >::iterator firstIt = mPackets.begin(); - List >::iterator it = --mPackets.end(); - for (;;) { - int32_t extendedSeqNo = (*it)->int32Data(); - - if (extendedSeqNo == newExtendedSeqNo) { - // Duplicate packet. - return; - } - - if (extendedSeqNo < newExtendedSeqNo) { - // Insert new packet after the one at "it". - mPackets.insert(++it, packet); - break; - } - - if (it == firstIt) { - // Insert new packet before the first existing one. - mPackets.insert(it, packet); - break; - } - - --it; - } - - dequeueMore(); -} - -void RTPReceiver::Source::dequeueMore() { - int64_t nowUs = ALooper::GetNowUs(); - if (mNextReportTimeUs < 0ll || nowUs >= mNextReportTimeUs) { - if (mNextReportTimeUs >= 0ll) { - uint32_t expected = (mMaxSeq | mCycles) - mBaseSeq + 1; - - uint32_t expectedInterval = expected - mExpectedPrior; - mExpectedPrior = expected; - - uint32_t receivedInterval = mReceived - mReceivedPrior; - mReceivedPrior = mReceived; - - int64_t lostInterval = - (int64_t)expectedInterval - (int64_t)receivedInterval; - - int32_t declaredLostInterval = - mNumDeclaredLost - mNumDeclaredLostPrior; - - mNumDeclaredLostPrior = mNumDeclaredLost; - - if (declaredLostInterval > 0) { - ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", - lostInterval, - 100.0f * lostInterval / expectedInterval, - declaredLostInterval); - } - } - - mNextReportTimeUs = nowUs + kReportIntervalUs; - -#if TRACK_PACKET_LOSS - for (size_t i = 0; i < mLostPackets.size(); ++i) { - int32_t key = mLostPackets.keyAt(i); - uint32_t value = mLostPackets.valueAt(i); - - AString status; - if (value & STATUS_REQUESTED_RETRANSMISSION) { - status.append("retrans "); - } - if (value & STATUS_ARRIVED_LATE) { - status.append("arrived-late "); - } - ALOGI("Packet %d declared lost %s", key, status.c_str()); - } -#endif - } - - sp packet; - while ((packet = getNextPacket()) != NULL) { - if (mDeclareLostTimerPending) { - cancelTimers(); - } - - CHECK_GE(mAwaitingExtSeqNo, 0); -#if TRACK_PACKET_LOSS - mLostPackets.removeItem(mAwaitingExtSeqNo); -#endif - - int32_t packetType; - CHECK(packet->meta()->findInt32("PT", &packetType)); - - if (packetType != mActivePacketType) { - mActiveAssembler = mReceiver->makeAssembler(packetType); - mActivePacketType = packetType; - } - - if (mActiveAssembler != NULL) { - status_t err = mActiveAssembler->processPacket(packet); - if (err != OK) { - ALOGV("assembler returned error %d", err); - } - } - - ++mAwaitingExtSeqNo; - } - - if (mDeclareLostTimerPending) { - return; - } - - if (mPackets.empty()) { - return; - } - - CHECK_GE(mAwaitingExtSeqNo, 0); - - const sp &firstPacket = *mPackets.begin(); - - uint32_t rtpTime; - CHECK(firstPacket->meta()->findInt32( - "rtp-time", (int32_t *)&rtpTime)); - - - int64_t rtpUs = (rtpTime * 100ll) / 9ll; - - int64_t maxArrivalTimeUs = - mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs; - - nowUs = ALooper::GetNowUs(); - - CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data()); - - ALOGV("waiting for %d, comparing against %d, %lld us left", - mAwaitingExtSeqNo, - firstPacket->int32Data(), - maxArrivalTimeUs - nowUs); - - postDeclareLostTimer(maxArrivalTimeUs + kPacketLostAfterUs); - - if (kRequestRetransmissionAfterUs > 0ll) { - postRetransmitTimer( - maxArrivalTimeUs + kRequestRetransmissionAfterUs); - } -} - -sp RTPReceiver::Source::getNextPacket() { - if (mPackets.empty()) { - return NULL; - } - - int32_t extSeqNo = (*mPackets.begin())->int32Data(); - - if (mAwaitingExtSeqNo < 0) { - mAwaitingExtSeqNo = extSeqNo; - } else if (extSeqNo != mAwaitingExtSeqNo) { - return NULL; - } - - sp packet = *mPackets.begin(); - mPackets.erase(mPackets.begin()); - - return packet; -} - -void RTPReceiver::Source::resync() { - mAwaitingExtSeqNo = -1; -} - -void RTPReceiver::Source::addReportBlock( - uint32_t ssrc, const sp &buf) { - uint32_t extMaxSeq = mMaxSeq | mCycles; - uint32_t expected = extMaxSeq - mBaseSeq + 1; - - int64_t lost = (int64_t)expected - (int64_t)mReceived; - if (lost > 0x7fffff) { - lost = 0x7fffff; - } else if (lost < -0x800000) { - lost = -0x800000; - } - - uint32_t expectedInterval = expected - mExpectedPrior; - mExpectedPrior = expected; - - uint32_t receivedInterval = mReceived - mReceivedPrior; - mReceivedPrior = mReceived; - - int64_t lostInterval = expectedInterval - receivedInterval; - - uint8_t fractionLost; - if (expectedInterval == 0 || lostInterval <=0) { - fractionLost = 0; - } else { - fractionLost = (lostInterval << 8) / expectedInterval; - } - - uint8_t *ptr = buf->data() + buf->size(); - - ptr[0] = ssrc >> 24; - ptr[1] = (ssrc >> 16) & 0xff; - ptr[2] = (ssrc >> 8) & 0xff; - ptr[3] = ssrc & 0xff; - - ptr[4] = fractionLost; - - ptr[5] = (lost >> 16) & 0xff; - ptr[6] = (lost >> 8) & 0xff; - ptr[7] = lost & 0xff; - - ptr[8] = extMaxSeq >> 24; - ptr[9] = (extMaxSeq >> 16) & 0xff; - ptr[10] = (extMaxSeq >> 8) & 0xff; - ptr[11] = extMaxSeq & 0xff; - - // XXX TODO: - - ptr[12] = 0x00; // interarrival jitter - ptr[13] = 0x00; - ptr[14] = 0x00; - ptr[15] = 0x00; - - ptr[16] = 0x00; // last SR - ptr[17] = 0x00; - ptr[18] = 0x00; - ptr[19] = 0x00; - - ptr[20] = 0x00; // delay since last SR - ptr[21] = 0x00; - ptr[22] = 0x00; - ptr[23] = 0x00; -} - -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::RTPReceiver( - const sp &netSession, - const sp ¬ify, - uint32_t flags) - : mNetSession(netSession), - mNotify(notify), - mFlags(flags), - mRTPMode(TRANSPORT_UNDEFINED), - mRTCPMode(TRANSPORT_UNDEFINED), - mRTPSessionID(0), - mRTCPSessionID(0), - mRTPConnected(false), - mRTCPConnected(false), - mRTPClientSessionID(0), - mRTCPClientSessionID(0) { -} - -RTPReceiver::~RTPReceiver() { - if (mRTCPClientSessionID != 0) { - mNetSession->destroySession(mRTCPClientSessionID); - mRTCPClientSessionID = 0; - } - - if (mRTPClientSessionID != 0) { - mNetSession->destroySession(mRTPClientSessionID); - mRTPClientSessionID = 0; - } - - if (mRTCPSessionID != 0) { - mNetSession->destroySession(mRTCPSessionID); - mRTCPSessionID = 0; - } - - if (mRTPSessionID != 0) { - mNetSession->destroySession(mRTPSessionID); - mRTPSessionID = 0; - } -} - -status_t RTPReceiver::initAsync( - TransportMode rtpMode, - TransportMode rtcpMode, - int32_t *outLocalRTPPort) { - if (mRTPMode != TRANSPORT_UNDEFINED - || rtpMode == TRANSPORT_UNDEFINED - || rtpMode == TRANSPORT_NONE - || rtcpMode == TRANSPORT_UNDEFINED) { - return INVALID_OPERATION; - } - - CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED); - CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED); - - sp rtpNotify = new AMessage(kWhatRTPNotify, id()); - - sp rtcpNotify; - if (rtcpMode != TRANSPORT_NONE) { - rtcpNotify = new AMessage(kWhatRTCPNotify, id()); - } - - CHECK_EQ(mRTPSessionID, 0); - CHECK_EQ(mRTCPSessionID, 0); - - int32_t localRTPPort; - - struct in_addr ifaceAddr; - ifaceAddr.s_addr = INADDR_ANY; - - for (;;) { - localRTPPort = PickRandomRTPPort(); - - status_t err; - if (rtpMode == TRANSPORT_UDP) { - err = mNetSession->createUDPSession( - localRTPPort, - rtpNotify, - &mRTPSessionID); - } else { - CHECK_EQ(rtpMode, TRANSPORT_TCP); - err = mNetSession->createTCPDatagramSession( - ifaceAddr, - localRTPPort, - rtpNotify, - &mRTPSessionID); - } - - if (err != OK) { - continue; - } - - if (rtcpMode == TRANSPORT_NONE) { - break; - } else if (rtcpMode == TRANSPORT_UDP) { - err = mNetSession->createUDPSession( - localRTPPort + 1, - rtcpNotify, - &mRTCPSessionID); - } else { - CHECK_EQ(rtpMode, TRANSPORT_TCP); - err = mNetSession->createTCPDatagramSession( - ifaceAddr, - localRTPPort + 1, - rtcpNotify, - &mRTCPSessionID); - } - - if (err == OK) { - break; - } - - mNetSession->destroySession(mRTPSessionID); - mRTPSessionID = 0; - } - - mRTPMode = rtpMode; - mRTCPMode = rtcpMode; - *outLocalRTPPort = localRTPPort; - - return OK; -} - -status_t RTPReceiver::connect( - const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) { - status_t err; - - if (mRTPMode == TRANSPORT_UDP) { - CHECK(!mRTPConnected); - - err = mNetSession->connectUDPSession( - mRTPSessionID, remoteHost, remoteRTPPort); - - if (err != OK) { - notifyInitDone(err); - return err; - } - - ALOGI("connectUDPSession RTP successful."); - - mRTPConnected = true; - } - - if (mRTCPMode == TRANSPORT_UDP) { - CHECK(!mRTCPConnected); - - err = mNetSession->connectUDPSession( - mRTCPSessionID, remoteHost, remoteRTCPPort); - - if (err != OK) { - notifyInitDone(err); - return err; - } - - scheduleSendRR(); - - ALOGI("connectUDPSession RTCP successful."); - - mRTCPConnected = true; - } - - if (mRTPConnected - && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { - notifyInitDone(OK); - } - - return OK; -} - -status_t RTPReceiver::informSender(const sp ¶ms) { - if (!mRTCPConnected) { - return INVALID_OPERATION; - } - - int64_t avgLatencyUs; - CHECK(params->findInt64("avgLatencyUs", &avgLatencyUs)); - - int64_t maxLatencyUs; - CHECK(params->findInt64("maxLatencyUs", &maxLatencyUs)); - - sp buf = new ABuffer(28); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 0; - ptr[1] = 204; // APP - ptr[2] = 0; - - CHECK((buf->size() % 4) == 0u); - ptr[3] = (buf->size() / 4) - 1; - - ptr[4] = kSourceID >> 24; // SSRC - ptr[5] = (kSourceID >> 16) & 0xff; - ptr[6] = (kSourceID >> 8) & 0xff; - ptr[7] = kSourceID & 0xff; - ptr[8] = 'l'; - ptr[9] = 'a'; - ptr[10] = 't'; - ptr[11] = 'e'; - - ptr[12] = avgLatencyUs >> 56; - ptr[13] = (avgLatencyUs >> 48) & 0xff; - ptr[14] = (avgLatencyUs >> 40) & 0xff; - ptr[15] = (avgLatencyUs >> 32) & 0xff; - ptr[16] = (avgLatencyUs >> 24) & 0xff; - ptr[17] = (avgLatencyUs >> 16) & 0xff; - ptr[18] = (avgLatencyUs >> 8) & 0xff; - ptr[19] = avgLatencyUs & 0xff; - - ptr[20] = maxLatencyUs >> 56; - ptr[21] = (maxLatencyUs >> 48) & 0xff; - ptr[22] = (maxLatencyUs >> 40) & 0xff; - ptr[23] = (maxLatencyUs >> 32) & 0xff; - ptr[24] = (maxLatencyUs >> 24) & 0xff; - ptr[25] = (maxLatencyUs >> 16) & 0xff; - ptr[26] = (maxLatencyUs >> 8) & 0xff; - ptr[27] = maxLatencyUs & 0xff; - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); - - return OK; -} - -void RTPReceiver::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatRTPNotify: - case kWhatRTCPNotify: - onNetNotify(msg->what() == kWhatRTPNotify, msg); - break; - - case kWhatSendRR: - { - onSendRR(); - break; - } - - default: - TRESPASS(); - } -} - -void RTPReceiver::onNetNotify(bool isRTP, const sp &msg) { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - int32_t errorOccuredDuringSend; - CHECK(msg->findInt32("send", &errorOccuredDuringSend)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred during %s in session %d " - "(%d, '%s' (%s)).", - errorOccuredDuringSend ? "send" : "receive", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - if (sessionID == mRTPSessionID) { - mRTPSessionID = 0; - } else if (sessionID == mRTCPSessionID) { - mRTCPSessionID = 0; - } else if (sessionID == mRTPClientSessionID) { - mRTPClientSessionID = 0; - } else if (sessionID == mRTCPClientSessionID) { - mRTCPClientSessionID = 0; - } - - if (!mRTPConnected - || (mRTCPMode != TRANSPORT_NONE && !mRTCPConnected)) { - notifyInitDone(err); - break; - } - - notifyError(err); - break; - } - - case ANetworkSession::kWhatDatagram: - { - sp data; - CHECK(msg->findBuffer("data", &data)); - - if (isRTP) { - if (mFlags & FLAG_AUTO_CONNECT) { - AString fromAddr; - CHECK(msg->findString("fromAddr", &fromAddr)); - - int32_t fromPort; - CHECK(msg->findInt32("fromPort", &fromPort)); - - CHECK_EQ((status_t)OK, - connect( - fromAddr.c_str(), fromPort, fromPort + 1)); - - mFlags &= ~FLAG_AUTO_CONNECT; - } - - onRTPData(data); - } else { - onRTCPData(data); - } - break; - } - - case ANetworkSession::kWhatClientConnected: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - if (isRTP) { - CHECK_EQ(mRTPMode, TRANSPORT_TCP); - - if (mRTPClientSessionID != 0) { - // We only allow a single client connection. - mNetSession->destroySession(sessionID); - sessionID = 0; - break; - } - - mRTPClientSessionID = sessionID; - mRTPConnected = true; - } else { - CHECK_EQ(mRTCPMode, TRANSPORT_TCP); - - if (mRTCPClientSessionID != 0) { - // We only allow a single client connection. - mNetSession->destroySession(sessionID); - sessionID = 0; - break; - } - - mRTCPClientSessionID = sessionID; - mRTCPConnected = true; - } - - if (mRTPConnected - && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { - notifyInitDone(OK); - } - break; - } - } -} - -void RTPReceiver::notifyInitDone(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatInitDone); - notify->setInt32("err", err); - notify->post(); -} - -void RTPReceiver::notifyError(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatError); - notify->setInt32("err", err); - notify->post(); -} - -void RTPReceiver::notifyPacketLost() { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatPacketLost); - notify->post(); -} - -status_t RTPReceiver::onRTPData(const sp &buffer) { - size_t size = buffer->size(); - if (size < 12) { - // Too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - const uint8_t *data = buffer->data(); - - if ((data[0] >> 6) != 2) { - // Unsupported version. - return ERROR_UNSUPPORTED; - } - - if (data[0] & 0x20) { - // Padding present. - - size_t paddingLength = data[size - 1]; - - if (paddingLength + 12 > size) { - // If we removed this much padding we'd end up with something - // that's too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - size -= paddingLength; - } - - int numCSRCs = data[0] & 0x0f; - - size_t payloadOffset = 12 + 4 * numCSRCs; - - if (size < payloadOffset) { - // Not enough data to fit the basic header and all the CSRC entries. - return ERROR_MALFORMED; - } - - if (data[0] & 0x10) { - // Header eXtension present. - - if (size < payloadOffset + 4) { - // Not enough data to fit the basic header, all CSRC entries - // and the first 4 bytes of the extension header. - - return ERROR_MALFORMED; - } - - const uint8_t *extensionData = &data[payloadOffset]; - - size_t extensionLength = - 4 * (extensionData[2] << 8 | extensionData[3]); - - if (size < payloadOffset + 4 + extensionLength) { - return ERROR_MALFORMED; - } - - payloadOffset += 4 + extensionLength; - } - - uint32_t srcId = U32_AT(&data[8]); - uint32_t rtpTime = U32_AT(&data[4]); - uint16_t seqNo = U16_AT(&data[2]); - - sp meta = buffer->meta(); - meta->setInt32("ssrc", srcId); - meta->setInt32("rtp-time", rtpTime); - meta->setInt32("PT", data[1] & 0x7f); - meta->setInt32("M", data[1] >> 7); - - buffer->setRange(payloadOffset, size - payloadOffset); - - ssize_t index = mSources.indexOfKey(srcId); - sp source; - if (index < 0) { - source = new Source(this, srcId); - looper()->registerHandler(source); - - mSources.add(srcId, source); - } else { - source = mSources.valueAt(index); - } - - source->onPacketReceived(seqNo, buffer); - - return OK; -} - -status_t RTPReceiver::onRTCPData(const sp &data) { - ALOGI("onRTCPData"); - return OK; -} - -void RTPReceiver::addSDES(const sp &buffer) { - uint8_t *data = buffer->data() + buffer->size(); - data[0] = 0x80 | 1; - data[1] = 202; // SDES - data[4] = kSourceID >> 24; // SSRC - data[5] = (kSourceID >> 16) & 0xff; - data[6] = (kSourceID >> 8) & 0xff; - data[7] = kSourceID & 0xff; - - size_t offset = 8; - - data[offset++] = 1; // CNAME - - AString cname = "stagefright@somewhere"; - data[offset++] = cname.size(); - - memcpy(&data[offset], cname.c_str(), cname.size()); - offset += cname.size(); - - data[offset++] = 6; // TOOL - - AString tool = "stagefright/1.0"; - data[offset++] = tool.size(); - - memcpy(&data[offset], tool.c_str(), tool.size()); - offset += tool.size(); - - data[offset++] = 0; - - if ((offset % 4) > 0) { - size_t count = 4 - (offset % 4); - switch (count) { - case 3: - data[offset++] = 0; - case 2: - data[offset++] = 0; - case 1: - data[offset++] = 0; - } - } - - size_t numWords = (offset / 4) - 1; - data[2] = numWords >> 8; - data[3] = numWords & 0xff; - - buffer->setRange(buffer->offset(), buffer->size() + offset); -} - -void RTPReceiver::scheduleSendRR() { - (new AMessage(kWhatSendRR, id()))->post(5000000ll); -} - -void RTPReceiver::onSendRR() { -#if 0 - sp buf = new ABuffer(kMaxUDPPacketSize); - buf->setRange(0, 0); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 0; - ptr[1] = 201; // RR - ptr[2] = 0; - ptr[3] = 1; - ptr[4] = kSourceID >> 24; // SSRC - ptr[5] = (kSourceID >> 16) & 0xff; - ptr[6] = (kSourceID >> 8) & 0xff; - ptr[7] = kSourceID & 0xff; - - buf->setRange(0, 8); - - size_t numReportBlocks = 0; - for (size_t i = 0; i < mSources.size(); ++i) { - uint32_t ssrc = mSources.keyAt(i); - sp source = mSources.valueAt(i); - - if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) { - // Cannot fit another report block. - break; - } - - source->addReportBlock(ssrc, buf); - ++numReportBlocks; - } - - ptr[0] |= numReportBlocks; // 5 bit - - size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks; - ptr[2] = sizeInWordsMinus1 >> 8; - ptr[3] = sizeInWordsMinus1 & 0xff; - - buf->setRange(0, (sizeInWordsMinus1 + 1) * 4); - - addSDES(buf); - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); -#endif - - scheduleSendRR(); -} - -status_t RTPReceiver::registerPacketType( - uint8_t packetType, PacketizationMode mode) { - mPacketTypes.add(packetType, mode); - - return OK; -} - -sp RTPReceiver::makeAssembler(uint8_t packetType) { - ssize_t index = mPacketTypes.indexOfKey(packetType); - if (index < 0) { - return NULL; - } - - PacketizationMode mode = mPacketTypes.valueAt(index); - - switch (mode) { - case PACKETIZATION_NONE: - case PACKETIZATION_TRANSPORT_STREAM: - return new TSAssembler(mNotify); - - case PACKETIZATION_H264: - return new H264Assembler(mNotify); - - default: - return NULL; - } -} - -void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) { - int32_t blp = 0; - - sp buf = new ABuffer(16); - buf->setRange(0, 0); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 1; // generic NACK - ptr[1] = 205; // TSFB - ptr[2] = 0; - ptr[3] = 3; - ptr[8] = (senderSSRC >> 24) & 0xff; - ptr[9] = (senderSSRC >> 16) & 0xff; - ptr[10] = (senderSSRC >> 8) & 0xff; - ptr[11] = (senderSSRC & 0xff); - ptr[8] = (kSourceID >> 24) & 0xff; - ptr[9] = (kSourceID >> 16) & 0xff; - ptr[10] = (kSourceID >> 8) & 0xff; - ptr[11] = (kSourceID & 0xff); - ptr[12] = (extSeqNo >> 8) & 0xff; - ptr[13] = (extSeqNo & 0xff); - ptr[14] = (blp >> 8) & 0xff; - ptr[15] = (blp & 0xff); - - buf->setRange(0, 16); - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); -} - -void RTPReceiver::Source::modifyPacketStatus(int32_t extSeqNo, uint32_t mask) { -#if TRACK_PACKET_LOSS - ssize_t index = mLostPackets.indexOfKey(extSeqNo); - if (index < 0) { - mLostPackets.add(extSeqNo, mask); - } else { - mLostPackets.editValueAt(index) |= mask; - } -#endif -} - -void RTPReceiver::Source::postRetransmitTimer(int64_t timeUs) { - int64_t delayUs = timeUs - ALooper::GetNowUs(); - sp msg = new AMessage(kWhatRetransmit, id()); - msg->setInt32("generation", mRetransmitGeneration); - msg->post(delayUs); -} - -void RTPReceiver::Source::postDeclareLostTimer(int64_t timeUs) { - CHECK(!mDeclareLostTimerPending); - mDeclareLostTimerPending = true; - - int64_t delayUs = timeUs - ALooper::GetNowUs(); - sp msg = new AMessage(kWhatDeclareLost, id()); - msg->setInt32("generation", mDeclareLostGeneration); - msg->post(delayUs); -} - -void RTPReceiver::Source::cancelTimers() { - ++mRetransmitGeneration; - ++mDeclareLostGeneration; - mDeclareLostTimerPending = false; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h deleted file mode 100644 index 240ab2e..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2013, 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 RTP_RECEIVER_H_ - -#define RTP_RECEIVER_H_ - -#include "RTPBase.h" - -#include - -namespace android { - -struct ABuffer; -struct ANetworkSession; - -// An object of this class facilitates receiving of media data on an RTP -// channel. The channel is established over a UDP or TCP connection depending -// on which "TransportMode" was chosen. In addition different RTP packetization -// schemes are supported such as "Transport Stream Packets over RTP", -// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)" -struct RTPReceiver : public RTPBase, public AHandler { - enum { - kWhatInitDone, - kWhatError, - kWhatAccessUnit, - kWhatPacketLost, - }; - - enum Flags { - FLAG_AUTO_CONNECT = 1, - }; - RTPReceiver( - const sp &netSession, - const sp ¬ify, - uint32_t flags = 0); - - status_t registerPacketType( - uint8_t packetType, PacketizationMode mode); - - status_t initAsync( - TransportMode rtpMode, - TransportMode rtcpMode, - int32_t *outLocalRTPPort); - - status_t connect( - const char *remoteHost, - int32_t remoteRTPPort, - int32_t remoteRTCPPort); - - status_t informSender(const sp ¶ms); - -protected: - virtual ~RTPReceiver(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatRTPNotify, - kWhatRTCPNotify, - kWhatSendRR, - }; - - enum { - kSourceID = 0xdeadbeef, - kPacketLostAfterUs = 100000, - kRequestRetransmissionAfterUs = -1, - }; - - struct Assembler; - struct H264Assembler; - struct Source; - struct TSAssembler; - - sp mNetSession; - sp mNotify; - uint32_t mFlags; - TransportMode mRTPMode; - TransportMode mRTCPMode; - int32_t mRTPSessionID; - int32_t mRTCPSessionID; - bool mRTPConnected; - bool mRTCPConnected; - - int32_t mRTPClientSessionID; // in TRANSPORT_TCP mode. - int32_t mRTCPClientSessionID; // in TRANSPORT_TCP mode. - - KeyedVector mPacketTypes; - KeyedVector > mSources; - - void onNetNotify(bool isRTP, const sp &msg); - status_t onRTPData(const sp &data); - status_t onRTCPData(const sp &data); - void onSendRR(); - - void scheduleSendRR(); - void addSDES(const sp &buffer); - - void notifyInitDone(status_t err); - void notifyError(status_t err); - void notifyPacketLost(); - - sp makeAssembler(uint8_t packetType); - - void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo); - - DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver); -}; - -} // namespace android - -#endif // RTP_RECEIVER_H_ diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 6bbe650..095fd97 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -767,17 +767,6 @@ status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) { } status_t RTPSender::parseAPP(const uint8_t *data, size_t size) { - if (!memcmp("late", &data[8], 4)) { - int64_t avgLatencyUs = (int64_t)U64_AT(&data[12]); - int64_t maxLatencyUs = (int64_t)U64_AT(&data[20]); - - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatInformSender); - notify->setInt64("avgLatencyUs", avgLatencyUs); - notify->setInt64("maxLatencyUs", maxLatencyUs); - notify->post(); - } - return OK; } diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index fefcab7..7dc138a 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -37,7 +37,6 @@ struct RTPSender : public RTPBase, public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, - kWhatInformSender, }; RTPSender( const sp &netSession, diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp deleted file mode 100644 index 764a38b..0000000 --- a/media/libstagefright/wifi-display/rtptest.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/* - * Copyright 2013, 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_NEBUG 0 -#define LOG_TAG "rtptest" -#include - -#include "ANetworkSession.h" -#include "rtp/RTPSender.h" -#include "rtp/RTPReceiver.h" -#include "TimeSyncer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4" - -namespace android { - -struct PacketSource : public RefBase { - PacketSource() {} - - virtual sp getNextAccessUnit() = 0; - -protected: - virtual ~PacketSource() {} - -private: - DISALLOW_EVIL_CONSTRUCTORS(PacketSource); -}; - -struct MediaPacketSource : public PacketSource { - MediaPacketSource() - : mMaxSampleSize(1024 * 1024) { - mExtractor = new NuMediaExtractor; - CHECK_EQ((status_t)OK, - mExtractor->setDataSource(MEDIA_FILENAME)); - - bool haveVideo = false; - for (size_t i = 0; i < mExtractor->countTracks(); ++i) { - sp format; - CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format)); - - AString mime; - CHECK(format->findString("mime", &mime)); - - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) { - mExtractor->selectTrack(i); - haveVideo = true; - break; - } - } - - CHECK(haveVideo); - } - - virtual sp getNextAccessUnit() { - int64_t timeUs; - status_t err = mExtractor->getSampleTime(&timeUs); - - if (err != OK) { - return NULL; - } - - sp accessUnit = new ABuffer(mMaxSampleSize); - CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit)); - - accessUnit->meta()->setInt64("timeUs", timeUs); - - CHECK_EQ((status_t)OK, mExtractor->advance()); - - return accessUnit; - } - -protected: - virtual ~MediaPacketSource() { - } - -private: - sp mExtractor; - size_t mMaxSampleSize; - - DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource); -}; - -struct SimplePacketSource : public PacketSource { - SimplePacketSource() - : mCounter(0) { - } - - virtual sp getNextAccessUnit() { - sp buffer = new ABuffer(4); - uint8_t *dst = buffer->data(); - dst[0] = mCounter >> 24; - dst[1] = (mCounter >> 16) & 0xff; - dst[2] = (mCounter >> 8) & 0xff; - dst[3] = mCounter & 0xff; - - buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate); - - ++mCounter; - - return buffer; - } - -protected: - virtual ~SimplePacketSource() { - } - -private: - enum { - kFrameRate = 30 - }; - - uint32_t mCounter; - - DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource); -}; - -struct TestHandler : public AHandler { - TestHandler(const sp &netSession); - - void listen(); - void connect(const char *host, int32_t port); - -protected: - virtual ~TestHandler(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatListen, - kWhatConnect, - kWhatReceiverNotify, - kWhatSenderNotify, - kWhatSendMore, - kWhatStop, - kWhatTimeSyncerNotify, - }; - -#if 1 - static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP; - static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP; -#else - static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP; - static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE; -#endif - -#if 1 - static const RTPBase::PacketizationMode kPacketizationMode - = RTPBase::PACKETIZATION_H264; -#else - static const RTPBase::PacketizationMode kPacketizationMode - = RTPBase::PACKETIZATION_NONE; -#endif - - sp mNetSession; - sp mSource; - sp mSender; - sp mReceiver; - - sp mTimeSyncer; - bool mTimeSyncerStarted; - - int64_t mFirstTimeRealUs; - int64_t mFirstTimeMediaUs; - - int64_t mTimeOffsetUs; - bool mTimeOffsetValid; - - status_t readMore(); - - DISALLOW_EVIL_CONSTRUCTORS(TestHandler); -}; - -TestHandler::TestHandler(const sp &netSession) - : mNetSession(netSession), - mTimeSyncerStarted(false), - mFirstTimeRealUs(-1ll), - mFirstTimeMediaUs(-1ll), - mTimeOffsetUs(-1ll), - mTimeOffsetValid(false) { -} - -TestHandler::~TestHandler() { -} - -void TestHandler::listen() { - sp msg = new AMessage(kWhatListen, id()); - msg->post(); -} - -void TestHandler::connect(const char *host, int32_t port) { - sp msg = new AMessage(kWhatConnect, id()); - msg->setString("host", host); - msg->setInt32("port", port); - msg->post(); -} - -static void dumpDelay(int64_t delayMs) { - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - ALOGI("(%4lld ms) %s\n", - delayMs, - kPattern + kPatternSize - n); -} - -void TestHandler::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatListen: - { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - notify = new AMessage(kWhatReceiverNotify, id()); - mReceiver = new RTPReceiver( - mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT); - looper()->registerHandler(mReceiver); - - CHECK_EQ((status_t)OK, - mReceiver->registerPacketType(33, kPacketizationMode)); - - int32_t receiverRTPPort; - CHECK_EQ((status_t)OK, - mReceiver->initAsync( - kRTPMode, - kRTCPMode, - &receiverRTPPort)); - - printf("picked receiverRTPPort %d\n", receiverRTPPort); - -#if 0 - CHECK_EQ((status_t)OK, - mReceiver->connect( - "127.0.0.1", senderRTPPort, senderRTPPort + 1)); -#endif - break; - } - - case kWhatConnect: - { - AString host; - CHECK(msg->findString("host", &host)); - - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - mTimeSyncer->startServer(8123); - - int32_t receiverRTPPort; - CHECK(msg->findInt32("port", &receiverRTPPort)); - -#if 1 - mSource = new MediaPacketSource; -#else - mSource = new SimplePacketSource; -#endif - - notify = new AMessage(kWhatSenderNotify, id()); - mSender = new RTPSender(mNetSession, notify); - - looper()->registerHandler(mSender); - - int32_t senderRTPPort; - CHECK_EQ((status_t)OK, - mSender->initAsync( - host.c_str(), - receiverRTPPort, - kRTPMode, - kRTCPMode == RTPBase::TRANSPORT_NONE - ? -1 : receiverRTPPort + 1, - kRTCPMode, - &senderRTPPort)); - - printf("picked senderRTPPort %d\n", senderRTPPort); - break; - } - - case kWhatSenderNotify: - { - ALOGI("kWhatSenderNotify"); - - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case RTPSender::kWhatInitDone: - { - int32_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGI("RTPSender::initAsync completed w/ err %d", err); - - if (err == OK) { - err = readMore(); - - if (err != OK) { - (new AMessage(kWhatStop, id()))->post(); - } - } - break; - } - - case RTPSender::kWhatError: - break; - } - break; - } - - case kWhatReceiverNotify: - { - ALOGV("kWhatReceiverNotify"); - - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case RTPReceiver::kWhatInitDone: - { - int32_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGI("RTPReceiver::initAsync completed w/ err %d", err); - break; - } - - case RTPReceiver::kWhatError: - break; - - case RTPReceiver::kWhatAccessUnit: - { -#if 0 - if (!mTimeSyncerStarted) { - mTimeSyncer->startClient("172.18.41.216", 8123); - mTimeSyncerStarted = true; - } - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - if (mTimeOffsetValid) { - timeUs -= mTimeOffsetUs; - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayMs = (nowUs - timeUs) / 1000ll; - - dumpDelay(delayMs); - } -#endif - break; - } - - case RTPReceiver::kWhatPacketLost: - ALOGV("kWhatPacketLost"); - break; - - default: - TRESPASS(); - } - break; - } - - case kWhatSendMore: - { - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - CHECK_EQ((status_t)OK, - mSender->queueBuffer( - accessUnit, - 33, - kPacketizationMode)); - - status_t err = readMore(); - - if (err != OK) { - (new AMessage(kWhatStop, id()))->post(); - } - break; - } - - case kWhatStop: - { - if (mReceiver != NULL) { - looper()->unregisterHandler(mReceiver->id()); - mReceiver.clear(); - } - - if (mSender != NULL) { - looper()->unregisterHandler(mSender->id()); - mSender.clear(); - } - - mSource.clear(); - - looper()->stop(); - break; - } - - case kWhatTimeSyncerNotify: - { - CHECK(msg->findInt64("offset", &mTimeOffsetUs)); - mTimeOffsetValid = true; - break; - } - - default: - TRESPASS(); - } -} - -status_t TestHandler::readMore() { - sp accessUnit = mSource->getNextAccessUnit(); - - if (accessUnit == NULL) { - return ERROR_END_OF_STREAM; - } - - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - int64_t nowUs = ALooper::GetNowUs(); - int64_t whenUs; - - if (mFirstTimeRealUs < 0ll) { - mFirstTimeRealUs = whenUs = nowUs; - mFirstTimeMediaUs = timeUs; - } else { - whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs; - } - - accessUnit->meta()->setInt64("timeUs", whenUs); - - sp msg = new AMessage(kWhatSendMore, id()); - msg->setBuffer("accessUnit", accessUnit); - msg->post(whenUs - nowUs); - - return OK; -} - -} // namespace android - -static void usage(const char *me) { - fprintf(stderr, - "usage: %s -c host:port\tconnect to remote host\n" - " -l \tlisten\n", - me); -} - -int main(int argc, char **argv) { - using namespace android; - - // srand(time(NULL)); - - ProcessState::self()->startThreadPool(); - - DataSource::RegisterDefaultSniffers(); - - bool listen = false; - int32_t connectToPort = -1; - AString connectToHost; - - int res; - while ((res = getopt(argc, argv, "hc:l")) >= 0) { - switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - usage(argv[0]); - exit(1); - } - - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 1 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case 'l': - { - listen = true; - break; - } - - case '?': - case 'h': - usage(argv[0]); - exit(1); - } - } - - if (!listen && connectToPort < 0) { - fprintf(stderr, - "You need to select either client or server mode.\n"); - exit(1); - } - - sp netSession = new ANetworkSession; - netSession->start(); - - sp looper = new ALooper; - - sp handler = new TestHandler(netSession); - looper->registerHandler(handler); - - if (listen) { - handler->listen(); - } - - if (connectToPort >= 0) { - handler->connect(connectToHost.c_str(), connectToPort); - } - - looper->start(true /* runOnCallingThread */); - - return 0; -} - diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp deleted file mode 100644 index 15f9c88..0000000 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ /dev/null @@ -1,625 +0,0 @@ -/* - * Copyright 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 "DirectRenderer" -#include - -#include "DirectRenderer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -/* - Drives the decoding process using a MediaCodec instance. Input buffers - queued by calls to "queueInputBuffer" are fed to the decoder as soon - as the decoder is ready for them, the client is notified about output - buffers as the decoder spits them out. -*/ -struct DirectRenderer::DecoderContext : public AHandler { - enum { - kWhatOutputBufferReady, - }; - DecoderContext(const sp ¬ify); - - status_t init( - const sp &format, - const sp &surfaceTex); - - void queueInputBuffer(const sp &accessUnit); - - status_t renderOutputBufferAndRelease(size_t index); - status_t releaseOutputBuffer(size_t index); - -protected: - virtual ~DecoderContext(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatDecoderNotify, - }; - - sp mNotify; - sp mDecoderLooper; - sp mDecoder; - Vector > mDecoderInputBuffers; - Vector > mDecoderOutputBuffers; - List mDecoderInputBuffersAvailable; - bool mDecoderNotificationPending; - - List > mAccessUnits; - - void onDecoderNotify(); - void scheduleDecoderNotification(); - void queueDecoderInputBuffers(); - - void queueOutputBuffer( - size_t index, int64_t timeUs, const sp &buffer); - - DISALLOW_EVIL_CONSTRUCTORS(DecoderContext); -}; - -//////////////////////////////////////////////////////////////////////////////// - -/* - A "push" audio renderer. The primary function of this renderer is to use - an AudioTrack in push mode and making sure not to block the event loop - be ensuring that calls to AudioTrack::write never block. This is done by - estimating an upper bound of data that can be written to the AudioTrack - buffer without delay. -*/ -struct DirectRenderer::AudioRenderer : public AHandler { - AudioRenderer(const sp &decoderContext); - - void queueInputBuffer( - size_t index, int64_t timeUs, const sp &buffer); - -protected: - virtual ~AudioRenderer(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatPushAudio, - }; - - struct BufferInfo { - size_t mIndex; - int64_t mTimeUs; - sp mBuffer; - }; - - sp mDecoderContext; - sp mAudioTrack; - - List mInputBuffers; - bool mPushPending; - - size_t mNumFramesWritten; - - void schedulePushIfNecessary(); - void onPushAudio(); - - ssize_t writeNonBlocking(const uint8_t *data, size_t size); - - DISALLOW_EVIL_CONSTRUCTORS(AudioRenderer); -}; - -//////////////////////////////////////////////////////////////////////////////// - -DirectRenderer::DecoderContext::DecoderContext(const sp ¬ify) - : mNotify(notify), - mDecoderNotificationPending(false) { -} - -DirectRenderer::DecoderContext::~DecoderContext() { - if (mDecoder != NULL) { - mDecoder->release(); - mDecoder.clear(); - - mDecoderLooper->stop(); - mDecoderLooper.clear(); - } -} - -status_t DirectRenderer::DecoderContext::init( - const sp &format, - const sp &surfaceTex) { - CHECK(mDecoder == NULL); - - AString mime; - CHECK(format->findString("mime", &mime)); - - mDecoderLooper = new ALooper; - mDecoderLooper->setName("video codec looper"); - - mDecoderLooper->start( - false /* runOnCallingThread */, - false /* canCallJava */, - PRIORITY_DEFAULT); - - mDecoder = MediaCodec::CreateByType( - mDecoderLooper, mime.c_str(), false /* encoder */); - - CHECK(mDecoder != NULL); - - status_t err = mDecoder->configure( - format, - surfaceTex == NULL - ? NULL : new Surface(surfaceTex), - NULL /* crypto */, - 0 /* flags */); - CHECK_EQ(err, (status_t)OK); - - err = mDecoder->start(); - CHECK_EQ(err, (status_t)OK); - - err = mDecoder->getInputBuffers( - &mDecoderInputBuffers); - CHECK_EQ(err, (status_t)OK); - - err = mDecoder->getOutputBuffers( - &mDecoderOutputBuffers); - CHECK_EQ(err, (status_t)OK); - - scheduleDecoderNotification(); - - return OK; -} - -void DirectRenderer::DecoderContext::queueInputBuffer( - const sp &accessUnit) { - CHECK(mDecoder != NULL); - - mAccessUnits.push_back(accessUnit); - queueDecoderInputBuffers(); -} - -status_t DirectRenderer::DecoderContext::renderOutputBufferAndRelease( - size_t index) { - return mDecoder->renderOutputBufferAndRelease(index); -} - -status_t DirectRenderer::DecoderContext::releaseOutputBuffer(size_t index) { - return mDecoder->releaseOutputBuffer(index); -} - -void DirectRenderer::DecoderContext::queueDecoderInputBuffers() { - if (mDecoder == NULL) { - return; - } - - bool submittedMore = false; - - while (!mAccessUnits.empty() - && !mDecoderInputBuffersAvailable.empty()) { - size_t index = *mDecoderInputBuffersAvailable.begin(); - - mDecoderInputBuffersAvailable.erase( - mDecoderInputBuffersAvailable.begin()); - - sp srcBuffer = *mAccessUnits.begin(); - mAccessUnits.erase(mAccessUnits.begin()); - - const sp &dstBuffer = - mDecoderInputBuffers.itemAt(index); - - memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size()); - - int64_t timeUs; - CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs)); - - status_t err = mDecoder->queueInputBuffer( - index, - 0 /* offset */, - srcBuffer->size(), - timeUs, - 0 /* flags */); - CHECK_EQ(err, (status_t)OK); - - submittedMore = true; - } - - if (submittedMore) { - scheduleDecoderNotification(); - } -} - -void DirectRenderer::DecoderContext::onMessageReceived( - const sp &msg) { - switch (msg->what()) { - case kWhatDecoderNotify: - { - onDecoderNotify(); - break; - } - - default: - TRESPASS(); - } -} - -void DirectRenderer::DecoderContext::onDecoderNotify() { - mDecoderNotificationPending = false; - - for (;;) { - size_t index; - status_t err = mDecoder->dequeueInputBuffer(&index); - - if (err == OK) { - mDecoderInputBuffersAvailable.push_back(index); - } else if (err == -EAGAIN) { - break; - } else { - TRESPASS(); - } - } - - queueDecoderInputBuffers(); - - for (;;) { - size_t index; - size_t offset; - size_t size; - int64_t timeUs; - uint32_t flags; - status_t err = mDecoder->dequeueOutputBuffer( - &index, - &offset, - &size, - &timeUs, - &flags); - - if (err == OK) { - queueOutputBuffer( - index, timeUs, mDecoderOutputBuffers.itemAt(index)); - } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { - err = mDecoder->getOutputBuffers( - &mDecoderOutputBuffers); - CHECK_EQ(err, (status_t)OK); - } else if (err == INFO_FORMAT_CHANGED) { - // We don't care. - } else if (err == -EAGAIN) { - break; - } else { - TRESPASS(); - } - } - - scheduleDecoderNotification(); -} - -void DirectRenderer::DecoderContext::scheduleDecoderNotification() { - if (mDecoderNotificationPending) { - return; - } - - sp notify = - new AMessage(kWhatDecoderNotify, id()); - - mDecoder->requestActivityNotification(notify); - mDecoderNotificationPending = true; -} - -void DirectRenderer::DecoderContext::queueOutputBuffer( - size_t index, int64_t timeUs, const sp &buffer) { - sp msg = mNotify->dup(); - msg->setInt32("what", kWhatOutputBufferReady); - msg->setSize("index", index); - msg->setInt64("timeUs", timeUs); - msg->setBuffer("buffer", buffer); - msg->post(); -} - -//////////////////////////////////////////////////////////////////////////////// - -DirectRenderer::AudioRenderer::AudioRenderer( - const sp &decoderContext) - : mDecoderContext(decoderContext), - mPushPending(false), - mNumFramesWritten(0) { - mAudioTrack = new AudioTrack( - AUDIO_STREAM_DEFAULT, - 48000.0f, - AUDIO_FORMAT_PCM, - AUDIO_CHANNEL_OUT_STEREO, - (int)0 /* frameCount */); - - CHECK_EQ((status_t)OK, mAudioTrack->initCheck()); - - mAudioTrack->start(); -} - -DirectRenderer::AudioRenderer::~AudioRenderer() { -} - -void DirectRenderer::AudioRenderer::queueInputBuffer( - size_t index, int64_t timeUs, const sp &buffer) { - BufferInfo info; - info.mIndex = index; - info.mTimeUs = timeUs; - info.mBuffer = buffer; - - mInputBuffers.push_back(info); - schedulePushIfNecessary(); -} - -void DirectRenderer::AudioRenderer::onMessageReceived( - const sp &msg) { - switch (msg->what()) { - case kWhatPushAudio: - { - onPushAudio(); - break; - } - - default: - break; - } -} - -void DirectRenderer::AudioRenderer::schedulePushIfNecessary() { - if (mPushPending || mInputBuffers.empty()) { - return; - } - - mPushPending = true; - - uint32_t numFramesPlayed; - CHECK_EQ(mAudioTrack->getPosition(&numFramesPlayed), - (status_t)OK); - - uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed; - - // This is how long the audio sink will have data to - // play back. - const float msecsPerFrame = 1000.0f / mAudioTrack->getSampleRate(); - - int64_t delayUs = - msecsPerFrame * numFramesPendingPlayout * 1000ll; - - // Let's give it more data after about half that time - // has elapsed. - (new AMessage(kWhatPushAudio, id()))->post(delayUs / 2); -} - -void DirectRenderer::AudioRenderer::onPushAudio() { - mPushPending = false; - - while (!mInputBuffers.empty()) { - const BufferInfo &info = *mInputBuffers.begin(); - - ssize_t n = writeNonBlocking( - info.mBuffer->data(), info.mBuffer->size()); - - if (n < (ssize_t)info.mBuffer->size()) { - CHECK_GE(n, 0); - - info.mBuffer->setRange( - info.mBuffer->offset() + n, info.mBuffer->size() - n); - break; - } - - mDecoderContext->releaseOutputBuffer(info.mIndex); - - mInputBuffers.erase(mInputBuffers.begin()); - } - - schedulePushIfNecessary(); -} - -ssize_t DirectRenderer::AudioRenderer::writeNonBlocking( - const uint8_t *data, size_t size) { - uint32_t numFramesPlayed; - status_t err = mAudioTrack->getPosition(&numFramesPlayed); - if (err != OK) { - return err; - } - - ssize_t numFramesAvailableToWrite = - mAudioTrack->frameCount() - (mNumFramesWritten - numFramesPlayed); - - size_t numBytesAvailableToWrite = - numFramesAvailableToWrite * mAudioTrack->frameSize(); - - if (size > numBytesAvailableToWrite) { - size = numBytesAvailableToWrite; - } - - CHECK_EQ(mAudioTrack->write(data, size), (ssize_t)size); - - size_t numFramesWritten = size / mAudioTrack->frameSize(); - mNumFramesWritten += numFramesWritten; - - return size; -} - -//////////////////////////////////////////////////////////////////////////////// - -DirectRenderer::DirectRenderer( - const sp &bufferProducer) - : mSurfaceTex(bufferProducer), - mVideoRenderPending(false), - mNumFramesLate(0), - mNumFrames(0) { -} - -DirectRenderer::~DirectRenderer() { -} - -void DirectRenderer::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatDecoderNotify: - { - onDecoderNotify(msg); - break; - } - - case kWhatRenderVideo: - { - onRenderVideo(); - break; - } - - default: - TRESPASS(); - } -} - -void DirectRenderer::setFormat(size_t trackIndex, const sp &format) { - CHECK_LT(trackIndex, 2u); - - CHECK(mDecoderContext[trackIndex] == NULL); - - sp notify = new AMessage(kWhatDecoderNotify, id()); - notify->setSize("trackIndex", trackIndex); - - mDecoderContext[trackIndex] = new DecoderContext(notify); - looper()->registerHandler(mDecoderContext[trackIndex]); - - CHECK_EQ((status_t)OK, - mDecoderContext[trackIndex]->init( - format, trackIndex == 0 ? mSurfaceTex : NULL)); - - if (trackIndex == 1) { - // Audio - mAudioRenderer = new AudioRenderer(mDecoderContext[1]); - looper()->registerHandler(mAudioRenderer); - } -} - -void DirectRenderer::queueAccessUnit( - size_t trackIndex, const sp &accessUnit) { - CHECK_LT(trackIndex, 2u); - - if (mDecoderContext[trackIndex] == NULL) { - CHECK_EQ(trackIndex, 0u); - - sp format = new AMessage; - format->setString("mime", "video/avc"); - format->setInt32("width", 640); - format->setInt32("height", 360); - - setFormat(trackIndex, format); - } - - mDecoderContext[trackIndex]->queueInputBuffer(accessUnit); -} - -void DirectRenderer::onDecoderNotify(const sp &msg) { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case DecoderContext::kWhatOutputBufferReady: - { - size_t index; - CHECK(msg->findSize("index", &index)); - - int64_t timeUs; - CHECK(msg->findInt64("timeUs", &timeUs)); - - sp buffer; - CHECK(msg->findBuffer("buffer", &buffer)); - - queueOutputBuffer(trackIndex, index, timeUs, buffer); - break; - } - - default: - TRESPASS(); - } -} - -void DirectRenderer::queueOutputBuffer( - size_t trackIndex, - size_t index, int64_t timeUs, const sp &buffer) { - if (trackIndex == 1) { - // Audio - mAudioRenderer->queueInputBuffer(index, timeUs, buffer); - return; - } - - OutputInfo info; - info.mIndex = index; - info.mTimeUs = timeUs; - info.mBuffer = buffer; - mVideoOutputBuffers.push_back(info); - - scheduleVideoRenderIfNecessary(); -} - -void DirectRenderer::scheduleVideoRenderIfNecessary() { - if (mVideoRenderPending || mVideoOutputBuffers.empty()) { - return; - } - - mVideoRenderPending = true; - - int64_t timeUs = (*mVideoOutputBuffers.begin()).mTimeUs; - int64_t nowUs = ALooper::GetNowUs(); - - int64_t delayUs = timeUs - nowUs; - - (new AMessage(kWhatRenderVideo, id()))->post(delayUs); -} - -void DirectRenderer::onRenderVideo() { - mVideoRenderPending = false; - - int64_t nowUs = ALooper::GetNowUs(); - - while (!mVideoOutputBuffers.empty()) { - const OutputInfo &info = *mVideoOutputBuffers.begin(); - - if (info.mTimeUs > nowUs) { - break; - } - - if (info.mTimeUs + 15000ll < nowUs) { - ++mNumFramesLate; - } - ++mNumFrames; - - status_t err = - mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex); - CHECK_EQ(err, (status_t)OK); - - mVideoOutputBuffers.erase(mVideoOutputBuffers.begin()); - } - - scheduleVideoRenderIfNecessary(); -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h deleted file mode 100644 index c5a4a83..0000000 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 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 DIRECT_RENDERER_H_ - -#define DIRECT_RENDERER_H_ - -#include - -namespace android { - -struct ABuffer; -struct AudioTrack; -struct IGraphicBufferProducer; -struct MediaCodec; - -// Renders audio and video data queued by calls to "queueAccessUnit". -struct DirectRenderer : public AHandler { - DirectRenderer(const sp &bufferProducer); - - void setFormat(size_t trackIndex, const sp &format); - void queueAccessUnit(size_t trackIndex, const sp &accessUnit); - -protected: - virtual void onMessageReceived(const sp &msg); - virtual ~DirectRenderer(); - -private: - struct DecoderContext; - struct AudioRenderer; - - enum { - kWhatDecoderNotify, - kWhatRenderVideo, - }; - - struct OutputInfo { - size_t mIndex; - int64_t mTimeUs; - sp mBuffer; - }; - - sp mSurfaceTex; - - sp mDecoderContext[2]; - List mVideoOutputBuffers; - - bool mVideoRenderPending; - - sp mAudioRenderer; - - int32_t mNumFramesLate; - int32_t mNumFrames; - - void onDecoderNotify(const sp &msg); - - void queueOutputBuffer( - size_t trackIndex, - size_t index, int64_t timeUs, const sp &buffer); - - void scheduleVideoRenderIfNecessary(); - void onRenderVideo(); - - DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); -}; - -} // namespace android - -#endif // DIRECT_RENDERER_H_ diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp deleted file mode 100644 index 5db2099..0000000 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ /dev/null @@ -1,917 +0,0 @@ -/* - * Copyright 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 "WifiDisplaySink" -#include - -#include "WifiDisplaySink.h" - -#include "DirectRenderer.h" -#include "MediaReceiver.h" -#include "ParsedMessage.h" -#include "TimeSyncer.h" - -#include -#include -#include -#include -#include -#include - -namespace android { - -// static -const AString WifiDisplaySink::sUserAgent = MakeUserAgent(); - -WifiDisplaySink::WifiDisplaySink( - uint32_t flags, - const sp &netSession, - const sp &bufferProducer, - const sp ¬ify) - : mState(UNDEFINED), - mFlags(flags), - mNetSession(netSession), - mSurfaceTex(bufferProducer), - mNotify(notify), - mUsingTCPTransport(false), - mUsingTCPInterleaving(false), - mSessionID(0), - mNextCSeq(1), - mIDRFrameRequestPending(false), - mTimeOffsetUs(0ll), - mTimeOffsetValid(false), - mSetupDeferred(false), - mLatencyCount(0), - mLatencySumUs(0ll), - mLatencyMaxUs(0ll), - mMaxDelayMs(-1ll) { - // We support any and all resolutions, but prefer 720p30 - mSinkSupportedVideoFormats.setNativeResolution( - VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 - - mSinkSupportedVideoFormats.enableAll(); -} - -WifiDisplaySink::~WifiDisplaySink() { -} - -void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) { - sp msg = new AMessage(kWhatStart, id()); - msg->setString("sourceHost", sourceHost); - msg->setInt32("sourcePort", sourcePort); - msg->post(); -} - -void WifiDisplaySink::start(const char *uri) { - sp msg = new AMessage(kWhatStart, id()); - msg->setString("setupURI", uri); - msg->post(); -} - -// static -bool WifiDisplaySink::ParseURL( - const char *url, AString *host, int32_t *port, AString *path, - AString *user, AString *pass) { - host->clear(); - *port = 0; - path->clear(); - user->clear(); - pass->clear(); - - if (strncasecmp("rtsp://", url, 7)) { - return false; - } - - const char *slashPos = strchr(&url[7], '/'); - - if (slashPos == NULL) { - host->setTo(&url[7]); - path->setTo("/"); - } else { - host->setTo(&url[7], slashPos - &url[7]); - path->setTo(slashPos); - } - - ssize_t atPos = host->find("@"); - - if (atPos >= 0) { - // Split of user:pass@ from hostname. - - AString userPass(*host, 0, atPos); - host->erase(0, atPos + 1); - - ssize_t colonPos = userPass.find(":"); - - if (colonPos < 0) { - *user = userPass; - } else { - user->setTo(userPass, 0, colonPos); - pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1); - } - } - - const char *colonPos = strchr(host->c_str(), ':'); - - if (colonPos != NULL) { - char *end; - unsigned long x = strtoul(colonPos + 1, &end, 10); - - if (end == colonPos + 1 || *end != '\0' || x >= 65536) { - return false; - } - - *port = x; - - size_t colonOffset = colonPos - host->c_str(); - size_t trailing = host->size() - colonOffset; - host->erase(colonOffset, trailing); - } else { - *port = 554; - } - - return true; -} - -void WifiDisplaySink::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatStart: - { - sleep(2); // XXX - - int32_t sourcePort; - CHECK(msg->findString("sourceHost", &mRTSPHost)); - CHECK(msg->findInt32("sourcePort", &sourcePort)); - - sp notify = new AMessage(kWhatRTSPNotify, id()); - - status_t err = mNetSession->createRTSPClient( - mRTSPHost.c_str(), sourcePort, notify, &mSessionID); - CHECK_EQ(err, (status_t)OK); - - mState = CONNECTING; - break; - } - - case kWhatRTSPNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred in session %d (%d, '%s/%s').", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - if (sessionID == mSessionID) { - ALOGI("Lost control connection."); - - // The control connection is dead now. - mNetSession->destroySession(mSessionID); - mSessionID = 0; - - if (mNotify == NULL) { - looper()->stop(); - } else { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatDisconnected); - notify->post(); - } - } - break; - } - - case ANetworkSession::kWhatConnected: - { - ALOGI("We're now connected."); - mState = CONNECTED; - - if (mFlags & FLAG_SPECIAL_MODE) { - sp notify = new AMessage( - kWhatTimeSyncerNotify, id()); - - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - mTimeSyncer->startClient(mRTSPHost.c_str(), 8123); - } - break; - } - - case ANetworkSession::kWhatData: - { - onReceiveClientData(msg); - break; - } - - default: - TRESPASS(); - } - break; - } - - case kWhatStop: - { - looper()->stop(); - break; - } - - case kWhatMediaReceiverNotify: - { - onMediaReceiverNotify(msg); - break; - } - - case kWhatTimeSyncerNotify: - { - int32_t what; - CHECK(msg->findInt32("what", &what)); - - if (what == TimeSyncer::kWhatTimeOffset) { - CHECK(msg->findInt64("offset", &mTimeOffsetUs)); - mTimeOffsetValid = true; - - if (mSetupDeferred) { - CHECK_EQ((status_t)OK, - sendSetup( - mSessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0")); - - mSetupDeferred = false; - } - } - break; - } - - case kWhatReportLateness: - { - if (mLatencyCount > 0) { - int64_t avgLatencyUs = mLatencySumUs / mLatencyCount; - - ALOGV("avg. latency = %lld ms (max %lld ms)", - avgLatencyUs / 1000ll, - mLatencyMaxUs / 1000ll); - - sp params = new AMessage; - params->setInt64("avgLatencyUs", avgLatencyUs); - params->setInt64("maxLatencyUs", mLatencyMaxUs); - mMediaReceiver->informSender(0 /* trackIndex */, params); - } - - mLatencyCount = 0; - mLatencySumUs = 0ll; - mLatencyMaxUs = 0ll; - - msg->post(kReportLatenessEveryUs); - break; - } - - default: - TRESPASS(); - } -} - -void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) { - int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll; - - if (delayMs > mMaxDelayMs) { - mMaxDelayMs = delayMs; - } - - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - ALOGI("[%lld]: (%4lld ms / %4lld ms) %s", - timeUs / 1000, - delayMs, - mMaxDelayMs, - kPattern + kPatternSize - n); -} - -void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case MediaReceiver::kWhatInitDone: - { - status_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGI("MediaReceiver initialization completed w/ err %d", err); - break; - } - - case MediaReceiver::kWhatError: - { - status_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGE("MediaReceiver signaled error %d", err); - break; - } - - case MediaReceiver::kWhatAccessUnit: - { - if (mRenderer == NULL) { - mRenderer = new DirectRenderer(mSurfaceTex); - looper()->registerHandler(mRenderer); - } - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) { - mTimeOffsetUs = timeUs - ALooper::GetNowUs(); - mTimeOffsetValid = true; - } - - CHECK(mTimeOffsetValid); - - // We are the timesync _client_, - // client time = server time - time offset. - timeUs -= mTimeOffsetUs; - - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayUs = nowUs - timeUs; - - mLatencySumUs += delayUs; - if (mLatencyCount == 0 || delayUs > mLatencyMaxUs) { - mLatencyMaxUs = delayUs; - } - ++mLatencyCount; - - // dumpDelay(trackIndex, timeUs); - - timeUs += 220000ll; // Assume 220 ms of latency - accessUnit->meta()->setInt64("timeUs", timeUs); - - sp format; - if (msg->findMessage("format", &format)) { - mRenderer->setFormat(trackIndex, format); - } - - mRenderer->queueAccessUnit(trackIndex, accessUnit); - break; - } - - case MediaReceiver::kWhatPacketLost: - { -#if 0 - if (!mIDRFrameRequestPending) { - ALOGI("requesting IDR frame"); - - sendIDRFrameRequest(mSessionID); - } -#endif - break; - } - - default: - TRESPASS(); - } -} - -void WifiDisplaySink::registerResponseHandler( - int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) { - ResponseID id; - id.mSessionID = sessionID; - id.mCSeq = cseq; - mResponseHandlers.add(id, func); -} - -status_t WifiDisplaySink::sendM2(int32_t sessionID) { - AString request = "OPTIONS * RTSP/1.0\r\n"; - AppendCommonResponse(&request, mNextCSeq); - - request.append( - "Require: org.wfa.wfd1.0\r\n" - "\r\n"); - - status_t err = - mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response); - - ++mNextCSeq; - - return OK; -} - -status_t WifiDisplaySink::onReceiveM2Response( - int32_t sessionID, const sp &msg) { - int32_t statusCode; - if (!msg->getStatusCode(&statusCode)) { - return ERROR_MALFORMED; - } - - if (statusCode != 200) { - return ERROR_UNSUPPORTED; - } - - return OK; -} - -status_t WifiDisplaySink::onReceiveSetupResponse( - int32_t sessionID, const sp &msg) { - int32_t statusCode; - if (!msg->getStatusCode(&statusCode)) { - return ERROR_MALFORMED; - } - - if (statusCode != 200) { - return ERROR_UNSUPPORTED; - } - - if (!msg->findString("session", &mPlaybackSessionID)) { - return ERROR_MALFORMED; - } - - if (!ParsedMessage::GetInt32Attribute( - mPlaybackSessionID.c_str(), - "timeout", - &mPlaybackSessionTimeoutSecs)) { - mPlaybackSessionTimeoutSecs = -1; - } - - ssize_t colonPos = mPlaybackSessionID.find(";"); - if (colonPos >= 0) { - // Strip any options from the returned session id. - mPlaybackSessionID.erase( - colonPos, mPlaybackSessionID.size() - colonPos); - } - - status_t err = configureTransport(msg); - - if (err != OK) { - return err; - } - - mState = PAUSED; - - return sendPlay( - sessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); -} - -status_t WifiDisplaySink::configureTransport(const sp &msg) { - if (mUsingTCPTransport && !(mFlags & FLAG_SPECIAL_MODE)) { - // In "special" mode we still use a UDP RTCP back-channel that - // needs connecting. - return OK; - } - - AString transport; - if (!msg->findString("transport", &transport)) { - ALOGE("Missing 'transport' field in SETUP response."); - return ERROR_MALFORMED; - } - - AString sourceHost; - if (!ParsedMessage::GetAttribute( - transport.c_str(), "source", &sourceHost)) { - sourceHost = mRTSPHost; - } - - AString serverPortStr; - if (!ParsedMessage::GetAttribute( - transport.c_str(), "server_port", &serverPortStr)) { - ALOGE("Missing 'server_port' in Transport field."); - return ERROR_MALFORMED; - } - - int rtpPort, rtcpPort; - if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2 - || rtpPort <= 0 || rtpPort > 65535 - || rtcpPort <=0 || rtcpPort > 65535 - || rtcpPort != rtpPort + 1) { - ALOGE("Invalid server_port description '%s'.", - serverPortStr.c_str()); - - return ERROR_MALFORMED; - } - - if (rtpPort & 1) { - ALOGW("Server picked an odd numbered RTP port."); - } - - return mMediaReceiver->connectTrack( - 0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort); -} - -status_t WifiDisplaySink::onReceivePlayResponse( - int32_t sessionID, const sp &msg) { - int32_t statusCode; - if (!msg->getStatusCode(&statusCode)) { - return ERROR_MALFORMED; - } - - if (statusCode != 200) { - return ERROR_UNSUPPORTED; - } - - mState = PLAYING; - - (new AMessage(kWhatReportLateness, id()))->post(kReportLatenessEveryUs); - - return OK; -} - -status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse( - int32_t sessionID, const sp &msg) { - CHECK(mIDRFrameRequestPending); - mIDRFrameRequestPending = false; - - return OK; -} - -void WifiDisplaySink::onReceiveClientData(const sp &msg) { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp obj; - CHECK(msg->findObject("data", &obj)); - - sp data = - static_cast(obj.get()); - - ALOGV("session %d received '%s'", - sessionID, data->debugString().c_str()); - - AString method; - AString uri; - data->getRequestField(0, &method); - - int32_t cseq; - if (!data->findInt32("cseq", &cseq)) { - sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */); - return; - } - - if (method.startsWith("RTSP/")) { - // This is a response. - - ResponseID id; - id.mSessionID = sessionID; - id.mCSeq = cseq; - - ssize_t index = mResponseHandlers.indexOfKey(id); - - if (index < 0) { - ALOGW("Received unsolicited server response, cseq %d", cseq); - return; - } - - HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index); - mResponseHandlers.removeItemsAt(index); - - status_t err = (this->*func)(sessionID, data); - CHECK_EQ(err, (status_t)OK); - } else { - AString version; - data->getRequestField(2, &version); - if (!(version == AString("RTSP/1.0"))) { - sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq); - return; - } - - if (method == "OPTIONS") { - onOptionsRequest(sessionID, cseq, data); - } else if (method == "GET_PARAMETER") { - onGetParameterRequest(sessionID, cseq, data); - } else if (method == "SET_PARAMETER") { - onSetParameterRequest(sessionID, cseq, data); - } else { - sendErrorResponse(sessionID, "405 Method Not Allowed", cseq); - } - } -} - -void WifiDisplaySink::onOptionsRequest( - int32_t sessionID, - int32_t cseq, - const sp &data) { - AString response = "RTSP/1.0 200 OK\r\n"; - AppendCommonResponse(&response, cseq); - response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n"); - response.append("\r\n"); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); - - err = sendM2(sessionID); - CHECK_EQ(err, (status_t)OK); -} - -void WifiDisplaySink::onGetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data) { - AString body; - - if (mState == CONNECTED) { - mUsingTCPTransport = false; - mUsingTCPInterleaving = false; - - char val[PROPERTY_VALUE_MAX]; - if (property_get("media.wfd-sink.tcp-mode", val, NULL)) { - if (!strcasecmp("true", val) || !strcmp("1", val)) { - ALOGI("Using TCP unicast transport."); - mUsingTCPTransport = true; - mUsingTCPInterleaving = false; - } else if (!strcasecmp("interleaved", val)) { - ALOGI("Using TCP interleaved transport."); - mUsingTCPTransport = true; - mUsingTCPInterleaving = true; - } - } else if (mFlags & FLAG_SPECIAL_MODE) { - mUsingTCPTransport = true; - } - - body = "wfd_video_formats: "; - body.append(mSinkSupportedVideoFormats.getFormatSpec()); - - body.append( - "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n" - "wfd_client_rtp_ports: RTP/AVP/"); - - if (mUsingTCPTransport) { - body.append("TCP;"); - if (mUsingTCPInterleaving) { - body.append("interleaved"); - } else { - body.append("unicast 19000 0"); - } - } else { - body.append("UDP;unicast 19000 0"); - } - - body.append(" mode=play\r\n"); - } - - AString response = "RTSP/1.0 200 OK\r\n"; - AppendCommonResponse(&response, cseq); - response.append("Content-Type: text/parameters\r\n"); - response.append(StringPrintf("Content-Length: %d\r\n", body.size())); - response.append("\r\n"); - response.append(body); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); -} - -status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { - sp notify = new AMessage(kWhatMediaReceiverNotify, id()); - - mMediaReceiverLooper = new ALooper; - mMediaReceiverLooper->setName("media_receiver"); - - mMediaReceiverLooper->start( - false /* runOnCallingThread */, - false /* canCallJava */, - PRIORITY_AUDIO); - - mMediaReceiver = new MediaReceiver(mNetSession, notify); - mMediaReceiverLooper->registerHandler(mMediaReceiver); - - RTPReceiver::TransportMode rtpMode = RTPReceiver::TRANSPORT_UDP; - if (mUsingTCPTransport) { - if (mUsingTCPInterleaving) { - rtpMode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED; - } else { - rtpMode = RTPReceiver::TRANSPORT_TCP; - } - } - - int32_t localRTPPort; - status_t err = mMediaReceiver->addTrack( - rtpMode, RTPReceiver::TRANSPORT_UDP /* rtcpMode */, &localRTPPort); - - if (err == OK) { - err = mMediaReceiver->initAsync(MediaReceiver::MODE_TRANSPORT_STREAM); - } - - if (err != OK) { - mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id()); - mMediaReceiver.clear(); - - mMediaReceiverLooper->stop(); - mMediaReceiverLooper.clear(); - - return err; - } - - AString request = StringPrintf("SETUP %s RTSP/1.0\r\n", uri); - - AppendCommonResponse(&request, mNextCSeq); - - if (rtpMode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) { - request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n"); - } else if (rtpMode == RTPReceiver::TRANSPORT_TCP) { - if (mFlags & FLAG_SPECIAL_MODE) { - // This isn't quite true, since the RTP connection is through TCP - // and the RTCP connection through UDP... - request.append( - StringPrintf( - "Transport: RTP/AVP/TCP;unicast;client_port=%d-%d\r\n", - localRTPPort, localRTPPort + 1)); - } else { - request.append( - StringPrintf( - "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n", - localRTPPort)); - } - } else { - request.append( - StringPrintf( - "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", - localRTPPort, - localRTPPort + 1)); - } - - request.append("\r\n"); - - ALOGV("request = '%s'", request.c_str()); - - err = mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse); - - ++mNextCSeq; - - return OK; -} - -status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) { - AString request = StringPrintf("PLAY %s RTSP/1.0\r\n", uri); - - AppendCommonResponse(&request, mNextCSeq); - - request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); - request.append("\r\n"); - - status_t err = - mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse); - - ++mNextCSeq; - - return OK; -} - -status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) { - CHECK(!mIDRFrameRequestPending); - - AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; - - AppendCommonResponse(&request, mNextCSeq); - - AString content = "wfd_idr_request\r\n"; - - request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); - request.append(StringPrintf("Content-Length: %d\r\n", content.size())); - request.append("\r\n"); - request.append(content); - - status_t err = - mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, - mNextCSeq, - &WifiDisplaySink::onReceiveIDRFrameRequestResponse); - - ++mNextCSeq; - - mIDRFrameRequestPending = true; - - return OK; -} - -void WifiDisplaySink::onSetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data) { - const char *content = data->getContent(); - - if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) { - if ((mFlags & FLAG_SPECIAL_MODE) && !mTimeOffsetValid) { - mSetupDeferred = true; - } else { - status_t err = - sendSetup( - sessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); - - CHECK_EQ(err, (status_t)OK); - } - } - - AString response = "RTSP/1.0 200 OK\r\n"; - AppendCommonResponse(&response, cseq); - response.append("\r\n"); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); -} - -void WifiDisplaySink::sendErrorResponse( - int32_t sessionID, - const char *errorDetail, - int32_t cseq) { - AString response; - response.append("RTSP/1.0 "); - response.append(errorDetail); - response.append("\r\n"); - - AppendCommonResponse(&response, cseq); - - response.append("\r\n"); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); -} - -// static -void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) { - time_t now = time(NULL); - struct tm *now2 = gmtime(&now); - char buf[128]; - strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2); - - response->append("Date: "); - response->append(buf); - response->append("\r\n"); - - response->append(StringPrintf("User-Agent: %s\r\n", sUserAgent.c_str())); - - if (cseq >= 0) { - response->append(StringPrintf("CSeq: %d\r\n", cseq)); - } -} - -} // namespace android diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h deleted file mode 100644 index adb9d89..0000000 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 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 WIFI_DISPLAY_SINK_H_ - -#define WIFI_DISPLAY_SINK_H_ - -#include "ANetworkSession.h" - -#include "VideoFormats.h" - -#include -#include - -namespace android { - -struct AMessage; -struct DirectRenderer; -struct MediaReceiver; -struct ParsedMessage; -struct TimeSyncer; - -// Represents the RTSP client acting as a wifi display sink. -// Connects to a wifi display source and renders the incoming -// transport stream using a MediaPlayer instance. -struct WifiDisplaySink : public AHandler { - enum { - kWhatDisconnected, - }; - - enum Flags { - FLAG_SPECIAL_MODE = 1, - }; - - // If no notification message is specified (notify == NULL) - // the sink will stop its looper() once the session ends, - // otherwise it will post an appropriate notification but leave - // the looper() running. - WifiDisplaySink( - uint32_t flags, - const sp &netSession, - const sp &bufferProducer = NULL, - const sp ¬ify = NULL); - - void start(const char *sourceHost, int32_t sourcePort); - void start(const char *uri); - -protected: - virtual ~WifiDisplaySink(); - virtual void onMessageReceived(const sp &msg); - -private: - enum State { - UNDEFINED, - CONNECTING, - CONNECTED, - PAUSED, - PLAYING, - }; - - enum { - kWhatStart, - kWhatRTSPNotify, - kWhatStop, - kWhatMediaReceiverNotify, - kWhatTimeSyncerNotify, - kWhatReportLateness, - }; - - struct ResponseID { - int32_t mSessionID; - int32_t mCSeq; - - bool operator<(const ResponseID &other) const { - return mSessionID < other.mSessionID - || (mSessionID == other.mSessionID - && mCSeq < other.mCSeq); - } - }; - - typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)( - int32_t sessionID, const sp &msg); - - static const int64_t kReportLatenessEveryUs = 1000000ll; - - static const AString sUserAgent; - - State mState; - uint32_t mFlags; - VideoFormats mSinkSupportedVideoFormats; - sp mNetSession; - sp mSurfaceTex; - sp mNotify; - sp mTimeSyncer; - bool mUsingTCPTransport; - bool mUsingTCPInterleaving; - AString mRTSPHost; - int32_t mSessionID; - - int32_t mNextCSeq; - - KeyedVector mResponseHandlers; - - sp mMediaReceiverLooper; - sp mMediaReceiver; - sp mRenderer; - - AString mPlaybackSessionID; - int32_t mPlaybackSessionTimeoutSecs; - - bool mIDRFrameRequestPending; - - int64_t mTimeOffsetUs; - bool mTimeOffsetValid; - - bool mSetupDeferred; - - size_t mLatencyCount; - int64_t mLatencySumUs; - int64_t mLatencyMaxUs; - - int64_t mMaxDelayMs; - - status_t sendM2(int32_t sessionID); - status_t sendSetup(int32_t sessionID, const char *uri); - status_t sendPlay(int32_t sessionID, const char *uri); - status_t sendIDRFrameRequest(int32_t sessionID); - - status_t onReceiveM2Response( - int32_t sessionID, const sp &msg); - - status_t onReceiveSetupResponse( - int32_t sessionID, const sp &msg); - - status_t configureTransport(const sp &msg); - - status_t onReceivePlayResponse( - int32_t sessionID, const sp &msg); - - status_t onReceiveIDRFrameRequestResponse( - int32_t sessionID, const sp &msg); - - void registerResponseHandler( - int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func); - - void onReceiveClientData(const sp &msg); - - void onOptionsRequest( - int32_t sessionID, - int32_t cseq, - const sp &data); - - void onGetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data); - - void onSetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data); - - void onMediaReceiverNotify(const sp &msg); - - void sendErrorResponse( - int32_t sessionID, - const char *errorDetail, - int32_t cseq); - - static void AppendCommonResponse(AString *response, int32_t cseq); - - bool ParseURL( - const char *url, AString *host, int32_t *port, AString *path, - AString *user, AString *pass); - - void dumpDelay(size_t trackIndex, int64_t timeUs); - - DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink); -}; - -} // namespace android - -#endif // WIFI_DISPLAY_SINK_H_ diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index cacfcca..3d7b865 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -559,8 +559,6 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( converter->dropAFrame(); } } - } else if (what == MediaSender::kWhatInformSender) { - onSinkFeedback(msg); } else { TRESPASS(); } @@ -656,89 +654,6 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } -void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp &msg) { - int64_t avgLatencyUs; - CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); - - int64_t maxLatencyUs; - CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); - - ALOGI("sink reports avg. latency of %lld ms (max %lld ms)", - avgLatencyUs / 1000ll, - maxLatencyUs / 1000ll); - - if (mVideoTrackIndex >= 0) { - const sp &videoTrack = mTracks.valueFor(mVideoTrackIndex); - sp converter = videoTrack->converter(); - - if (converter != NULL) { - int32_t videoBitrate = - Converter::GetInt32Property("media.wfd.video-bitrate", -1); - - char val[PROPERTY_VALUE_MAX]; - if (videoBitrate < 0 - && property_get("media.wfd.video-bitrate", val, NULL) - && !strcasecmp("adaptive", val)) { - videoBitrate = converter->getVideoBitrate(); - - if (avgLatencyUs > 300000ll) { - videoBitrate *= 0.6; - } else if (avgLatencyUs < 100000ll) { - videoBitrate *= 1.1; - } - } - - if (videoBitrate > 0) { - if (videoBitrate < 500000) { - videoBitrate = 500000; - } else if (videoBitrate > 10000000) { - videoBitrate = 10000000; - } - - if (videoBitrate != converter->getVideoBitrate()) { - ALOGI("setting video bitrate to %d bps", videoBitrate); - - converter->setVideoBitrate(videoBitrate); - } - } - } - - sp repeaterSource = videoTrack->repeaterSource(); - if (repeaterSource != NULL) { - double rateHz = - Converter::GetInt32Property( - "media.wfd.video-framerate", -1); - - char val[PROPERTY_VALUE_MAX]; - if (rateHz < 0.0 - && property_get("media.wfd.video-framerate", val, NULL) - && !strcasecmp("adaptive", val)) { - rateHz = repeaterSource->getFrameRate(); - - if (avgLatencyUs > 300000ll) { - rateHz *= 0.9; - } else if (avgLatencyUs < 200000ll) { - rateHz *= 1.1; - } - } - - if (rateHz > 0) { - if (rateHz < 5.0) { - rateHz = 5.0; - } else if (rateHz > 30.0) { - rateHz = 30.0; - } - - if (rateHz != repeaterSource->getFrameRate()) { - ALOGI("setting frame rate to %.2f Hz", rateHz); - - repeaterSource->setFrameRate(rateHz); - } - } - } - } -} - status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( bool enableAudio, bool enableVideo) { DataSource::RegisterDefaultSniffers(); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 4a49811..2b5bee9 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -23,7 +23,6 @@ #include "Parameters.h" #include "ParsedMessage.h" #include "rtp/RTPSender.h" -#include "TimeSyncer.h" #include #include @@ -165,14 +164,6 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { } else { err = -EINVAL; } - } - - if (err == OK) { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - mTimeSyncer->startServer(8123); mState = AWAITING_CLIENT_CONNECTION; } @@ -548,11 +539,6 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { break; } - case kWhatTimeSyncerNotify: - { - break; - } - default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 3efa0b4..44d3e4d 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -30,7 +30,6 @@ namespace android { struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; -struct TimeSyncer; // Represents the RTSP server acting as a wifi display source. // Manages incoming connections, sets up Playback sessions as necessary. @@ -83,7 +82,6 @@ private: kWhatHDCPNotify, kWhatFinishStop2, kWhatTeardownTriggerTimedOut, - kWhatTimeSyncerNotify, }; struct ResponseID { @@ -120,7 +118,6 @@ private: sp mNetSession; sp mClient; AString mMediaPath; - sp mTimeSyncer; struct in_addr mInterfaceAddr; int32_t mSessionID; diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp deleted file mode 100644 index 111846d..0000000 --- a/media/libstagefright/wifi-display/udptest.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 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_NEBUG 0 -#define LOG_TAG "udptest" -#include - -#include "ANetworkSession.h" -#include "TimeSyncer.h" - -#include -#include - -namespace android { - -} // namespace android - -static void usage(const char *me) { - fprintf(stderr, - "usage: %s -c host[:port]\tconnect to test server\n" - " -l \tcreate a test server\n", - me); -} - -int main(int argc, char **argv) { - using namespace android; - - ProcessState::self()->startThreadPool(); - - int32_t localPort = -1; - int32_t connectToPort = -1; - AString connectToHost; - - int res; - while ((res = getopt(argc, argv, "hc:l:")) >= 0) { - switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - connectToHost = optarg; - connectToPort = 49152; - } else { - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 1 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - } - break; - } - - case 'l': - { - char *end; - localPort = strtol(optarg, &end, 10); - - if (*end != '\0' || end == optarg - || localPort < 1 || localPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case '?': - case 'h': - usage(argv[0]); - exit(1); - } - } - - if (localPort < 0 && connectToPort < 0) { - fprintf(stderr, - "You need to select either client or server mode.\n"); - exit(1); - } - - sp netSession = new ANetworkSession; - netSession->start(); - - sp looper = new ALooper; - - sp handler = new TimeSyncer(netSession, NULL /* notify */); - looper->registerHandler(handler); - - if (localPort >= 0) { - handler->startServer(localPort); - } else { - handler->startClient(connectToHost.c_str(), connectToPort); - } - - looper->start(true /* runOnCallingThread */); - - return 0; -} - diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 9fee4d0..c947765 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -18,7 +18,6 @@ #define LOG_TAG "wfd" #include -#include "sink/WifiDisplaySink.h" #include "source/WifiDisplaySource.h" #include @@ -39,12 +38,8 @@ namespace android { static void usage(const char *me) { fprintf(stderr, "usage:\n" - " %s -c host[:port]\tconnect to wifi source\n" - " -u uri \tconnect to an rtsp uri\n" - " -l ip[:port] \tlisten on the specified port " - " -f(ilename) \tstream media " - "(create a sink)\n" - " -s(pecial) \trun in 'special' mode\n", + " %s -l iface[:port]\tcreate a wifi display source\n" + " -f(ilename) \tstream media\n", me); } @@ -214,48 +209,14 @@ int main(int argc, char **argv) { DataSource::RegisterDefaultSniffers(); - AString connectToHost; - int32_t connectToPort = -1; - AString uri; - AString listenOnAddr; int32_t listenOnPort = -1; AString path; - bool specialMode = false; - int res; - while ((res = getopt(argc, argv, "hc:l:u:f:s")) >= 0) { + while ((res = getopt(argc, argv, "hl:f:")) >= 0) { switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - connectToHost = optarg; - connectToPort = WifiDisplaySource::kWifiDisplayDefaultPort; - } else { - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 1 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - } - break; - } - - case 'u': - { - uri = optarg; - break; - } - case 'f': { path = optarg; @@ -284,12 +245,6 @@ int main(int argc, char **argv) { break; } - case 's': - { - specialMode = true; - break; - } - case '?': case 'h': default: @@ -298,13 +253,6 @@ int main(int argc, char **argv) { } } - if (connectToPort >= 0 && listenOnPort >= 0) { - fprintf(stderr, - "You can connect to a source or create one, " - "but not both at the same time.\n"); - exit(1); - } - if (listenOnPort >= 0) { if (path.empty()) { createSource(listenOnAddr, listenOnPort); @@ -315,72 +263,7 @@ int main(int argc, char **argv) { exit(0); } - if (connectToPort < 0 && uri.empty()) { - fprintf(stderr, - "You need to select either source host or uri.\n"); - - exit(1); - } - - if (connectToPort >= 0 && !uri.empty()) { - fprintf(stderr, - "You need to either connect to a wfd host or an rtsp url, " - "not both.\n"); - exit(1); - } - - sp composerClient = new SurfaceComposerClient; - CHECK_EQ(composerClient->initCheck(), (status_t)OK); - - sp display(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); - DisplayInfo info; - SurfaceComposerClient::getDisplayInfo(display, &info); - ssize_t displayWidth = info.w; - ssize_t displayHeight = info.h; - - ALOGV("display is %d x %d\n", displayWidth, displayHeight); - - sp control = - composerClient->createSurface( - String8("A Surface"), - displayWidth, - displayHeight, - PIXEL_FORMAT_RGB_565, - 0); - - CHECK(control != NULL); - CHECK(control->isValid()); - - SurfaceComposerClient::openGlobalTransaction(); - CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); - CHECK_EQ(control->show(), (status_t)OK); - SurfaceComposerClient::closeGlobalTransaction(); - - sp surface = control->getSurface(); - CHECK(surface != NULL); - - sp session = new ANetworkSession; - session->start(); - - sp looper = new ALooper; - - sp sink = new WifiDisplaySink( - specialMode ? WifiDisplaySink::FLAG_SPECIAL_MODE : 0 /* flags */, - session, - surface->getIGraphicBufferProducer()); - - looper->registerHandler(sink); - - if (connectToPort >= 0) { - sink->start(connectToHost.c_str(), connectToPort); - } else { - sink->start(uri.c_str()); - } - - looper->start(true /* runOnCallingThread */); - - composerClient->dispose(); + usage(argv[0]); return 0; } -- cgit v1.1 From a07f17ca46db04c9d5d9e7d6b2878db59ca2b9ea Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 23 Apr 2013 12:39:37 -0700 Subject: Remove timing jitter during startup of audio This fixes a regression introduced recently, that increased timing jitter during the startup of the FastMixer and AudioTrack callback threads. The regression was to make requestPriority() asynchronous as a way to avoid an apparent priority inversion in system_server. This means that the target thread could run briefly with the initial priority, before the new priority takes effect. This change removes the startup jitter for FastMixer, by making the requestPriority() synchronous again for that case. It doesn't matter that this restores the priority inversion involving normal mixer thread, because it happens during startup of both threads. The change also removes the startup jitter for the AudioTrack callback thread, by having the target thread check whether the requestPriority() has completed yet. If not, the target thread blocks with a timeout until the priority boost finishes. Finally, we now log an error message if the expected priority boost doesn't happen. Bug: 8698989 Change-Id: Id590e9a274b70ec1ba85b44a585ee37a22e41cbc --- media/libmedia/AudioTrack.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 1bd839f..7eeb4f8 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -893,9 +893,11 @@ status_t AudioTrack::createTrack_l( ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp); } frameCount = temp; + mAwaitBoost = false; if (flags & AUDIO_OUTPUT_FLAG_FAST) { if (trackFlags & IAudioFlinger::TRACK_FAST) { ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount); + mAwaitBoost = true; } else { ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", frameCount); // once denied, do not request again if IAudioTrack is re-created @@ -1219,6 +1221,25 @@ bool AudioTrack::processAudioBuffer(const sp& thread) size_t writtenSize; mLock.lock(); + if (mAwaitBoost) { + mAwaitBoost = false; + mLock.unlock(); + static const int32_t kMaxTries = 5; + int32_t tryCounter = kMaxTries; + uint32_t pollUs = 10000; + do { + int policy = sched_getscheduler(0); + if (policy == SCHED_FIFO || policy == SCHED_RR) { + break; + } + usleep(pollUs); + pollUs <<= 1; + } while (tryCounter-- > 0); + if (tryCounter < 0) { + ALOGE("did not receive expected priority boost on time"); + } + return true; + } // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed // while we are accessing the cblk sp audioTrack = mAudioTrack; -- cgit v1.1 From b5f28d4749b898d92fe5e56236b417e37b6fe84f Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 25 Apr 2013 15:11:19 -0700 Subject: Handle the case where an asynchronous prepare was initiated and then the mediaplayer was reset. Change-Id: Ib241747c5dc002b88a3854569c1f8340b2a8ef41 related-to-bug: 8688986 --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 7 +++++++ media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 607ec6a..b89b1c8 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -1257,6 +1257,13 @@ void NuPlayer::onSourceNotify(const sp &msg) { switch (what) { case Source::kWhatPrepared: { + if (mSource == NULL) { + // This is a stale notification from a source that was + // asynchronously preparing when the client called reset(). + // We handled the reset, the source is gone. + break; + } + int32_t err; CHECK(msg->findInt32("err", &err)); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index bdafb29..68b9623 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -333,6 +333,14 @@ status_t NuPlayerDriver::reset() { case STATE_RESET_IN_PROGRESS: return INVALID_OPERATION; + case STATE_PREPARING: + { + CHECK(mIsAsyncPrepare); + + notifyListener(MEDIA_PREPARED); + break; + } + default: break; } @@ -503,6 +511,14 @@ void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) { void NuPlayerDriver::notifyPrepareCompleted(status_t err) { Mutex::Autolock autoLock(mLock); + if (mState != STATE_PREPARING) { + // We were preparing asynchronously when the client called + // reset(), we sent a premature "prepared" notification and + // then initiated the reset. This notification is stale. + CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE); + return; + } + CHECK_EQ(mState, STATE_PREPARING); mAsyncResult = err; -- cgit v1.1 From c86ef45279185b474bd6af0a7ae407f8ab577f13 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 26 Apr 2013 08:42:50 -0700 Subject: Revert "Remove all traces of wifi display sink implementation and supporting code." This reverts commit 3a9682a86ead84d6f60d3f3aa01b2b4d34af983d. --- media/libstagefright/wifi-display/Android.mk | 76 ++ .../libstagefright/wifi-display/MediaReceiver.cpp | 328 ++++++ media/libstagefright/wifi-display/MediaReceiver.h | 111 ++ media/libstagefright/wifi-display/MediaSender.cpp | 16 + media/libstagefright/wifi-display/MediaSender.h | 1 + media/libstagefright/wifi-display/SNTPClient.cpp | 174 +++ media/libstagefright/wifi-display/SNTPClient.h | 62 ++ media/libstagefright/wifi-display/TimeSyncer.cpp | 338 ++++++ media/libstagefright/wifi-display/TimeSyncer.h | 109 ++ media/libstagefright/wifi-display/nettest.cpp | 400 +++++++ .../wifi-display/rtp/RTPAssembler.cpp | 328 ++++++ .../libstagefright/wifi-display/rtp/RTPAssembler.h | 92 ++ .../wifi-display/rtp/RTPReceiver.cpp | 1153 ++++++++++++++++++++ .../libstagefright/wifi-display/rtp/RTPReceiver.h | 125 +++ .../libstagefright/wifi-display/rtp/RTPSender.cpp | 11 + media/libstagefright/wifi-display/rtp/RTPSender.h | 1 + media/libstagefright/wifi-display/rtptest.cpp | 565 ++++++++++ .../wifi-display/sink/DirectRenderer.cpp | 625 +++++++++++ .../wifi-display/sink/DirectRenderer.h | 82 ++ .../wifi-display/sink/WifiDisplaySink.cpp | 917 ++++++++++++++++ .../wifi-display/sink/WifiDisplaySink.h | 196 ++++ .../wifi-display/source/PlaybackSession.cpp | 85 ++ .../wifi-display/source/WifiDisplaySource.cpp | 14 + .../wifi-display/source/WifiDisplaySource.h | 3 + media/libstagefright/wifi-display/udptest.cpp | 116 ++ media/libstagefright/wifi-display/wfd.cpp | 125 ++- 26 files changed, 6049 insertions(+), 4 deletions(-) create mode 100644 media/libstagefright/wifi-display/MediaReceiver.cpp create mode 100644 media/libstagefright/wifi-display/MediaReceiver.h create mode 100644 media/libstagefright/wifi-display/SNTPClient.cpp create mode 100644 media/libstagefright/wifi-display/SNTPClient.h create mode 100644 media/libstagefright/wifi-display/TimeSyncer.cpp create mode 100644 media/libstagefright/wifi-display/TimeSyncer.h create mode 100644 media/libstagefright/wifi-display/nettest.cpp create mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.cpp create mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.h create mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.cpp create mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.h create mode 100644 media/libstagefright/wifi-display/rtptest.cpp create mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.cpp create mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.h create mode 100644 media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp create mode 100644 media/libstagefright/wifi-display/sink/WifiDisplaySink.h create mode 100644 media/libstagefright/wifi-display/udptest.cpp (limited to 'media') diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 061ae89..f99ef60 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -4,10 +4,17 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ANetworkSession.cpp \ + MediaReceiver.cpp \ MediaSender.cpp \ Parameters.cpp \ ParsedMessage.cpp \ + rtp/RTPAssembler.cpp \ + rtp/RTPReceiver.cpp \ rtp/RTPSender.cpp \ + sink/DirectRenderer.cpp \ + sink/WifiDisplaySink.cpp \ + SNTPClient.cpp \ + TimeSyncer.cpp \ source/Converter.cpp \ source/MediaPuller.cpp \ source/PlaybackSession.cpp \ @@ -60,3 +67,72 @@ LOCAL_MODULE:= wfd LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + udptest.cpp \ + +LOCAL_SHARED_LIBRARIES:= \ + libbinder \ + libgui \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libstagefright_wfd \ + libutils \ + liblog \ + +LOCAL_MODULE:= udptest + +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + rtptest.cpp \ + +LOCAL_SHARED_LIBRARIES:= \ + libbinder \ + libgui \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libstagefright_wfd \ + libutils \ + liblog \ + +LOCAL_MODULE:= rtptest + +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + nettest.cpp \ + +LOCAL_SHARED_LIBRARIES:= \ + libbinder \ + libgui \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libstagefright_wfd \ + libutils \ + liblog \ + +LOCAL_MODULE:= nettest + +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp new file mode 100644 index 0000000..364acb9 --- /dev/null +++ b/media/libstagefright/wifi-display/MediaReceiver.cpp @@ -0,0 +1,328 @@ +/* + * Copyright 2013, 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 "MediaReceiver" +#include + +#include "MediaReceiver.h" + +#include "ANetworkSession.h" +#include "AnotherPacketSource.h" +#include "rtp/RTPReceiver.h" + +#include +#include +#include +#include +#include + +namespace android { + +MediaReceiver::MediaReceiver( + const sp &netSession, + const sp ¬ify) + : mNetSession(netSession), + mNotify(notify), + mMode(MODE_UNDEFINED), + mGeneration(0), + mInitStatus(OK), + mInitDoneCount(0) { +} + +MediaReceiver::~MediaReceiver() { +} + +ssize_t MediaReceiver::addTrack( + RTPReceiver::TransportMode rtpMode, + RTPReceiver::TransportMode rtcpMode, + int32_t *localRTPPort) { + if (mMode != MODE_UNDEFINED) { + return INVALID_OPERATION; + } + + size_t trackIndex = mTrackInfos.size(); + + TrackInfo info; + + sp notify = new AMessage(kWhatReceiverNotify, id()); + notify->setInt32("generation", mGeneration); + notify->setSize("trackIndex", trackIndex); + + info.mReceiver = new RTPReceiver(mNetSession, notify); + looper()->registerHandler(info.mReceiver); + + info.mReceiver->registerPacketType( + 33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM); + + info.mReceiver->registerPacketType( + 96, RTPReceiver::PACKETIZATION_AAC); + + info.mReceiver->registerPacketType( + 97, RTPReceiver::PACKETIZATION_H264); + + status_t err = info.mReceiver->initAsync( + rtpMode, + rtcpMode, + localRTPPort); + + if (err != OK) { + looper()->unregisterHandler(info.mReceiver->id()); + info.mReceiver.clear(); + + return err; + } + + mTrackInfos.push_back(info); + + return trackIndex; +} + +status_t MediaReceiver::connectTrack( + size_t trackIndex, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort) { + if (trackIndex >= mTrackInfos.size()) { + return -ERANGE; + } + + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort); +} + +status_t MediaReceiver::initAsync(Mode mode) { + if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW) + && mTrackInfos.size() > 1) { + return INVALID_OPERATION; + } + + sp msg = new AMessage(kWhatInit, id()); + msg->setInt32("mode", mode); + msg->post(); + + return OK; +} + +void MediaReceiver::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatInit: + { + int32_t mode; + CHECK(msg->findInt32("mode", &mode)); + + CHECK_EQ(mMode, MODE_UNDEFINED); + mMode = (Mode)mode; + + if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { + notifyInitDone(mInitStatus); + } + + mTSParser = new ATSParser( + ATSParser::ALIGNED_VIDEO_DATA + | ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE); + + mFormatKnownMask = 0; + break; + } + + case kWhatReceiverNotify: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + if (generation != mGeneration) { + break; + } + + onReceiverNotify(msg); + break; + } + + default: + TRESPASS(); + } +} + +void MediaReceiver::onReceiverNotify(const sp &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case RTPReceiver::kWhatInitDone: + { + ++mInitDoneCount; + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + if (err != OK) { + mInitStatus = err; + ++mGeneration; + } + + if (mMode != MODE_UNDEFINED) { + if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { + notifyInitDone(mInitStatus); + } + } + break; + } + + case RTPReceiver::kWhatError: + { + int32_t err; + CHECK(msg->findInt32("err", &err)); + + notifyError(err); + break; + } + + case RTPReceiver::kWhatAccessUnit: + { + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + int32_t followsDiscontinuity; + if (!msg->findInt32( + "followsDiscontinuity", &followsDiscontinuity)) { + followsDiscontinuity = 0; + } + + if (mMode == MODE_TRANSPORT_STREAM) { + if (followsDiscontinuity) { + mTSParser->signalDiscontinuity( + ATSParser::DISCONTINUITY_TIME, NULL /* extra */); + } + + for (size_t offset = 0; + offset < accessUnit->size(); offset += 188) { + status_t err = mTSParser->feedTSPacket( + accessUnit->data() + offset, 188); + + if (err != OK) { + notifyError(err); + break; + } + } + + drainPackets(0 /* trackIndex */, ATSParser::VIDEO); + drainPackets(1 /* trackIndex */, ATSParser::AUDIO); + } else { + postAccessUnit(trackIndex, accessUnit, NULL); + } + break; + } + + case RTPReceiver::kWhatPacketLost: + { + notifyPacketLost(); + break; + } + + default: + TRESPASS(); + } +} + +void MediaReceiver::drainPackets( + size_t trackIndex, ATSParser::SourceType type) { + sp source = + static_cast( + mTSParser->getSource(type).get()); + + if (source == NULL) { + return; + } + + sp format; + if (!(mFormatKnownMask & (1ul << trackIndex))) { + sp meta = source->getFormat(); + CHECK(meta != NULL); + + CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format)); + + mFormatKnownMask |= 1ul << trackIndex; + } + + status_t finalResult; + while (source->hasBufferAvailable(&finalResult)) { + sp accessUnit; + status_t err = source->dequeueAccessUnit(&accessUnit); + if (err == OK) { + postAccessUnit(trackIndex, accessUnit, format); + format.clear(); + } else if (err != INFO_DISCONTINUITY) { + notifyError(err); + } + } + + if (finalResult != OK) { + notifyError(finalResult); + } +} + +void MediaReceiver::notifyInitDone(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInitDone); + notify->setInt32("err", err); + notify->post(); +} + +void MediaReceiver::notifyError(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +void MediaReceiver::notifyPacketLost() { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatPacketLost); + notify->post(); +} + +void MediaReceiver::postAccessUnit( + size_t trackIndex, + const sp &accessUnit, + const sp &format) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatAccessUnit); + notify->setSize("trackIndex", trackIndex); + notify->setBuffer("accessUnit", accessUnit); + + if (format != NULL) { + notify->setMessage("format", format); + } + + notify->post(); +} + +status_t MediaReceiver::informSender( + size_t trackIndex, const sp ¶ms) { + if (trackIndex >= mTrackInfos.size()) { + return -ERANGE; + } + + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + return info->mReceiver->informSender(params); +} + +} // namespace android + + diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h new file mode 100644 index 0000000..afbb407 --- /dev/null +++ b/media/libstagefright/wifi-display/MediaReceiver.h @@ -0,0 +1,111 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ATSParser.h" +#include "rtp/RTPReceiver.h" + +namespace android { + +struct ABuffer; +struct ANetworkSession; +struct AMessage; +struct ATSParser; + +// This class facilitates receiving of media data for one or more tracks +// over RTP. Either a 1:1 track to RTP channel mapping is used or a single +// RTP channel provides the data for a transport stream that is consequently +// demuxed and its track's data provided to the observer. +struct MediaReceiver : public AHandler { + enum { + kWhatInitDone, + kWhatError, + kWhatAccessUnit, + kWhatPacketLost, + }; + + MediaReceiver( + const sp &netSession, + const sp ¬ify); + + ssize_t addTrack( + RTPReceiver::TransportMode rtpMode, + RTPReceiver::TransportMode rtcpMode, + int32_t *localRTPPort); + + status_t connectTrack( + size_t trackIndex, + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort); + + enum Mode { + MODE_UNDEFINED, + MODE_TRANSPORT_STREAM, + MODE_TRANSPORT_STREAM_RAW, + MODE_ELEMENTARY_STREAMS, + }; + status_t initAsync(Mode mode); + + status_t informSender(size_t trackIndex, const sp ¶ms); + +protected: + virtual void onMessageReceived(const sp &msg); + virtual ~MediaReceiver(); + +private: + enum { + kWhatInit, + kWhatReceiverNotify, + }; + + struct TrackInfo { + sp mReceiver; + }; + + sp mNetSession; + sp mNotify; + + Mode mMode; + int32_t mGeneration; + + Vector mTrackInfos; + + status_t mInitStatus; + size_t mInitDoneCount; + + sp mTSParser; + uint32_t mFormatKnownMask; + + void onReceiverNotify(const sp &msg); + + void drainPackets(size_t trackIndex, ATSParser::SourceType type); + + void notifyInitDone(status_t err); + void notifyError(status_t err); + void notifyPacketLost(); + + void postAccessUnit( + size_t trackIndex, + const sp &accessUnit, + const sp &format); + + DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver); +}; + +} // namespace android + diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 8a3566f..33af66d 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -341,6 +341,22 @@ void MediaSender::onSenderNotify(const sp &msg) { break; } + case kWhatInformSender: + { + int64_t avgLatencyUs; + CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInformSender); + notify->setInt64("avgLatencyUs", avgLatencyUs); + notify->setInt64("maxLatencyUs", maxLatencyUs); + notify->post(); + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h index 64722c5..04538ea 100644 --- a/media/libstagefright/wifi-display/MediaSender.h +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -43,6 +43,7 @@ struct MediaSender : public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, + kWhatInformSender, }; MediaSender( diff --git a/media/libstagefright/wifi-display/SNTPClient.cpp b/media/libstagefright/wifi-display/SNTPClient.cpp new file mode 100644 index 0000000..5c0af6a --- /dev/null +++ b/media/libstagefright/wifi-display/SNTPClient.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2013, 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 "SNTPClient.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace android { + +SNTPClient::SNTPClient() { +} + +status_t SNTPClient::requestTime(const char *host) { + struct hostent *ent; + int64_t requestTimeNTP, requestTimeUs; + ssize_t n; + int64_t responseTimeUs, responseTimeNTP; + int64_t originateTimeNTP, receiveTimeNTP, transmitTimeNTP; + int64_t roundTripTimeNTP, clockOffsetNTP; + + status_t err = UNKNOWN_ERROR; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + + if (s < 0) { + err = -errno; + + goto bail; + } + + ent = gethostbyname(host); + + if (ent == NULL) { + err = -ENOENT; + goto bail2; + } + + struct sockaddr_in hostAddr; + memset(hostAddr.sin_zero, 0, sizeof(hostAddr.sin_zero)); + hostAddr.sin_family = AF_INET; + hostAddr.sin_port = htons(kNTPPort); + hostAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; + + uint8_t packet[kNTPPacketSize]; + memset(packet, 0, sizeof(packet)); + + packet[0] = kNTPModeClient | (kNTPVersion << 3); + + requestTimeNTP = getNowNTP(); + requestTimeUs = ALooper::GetNowUs(); + writeTimeStamp(&packet[kNTPTransmitTimeOffset], requestTimeNTP); + + n = sendto( + s, packet, sizeof(packet), 0, + (const struct sockaddr *)&hostAddr, sizeof(hostAddr)); + + if (n < 0) { + err = -errno; + goto bail2; + } + + memset(packet, 0, sizeof(packet)); + + do { + n = recv(s, packet, sizeof(packet), 0); + } while (n < 0 && errno == EINTR); + + if (n < 0) { + err = -errno; + goto bail2; + } + + responseTimeUs = ALooper::GetNowUs(); + + responseTimeNTP = requestTimeNTP + makeNTP(responseTimeUs - requestTimeUs); + + originateTimeNTP = readTimeStamp(&packet[kNTPOriginateTimeOffset]); + receiveTimeNTP = readTimeStamp(&packet[kNTPReceiveTimeOffset]); + transmitTimeNTP = readTimeStamp(&packet[kNTPTransmitTimeOffset]); + + roundTripTimeNTP = + makeNTP(responseTimeUs - requestTimeUs) + - (transmitTimeNTP - receiveTimeNTP); + + clockOffsetNTP = + ((receiveTimeNTP - originateTimeNTP) + + (transmitTimeNTP - responseTimeNTP)) / 2; + + mTimeReferenceNTP = responseTimeNTP + clockOffsetNTP; + mTimeReferenceUs = responseTimeUs; + mRoundTripTimeNTP = roundTripTimeNTP; + + err = OK; + +bail2: + close(s); + s = -1; + +bail: + return err; +} + +int64_t SNTPClient::adjustTimeUs(int64_t timeUs) const { + uint64_t nowNTP = + mTimeReferenceNTP + makeNTP(timeUs - mTimeReferenceUs); + + int64_t nowUs = + (nowNTP >> 32) * 1000000ll + + ((nowNTP & 0xffffffff) * 1000000ll) / (1ll << 32); + + nowUs -= ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; + + return nowUs; +} + +// static +void SNTPClient::writeTimeStamp(uint8_t *dst, uint64_t ntpTime) { + *dst++ = (ntpTime >> 56) & 0xff; + *dst++ = (ntpTime >> 48) & 0xff; + *dst++ = (ntpTime >> 40) & 0xff; + *dst++ = (ntpTime >> 32) & 0xff; + *dst++ = (ntpTime >> 24) & 0xff; + *dst++ = (ntpTime >> 16) & 0xff; + *dst++ = (ntpTime >> 8) & 0xff; + *dst++ = ntpTime & 0xff; +} + +// static +uint64_t SNTPClient::readTimeStamp(const uint8_t *dst) { + return U64_AT(dst); +} + +// static +uint64_t SNTPClient::getNowNTP() { + struct timeval tv; + gettimeofday(&tv, NULL /* time zone */); + + uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec; + + nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; + + return makeNTP(nowUs); +} + +// static +uint64_t SNTPClient::makeNTP(uint64_t deltaUs) { + uint64_t hi = deltaUs / 1000000ll; + uint64_t lo = ((1ll << 32) * (deltaUs % 1000000ll)) / 1000000ll; + + return (hi << 32) | lo; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/SNTPClient.h b/media/libstagefright/wifi-display/SNTPClient.h new file mode 100644 index 0000000..967d1fc --- /dev/null +++ b/media/libstagefright/wifi-display/SNTPClient.h @@ -0,0 +1,62 @@ +/* + * Copyright 2013, 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 SNTP_CLIENT_H_ + +#define SNTP_CLIENT_H_ + +#include +#include + +namespace android { + +// Implementation of the SNTP (Simple Network Time Protocol) +struct SNTPClient { + SNTPClient(); + + status_t requestTime(const char *host); + + // given a time obtained from ALooper::GetNowUs() + // return the number of us elapsed since Jan 1 1970 00:00:00 (UTC). + int64_t adjustTimeUs(int64_t timeUs) const; + +private: + enum { + kNTPPort = 123, + kNTPPacketSize = 48, + kNTPModeClient = 3, + kNTPVersion = 3, + kNTPTransmitTimeOffset = 40, + kNTPOriginateTimeOffset = 24, + kNTPReceiveTimeOffset = 32, + }; + + uint64_t mTimeReferenceNTP; + int64_t mTimeReferenceUs; + int64_t mRoundTripTimeNTP; + + static void writeTimeStamp(uint8_t *dst, uint64_t ntpTime); + static uint64_t readTimeStamp(const uint8_t *dst); + + static uint64_t getNowNTP(); + static uint64_t makeNTP(uint64_t deltaUs); + + DISALLOW_EVIL_CONSTRUCTORS(SNTPClient); +}; + +} // namespace android + +#endif // SNTP_CLIENT_H_ diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp new file mode 100644 index 0000000..cb429bc --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSyncer.cpp @@ -0,0 +1,338 @@ +/* + * Copyright 2013, 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_NEBUG 0 +#define LOG_TAG "TimeSyncer" +#include + +#include "TimeSyncer.h" + +#include "ANetworkSession.h" + +#include +#include +#include +#include +#include +#include + +namespace android { + +TimeSyncer::TimeSyncer( + const sp &netSession, const sp ¬ify) + : mNetSession(netSession), + mNotify(notify), + mIsServer(false), + mConnected(false), + mUDPSession(0), + mSeqNo(0), + mTotalTimeUs(0.0), + mPendingT1(0ll), + mTimeoutGeneration(0) { +} + +TimeSyncer::~TimeSyncer() { +} + +void TimeSyncer::startServer(unsigned localPort) { + sp msg = new AMessage(kWhatStartServer, id()); + msg->setInt32("localPort", localPort); + msg->post(); +} + +void TimeSyncer::startClient(const char *remoteHost, unsigned remotePort) { + sp msg = new AMessage(kWhatStartClient, id()); + msg->setString("remoteHost", remoteHost); + msg->setInt32("remotePort", remotePort); + msg->post(); +} + +void TimeSyncer::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatStartClient: + { + AString remoteHost; + CHECK(msg->findString("remoteHost", &remoteHost)); + + int32_t remotePort; + CHECK(msg->findInt32("remotePort", &remotePort)); + + sp notify = new AMessage(kWhatUDPNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createUDPSession( + 0 /* localPort */, + remoteHost.c_str(), + remotePort, + notify, + &mUDPSession)); + + postSendPacket(); + break; + } + + case kWhatStartServer: + { + mIsServer = true; + + int32_t localPort; + CHECK(msg->findInt32("localPort", &localPort)); + + sp notify = new AMessage(kWhatUDPNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createUDPSession( + localPort, notify, &mUDPSession)); + + break; + } + + case kWhatSendPacket: + { + if (mHistory.size() == 0) { + ALOGI("starting batch"); + } + + TimeInfo ti; + memset(&ti, 0, sizeof(ti)); + + ti.mT1 = ALooper::GetNowUs(); + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mUDPSession, &ti, sizeof(ti))); + + mPendingT1 = ti.mT1; + postTimeout(); + break; + } + + case kWhatTimedOut: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mTimeoutGeneration) { + break; + } + + ALOGI("timed out, sending another request"); + postSendPacket(); + break; + } + + case kWhatUDPNotify: + { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred in session %d (%d, '%s/%s').", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + mNetSession->destroySession(sessionID); + + cancelTimeout(); + + notifyError(err); + break; + } + + case ANetworkSession::kWhatDatagram: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + sp packet; + CHECK(msg->findBuffer("data", &packet)); + + int64_t arrivalTimeUs; + CHECK(packet->meta()->findInt64( + "arrivalTimeUs", &arrivalTimeUs)); + + CHECK_EQ(packet->size(), sizeof(TimeInfo)); + + TimeInfo *ti = (TimeInfo *)packet->data(); + + if (mIsServer) { + if (!mConnected) { + AString fromAddr; + CHECK(msg->findString("fromAddr", &fromAddr)); + + int32_t fromPort; + CHECK(msg->findInt32("fromPort", &fromPort)); + + CHECK_EQ((status_t)OK, + mNetSession->connectUDPSession( + mUDPSession, fromAddr.c_str(), fromPort)); + + mConnected = true; + } + + ti->mT2 = arrivalTimeUs; + ti->mT3 = ALooper::GetNowUs(); + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mUDPSession, ti, sizeof(*ti))); + } else { + if (ti->mT1 != mPendingT1) { + break; + } + + cancelTimeout(); + mPendingT1 = 0; + + ti->mT4 = arrivalTimeUs; + + // One way delay for a packet to travel from client + // to server or back (assumed to be the same either way). + int64_t delay = + (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; + + // Offset between the client clock (T1, T4) and the + // server clock (T2, T3) timestamps. + int64_t offset = + (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; + + mHistory.push_back(*ti); + + ALOGV("delay = %lld us,\toffset %lld us", + delay, + offset); + + if (mHistory.size() < kNumPacketsPerBatch) { + postSendPacket(1000000ll / 30); + } else { + notifyOffset(); + + ALOGI("batch done"); + + mHistory.clear(); + postSendPacket(kBatchDelayUs); + } + } + break; + } + + default: + TRESPASS(); + } + + break; + } + + default: + TRESPASS(); + } +} + +void TimeSyncer::postSendPacket(int64_t delayUs) { + (new AMessage(kWhatSendPacket, id()))->post(delayUs); +} + +void TimeSyncer::postTimeout() { + sp msg = new AMessage(kWhatTimedOut, id()); + msg->setInt32("generation", mTimeoutGeneration); + msg->post(kTimeoutDelayUs); +} + +void TimeSyncer::cancelTimeout() { + ++mTimeoutGeneration; +} + +void TimeSyncer::notifyError(status_t err) { + if (mNotify == NULL) { + looper()->stop(); + return; + } + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +// static +int TimeSyncer::CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2) { + int64_t rt1 = ti1->mT4 - ti1->mT1; + int64_t rt2 = ti2->mT4 - ti2->mT1; + + if (rt1 < rt2) { + return -1; + } else if (rt1 > rt2) { + return 1; + } + + return 0; +} + +void TimeSyncer::notifyOffset() { + mHistory.sort(CompareRountripTime); + + int64_t sum = 0ll; + size_t count = 0; + + // Only consider the third of the information associated with the best + // (smallest) roundtrip times. + for (size_t i = 0; i < mHistory.size() / 3; ++i) { + const TimeInfo *ti = &mHistory[i]; + +#if 0 + // One way delay for a packet to travel from client + // to server or back (assumed to be the same either way). + int64_t delay = + (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; +#endif + + // Offset between the client clock (T1, T4) and the + // server clock (T2, T3) timestamps. + int64_t offset = + (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; + + ALOGV("(%d) RT: %lld us, offset: %lld us", + i, ti->mT4 - ti->mT1, offset); + + sum += offset; + ++count; + } + + if (mNotify == NULL) { + ALOGI("avg. offset is %lld", sum / count); + return; + } + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatTimeOffset); + notify->setInt64("offset", sum / count); + notify->post(); +} + +} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h new file mode 100644 index 0000000..4e7571f --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSyncer.h @@ -0,0 +1,109 @@ +/* + * Copyright 2013, 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 TIME_SYNCER_H_ + +#define TIME_SYNCER_H_ + +#include + +namespace android { + +struct ANetworkSession; + +/* + TimeSyncer allows us to synchronize time between a client and a server. + The client sends a UDP packet containing its send-time to the server, + the server sends that packet back to the client amended with information + about when it was received as well as the time the reply was sent back. + Finally the client receives the reply and has now enough information to + compute the clock offset between client and server assuming that packet + exchange is symmetric, i.e. time for a packet client->server and + server->client is roughly equal. + This exchange is repeated a number of times and the average offset computed + over the 30% of packets that had the lowest roundtrip times. + The offset is determined every 10 secs to account for slight differences in + clock frequency. +*/ +struct TimeSyncer : public AHandler { + enum { + kWhatError, + kWhatTimeOffset, + }; + TimeSyncer( + const sp &netSession, + const sp ¬ify); + + void startServer(unsigned localPort); + void startClient(const char *remoteHost, unsigned remotePort); + +protected: + virtual ~TimeSyncer(); + + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatStartServer, + kWhatStartClient, + kWhatUDPNotify, + kWhatSendPacket, + kWhatTimedOut, + }; + + struct TimeInfo { + int64_t mT1; // client timestamp at send + int64_t mT2; // server timestamp at receive + int64_t mT3; // server timestamp at send + int64_t mT4; // client timestamp at receive + }; + + enum { + kNumPacketsPerBatch = 30, + }; + static const int64_t kTimeoutDelayUs = 500000ll; + static const int64_t kBatchDelayUs = 60000000ll; // every minute + + sp mNetSession; + sp mNotify; + + bool mIsServer; + bool mConnected; + int32_t mUDPSession; + uint32_t mSeqNo; + double mTotalTimeUs; + + Vector mHistory; + + int64_t mPendingT1; + int32_t mTimeoutGeneration; + + void postSendPacket(int64_t delayUs = 0ll); + + void postTimeout(); + void cancelTimeout(); + + void notifyError(status_t err); + void notifyOffset(); + + static int CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2); + + DISALLOW_EVIL_CONSTRUCTORS(TimeSyncer); +}; + +} // namespace android + +#endif // TIME_SYNCER_H_ diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp new file mode 100644 index 0000000..0779bf5 --- /dev/null +++ b/media/libstagefright/wifi-display/nettest.cpp @@ -0,0 +1,400 @@ +/* + * Copyright 2013, 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_NEBUG 0 +#define LOG_TAG "nettest" +#include + +#include "ANetworkSession.h" +#include "TimeSyncer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +struct TestHandler : public AHandler { + TestHandler(const sp &netSession); + + void listen(int32_t port); + void connect(const char *host, int32_t port); + +protected: + virtual ~TestHandler(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kTimeSyncerPort = 8123, + }; + + enum { + kWhatListen, + kWhatConnect, + kWhatTimeSyncerNotify, + kWhatNetNotify, + kWhatSendMore, + kWhatStop, + }; + + sp mNetSession; + sp mTimeSyncer; + + int32_t mServerSessionID; + int32_t mSessionID; + + int64_t mTimeOffsetUs; + bool mTimeOffsetValid; + + int32_t mCounter; + + int64_t mMaxDelayMs; + + void dumpDelay(int32_t counter, int64_t delayMs); + + DISALLOW_EVIL_CONSTRUCTORS(TestHandler); +}; + +TestHandler::TestHandler(const sp &netSession) + : mNetSession(netSession), + mServerSessionID(0), + mSessionID(0), + mTimeOffsetUs(-1ll), + mTimeOffsetValid(false), + mCounter(0), + mMaxDelayMs(-1ll) { +} + +TestHandler::~TestHandler() { +} + +void TestHandler::listen(int32_t port) { + sp msg = new AMessage(kWhatListen, id()); + msg->setInt32("port", port); + msg->post(); +} + +void TestHandler::connect(const char *host, int32_t port) { + sp msg = new AMessage(kWhatConnect, id()); + msg->setString("host", host); + msg->setInt32("port", port); + msg->post(); +} + +void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) { + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + if (delayMs > mMaxDelayMs) { + mMaxDelayMs = delayMs; + } + + ALOGI("[%d] (%4lld ms / %4lld ms) %s", + counter, + delayMs, + mMaxDelayMs, + kPattern + kPatternSize - n); +} + +void TestHandler::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatListen: + { + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + notify = new AMessage(kWhatNetNotify, id()); + + int32_t port; + CHECK(msg->findInt32("port", &port)); + + struct in_addr ifaceAddr; + ifaceAddr.s_addr = INADDR_ANY; + + CHECK_EQ((status_t)OK, + mNetSession->createTCPDatagramSession( + ifaceAddr, + port, + notify, + &mServerSessionID)); + break; + } + + case kWhatConnect: + { + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + mTimeSyncer->startServer(kTimeSyncerPort); + + AString host; + CHECK(msg->findString("host", &host)); + + int32_t port; + CHECK(msg->findInt32("port", &port)); + + notify = new AMessage(kWhatNetNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createTCPDatagramSession( + 0 /* localPort */, + host.c_str(), + port, + notify, + &mSessionID)); + break; + } + + case kWhatNetNotify: + { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatConnected: + { + ALOGI("kWhatConnected"); + + (new AMessage(kWhatSendMore, id()))->post(); + break; + } + + case ANetworkSession::kWhatClientConnected: + { + ALOGI("kWhatClientConnected"); + + CHECK_EQ(mSessionID, 0); + CHECK(msg->findInt32("sessionID", &mSessionID)); + + AString clientIP; + CHECK(msg->findString("client-ip", &clientIP)); + + mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort); + break; + } + + case ANetworkSession::kWhatDatagram: + { + sp packet; + CHECK(msg->findBuffer("data", &packet)); + + CHECK_EQ(packet->size(), 12u); + + int32_t counter = U32_AT(packet->data()); + int64_t timeUs = U64_AT(packet->data() + 4); + + if (mTimeOffsetValid) { + timeUs -= mTimeOffsetUs; + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayMs = (nowUs - timeUs) / 1000ll; + + dumpDelay(counter, delayMs); + } else { + ALOGI("received %d", counter); + } + break; + } + + case ANetworkSession::kWhatError: + { + ALOGE("kWhatError"); + break; + } + + default: + TRESPASS(); + } + break; + } + + case kWhatTimeSyncerNotify: + { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + break; + } + + case kWhatSendMore: + { + uint8_t buffer[4 + 8]; + buffer[0] = mCounter >> 24; + buffer[1] = (mCounter >> 16) & 0xff; + buffer[2] = (mCounter >> 8) & 0xff; + buffer[3] = mCounter & 0xff; + + int64_t nowUs = ALooper::GetNowUs(); + + buffer[4] = nowUs >> 56; + buffer[5] = (nowUs >> 48) & 0xff; + buffer[6] = (nowUs >> 40) & 0xff; + buffer[7] = (nowUs >> 32) & 0xff; + buffer[8] = (nowUs >> 24) & 0xff; + buffer[9] = (nowUs >> 16) & 0xff; + buffer[10] = (nowUs >> 8) & 0xff; + buffer[11] = nowUs & 0xff; + + ++mCounter; + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mSessionID, + buffer, + sizeof(buffer), + true /* timeValid */, + nowUs)); + + msg->post(100000ll); + break; + } + + case kWhatStop: + { + if (mSessionID != 0) { + mNetSession->destroySession(mSessionID); + mSessionID = 0; + } + + if (mServerSessionID != 0) { + mNetSession->destroySession(mServerSessionID); + mServerSessionID = 0; + } + + looper()->stop(); + break; + } + + default: + TRESPASS(); + } +} + +} // namespace android + +static void usage(const char *me) { + fprintf(stderr, + "usage: %s -c host:port\tconnect to remote host\n" + " -l port \tlisten\n", + me); +} + +int main(int argc, char **argv) { + using namespace android; + + // srand(time(NULL)); + + ProcessState::self()->startThreadPool(); + + DataSource::RegisterDefaultSniffers(); + + int32_t connectToPort = -1; + AString connectToHost; + + int32_t listenOnPort = -1; + + int res; + while ((res = getopt(argc, argv, "hc:l:")) >= 0) { + switch (res) { + case 'c': + { + const char *colonPos = strrchr(optarg, ':'); + + if (colonPos == NULL) { + usage(argv[0]); + exit(1); + } + + connectToHost.setTo(optarg, colonPos - optarg); + + char *end; + connectToPort = strtol(colonPos + 1, &end, 10); + + if (*end != '\0' || end == colonPos + 1 + || connectToPort < 0 || connectToPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + break; + } + + case 'l': + { + char *end; + listenOnPort = strtol(optarg, &end, 10); + + if (*end != '\0' || end == optarg + || listenOnPort < 0 || listenOnPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + break; + } + + case '?': + case 'h': + usage(argv[0]); + exit(1); + } + } + + if ((listenOnPort < 0 && connectToPort < 0) + || (listenOnPort >= 0 && connectToPort >= 0)) { + fprintf(stderr, + "You need to select either client or server mode.\n"); + exit(1); + } + + sp netSession = new ANetworkSession; + netSession->start(); + + sp looper = new ALooper; + + sp handler = new TestHandler(netSession); + looper->registerHandler(handler); + + if (listenOnPort) { + handler->listen(listenOnPort); + } + + if (connectToPort >= 0) { + handler->connect(connectToHost.c_str(), connectToPort); + } + + looper->start(true /* runOnCallingThread */); + + return 0; +} diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp new file mode 100644 index 0000000..7a96081 --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp @@ -0,0 +1,328 @@ +/* + * Copyright 2013, 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 "RTPAssembler" +#include + +#include "RTPAssembler.h" + +#include +#include +#include +#include +#include + +namespace android { + +RTPReceiver::Assembler::Assembler(const sp ¬ify) + : mNotify(notify) { +} + +void RTPReceiver::Assembler::postAccessUnit( + const sp &accessUnit, bool followsDiscontinuity) { + sp notify = mNotify->dup(); + notify->setInt32("what", RTPReceiver::kWhatAccessUnit); + notify->setBuffer("accessUnit", accessUnit); + notify->setInt32("followsDiscontinuity", followsDiscontinuity); + notify->post(); +} +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::TSAssembler::TSAssembler(const sp ¬ify) + : Assembler(notify), + mSawDiscontinuity(false) { +} + +void RTPReceiver::TSAssembler::signalDiscontinuity() { + mSawDiscontinuity = true; +} + +status_t RTPReceiver::TSAssembler::processPacket(const sp &packet) { + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + + packet->meta()->setInt64("timeUs", (rtpTime * 100ll) / 9); + + postAccessUnit(packet, mSawDiscontinuity); + + if (mSawDiscontinuity) { + mSawDiscontinuity = false; + } + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::H264Assembler::H264Assembler(const sp ¬ify) + : Assembler(notify), + mState(0), + mIndicator(0), + mNALType(0), + mAccessUnitRTPTime(0) { +} + +void RTPReceiver::H264Assembler::signalDiscontinuity() { + reset(); +} + +status_t RTPReceiver::H264Assembler::processPacket(const sp &packet) { + status_t err = internalProcessPacket(packet); + + if (err != OK) { + reset(); + } + + return err; +} + +status_t RTPReceiver::H264Assembler::internalProcessPacket( + const sp &packet) { + const uint8_t *data = packet->data(); + size_t size = packet->size(); + + switch (mState) { + case 0: + { + if (size < 1 || (data[0] & 0x80)) { + ALOGV("Malformed H264 RTP packet (empty or F-bit set)"); + return ERROR_MALFORMED; + } + + unsigned nalType = data[0] & 0x1f; + if (nalType >= 1 && nalType <= 23) { + addSingleNALUnit(packet); + ALOGV("added single NAL packet"); + } else if (nalType == 28) { + // FU-A + unsigned indicator = data[0]; + CHECK((indicator & 0x1f) == 28); + + if (size < 2) { + ALOGV("Malformed H264 FU-A packet (single byte)"); + return ERROR_MALFORMED; + } + + if (!(data[1] & 0x80)) { + ALOGV("Malformed H264 FU-A packet (no start bit)"); + return ERROR_MALFORMED; + } + + mIndicator = data[0]; + mNALType = data[1] & 0x1f; + uint32_t nri = (data[0] >> 5) & 3; + + clearAccumulator(); + + uint8_t byte = mNALType | (nri << 5); + appendToAccumulator(&byte, 1); + appendToAccumulator(data + 2, size - 2); + + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + mAccumulator->meta()->setInt32("rtp-time", rtpTime); + + if (data[1] & 0x40) { + // Huh? End bit also set on the first buffer. + addSingleNALUnit(mAccumulator); + clearAccumulator(); + + ALOGV("added FU-A"); + break; + } + + mState = 1; + } else if (nalType == 24) { + // STAP-A + + status_t err = addSingleTimeAggregationPacket(packet); + if (err != OK) { + return err; + } + } else { + ALOGV("Malformed H264 packet (unknown type %d)", nalType); + return ERROR_UNSUPPORTED; + } + break; + } + + case 1: + { + if (size < 2 + || data[0] != mIndicator + || (data[1] & 0x1f) != mNALType + || (data[1] & 0x80)) { + ALOGV("Malformed H264 FU-A packet (indicator, " + "type or start bit mismatch)"); + + return ERROR_MALFORMED; + } + + appendToAccumulator(data + 2, size - 2); + + if (data[1] & 0x40) { + addSingleNALUnit(mAccumulator); + + clearAccumulator(); + mState = 0; + + ALOGV("added FU-A"); + } + break; + } + + default: + TRESPASS(); + } + + int32_t marker; + CHECK(packet->meta()->findInt32("M", &marker)); + + if (marker) { + flushAccessUnit(); + } + + return OK; +} + +void RTPReceiver::H264Assembler::reset() { + mNALUnits.clear(); + + clearAccumulator(); + mState = 0; +} + +void RTPReceiver::H264Assembler::clearAccumulator() { + if (mAccumulator != NULL) { + // XXX Too expensive. + mAccumulator.clear(); + } +} + +void RTPReceiver::H264Assembler::appendToAccumulator( + const void *data, size_t size) { + if (mAccumulator == NULL) { + mAccumulator = new ABuffer(size); + memcpy(mAccumulator->data(), data, size); + return; + } + + if (mAccumulator->size() + size > mAccumulator->capacity()) { + sp buf = new ABuffer(mAccumulator->size() + size); + memcpy(buf->data(), mAccumulator->data(), mAccumulator->size()); + buf->setRange(0, mAccumulator->size()); + + int32_t rtpTime; + if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) { + buf->meta()->setInt32("rtp-time", rtpTime); + } + + mAccumulator = buf; + } + + memcpy(mAccumulator->data() + mAccumulator->size(), data, size); + mAccumulator->setRange(0, mAccumulator->size() + size); +} + +void RTPReceiver::H264Assembler::addSingleNALUnit(const sp &packet) { + if (mNALUnits.empty()) { + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + + mAccessUnitRTPTime = rtpTime; + } + + mNALUnits.push_back(packet); +} + +void RTPReceiver::H264Assembler::flushAccessUnit() { + if (mNALUnits.empty()) { + return; + } + + size_t totalSize = 0; + for (List >::iterator it = mNALUnits.begin(); + it != mNALUnits.end(); ++it) { + totalSize += 4 + (*it)->size(); + } + + sp accessUnit = new ABuffer(totalSize); + size_t offset = 0; + for (List >::iterator it = mNALUnits.begin(); + it != mNALUnits.end(); ++it) { + const sp nalUnit = *it; + + memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4); + + memcpy(accessUnit->data() + offset + 4, + nalUnit->data(), + nalUnit->size()); + + offset += 4 + nalUnit->size(); + } + + mNALUnits.clear(); + + accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll); + postAccessUnit(accessUnit, false /* followsDiscontinuity */); +} + +status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket( + const sp &packet) { + const uint8_t *data = packet->data(); + size_t size = packet->size(); + + if (size < 3) { + ALOGV("Malformed H264 STAP-A packet (too small)"); + return ERROR_MALFORMED; + } + + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + + ++data; + --size; + while (size >= 2) { + size_t nalSize = (data[0] << 8) | data[1]; + + if (size < nalSize + 2) { + ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)"); + return ERROR_MALFORMED; + } + + sp unit = new ABuffer(nalSize); + memcpy(unit->data(), &data[2], nalSize); + + unit->meta()->setInt32("rtp-time", rtpTime); + + addSingleNALUnit(unit); + + data += 2 + nalSize; + size -= 2 + nalSize; + } + + if (size != 0) { + ALOGV("Unexpected padding at end of STAP-A packet."); + } + + ALOGV("added STAP-A"); + + return OK; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.h b/media/libstagefright/wifi-display/rtp/RTPAssembler.h new file mode 100644 index 0000000..e456d32 --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.h @@ -0,0 +1,92 @@ +/* + * Copyright 2013, 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 RTP_ASSEMBLER_H_ + +#define RTP_ASSEMBLER_H_ + +#include "RTPReceiver.h" + +namespace android { + +// A helper class to reassemble the payload of RTP packets into access +// units depending on the packetization scheme. +struct RTPReceiver::Assembler : public RefBase { + Assembler(const sp ¬ify); + + virtual void signalDiscontinuity() = 0; + virtual status_t processPacket(const sp &packet) = 0; + +protected: + virtual ~Assembler() {} + + void postAccessUnit( + const sp &accessUnit, bool followsDiscontinuity); + +private: + sp mNotify; + + DISALLOW_EVIL_CONSTRUCTORS(Assembler); +}; + +struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler { + TSAssembler(const sp ¬ify); + + virtual void signalDiscontinuity(); + virtual status_t processPacket(const sp &packet); + +private: + bool mSawDiscontinuity; + + DISALLOW_EVIL_CONSTRUCTORS(TSAssembler); +}; + +struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler { + H264Assembler(const sp ¬ify); + + virtual void signalDiscontinuity(); + virtual status_t processPacket(const sp &packet); + +private: + int32_t mState; + + uint8_t mIndicator; + uint8_t mNALType; + + sp mAccumulator; + + List > mNALUnits; + int32_t mAccessUnitRTPTime; + + status_t internalProcessPacket(const sp &packet); + + void addSingleNALUnit(const sp &packet); + status_t addSingleTimeAggregationPacket(const sp &packet); + + void flushAccessUnit(); + + void clearAccumulator(); + void appendToAccumulator(const void *data, size_t size); + + void reset(); + + DISALLOW_EVIL_CONSTRUCTORS(H264Assembler); +}; + +} // namespace android + +#endif // RTP_ASSEMBLER_H_ + diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp new file mode 100644 index 0000000..8fa1dae --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -0,0 +1,1153 @@ +/* + * Copyright 2013, 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 "RTPReceiver" +#include + +#include "RTPAssembler.h" +#include "RTPReceiver.h" + +#include "ANetworkSession.h" + +#include +#include +#include +#include +#include +#include + +#define TRACK_PACKET_LOSS 0 + +namespace android { + +//////////////////////////////////////////////////////////////////////////////// + +struct RTPReceiver::Source : public AHandler { + Source(RTPReceiver *receiver, uint32_t ssrc); + + void onPacketReceived(uint16_t seq, const sp &buffer); + + void addReportBlock(uint32_t ssrc, const sp &buf); + +protected: + virtual ~Source(); + + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatRetransmit, + kWhatDeclareLost, + }; + + static const uint32_t kMinSequential = 2; + static const uint32_t kMaxDropout = 3000; + static const uint32_t kMaxMisorder = 100; + static const uint32_t kRTPSeqMod = 1u << 16; + static const int64_t kReportIntervalUs = 10000000ll; + + RTPReceiver *mReceiver; + uint32_t mSSRC; + bool mFirst; + uint16_t mMaxSeq; + uint32_t mCycles; + uint32_t mBaseSeq; + uint32_t mReceived; + uint32_t mExpectedPrior; + uint32_t mReceivedPrior; + + int64_t mFirstArrivalTimeUs; + int64_t mFirstRTPTimeUs; + + // Ordered by extended seq number. + List > mPackets; + + enum StatusBits { + STATUS_DECLARED_LOST = 1, + STATUS_REQUESTED_RETRANSMISSION = 2, + STATUS_ARRIVED_LATE = 4, + }; +#if TRACK_PACKET_LOSS + KeyedVector mLostPackets; +#endif + + void modifyPacketStatus(int32_t extSeqNo, uint32_t mask); + + int32_t mAwaitingExtSeqNo; + bool mRequestedRetransmission; + + int32_t mActivePacketType; + sp mActiveAssembler; + + int64_t mNextReportTimeUs; + + int32_t mNumDeclaredLost; + int32_t mNumDeclaredLostPrior; + + int32_t mRetransmitGeneration; + int32_t mDeclareLostGeneration; + bool mDeclareLostTimerPending; + + void queuePacket(const sp &packet); + void dequeueMore(); + + sp getNextPacket(); + void resync(); + + void postRetransmitTimer(int64_t delayUs); + void postDeclareLostTimer(int64_t delayUs); + void cancelTimers(); + + DISALLOW_EVIL_CONSTRUCTORS(Source); +}; + +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc) + : mReceiver(receiver), + mSSRC(ssrc), + mFirst(true), + mMaxSeq(0), + mCycles(0), + mBaseSeq(0), + mReceived(0), + mExpectedPrior(0), + mReceivedPrior(0), + mFirstArrivalTimeUs(-1ll), + mFirstRTPTimeUs(-1ll), + mAwaitingExtSeqNo(-1), + mRequestedRetransmission(false), + mActivePacketType(-1), + mNextReportTimeUs(-1ll), + mNumDeclaredLost(0), + mNumDeclaredLostPrior(0), + mRetransmitGeneration(0), + mDeclareLostGeneration(0), + mDeclareLostTimerPending(false) { +} + +RTPReceiver::Source::~Source() { +} + +void RTPReceiver::Source::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatRetransmit: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mRetransmitGeneration) { + break; + } + + mRequestedRetransmission = true; + mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo); + + modifyPacketStatus( + mAwaitingExtSeqNo, STATUS_REQUESTED_RETRANSMISSION); + break; + } + + case kWhatDeclareLost: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mDeclareLostGeneration) { + break; + } + + cancelTimers(); + + ALOGV("Lost packet extSeqNo %d %s", + mAwaitingExtSeqNo, + mRequestedRetransmission ? "*" : ""); + + mRequestedRetransmission = false; + if (mActiveAssembler != NULL) { + mActiveAssembler->signalDiscontinuity(); + } + + modifyPacketStatus(mAwaitingExtSeqNo, STATUS_DECLARED_LOST); + + // resync(); + ++mAwaitingExtSeqNo; + ++mNumDeclaredLost; + + mReceiver->notifyPacketLost(); + + dequeueMore(); + break; + } + + default: + TRESPASS(); + } +} + +void RTPReceiver::Source::onPacketReceived( + uint16_t seq, const sp &buffer) { + if (mFirst) { + buffer->setInt32Data(mCycles | seq); + queuePacket(buffer); + + mFirst = false; + mBaseSeq = seq; + mMaxSeq = seq; + ++mReceived; + return; + } + + uint16_t udelta = seq - mMaxSeq; + + if (udelta < kMaxDropout) { + // In order, with permissible gap. + + if (seq < mMaxSeq) { + // Sequence number wrapped - count another 64K cycle + mCycles += kRTPSeqMod; + } + + mMaxSeq = seq; + + ++mReceived; + } else if (udelta <= kRTPSeqMod - kMaxMisorder) { + // The sequence number made a very large jump + return; + } else { + // Duplicate or reordered packet. + } + + buffer->setInt32Data(mCycles | seq); + queuePacket(buffer); +} + +void RTPReceiver::Source::queuePacket(const sp &packet) { + int32_t newExtendedSeqNo = packet->int32Data(); + + if (mFirstArrivalTimeUs < 0ll) { + mFirstArrivalTimeUs = ALooper::GetNowUs(); + + uint32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); + + mFirstRTPTimeUs = (rtpTime * 100ll) / 9ll; + } + + if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) { + // We're no longer interested in these. They're old. + ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo); + + modifyPacketStatus(newExtendedSeqNo, STATUS_ARRIVED_LATE); + return; + } + + if (mPackets.empty()) { + mPackets.push_back(packet); + dequeueMore(); + return; + } + + List >::iterator firstIt = mPackets.begin(); + List >::iterator it = --mPackets.end(); + for (;;) { + int32_t extendedSeqNo = (*it)->int32Data(); + + if (extendedSeqNo == newExtendedSeqNo) { + // Duplicate packet. + return; + } + + if (extendedSeqNo < newExtendedSeqNo) { + // Insert new packet after the one at "it". + mPackets.insert(++it, packet); + break; + } + + if (it == firstIt) { + // Insert new packet before the first existing one. + mPackets.insert(it, packet); + break; + } + + --it; + } + + dequeueMore(); +} + +void RTPReceiver::Source::dequeueMore() { + int64_t nowUs = ALooper::GetNowUs(); + if (mNextReportTimeUs < 0ll || nowUs >= mNextReportTimeUs) { + if (mNextReportTimeUs >= 0ll) { + uint32_t expected = (mMaxSeq | mCycles) - mBaseSeq + 1; + + uint32_t expectedInterval = expected - mExpectedPrior; + mExpectedPrior = expected; + + uint32_t receivedInterval = mReceived - mReceivedPrior; + mReceivedPrior = mReceived; + + int64_t lostInterval = + (int64_t)expectedInterval - (int64_t)receivedInterval; + + int32_t declaredLostInterval = + mNumDeclaredLost - mNumDeclaredLostPrior; + + mNumDeclaredLostPrior = mNumDeclaredLost; + + if (declaredLostInterval > 0) { + ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", + lostInterval, + 100.0f * lostInterval / expectedInterval, + declaredLostInterval); + } + } + + mNextReportTimeUs = nowUs + kReportIntervalUs; + +#if TRACK_PACKET_LOSS + for (size_t i = 0; i < mLostPackets.size(); ++i) { + int32_t key = mLostPackets.keyAt(i); + uint32_t value = mLostPackets.valueAt(i); + + AString status; + if (value & STATUS_REQUESTED_RETRANSMISSION) { + status.append("retrans "); + } + if (value & STATUS_ARRIVED_LATE) { + status.append("arrived-late "); + } + ALOGI("Packet %d declared lost %s", key, status.c_str()); + } +#endif + } + + sp packet; + while ((packet = getNextPacket()) != NULL) { + if (mDeclareLostTimerPending) { + cancelTimers(); + } + + CHECK_GE(mAwaitingExtSeqNo, 0); +#if TRACK_PACKET_LOSS + mLostPackets.removeItem(mAwaitingExtSeqNo); +#endif + + int32_t packetType; + CHECK(packet->meta()->findInt32("PT", &packetType)); + + if (packetType != mActivePacketType) { + mActiveAssembler = mReceiver->makeAssembler(packetType); + mActivePacketType = packetType; + } + + if (mActiveAssembler != NULL) { + status_t err = mActiveAssembler->processPacket(packet); + if (err != OK) { + ALOGV("assembler returned error %d", err); + } + } + + ++mAwaitingExtSeqNo; + } + + if (mDeclareLostTimerPending) { + return; + } + + if (mPackets.empty()) { + return; + } + + CHECK_GE(mAwaitingExtSeqNo, 0); + + const sp &firstPacket = *mPackets.begin(); + + uint32_t rtpTime; + CHECK(firstPacket->meta()->findInt32( + "rtp-time", (int32_t *)&rtpTime)); + + + int64_t rtpUs = (rtpTime * 100ll) / 9ll; + + int64_t maxArrivalTimeUs = + mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs; + + nowUs = ALooper::GetNowUs(); + + CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data()); + + ALOGV("waiting for %d, comparing against %d, %lld us left", + mAwaitingExtSeqNo, + firstPacket->int32Data(), + maxArrivalTimeUs - nowUs); + + postDeclareLostTimer(maxArrivalTimeUs + kPacketLostAfterUs); + + if (kRequestRetransmissionAfterUs > 0ll) { + postRetransmitTimer( + maxArrivalTimeUs + kRequestRetransmissionAfterUs); + } +} + +sp RTPReceiver::Source::getNextPacket() { + if (mPackets.empty()) { + return NULL; + } + + int32_t extSeqNo = (*mPackets.begin())->int32Data(); + + if (mAwaitingExtSeqNo < 0) { + mAwaitingExtSeqNo = extSeqNo; + } else if (extSeqNo != mAwaitingExtSeqNo) { + return NULL; + } + + sp packet = *mPackets.begin(); + mPackets.erase(mPackets.begin()); + + return packet; +} + +void RTPReceiver::Source::resync() { + mAwaitingExtSeqNo = -1; +} + +void RTPReceiver::Source::addReportBlock( + uint32_t ssrc, const sp &buf) { + uint32_t extMaxSeq = mMaxSeq | mCycles; + uint32_t expected = extMaxSeq - mBaseSeq + 1; + + int64_t lost = (int64_t)expected - (int64_t)mReceived; + if (lost > 0x7fffff) { + lost = 0x7fffff; + } else if (lost < -0x800000) { + lost = -0x800000; + } + + uint32_t expectedInterval = expected - mExpectedPrior; + mExpectedPrior = expected; + + uint32_t receivedInterval = mReceived - mReceivedPrior; + mReceivedPrior = mReceived; + + int64_t lostInterval = expectedInterval - receivedInterval; + + uint8_t fractionLost; + if (expectedInterval == 0 || lostInterval <=0) { + fractionLost = 0; + } else { + fractionLost = (lostInterval << 8) / expectedInterval; + } + + uint8_t *ptr = buf->data() + buf->size(); + + ptr[0] = ssrc >> 24; + ptr[1] = (ssrc >> 16) & 0xff; + ptr[2] = (ssrc >> 8) & 0xff; + ptr[3] = ssrc & 0xff; + + ptr[4] = fractionLost; + + ptr[5] = (lost >> 16) & 0xff; + ptr[6] = (lost >> 8) & 0xff; + ptr[7] = lost & 0xff; + + ptr[8] = extMaxSeq >> 24; + ptr[9] = (extMaxSeq >> 16) & 0xff; + ptr[10] = (extMaxSeq >> 8) & 0xff; + ptr[11] = extMaxSeq & 0xff; + + // XXX TODO: + + ptr[12] = 0x00; // interarrival jitter + ptr[13] = 0x00; + ptr[14] = 0x00; + ptr[15] = 0x00; + + ptr[16] = 0x00; // last SR + ptr[17] = 0x00; + ptr[18] = 0x00; + ptr[19] = 0x00; + + ptr[20] = 0x00; // delay since last SR + ptr[21] = 0x00; + ptr[22] = 0x00; + ptr[23] = 0x00; +} + +//////////////////////////////////////////////////////////////////////////////// + +RTPReceiver::RTPReceiver( + const sp &netSession, + const sp ¬ify, + uint32_t flags) + : mNetSession(netSession), + mNotify(notify), + mFlags(flags), + mRTPMode(TRANSPORT_UNDEFINED), + mRTCPMode(TRANSPORT_UNDEFINED), + mRTPSessionID(0), + mRTCPSessionID(0), + mRTPConnected(false), + mRTCPConnected(false), + mRTPClientSessionID(0), + mRTCPClientSessionID(0) { +} + +RTPReceiver::~RTPReceiver() { + if (mRTCPClientSessionID != 0) { + mNetSession->destroySession(mRTCPClientSessionID); + mRTCPClientSessionID = 0; + } + + if (mRTPClientSessionID != 0) { + mNetSession->destroySession(mRTPClientSessionID); + mRTPClientSessionID = 0; + } + + if (mRTCPSessionID != 0) { + mNetSession->destroySession(mRTCPSessionID); + mRTCPSessionID = 0; + } + + if (mRTPSessionID != 0) { + mNetSession->destroySession(mRTPSessionID); + mRTPSessionID = 0; + } +} + +status_t RTPReceiver::initAsync( + TransportMode rtpMode, + TransportMode rtcpMode, + int32_t *outLocalRTPPort) { + if (mRTPMode != TRANSPORT_UNDEFINED + || rtpMode == TRANSPORT_UNDEFINED + || rtpMode == TRANSPORT_NONE + || rtcpMode == TRANSPORT_UNDEFINED) { + return INVALID_OPERATION; + } + + CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED); + CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED); + + sp rtpNotify = new AMessage(kWhatRTPNotify, id()); + + sp rtcpNotify; + if (rtcpMode != TRANSPORT_NONE) { + rtcpNotify = new AMessage(kWhatRTCPNotify, id()); + } + + CHECK_EQ(mRTPSessionID, 0); + CHECK_EQ(mRTCPSessionID, 0); + + int32_t localRTPPort; + + struct in_addr ifaceAddr; + ifaceAddr.s_addr = INADDR_ANY; + + for (;;) { + localRTPPort = PickRandomRTPPort(); + + status_t err; + if (rtpMode == TRANSPORT_UDP) { + err = mNetSession->createUDPSession( + localRTPPort, + rtpNotify, + &mRTPSessionID); + } else { + CHECK_EQ(rtpMode, TRANSPORT_TCP); + err = mNetSession->createTCPDatagramSession( + ifaceAddr, + localRTPPort, + rtpNotify, + &mRTPSessionID); + } + + if (err != OK) { + continue; + } + + if (rtcpMode == TRANSPORT_NONE) { + break; + } else if (rtcpMode == TRANSPORT_UDP) { + err = mNetSession->createUDPSession( + localRTPPort + 1, + rtcpNotify, + &mRTCPSessionID); + } else { + CHECK_EQ(rtpMode, TRANSPORT_TCP); + err = mNetSession->createTCPDatagramSession( + ifaceAddr, + localRTPPort + 1, + rtcpNotify, + &mRTCPSessionID); + } + + if (err == OK) { + break; + } + + mNetSession->destroySession(mRTPSessionID); + mRTPSessionID = 0; + } + + mRTPMode = rtpMode; + mRTCPMode = rtcpMode; + *outLocalRTPPort = localRTPPort; + + return OK; +} + +status_t RTPReceiver::connect( + const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) { + status_t err; + + if (mRTPMode == TRANSPORT_UDP) { + CHECK(!mRTPConnected); + + err = mNetSession->connectUDPSession( + mRTPSessionID, remoteHost, remoteRTPPort); + + if (err != OK) { + notifyInitDone(err); + return err; + } + + ALOGI("connectUDPSession RTP successful."); + + mRTPConnected = true; + } + + if (mRTCPMode == TRANSPORT_UDP) { + CHECK(!mRTCPConnected); + + err = mNetSession->connectUDPSession( + mRTCPSessionID, remoteHost, remoteRTCPPort); + + if (err != OK) { + notifyInitDone(err); + return err; + } + + scheduleSendRR(); + + ALOGI("connectUDPSession RTCP successful."); + + mRTCPConnected = true; + } + + if (mRTPConnected + && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { + notifyInitDone(OK); + } + + return OK; +} + +status_t RTPReceiver::informSender(const sp ¶ms) { + if (!mRTCPConnected) { + return INVALID_OPERATION; + } + + int64_t avgLatencyUs; + CHECK(params->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(params->findInt64("maxLatencyUs", &maxLatencyUs)); + + sp buf = new ABuffer(28); + + uint8_t *ptr = buf->data(); + ptr[0] = 0x80 | 0; + ptr[1] = 204; // APP + ptr[2] = 0; + + CHECK((buf->size() % 4) == 0u); + ptr[3] = (buf->size() / 4) - 1; + + ptr[4] = kSourceID >> 24; // SSRC + ptr[5] = (kSourceID >> 16) & 0xff; + ptr[6] = (kSourceID >> 8) & 0xff; + ptr[7] = kSourceID & 0xff; + ptr[8] = 'l'; + ptr[9] = 'a'; + ptr[10] = 't'; + ptr[11] = 'e'; + + ptr[12] = avgLatencyUs >> 56; + ptr[13] = (avgLatencyUs >> 48) & 0xff; + ptr[14] = (avgLatencyUs >> 40) & 0xff; + ptr[15] = (avgLatencyUs >> 32) & 0xff; + ptr[16] = (avgLatencyUs >> 24) & 0xff; + ptr[17] = (avgLatencyUs >> 16) & 0xff; + ptr[18] = (avgLatencyUs >> 8) & 0xff; + ptr[19] = avgLatencyUs & 0xff; + + ptr[20] = maxLatencyUs >> 56; + ptr[21] = (maxLatencyUs >> 48) & 0xff; + ptr[22] = (maxLatencyUs >> 40) & 0xff; + ptr[23] = (maxLatencyUs >> 32) & 0xff; + ptr[24] = (maxLatencyUs >> 24) & 0xff; + ptr[25] = (maxLatencyUs >> 16) & 0xff; + ptr[26] = (maxLatencyUs >> 8) & 0xff; + ptr[27] = maxLatencyUs & 0xff; + + mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); + + return OK; +} + +void RTPReceiver::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatRTPNotify: + case kWhatRTCPNotify: + onNetNotify(msg->what() == kWhatRTPNotify, msg); + break; + + case kWhatSendRR: + { + onSendRR(); + break; + } + + default: + TRESPASS(); + } +} + +void RTPReceiver::onNetNotify(bool isRTP, const sp &msg) { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + int32_t errorOccuredDuringSend; + CHECK(msg->findInt32("send", &errorOccuredDuringSend)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred during %s in session %d " + "(%d, '%s' (%s)).", + errorOccuredDuringSend ? "send" : "receive", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + mNetSession->destroySession(sessionID); + + if (sessionID == mRTPSessionID) { + mRTPSessionID = 0; + } else if (sessionID == mRTCPSessionID) { + mRTCPSessionID = 0; + } else if (sessionID == mRTPClientSessionID) { + mRTPClientSessionID = 0; + } else if (sessionID == mRTCPClientSessionID) { + mRTCPClientSessionID = 0; + } + + if (!mRTPConnected + || (mRTCPMode != TRANSPORT_NONE && !mRTCPConnected)) { + notifyInitDone(err); + break; + } + + notifyError(err); + break; + } + + case ANetworkSession::kWhatDatagram: + { + sp data; + CHECK(msg->findBuffer("data", &data)); + + if (isRTP) { + if (mFlags & FLAG_AUTO_CONNECT) { + AString fromAddr; + CHECK(msg->findString("fromAddr", &fromAddr)); + + int32_t fromPort; + CHECK(msg->findInt32("fromPort", &fromPort)); + + CHECK_EQ((status_t)OK, + connect( + fromAddr.c_str(), fromPort, fromPort + 1)); + + mFlags &= ~FLAG_AUTO_CONNECT; + } + + onRTPData(data); + } else { + onRTCPData(data); + } + break; + } + + case ANetworkSession::kWhatClientConnected: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + if (isRTP) { + CHECK_EQ(mRTPMode, TRANSPORT_TCP); + + if (mRTPClientSessionID != 0) { + // We only allow a single client connection. + mNetSession->destroySession(sessionID); + sessionID = 0; + break; + } + + mRTPClientSessionID = sessionID; + mRTPConnected = true; + } else { + CHECK_EQ(mRTCPMode, TRANSPORT_TCP); + + if (mRTCPClientSessionID != 0) { + // We only allow a single client connection. + mNetSession->destroySession(sessionID); + sessionID = 0; + break; + } + + mRTCPClientSessionID = sessionID; + mRTCPConnected = true; + } + + if (mRTPConnected + && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { + notifyInitDone(OK); + } + break; + } + } +} + +void RTPReceiver::notifyInitDone(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInitDone); + notify->setInt32("err", err); + notify->post(); +} + +void RTPReceiver::notifyError(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +void RTPReceiver::notifyPacketLost() { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatPacketLost); + notify->post(); +} + +status_t RTPReceiver::onRTPData(const sp &buffer) { + size_t size = buffer->size(); + if (size < 12) { + // Too short to be a valid RTP header. + return ERROR_MALFORMED; + } + + const uint8_t *data = buffer->data(); + + if ((data[0] >> 6) != 2) { + // Unsupported version. + return ERROR_UNSUPPORTED; + } + + if (data[0] & 0x20) { + // Padding present. + + size_t paddingLength = data[size - 1]; + + if (paddingLength + 12 > size) { + // If we removed this much padding we'd end up with something + // that's too short to be a valid RTP header. + return ERROR_MALFORMED; + } + + size -= paddingLength; + } + + int numCSRCs = data[0] & 0x0f; + + size_t payloadOffset = 12 + 4 * numCSRCs; + + if (size < payloadOffset) { + // Not enough data to fit the basic header and all the CSRC entries. + return ERROR_MALFORMED; + } + + if (data[0] & 0x10) { + // Header eXtension present. + + if (size < payloadOffset + 4) { + // Not enough data to fit the basic header, all CSRC entries + // and the first 4 bytes of the extension header. + + return ERROR_MALFORMED; + } + + const uint8_t *extensionData = &data[payloadOffset]; + + size_t extensionLength = + 4 * (extensionData[2] << 8 | extensionData[3]); + + if (size < payloadOffset + 4 + extensionLength) { + return ERROR_MALFORMED; + } + + payloadOffset += 4 + extensionLength; + } + + uint32_t srcId = U32_AT(&data[8]); + uint32_t rtpTime = U32_AT(&data[4]); + uint16_t seqNo = U16_AT(&data[2]); + + sp meta = buffer->meta(); + meta->setInt32("ssrc", srcId); + meta->setInt32("rtp-time", rtpTime); + meta->setInt32("PT", data[1] & 0x7f); + meta->setInt32("M", data[1] >> 7); + + buffer->setRange(payloadOffset, size - payloadOffset); + + ssize_t index = mSources.indexOfKey(srcId); + sp source; + if (index < 0) { + source = new Source(this, srcId); + looper()->registerHandler(source); + + mSources.add(srcId, source); + } else { + source = mSources.valueAt(index); + } + + source->onPacketReceived(seqNo, buffer); + + return OK; +} + +status_t RTPReceiver::onRTCPData(const sp &data) { + ALOGI("onRTCPData"); + return OK; +} + +void RTPReceiver::addSDES(const sp &buffer) { + uint8_t *data = buffer->data() + buffer->size(); + data[0] = 0x80 | 1; + data[1] = 202; // SDES + data[4] = kSourceID >> 24; // SSRC + data[5] = (kSourceID >> 16) & 0xff; + data[6] = (kSourceID >> 8) & 0xff; + data[7] = kSourceID & 0xff; + + size_t offset = 8; + + data[offset++] = 1; // CNAME + + AString cname = "stagefright@somewhere"; + data[offset++] = cname.size(); + + memcpy(&data[offset], cname.c_str(), cname.size()); + offset += cname.size(); + + data[offset++] = 6; // TOOL + + AString tool = "stagefright/1.0"; + data[offset++] = tool.size(); + + memcpy(&data[offset], tool.c_str(), tool.size()); + offset += tool.size(); + + data[offset++] = 0; + + if ((offset % 4) > 0) { + size_t count = 4 - (offset % 4); + switch (count) { + case 3: + data[offset++] = 0; + case 2: + data[offset++] = 0; + case 1: + data[offset++] = 0; + } + } + + size_t numWords = (offset / 4) - 1; + data[2] = numWords >> 8; + data[3] = numWords & 0xff; + + buffer->setRange(buffer->offset(), buffer->size() + offset); +} + +void RTPReceiver::scheduleSendRR() { + (new AMessage(kWhatSendRR, id()))->post(5000000ll); +} + +void RTPReceiver::onSendRR() { +#if 0 + sp buf = new ABuffer(kMaxUDPPacketSize); + buf->setRange(0, 0); + + uint8_t *ptr = buf->data(); + ptr[0] = 0x80 | 0; + ptr[1] = 201; // RR + ptr[2] = 0; + ptr[3] = 1; + ptr[4] = kSourceID >> 24; // SSRC + ptr[5] = (kSourceID >> 16) & 0xff; + ptr[6] = (kSourceID >> 8) & 0xff; + ptr[7] = kSourceID & 0xff; + + buf->setRange(0, 8); + + size_t numReportBlocks = 0; + for (size_t i = 0; i < mSources.size(); ++i) { + uint32_t ssrc = mSources.keyAt(i); + sp source = mSources.valueAt(i); + + if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) { + // Cannot fit another report block. + break; + } + + source->addReportBlock(ssrc, buf); + ++numReportBlocks; + } + + ptr[0] |= numReportBlocks; // 5 bit + + size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks; + ptr[2] = sizeInWordsMinus1 >> 8; + ptr[3] = sizeInWordsMinus1 & 0xff; + + buf->setRange(0, (sizeInWordsMinus1 + 1) * 4); + + addSDES(buf); + + mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); +#endif + + scheduleSendRR(); +} + +status_t RTPReceiver::registerPacketType( + uint8_t packetType, PacketizationMode mode) { + mPacketTypes.add(packetType, mode); + + return OK; +} + +sp RTPReceiver::makeAssembler(uint8_t packetType) { + ssize_t index = mPacketTypes.indexOfKey(packetType); + if (index < 0) { + return NULL; + } + + PacketizationMode mode = mPacketTypes.valueAt(index); + + switch (mode) { + case PACKETIZATION_NONE: + case PACKETIZATION_TRANSPORT_STREAM: + return new TSAssembler(mNotify); + + case PACKETIZATION_H264: + return new H264Assembler(mNotify); + + default: + return NULL; + } +} + +void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) { + int32_t blp = 0; + + sp buf = new ABuffer(16); + buf->setRange(0, 0); + + uint8_t *ptr = buf->data(); + ptr[0] = 0x80 | 1; // generic NACK + ptr[1] = 205; // TSFB + ptr[2] = 0; + ptr[3] = 3; + ptr[8] = (senderSSRC >> 24) & 0xff; + ptr[9] = (senderSSRC >> 16) & 0xff; + ptr[10] = (senderSSRC >> 8) & 0xff; + ptr[11] = (senderSSRC & 0xff); + ptr[8] = (kSourceID >> 24) & 0xff; + ptr[9] = (kSourceID >> 16) & 0xff; + ptr[10] = (kSourceID >> 8) & 0xff; + ptr[11] = (kSourceID & 0xff); + ptr[12] = (extSeqNo >> 8) & 0xff; + ptr[13] = (extSeqNo & 0xff); + ptr[14] = (blp >> 8) & 0xff; + ptr[15] = (blp & 0xff); + + buf->setRange(0, 16); + + mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); +} + +void RTPReceiver::Source::modifyPacketStatus(int32_t extSeqNo, uint32_t mask) { +#if TRACK_PACKET_LOSS + ssize_t index = mLostPackets.indexOfKey(extSeqNo); + if (index < 0) { + mLostPackets.add(extSeqNo, mask); + } else { + mLostPackets.editValueAt(index) |= mask; + } +#endif +} + +void RTPReceiver::Source::postRetransmitTimer(int64_t timeUs) { + int64_t delayUs = timeUs - ALooper::GetNowUs(); + sp msg = new AMessage(kWhatRetransmit, id()); + msg->setInt32("generation", mRetransmitGeneration); + msg->post(delayUs); +} + +void RTPReceiver::Source::postDeclareLostTimer(int64_t timeUs) { + CHECK(!mDeclareLostTimerPending); + mDeclareLostTimerPending = true; + + int64_t delayUs = timeUs - ALooper::GetNowUs(); + sp msg = new AMessage(kWhatDeclareLost, id()); + msg->setInt32("generation", mDeclareLostGeneration); + msg->post(delayUs); +} + +void RTPReceiver::Source::cancelTimers() { + ++mRetransmitGeneration; + ++mDeclareLostGeneration; + mDeclareLostTimerPending = false; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h new file mode 100644 index 0000000..240ab2e --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h @@ -0,0 +1,125 @@ +/* + * Copyright 2013, 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 RTP_RECEIVER_H_ + +#define RTP_RECEIVER_H_ + +#include "RTPBase.h" + +#include + +namespace android { + +struct ABuffer; +struct ANetworkSession; + +// An object of this class facilitates receiving of media data on an RTP +// channel. The channel is established over a UDP or TCP connection depending +// on which "TransportMode" was chosen. In addition different RTP packetization +// schemes are supported such as "Transport Stream Packets over RTP", +// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)" +struct RTPReceiver : public RTPBase, public AHandler { + enum { + kWhatInitDone, + kWhatError, + kWhatAccessUnit, + kWhatPacketLost, + }; + + enum Flags { + FLAG_AUTO_CONNECT = 1, + }; + RTPReceiver( + const sp &netSession, + const sp ¬ify, + uint32_t flags = 0); + + status_t registerPacketType( + uint8_t packetType, PacketizationMode mode); + + status_t initAsync( + TransportMode rtpMode, + TransportMode rtcpMode, + int32_t *outLocalRTPPort); + + status_t connect( + const char *remoteHost, + int32_t remoteRTPPort, + int32_t remoteRTCPPort); + + status_t informSender(const sp ¶ms); + +protected: + virtual ~RTPReceiver(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatRTPNotify, + kWhatRTCPNotify, + kWhatSendRR, + }; + + enum { + kSourceID = 0xdeadbeef, + kPacketLostAfterUs = 100000, + kRequestRetransmissionAfterUs = -1, + }; + + struct Assembler; + struct H264Assembler; + struct Source; + struct TSAssembler; + + sp mNetSession; + sp mNotify; + uint32_t mFlags; + TransportMode mRTPMode; + TransportMode mRTCPMode; + int32_t mRTPSessionID; + int32_t mRTCPSessionID; + bool mRTPConnected; + bool mRTCPConnected; + + int32_t mRTPClientSessionID; // in TRANSPORT_TCP mode. + int32_t mRTCPClientSessionID; // in TRANSPORT_TCP mode. + + KeyedVector mPacketTypes; + KeyedVector > mSources; + + void onNetNotify(bool isRTP, const sp &msg); + status_t onRTPData(const sp &data); + status_t onRTCPData(const sp &data); + void onSendRR(); + + void scheduleSendRR(); + void addSDES(const sp &buffer); + + void notifyInitDone(status_t err); + void notifyError(status_t err); + void notifyPacketLost(); + + sp makeAssembler(uint8_t packetType); + + void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo); + + DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver); +}; + +} // namespace android + +#endif // RTP_RECEIVER_H_ diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 095fd97..6bbe650 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -767,6 +767,17 @@ status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) { } status_t RTPSender::parseAPP(const uint8_t *data, size_t size) { + if (!memcmp("late", &data[8], 4)) { + int64_t avgLatencyUs = (int64_t)U64_AT(&data[12]); + int64_t maxLatencyUs = (int64_t)U64_AT(&data[20]); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatInformSender); + notify->setInt64("avgLatencyUs", avgLatencyUs); + notify->setInt64("maxLatencyUs", maxLatencyUs); + notify->post(); + } + return OK; } diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 7dc138a..fefcab7 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -37,6 +37,7 @@ struct RTPSender : public RTPBase, public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, + kWhatInformSender, }; RTPSender( const sp &netSession, diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp new file mode 100644 index 0000000..764a38b --- /dev/null +++ b/media/libstagefright/wifi-display/rtptest.cpp @@ -0,0 +1,565 @@ +/* + * Copyright 2013, 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_NEBUG 0 +#define LOG_TAG "rtptest" +#include + +#include "ANetworkSession.h" +#include "rtp/RTPSender.h" +#include "rtp/RTPReceiver.h" +#include "TimeSyncer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4" + +namespace android { + +struct PacketSource : public RefBase { + PacketSource() {} + + virtual sp getNextAccessUnit() = 0; + +protected: + virtual ~PacketSource() {} + +private: + DISALLOW_EVIL_CONSTRUCTORS(PacketSource); +}; + +struct MediaPacketSource : public PacketSource { + MediaPacketSource() + : mMaxSampleSize(1024 * 1024) { + mExtractor = new NuMediaExtractor; + CHECK_EQ((status_t)OK, + mExtractor->setDataSource(MEDIA_FILENAME)); + + bool haveVideo = false; + for (size_t i = 0; i < mExtractor->countTracks(); ++i) { + sp format; + CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format)); + + AString mime; + CHECK(format->findString("mime", &mime)); + + if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) { + mExtractor->selectTrack(i); + haveVideo = true; + break; + } + } + + CHECK(haveVideo); + } + + virtual sp getNextAccessUnit() { + int64_t timeUs; + status_t err = mExtractor->getSampleTime(&timeUs); + + if (err != OK) { + return NULL; + } + + sp accessUnit = new ABuffer(mMaxSampleSize); + CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit)); + + accessUnit->meta()->setInt64("timeUs", timeUs); + + CHECK_EQ((status_t)OK, mExtractor->advance()); + + return accessUnit; + } + +protected: + virtual ~MediaPacketSource() { + } + +private: + sp mExtractor; + size_t mMaxSampleSize; + + DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource); +}; + +struct SimplePacketSource : public PacketSource { + SimplePacketSource() + : mCounter(0) { + } + + virtual sp getNextAccessUnit() { + sp buffer = new ABuffer(4); + uint8_t *dst = buffer->data(); + dst[0] = mCounter >> 24; + dst[1] = (mCounter >> 16) & 0xff; + dst[2] = (mCounter >> 8) & 0xff; + dst[3] = mCounter & 0xff; + + buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate); + + ++mCounter; + + return buffer; + } + +protected: + virtual ~SimplePacketSource() { + } + +private: + enum { + kFrameRate = 30 + }; + + uint32_t mCounter; + + DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource); +}; + +struct TestHandler : public AHandler { + TestHandler(const sp &netSession); + + void listen(); + void connect(const char *host, int32_t port); + +protected: + virtual ~TestHandler(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatListen, + kWhatConnect, + kWhatReceiverNotify, + kWhatSenderNotify, + kWhatSendMore, + kWhatStop, + kWhatTimeSyncerNotify, + }; + +#if 1 + static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP; + static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP; +#else + static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP; + static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE; +#endif + +#if 1 + static const RTPBase::PacketizationMode kPacketizationMode + = RTPBase::PACKETIZATION_H264; +#else + static const RTPBase::PacketizationMode kPacketizationMode + = RTPBase::PACKETIZATION_NONE; +#endif + + sp mNetSession; + sp mSource; + sp mSender; + sp mReceiver; + + sp mTimeSyncer; + bool mTimeSyncerStarted; + + int64_t mFirstTimeRealUs; + int64_t mFirstTimeMediaUs; + + int64_t mTimeOffsetUs; + bool mTimeOffsetValid; + + status_t readMore(); + + DISALLOW_EVIL_CONSTRUCTORS(TestHandler); +}; + +TestHandler::TestHandler(const sp &netSession) + : mNetSession(netSession), + mTimeSyncerStarted(false), + mFirstTimeRealUs(-1ll), + mFirstTimeMediaUs(-1ll), + mTimeOffsetUs(-1ll), + mTimeOffsetValid(false) { +} + +TestHandler::~TestHandler() { +} + +void TestHandler::listen() { + sp msg = new AMessage(kWhatListen, id()); + msg->post(); +} + +void TestHandler::connect(const char *host, int32_t port) { + sp msg = new AMessage(kWhatConnect, id()); + msg->setString("host", host); + msg->setInt32("port", port); + msg->post(); +} + +static void dumpDelay(int64_t delayMs) { + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("(%4lld ms) %s\n", + delayMs, + kPattern + kPatternSize - n); +} + +void TestHandler::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatListen: + { + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + notify = new AMessage(kWhatReceiverNotify, id()); + mReceiver = new RTPReceiver( + mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT); + looper()->registerHandler(mReceiver); + + CHECK_EQ((status_t)OK, + mReceiver->registerPacketType(33, kPacketizationMode)); + + int32_t receiverRTPPort; + CHECK_EQ((status_t)OK, + mReceiver->initAsync( + kRTPMode, + kRTCPMode, + &receiverRTPPort)); + + printf("picked receiverRTPPort %d\n", receiverRTPPort); + +#if 0 + CHECK_EQ((status_t)OK, + mReceiver->connect( + "127.0.0.1", senderRTPPort, senderRTPPort + 1)); +#endif + break; + } + + case kWhatConnect: + { + AString host; + CHECK(msg->findString("host", &host)); + + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + mTimeSyncer->startServer(8123); + + int32_t receiverRTPPort; + CHECK(msg->findInt32("port", &receiverRTPPort)); + +#if 1 + mSource = new MediaPacketSource; +#else + mSource = new SimplePacketSource; +#endif + + notify = new AMessage(kWhatSenderNotify, id()); + mSender = new RTPSender(mNetSession, notify); + + looper()->registerHandler(mSender); + + int32_t senderRTPPort; + CHECK_EQ((status_t)OK, + mSender->initAsync( + host.c_str(), + receiverRTPPort, + kRTPMode, + kRTCPMode == RTPBase::TRANSPORT_NONE + ? -1 : receiverRTPPort + 1, + kRTCPMode, + &senderRTPPort)); + + printf("picked senderRTPPort %d\n", senderRTPPort); + break; + } + + case kWhatSenderNotify: + { + ALOGI("kWhatSenderNotify"); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case RTPSender::kWhatInitDone: + { + int32_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGI("RTPSender::initAsync completed w/ err %d", err); + + if (err == OK) { + err = readMore(); + + if (err != OK) { + (new AMessage(kWhatStop, id()))->post(); + } + } + break; + } + + case RTPSender::kWhatError: + break; + } + break; + } + + case kWhatReceiverNotify: + { + ALOGV("kWhatReceiverNotify"); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case RTPReceiver::kWhatInitDone: + { + int32_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGI("RTPReceiver::initAsync completed w/ err %d", err); + break; + } + + case RTPReceiver::kWhatError: + break; + + case RTPReceiver::kWhatAccessUnit: + { +#if 0 + if (!mTimeSyncerStarted) { + mTimeSyncer->startClient("172.18.41.216", 8123); + mTimeSyncerStarted = true; + } + + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + if (mTimeOffsetValid) { + timeUs -= mTimeOffsetUs; + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayMs = (nowUs - timeUs) / 1000ll; + + dumpDelay(delayMs); + } +#endif + break; + } + + case RTPReceiver::kWhatPacketLost: + ALOGV("kWhatPacketLost"); + break; + + default: + TRESPASS(); + } + break; + } + + case kWhatSendMore: + { + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + CHECK_EQ((status_t)OK, + mSender->queueBuffer( + accessUnit, + 33, + kPacketizationMode)); + + status_t err = readMore(); + + if (err != OK) { + (new AMessage(kWhatStop, id()))->post(); + } + break; + } + + case kWhatStop: + { + if (mReceiver != NULL) { + looper()->unregisterHandler(mReceiver->id()); + mReceiver.clear(); + } + + if (mSender != NULL) { + looper()->unregisterHandler(mSender->id()); + mSender.clear(); + } + + mSource.clear(); + + looper()->stop(); + break; + } + + case kWhatTimeSyncerNotify: + { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + break; + } + + default: + TRESPASS(); + } +} + +status_t TestHandler::readMore() { + sp accessUnit = mSource->getNextAccessUnit(); + + if (accessUnit == NULL) { + return ERROR_END_OF_STREAM; + } + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + int64_t nowUs = ALooper::GetNowUs(); + int64_t whenUs; + + if (mFirstTimeRealUs < 0ll) { + mFirstTimeRealUs = whenUs = nowUs; + mFirstTimeMediaUs = timeUs; + } else { + whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs; + } + + accessUnit->meta()->setInt64("timeUs", whenUs); + + sp msg = new AMessage(kWhatSendMore, id()); + msg->setBuffer("accessUnit", accessUnit); + msg->post(whenUs - nowUs); + + return OK; +} + +} // namespace android + +static void usage(const char *me) { + fprintf(stderr, + "usage: %s -c host:port\tconnect to remote host\n" + " -l \tlisten\n", + me); +} + +int main(int argc, char **argv) { + using namespace android; + + // srand(time(NULL)); + + ProcessState::self()->startThreadPool(); + + DataSource::RegisterDefaultSniffers(); + + bool listen = false; + int32_t connectToPort = -1; + AString connectToHost; + + int res; + while ((res = getopt(argc, argv, "hc:l")) >= 0) { + switch (res) { + case 'c': + { + const char *colonPos = strrchr(optarg, ':'); + + if (colonPos == NULL) { + usage(argv[0]); + exit(1); + } + + connectToHost.setTo(optarg, colonPos - optarg); + + char *end; + connectToPort = strtol(colonPos + 1, &end, 10); + + if (*end != '\0' || end == colonPos + 1 + || connectToPort < 1 || connectToPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + break; + } + + case 'l': + { + listen = true; + break; + } + + case '?': + case 'h': + usage(argv[0]); + exit(1); + } + } + + if (!listen && connectToPort < 0) { + fprintf(stderr, + "You need to select either client or server mode.\n"); + exit(1); + } + + sp netSession = new ANetworkSession; + netSession->start(); + + sp looper = new ALooper; + + sp handler = new TestHandler(netSession); + looper->registerHandler(handler); + + if (listen) { + handler->listen(); + } + + if (connectToPort >= 0) { + handler->connect(connectToHost.c_str(), connectToPort); + } + + looper->start(true /* runOnCallingThread */); + + return 0; +} + diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp new file mode 100644 index 0000000..15f9c88 --- /dev/null +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -0,0 +1,625 @@ +/* + * Copyright 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 "DirectRenderer" +#include + +#include "DirectRenderer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +/* + Drives the decoding process using a MediaCodec instance. Input buffers + queued by calls to "queueInputBuffer" are fed to the decoder as soon + as the decoder is ready for them, the client is notified about output + buffers as the decoder spits them out. +*/ +struct DirectRenderer::DecoderContext : public AHandler { + enum { + kWhatOutputBufferReady, + }; + DecoderContext(const sp ¬ify); + + status_t init( + const sp &format, + const sp &surfaceTex); + + void queueInputBuffer(const sp &accessUnit); + + status_t renderOutputBufferAndRelease(size_t index); + status_t releaseOutputBuffer(size_t index); + +protected: + virtual ~DecoderContext(); + + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatDecoderNotify, + }; + + sp mNotify; + sp mDecoderLooper; + sp mDecoder; + Vector > mDecoderInputBuffers; + Vector > mDecoderOutputBuffers; + List mDecoderInputBuffersAvailable; + bool mDecoderNotificationPending; + + List > mAccessUnits; + + void onDecoderNotify(); + void scheduleDecoderNotification(); + void queueDecoderInputBuffers(); + + void queueOutputBuffer( + size_t index, int64_t timeUs, const sp &buffer); + + DISALLOW_EVIL_CONSTRUCTORS(DecoderContext); +}; + +//////////////////////////////////////////////////////////////////////////////// + +/* + A "push" audio renderer. The primary function of this renderer is to use + an AudioTrack in push mode and making sure not to block the event loop + be ensuring that calls to AudioTrack::write never block. This is done by + estimating an upper bound of data that can be written to the AudioTrack + buffer without delay. +*/ +struct DirectRenderer::AudioRenderer : public AHandler { + AudioRenderer(const sp &decoderContext); + + void queueInputBuffer( + size_t index, int64_t timeUs, const sp &buffer); + +protected: + virtual ~AudioRenderer(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kWhatPushAudio, + }; + + struct BufferInfo { + size_t mIndex; + int64_t mTimeUs; + sp mBuffer; + }; + + sp mDecoderContext; + sp mAudioTrack; + + List mInputBuffers; + bool mPushPending; + + size_t mNumFramesWritten; + + void schedulePushIfNecessary(); + void onPushAudio(); + + ssize_t writeNonBlocking(const uint8_t *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(AudioRenderer); +}; + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::DecoderContext::DecoderContext(const sp ¬ify) + : mNotify(notify), + mDecoderNotificationPending(false) { +} + +DirectRenderer::DecoderContext::~DecoderContext() { + if (mDecoder != NULL) { + mDecoder->release(); + mDecoder.clear(); + + mDecoderLooper->stop(); + mDecoderLooper.clear(); + } +} + +status_t DirectRenderer::DecoderContext::init( + const sp &format, + const sp &surfaceTex) { + CHECK(mDecoder == NULL); + + AString mime; + CHECK(format->findString("mime", &mime)); + + mDecoderLooper = new ALooper; + mDecoderLooper->setName("video codec looper"); + + mDecoderLooper->start( + false /* runOnCallingThread */, + false /* canCallJava */, + PRIORITY_DEFAULT); + + mDecoder = MediaCodec::CreateByType( + mDecoderLooper, mime.c_str(), false /* encoder */); + + CHECK(mDecoder != NULL); + + status_t err = mDecoder->configure( + format, + surfaceTex == NULL + ? NULL : new Surface(surfaceTex), + NULL /* crypto */, + 0 /* flags */); + CHECK_EQ(err, (status_t)OK); + + err = mDecoder->start(); + CHECK_EQ(err, (status_t)OK); + + err = mDecoder->getInputBuffers( + &mDecoderInputBuffers); + CHECK_EQ(err, (status_t)OK); + + err = mDecoder->getOutputBuffers( + &mDecoderOutputBuffers); + CHECK_EQ(err, (status_t)OK); + + scheduleDecoderNotification(); + + return OK; +} + +void DirectRenderer::DecoderContext::queueInputBuffer( + const sp &accessUnit) { + CHECK(mDecoder != NULL); + + mAccessUnits.push_back(accessUnit); + queueDecoderInputBuffers(); +} + +status_t DirectRenderer::DecoderContext::renderOutputBufferAndRelease( + size_t index) { + return mDecoder->renderOutputBufferAndRelease(index); +} + +status_t DirectRenderer::DecoderContext::releaseOutputBuffer(size_t index) { + return mDecoder->releaseOutputBuffer(index); +} + +void DirectRenderer::DecoderContext::queueDecoderInputBuffers() { + if (mDecoder == NULL) { + return; + } + + bool submittedMore = false; + + while (!mAccessUnits.empty() + && !mDecoderInputBuffersAvailable.empty()) { + size_t index = *mDecoderInputBuffersAvailable.begin(); + + mDecoderInputBuffersAvailable.erase( + mDecoderInputBuffersAvailable.begin()); + + sp srcBuffer = *mAccessUnits.begin(); + mAccessUnits.erase(mAccessUnits.begin()); + + const sp &dstBuffer = + mDecoderInputBuffers.itemAt(index); + + memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size()); + + int64_t timeUs; + CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs)); + + status_t err = mDecoder->queueInputBuffer( + index, + 0 /* offset */, + srcBuffer->size(), + timeUs, + 0 /* flags */); + CHECK_EQ(err, (status_t)OK); + + submittedMore = true; + } + + if (submittedMore) { + scheduleDecoderNotification(); + } +} + +void DirectRenderer::DecoderContext::onMessageReceived( + const sp &msg) { + switch (msg->what()) { + case kWhatDecoderNotify: + { + onDecoderNotify(); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::DecoderContext::onDecoderNotify() { + mDecoderNotificationPending = false; + + for (;;) { + size_t index; + status_t err = mDecoder->dequeueInputBuffer(&index); + + if (err == OK) { + mDecoderInputBuffersAvailable.push_back(index); + } else if (err == -EAGAIN) { + break; + } else { + TRESPASS(); + } + } + + queueDecoderInputBuffers(); + + for (;;) { + size_t index; + size_t offset; + size_t size; + int64_t timeUs; + uint32_t flags; + status_t err = mDecoder->dequeueOutputBuffer( + &index, + &offset, + &size, + &timeUs, + &flags); + + if (err == OK) { + queueOutputBuffer( + index, timeUs, mDecoderOutputBuffers.itemAt(index)); + } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { + err = mDecoder->getOutputBuffers( + &mDecoderOutputBuffers); + CHECK_EQ(err, (status_t)OK); + } else if (err == INFO_FORMAT_CHANGED) { + // We don't care. + } else if (err == -EAGAIN) { + break; + } else { + TRESPASS(); + } + } + + scheduleDecoderNotification(); +} + +void DirectRenderer::DecoderContext::scheduleDecoderNotification() { + if (mDecoderNotificationPending) { + return; + } + + sp notify = + new AMessage(kWhatDecoderNotify, id()); + + mDecoder->requestActivityNotification(notify); + mDecoderNotificationPending = true; +} + +void DirectRenderer::DecoderContext::queueOutputBuffer( + size_t index, int64_t timeUs, const sp &buffer) { + sp msg = mNotify->dup(); + msg->setInt32("what", kWhatOutputBufferReady); + msg->setSize("index", index); + msg->setInt64("timeUs", timeUs); + msg->setBuffer("buffer", buffer); + msg->post(); +} + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::AudioRenderer::AudioRenderer( + const sp &decoderContext) + : mDecoderContext(decoderContext), + mPushPending(false), + mNumFramesWritten(0) { + mAudioTrack = new AudioTrack( + AUDIO_STREAM_DEFAULT, + 48000.0f, + AUDIO_FORMAT_PCM, + AUDIO_CHANNEL_OUT_STEREO, + (int)0 /* frameCount */); + + CHECK_EQ((status_t)OK, mAudioTrack->initCheck()); + + mAudioTrack->start(); +} + +DirectRenderer::AudioRenderer::~AudioRenderer() { +} + +void DirectRenderer::AudioRenderer::queueInputBuffer( + size_t index, int64_t timeUs, const sp &buffer) { + BufferInfo info; + info.mIndex = index; + info.mTimeUs = timeUs; + info.mBuffer = buffer; + + mInputBuffers.push_back(info); + schedulePushIfNecessary(); +} + +void DirectRenderer::AudioRenderer::onMessageReceived( + const sp &msg) { + switch (msg->what()) { + case kWhatPushAudio: + { + onPushAudio(); + break; + } + + default: + break; + } +} + +void DirectRenderer::AudioRenderer::schedulePushIfNecessary() { + if (mPushPending || mInputBuffers.empty()) { + return; + } + + mPushPending = true; + + uint32_t numFramesPlayed; + CHECK_EQ(mAudioTrack->getPosition(&numFramesPlayed), + (status_t)OK); + + uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed; + + // This is how long the audio sink will have data to + // play back. + const float msecsPerFrame = 1000.0f / mAudioTrack->getSampleRate(); + + int64_t delayUs = + msecsPerFrame * numFramesPendingPlayout * 1000ll; + + // Let's give it more data after about half that time + // has elapsed. + (new AMessage(kWhatPushAudio, id()))->post(delayUs / 2); +} + +void DirectRenderer::AudioRenderer::onPushAudio() { + mPushPending = false; + + while (!mInputBuffers.empty()) { + const BufferInfo &info = *mInputBuffers.begin(); + + ssize_t n = writeNonBlocking( + info.mBuffer->data(), info.mBuffer->size()); + + if (n < (ssize_t)info.mBuffer->size()) { + CHECK_GE(n, 0); + + info.mBuffer->setRange( + info.mBuffer->offset() + n, info.mBuffer->size() - n); + break; + } + + mDecoderContext->releaseOutputBuffer(info.mIndex); + + mInputBuffers.erase(mInputBuffers.begin()); + } + + schedulePushIfNecessary(); +} + +ssize_t DirectRenderer::AudioRenderer::writeNonBlocking( + const uint8_t *data, size_t size) { + uint32_t numFramesPlayed; + status_t err = mAudioTrack->getPosition(&numFramesPlayed); + if (err != OK) { + return err; + } + + ssize_t numFramesAvailableToWrite = + mAudioTrack->frameCount() - (mNumFramesWritten - numFramesPlayed); + + size_t numBytesAvailableToWrite = + numFramesAvailableToWrite * mAudioTrack->frameSize(); + + if (size > numBytesAvailableToWrite) { + size = numBytesAvailableToWrite; + } + + CHECK_EQ(mAudioTrack->write(data, size), (ssize_t)size); + + size_t numFramesWritten = size / mAudioTrack->frameSize(); + mNumFramesWritten += numFramesWritten; + + return size; +} + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::DirectRenderer( + const sp &bufferProducer) + : mSurfaceTex(bufferProducer), + mVideoRenderPending(false), + mNumFramesLate(0), + mNumFrames(0) { +} + +DirectRenderer::~DirectRenderer() { +} + +void DirectRenderer::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatDecoderNotify: + { + onDecoderNotify(msg); + break; + } + + case kWhatRenderVideo: + { + onRenderVideo(); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::setFormat(size_t trackIndex, const sp &format) { + CHECK_LT(trackIndex, 2u); + + CHECK(mDecoderContext[trackIndex] == NULL); + + sp notify = new AMessage(kWhatDecoderNotify, id()); + notify->setSize("trackIndex", trackIndex); + + mDecoderContext[trackIndex] = new DecoderContext(notify); + looper()->registerHandler(mDecoderContext[trackIndex]); + + CHECK_EQ((status_t)OK, + mDecoderContext[trackIndex]->init( + format, trackIndex == 0 ? mSurfaceTex : NULL)); + + if (trackIndex == 1) { + // Audio + mAudioRenderer = new AudioRenderer(mDecoderContext[1]); + looper()->registerHandler(mAudioRenderer); + } +} + +void DirectRenderer::queueAccessUnit( + size_t trackIndex, const sp &accessUnit) { + CHECK_LT(trackIndex, 2u); + + if (mDecoderContext[trackIndex] == NULL) { + CHECK_EQ(trackIndex, 0u); + + sp format = new AMessage; + format->setString("mime", "video/avc"); + format->setInt32("width", 640); + format->setInt32("height", 360); + + setFormat(trackIndex, format); + } + + mDecoderContext[trackIndex]->queueInputBuffer(accessUnit); +} + +void DirectRenderer::onDecoderNotify(const sp &msg) { + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case DecoderContext::kWhatOutputBufferReady: + { + size_t index; + CHECK(msg->findSize("index", &index)); + + int64_t timeUs; + CHECK(msg->findInt64("timeUs", &timeUs)); + + sp buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + queueOutputBuffer(trackIndex, index, timeUs, buffer); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::queueOutputBuffer( + size_t trackIndex, + size_t index, int64_t timeUs, const sp &buffer) { + if (trackIndex == 1) { + // Audio + mAudioRenderer->queueInputBuffer(index, timeUs, buffer); + return; + } + + OutputInfo info; + info.mIndex = index; + info.mTimeUs = timeUs; + info.mBuffer = buffer; + mVideoOutputBuffers.push_back(info); + + scheduleVideoRenderIfNecessary(); +} + +void DirectRenderer::scheduleVideoRenderIfNecessary() { + if (mVideoRenderPending || mVideoOutputBuffers.empty()) { + return; + } + + mVideoRenderPending = true; + + int64_t timeUs = (*mVideoOutputBuffers.begin()).mTimeUs; + int64_t nowUs = ALooper::GetNowUs(); + + int64_t delayUs = timeUs - nowUs; + + (new AMessage(kWhatRenderVideo, id()))->post(delayUs); +} + +void DirectRenderer::onRenderVideo() { + mVideoRenderPending = false; + + int64_t nowUs = ALooper::GetNowUs(); + + while (!mVideoOutputBuffers.empty()) { + const OutputInfo &info = *mVideoOutputBuffers.begin(); + + if (info.mTimeUs > nowUs) { + break; + } + + if (info.mTimeUs + 15000ll < nowUs) { + ++mNumFramesLate; + } + ++mNumFrames; + + status_t err = + mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex); + CHECK_EQ(err, (status_t)OK); + + mVideoOutputBuffers.erase(mVideoOutputBuffers.begin()); + } + + scheduleVideoRenderIfNecessary(); +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h new file mode 100644 index 0000000..c5a4a83 --- /dev/null +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -0,0 +1,82 @@ +/* + * Copyright 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 DIRECT_RENDERER_H_ + +#define DIRECT_RENDERER_H_ + +#include + +namespace android { + +struct ABuffer; +struct AudioTrack; +struct IGraphicBufferProducer; +struct MediaCodec; + +// Renders audio and video data queued by calls to "queueAccessUnit". +struct DirectRenderer : public AHandler { + DirectRenderer(const sp &bufferProducer); + + void setFormat(size_t trackIndex, const sp &format); + void queueAccessUnit(size_t trackIndex, const sp &accessUnit); + +protected: + virtual void onMessageReceived(const sp &msg); + virtual ~DirectRenderer(); + +private: + struct DecoderContext; + struct AudioRenderer; + + enum { + kWhatDecoderNotify, + kWhatRenderVideo, + }; + + struct OutputInfo { + size_t mIndex; + int64_t mTimeUs; + sp mBuffer; + }; + + sp mSurfaceTex; + + sp mDecoderContext[2]; + List mVideoOutputBuffers; + + bool mVideoRenderPending; + + sp mAudioRenderer; + + int32_t mNumFramesLate; + int32_t mNumFrames; + + void onDecoderNotify(const sp &msg); + + void queueOutputBuffer( + size_t trackIndex, + size_t index, int64_t timeUs, const sp &buffer); + + void scheduleVideoRenderIfNecessary(); + void onRenderVideo(); + + DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); +}; + +} // namespace android + +#endif // DIRECT_RENDERER_H_ diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp new file mode 100644 index 0000000..5db2099 --- /dev/null +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -0,0 +1,917 @@ +/* + * Copyright 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 "WifiDisplaySink" +#include + +#include "WifiDisplaySink.h" + +#include "DirectRenderer.h" +#include "MediaReceiver.h" +#include "ParsedMessage.h" +#include "TimeSyncer.h" + +#include +#include +#include +#include +#include +#include + +namespace android { + +// static +const AString WifiDisplaySink::sUserAgent = MakeUserAgent(); + +WifiDisplaySink::WifiDisplaySink( + uint32_t flags, + const sp &netSession, + const sp &bufferProducer, + const sp ¬ify) + : mState(UNDEFINED), + mFlags(flags), + mNetSession(netSession), + mSurfaceTex(bufferProducer), + mNotify(notify), + mUsingTCPTransport(false), + mUsingTCPInterleaving(false), + mSessionID(0), + mNextCSeq(1), + mIDRFrameRequestPending(false), + mTimeOffsetUs(0ll), + mTimeOffsetValid(false), + mSetupDeferred(false), + mLatencyCount(0), + mLatencySumUs(0ll), + mLatencyMaxUs(0ll), + mMaxDelayMs(-1ll) { + // We support any and all resolutions, but prefer 720p30 + mSinkSupportedVideoFormats.setNativeResolution( + VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 + + mSinkSupportedVideoFormats.enableAll(); +} + +WifiDisplaySink::~WifiDisplaySink() { +} + +void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) { + sp msg = new AMessage(kWhatStart, id()); + msg->setString("sourceHost", sourceHost); + msg->setInt32("sourcePort", sourcePort); + msg->post(); +} + +void WifiDisplaySink::start(const char *uri) { + sp msg = new AMessage(kWhatStart, id()); + msg->setString("setupURI", uri); + msg->post(); +} + +// static +bool WifiDisplaySink::ParseURL( + const char *url, AString *host, int32_t *port, AString *path, + AString *user, AString *pass) { + host->clear(); + *port = 0; + path->clear(); + user->clear(); + pass->clear(); + + if (strncasecmp("rtsp://", url, 7)) { + return false; + } + + const char *slashPos = strchr(&url[7], '/'); + + if (slashPos == NULL) { + host->setTo(&url[7]); + path->setTo("/"); + } else { + host->setTo(&url[7], slashPos - &url[7]); + path->setTo(slashPos); + } + + ssize_t atPos = host->find("@"); + + if (atPos >= 0) { + // Split of user:pass@ from hostname. + + AString userPass(*host, 0, atPos); + host->erase(0, atPos + 1); + + ssize_t colonPos = userPass.find(":"); + + if (colonPos < 0) { + *user = userPass; + } else { + user->setTo(userPass, 0, colonPos); + pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1); + } + } + + const char *colonPos = strchr(host->c_str(), ':'); + + if (colonPos != NULL) { + char *end; + unsigned long x = strtoul(colonPos + 1, &end, 10); + + if (end == colonPos + 1 || *end != '\0' || x >= 65536) { + return false; + } + + *port = x; + + size_t colonOffset = colonPos - host->c_str(); + size_t trailing = host->size() - colonOffset; + host->erase(colonOffset, trailing); + } else { + *port = 554; + } + + return true; +} + +void WifiDisplaySink::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatStart: + { + sleep(2); // XXX + + int32_t sourcePort; + CHECK(msg->findString("sourceHost", &mRTSPHost)); + CHECK(msg->findInt32("sourcePort", &sourcePort)); + + sp notify = new AMessage(kWhatRTSPNotify, id()); + + status_t err = mNetSession->createRTSPClient( + mRTSPHost.c_str(), sourcePort, notify, &mSessionID); + CHECK_EQ(err, (status_t)OK); + + mState = CONNECTING; + break; + } + + case kWhatRTSPNotify: + { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred in session %d (%d, '%s/%s').", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + if (sessionID == mSessionID) { + ALOGI("Lost control connection."); + + // The control connection is dead now. + mNetSession->destroySession(mSessionID); + mSessionID = 0; + + if (mNotify == NULL) { + looper()->stop(); + } else { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatDisconnected); + notify->post(); + } + } + break; + } + + case ANetworkSession::kWhatConnected: + { + ALOGI("We're now connected."); + mState = CONNECTED; + + if (mFlags & FLAG_SPECIAL_MODE) { + sp notify = new AMessage( + kWhatTimeSyncerNotify, id()); + + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + mTimeSyncer->startClient(mRTSPHost.c_str(), 8123); + } + break; + } + + case ANetworkSession::kWhatData: + { + onReceiveClientData(msg); + break; + } + + default: + TRESPASS(); + } + break; + } + + case kWhatStop: + { + looper()->stop(); + break; + } + + case kWhatMediaReceiverNotify: + { + onMediaReceiverNotify(msg); + break; + } + + case kWhatTimeSyncerNotify: + { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + if (what == TimeSyncer::kWhatTimeOffset) { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + + if (mSetupDeferred) { + CHECK_EQ((status_t)OK, + sendSetup( + mSessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0")); + + mSetupDeferred = false; + } + } + break; + } + + case kWhatReportLateness: + { + if (mLatencyCount > 0) { + int64_t avgLatencyUs = mLatencySumUs / mLatencyCount; + + ALOGV("avg. latency = %lld ms (max %lld ms)", + avgLatencyUs / 1000ll, + mLatencyMaxUs / 1000ll); + + sp params = new AMessage; + params->setInt64("avgLatencyUs", avgLatencyUs); + params->setInt64("maxLatencyUs", mLatencyMaxUs); + mMediaReceiver->informSender(0 /* trackIndex */, params); + } + + mLatencyCount = 0; + mLatencySumUs = 0ll; + mLatencyMaxUs = 0ll; + + msg->post(kReportLatenessEveryUs); + break; + } + + default: + TRESPASS(); + } +} + +void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) { + int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll; + + if (delayMs > mMaxDelayMs) { + mMaxDelayMs = delayMs; + } + + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("[%lld]: (%4lld ms / %4lld ms) %s", + timeUs / 1000, + delayMs, + mMaxDelayMs, + kPattern + kPatternSize - n); +} + +void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case MediaReceiver::kWhatInitDone: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGI("MediaReceiver initialization completed w/ err %d", err); + break; + } + + case MediaReceiver::kWhatError: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGE("MediaReceiver signaled error %d", err); + break; + } + + case MediaReceiver::kWhatAccessUnit: + { + if (mRenderer == NULL) { + mRenderer = new DirectRenderer(mSurfaceTex); + looper()->registerHandler(mRenderer); + } + + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) { + mTimeOffsetUs = timeUs - ALooper::GetNowUs(); + mTimeOffsetValid = true; + } + + CHECK(mTimeOffsetValid); + + // We are the timesync _client_, + // client time = server time - time offset. + timeUs -= mTimeOffsetUs; + + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayUs = nowUs - timeUs; + + mLatencySumUs += delayUs; + if (mLatencyCount == 0 || delayUs > mLatencyMaxUs) { + mLatencyMaxUs = delayUs; + } + ++mLatencyCount; + + // dumpDelay(trackIndex, timeUs); + + timeUs += 220000ll; // Assume 220 ms of latency + accessUnit->meta()->setInt64("timeUs", timeUs); + + sp format; + if (msg->findMessage("format", &format)) { + mRenderer->setFormat(trackIndex, format); + } + + mRenderer->queueAccessUnit(trackIndex, accessUnit); + break; + } + + case MediaReceiver::kWhatPacketLost: + { +#if 0 + if (!mIDRFrameRequestPending) { + ALOGI("requesting IDR frame"); + + sendIDRFrameRequest(mSessionID); + } +#endif + break; + } + + default: + TRESPASS(); + } +} + +void WifiDisplaySink::registerResponseHandler( + int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) { + ResponseID id; + id.mSessionID = sessionID; + id.mCSeq = cseq; + mResponseHandlers.add(id, func); +} + +status_t WifiDisplaySink::sendM2(int32_t sessionID) { + AString request = "OPTIONS * RTSP/1.0\r\n"; + AppendCommonResponse(&request, mNextCSeq); + + request.append( + "Require: org.wfa.wfd1.0\r\n" + "\r\n"); + + status_t err = + mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response); + + ++mNextCSeq; + + return OK; +} + +status_t WifiDisplaySink::onReceiveM2Response( + int32_t sessionID, const sp &msg) { + int32_t statusCode; + if (!msg->getStatusCode(&statusCode)) { + return ERROR_MALFORMED; + } + + if (statusCode != 200) { + return ERROR_UNSUPPORTED; + } + + return OK; +} + +status_t WifiDisplaySink::onReceiveSetupResponse( + int32_t sessionID, const sp &msg) { + int32_t statusCode; + if (!msg->getStatusCode(&statusCode)) { + return ERROR_MALFORMED; + } + + if (statusCode != 200) { + return ERROR_UNSUPPORTED; + } + + if (!msg->findString("session", &mPlaybackSessionID)) { + return ERROR_MALFORMED; + } + + if (!ParsedMessage::GetInt32Attribute( + mPlaybackSessionID.c_str(), + "timeout", + &mPlaybackSessionTimeoutSecs)) { + mPlaybackSessionTimeoutSecs = -1; + } + + ssize_t colonPos = mPlaybackSessionID.find(";"); + if (colonPos >= 0) { + // Strip any options from the returned session id. + mPlaybackSessionID.erase( + colonPos, mPlaybackSessionID.size() - colonPos); + } + + status_t err = configureTransport(msg); + + if (err != OK) { + return err; + } + + mState = PAUSED; + + return sendPlay( + sessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); +} + +status_t WifiDisplaySink::configureTransport(const sp &msg) { + if (mUsingTCPTransport && !(mFlags & FLAG_SPECIAL_MODE)) { + // In "special" mode we still use a UDP RTCP back-channel that + // needs connecting. + return OK; + } + + AString transport; + if (!msg->findString("transport", &transport)) { + ALOGE("Missing 'transport' field in SETUP response."); + return ERROR_MALFORMED; + } + + AString sourceHost; + if (!ParsedMessage::GetAttribute( + transport.c_str(), "source", &sourceHost)) { + sourceHost = mRTSPHost; + } + + AString serverPortStr; + if (!ParsedMessage::GetAttribute( + transport.c_str(), "server_port", &serverPortStr)) { + ALOGE("Missing 'server_port' in Transport field."); + return ERROR_MALFORMED; + } + + int rtpPort, rtcpPort; + if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2 + || rtpPort <= 0 || rtpPort > 65535 + || rtcpPort <=0 || rtcpPort > 65535 + || rtcpPort != rtpPort + 1) { + ALOGE("Invalid server_port description '%s'.", + serverPortStr.c_str()); + + return ERROR_MALFORMED; + } + + if (rtpPort & 1) { + ALOGW("Server picked an odd numbered RTP port."); + } + + return mMediaReceiver->connectTrack( + 0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort); +} + +status_t WifiDisplaySink::onReceivePlayResponse( + int32_t sessionID, const sp &msg) { + int32_t statusCode; + if (!msg->getStatusCode(&statusCode)) { + return ERROR_MALFORMED; + } + + if (statusCode != 200) { + return ERROR_UNSUPPORTED; + } + + mState = PLAYING; + + (new AMessage(kWhatReportLateness, id()))->post(kReportLatenessEveryUs); + + return OK; +} + +status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse( + int32_t sessionID, const sp &msg) { + CHECK(mIDRFrameRequestPending); + mIDRFrameRequestPending = false; + + return OK; +} + +void WifiDisplaySink::onReceiveClientData(const sp &msg) { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + sp obj; + CHECK(msg->findObject("data", &obj)); + + sp data = + static_cast(obj.get()); + + ALOGV("session %d received '%s'", + sessionID, data->debugString().c_str()); + + AString method; + AString uri; + data->getRequestField(0, &method); + + int32_t cseq; + if (!data->findInt32("cseq", &cseq)) { + sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */); + return; + } + + if (method.startsWith("RTSP/")) { + // This is a response. + + ResponseID id; + id.mSessionID = sessionID; + id.mCSeq = cseq; + + ssize_t index = mResponseHandlers.indexOfKey(id); + + if (index < 0) { + ALOGW("Received unsolicited server response, cseq %d", cseq); + return; + } + + HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index); + mResponseHandlers.removeItemsAt(index); + + status_t err = (this->*func)(sessionID, data); + CHECK_EQ(err, (status_t)OK); + } else { + AString version; + data->getRequestField(2, &version); + if (!(version == AString("RTSP/1.0"))) { + sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq); + return; + } + + if (method == "OPTIONS") { + onOptionsRequest(sessionID, cseq, data); + } else if (method == "GET_PARAMETER") { + onGetParameterRequest(sessionID, cseq, data); + } else if (method == "SET_PARAMETER") { + onSetParameterRequest(sessionID, cseq, data); + } else { + sendErrorResponse(sessionID, "405 Method Not Allowed", cseq); + } + } +} + +void WifiDisplaySink::onOptionsRequest( + int32_t sessionID, + int32_t cseq, + const sp &data) { + AString response = "RTSP/1.0 200 OK\r\n"; + AppendCommonResponse(&response, cseq); + response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n"); + response.append("\r\n"); + + status_t err = mNetSession->sendRequest(sessionID, response.c_str()); + CHECK_EQ(err, (status_t)OK); + + err = sendM2(sessionID); + CHECK_EQ(err, (status_t)OK); +} + +void WifiDisplaySink::onGetParameterRequest( + int32_t sessionID, + int32_t cseq, + const sp &data) { + AString body; + + if (mState == CONNECTED) { + mUsingTCPTransport = false; + mUsingTCPInterleaving = false; + + char val[PROPERTY_VALUE_MAX]; + if (property_get("media.wfd-sink.tcp-mode", val, NULL)) { + if (!strcasecmp("true", val) || !strcmp("1", val)) { + ALOGI("Using TCP unicast transport."); + mUsingTCPTransport = true; + mUsingTCPInterleaving = false; + } else if (!strcasecmp("interleaved", val)) { + ALOGI("Using TCP interleaved transport."); + mUsingTCPTransport = true; + mUsingTCPInterleaving = true; + } + } else if (mFlags & FLAG_SPECIAL_MODE) { + mUsingTCPTransport = true; + } + + body = "wfd_video_formats: "; + body.append(mSinkSupportedVideoFormats.getFormatSpec()); + + body.append( + "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n" + "wfd_client_rtp_ports: RTP/AVP/"); + + if (mUsingTCPTransport) { + body.append("TCP;"); + if (mUsingTCPInterleaving) { + body.append("interleaved"); + } else { + body.append("unicast 19000 0"); + } + } else { + body.append("UDP;unicast 19000 0"); + } + + body.append(" mode=play\r\n"); + } + + AString response = "RTSP/1.0 200 OK\r\n"; + AppendCommonResponse(&response, cseq); + response.append("Content-Type: text/parameters\r\n"); + response.append(StringPrintf("Content-Length: %d\r\n", body.size())); + response.append("\r\n"); + response.append(body); + + status_t err = mNetSession->sendRequest(sessionID, response.c_str()); + CHECK_EQ(err, (status_t)OK); +} + +status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { + sp notify = new AMessage(kWhatMediaReceiverNotify, id()); + + mMediaReceiverLooper = new ALooper; + mMediaReceiverLooper->setName("media_receiver"); + + mMediaReceiverLooper->start( + false /* runOnCallingThread */, + false /* canCallJava */, + PRIORITY_AUDIO); + + mMediaReceiver = new MediaReceiver(mNetSession, notify); + mMediaReceiverLooper->registerHandler(mMediaReceiver); + + RTPReceiver::TransportMode rtpMode = RTPReceiver::TRANSPORT_UDP; + if (mUsingTCPTransport) { + if (mUsingTCPInterleaving) { + rtpMode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED; + } else { + rtpMode = RTPReceiver::TRANSPORT_TCP; + } + } + + int32_t localRTPPort; + status_t err = mMediaReceiver->addTrack( + rtpMode, RTPReceiver::TRANSPORT_UDP /* rtcpMode */, &localRTPPort); + + if (err == OK) { + err = mMediaReceiver->initAsync(MediaReceiver::MODE_TRANSPORT_STREAM); + } + + if (err != OK) { + mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id()); + mMediaReceiver.clear(); + + mMediaReceiverLooper->stop(); + mMediaReceiverLooper.clear(); + + return err; + } + + AString request = StringPrintf("SETUP %s RTSP/1.0\r\n", uri); + + AppendCommonResponse(&request, mNextCSeq); + + if (rtpMode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) { + request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n"); + } else if (rtpMode == RTPReceiver::TRANSPORT_TCP) { + if (mFlags & FLAG_SPECIAL_MODE) { + // This isn't quite true, since the RTP connection is through TCP + // and the RTCP connection through UDP... + request.append( + StringPrintf( + "Transport: RTP/AVP/TCP;unicast;client_port=%d-%d\r\n", + localRTPPort, localRTPPort + 1)); + } else { + request.append( + StringPrintf( + "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n", + localRTPPort)); + } + } else { + request.append( + StringPrintf( + "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", + localRTPPort, + localRTPPort + 1)); + } + + request.append("\r\n"); + + ALOGV("request = '%s'", request.c_str()); + + err = mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse); + + ++mNextCSeq; + + return OK; +} + +status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) { + AString request = StringPrintf("PLAY %s RTSP/1.0\r\n", uri); + + AppendCommonResponse(&request, mNextCSeq); + + request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); + request.append("\r\n"); + + status_t err = + mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse); + + ++mNextCSeq; + + return OK; +} + +status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) { + CHECK(!mIDRFrameRequestPending); + + AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; + + AppendCommonResponse(&request, mNextCSeq); + + AString content = "wfd_idr_request\r\n"; + + request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); + request.append(StringPrintf("Content-Length: %d\r\n", content.size())); + request.append("\r\n"); + request.append(content); + + status_t err = + mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, + mNextCSeq, + &WifiDisplaySink::onReceiveIDRFrameRequestResponse); + + ++mNextCSeq; + + mIDRFrameRequestPending = true; + + return OK; +} + +void WifiDisplaySink::onSetParameterRequest( + int32_t sessionID, + int32_t cseq, + const sp &data) { + const char *content = data->getContent(); + + if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) { + if ((mFlags & FLAG_SPECIAL_MODE) && !mTimeOffsetValid) { + mSetupDeferred = true; + } else { + status_t err = + sendSetup( + sessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); + + CHECK_EQ(err, (status_t)OK); + } + } + + AString response = "RTSP/1.0 200 OK\r\n"; + AppendCommonResponse(&response, cseq); + response.append("\r\n"); + + status_t err = mNetSession->sendRequest(sessionID, response.c_str()); + CHECK_EQ(err, (status_t)OK); +} + +void WifiDisplaySink::sendErrorResponse( + int32_t sessionID, + const char *errorDetail, + int32_t cseq) { + AString response; + response.append("RTSP/1.0 "); + response.append(errorDetail); + response.append("\r\n"); + + AppendCommonResponse(&response, cseq); + + response.append("\r\n"); + + status_t err = mNetSession->sendRequest(sessionID, response.c_str()); + CHECK_EQ(err, (status_t)OK); +} + +// static +void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) { + time_t now = time(NULL); + struct tm *now2 = gmtime(&now); + char buf[128]; + strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2); + + response->append("Date: "); + response->append(buf); + response->append("\r\n"); + + response->append(StringPrintf("User-Agent: %s\r\n", sUserAgent.c_str())); + + if (cseq >= 0) { + response->append(StringPrintf("CSeq: %d\r\n", cseq)); + } +} + +} // namespace android diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h new file mode 100644 index 0000000..adb9d89 --- /dev/null +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -0,0 +1,196 @@ +/* + * Copyright 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 WIFI_DISPLAY_SINK_H_ + +#define WIFI_DISPLAY_SINK_H_ + +#include "ANetworkSession.h" + +#include "VideoFormats.h" + +#include +#include + +namespace android { + +struct AMessage; +struct DirectRenderer; +struct MediaReceiver; +struct ParsedMessage; +struct TimeSyncer; + +// Represents the RTSP client acting as a wifi display sink. +// Connects to a wifi display source and renders the incoming +// transport stream using a MediaPlayer instance. +struct WifiDisplaySink : public AHandler { + enum { + kWhatDisconnected, + }; + + enum Flags { + FLAG_SPECIAL_MODE = 1, + }; + + // If no notification message is specified (notify == NULL) + // the sink will stop its looper() once the session ends, + // otherwise it will post an appropriate notification but leave + // the looper() running. + WifiDisplaySink( + uint32_t flags, + const sp &netSession, + const sp &bufferProducer = NULL, + const sp ¬ify = NULL); + + void start(const char *sourceHost, int32_t sourcePort); + void start(const char *uri); + +protected: + virtual ~WifiDisplaySink(); + virtual void onMessageReceived(const sp &msg); + +private: + enum State { + UNDEFINED, + CONNECTING, + CONNECTED, + PAUSED, + PLAYING, + }; + + enum { + kWhatStart, + kWhatRTSPNotify, + kWhatStop, + kWhatMediaReceiverNotify, + kWhatTimeSyncerNotify, + kWhatReportLateness, + }; + + struct ResponseID { + int32_t mSessionID; + int32_t mCSeq; + + bool operator<(const ResponseID &other) const { + return mSessionID < other.mSessionID + || (mSessionID == other.mSessionID + && mCSeq < other.mCSeq); + } + }; + + typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)( + int32_t sessionID, const sp &msg); + + static const int64_t kReportLatenessEveryUs = 1000000ll; + + static const AString sUserAgent; + + State mState; + uint32_t mFlags; + VideoFormats mSinkSupportedVideoFormats; + sp mNetSession; + sp mSurfaceTex; + sp mNotify; + sp mTimeSyncer; + bool mUsingTCPTransport; + bool mUsingTCPInterleaving; + AString mRTSPHost; + int32_t mSessionID; + + int32_t mNextCSeq; + + KeyedVector mResponseHandlers; + + sp mMediaReceiverLooper; + sp mMediaReceiver; + sp mRenderer; + + AString mPlaybackSessionID; + int32_t mPlaybackSessionTimeoutSecs; + + bool mIDRFrameRequestPending; + + int64_t mTimeOffsetUs; + bool mTimeOffsetValid; + + bool mSetupDeferred; + + size_t mLatencyCount; + int64_t mLatencySumUs; + int64_t mLatencyMaxUs; + + int64_t mMaxDelayMs; + + status_t sendM2(int32_t sessionID); + status_t sendSetup(int32_t sessionID, const char *uri); + status_t sendPlay(int32_t sessionID, const char *uri); + status_t sendIDRFrameRequest(int32_t sessionID); + + status_t onReceiveM2Response( + int32_t sessionID, const sp &msg); + + status_t onReceiveSetupResponse( + int32_t sessionID, const sp &msg); + + status_t configureTransport(const sp &msg); + + status_t onReceivePlayResponse( + int32_t sessionID, const sp &msg); + + status_t onReceiveIDRFrameRequestResponse( + int32_t sessionID, const sp &msg); + + void registerResponseHandler( + int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func); + + void onReceiveClientData(const sp &msg); + + void onOptionsRequest( + int32_t sessionID, + int32_t cseq, + const sp &data); + + void onGetParameterRequest( + int32_t sessionID, + int32_t cseq, + const sp &data); + + void onSetParameterRequest( + int32_t sessionID, + int32_t cseq, + const sp &data); + + void onMediaReceiverNotify(const sp &msg); + + void sendErrorResponse( + int32_t sessionID, + const char *errorDetail, + int32_t cseq); + + static void AppendCommonResponse(AString *response, int32_t cseq); + + bool ParseURL( + const char *url, AString *host, int32_t *port, AString *path, + AString *user, AString *pass); + + void dumpDelay(size_t trackIndex, int64_t timeUs); + + DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink); +}; + +} // namespace android + +#endif // WIFI_DISPLAY_SINK_H_ diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 3d7b865..cacfcca 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -559,6 +559,8 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( converter->dropAFrame(); } } + } else if (what == MediaSender::kWhatInformSender) { + onSinkFeedback(msg); } else { TRESPASS(); } @@ -654,6 +656,89 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } +void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp &msg) { + int64_t avgLatencyUs; + CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); + + ALOGI("sink reports avg. latency of %lld ms (max %lld ms)", + avgLatencyUs / 1000ll, + maxLatencyUs / 1000ll); + + if (mVideoTrackIndex >= 0) { + const sp &videoTrack = mTracks.valueFor(mVideoTrackIndex); + sp converter = videoTrack->converter(); + + if (converter != NULL) { + int32_t videoBitrate = + Converter::GetInt32Property("media.wfd.video-bitrate", -1); + + char val[PROPERTY_VALUE_MAX]; + if (videoBitrate < 0 + && property_get("media.wfd.video-bitrate", val, NULL) + && !strcasecmp("adaptive", val)) { + videoBitrate = converter->getVideoBitrate(); + + if (avgLatencyUs > 300000ll) { + videoBitrate *= 0.6; + } else if (avgLatencyUs < 100000ll) { + videoBitrate *= 1.1; + } + } + + if (videoBitrate > 0) { + if (videoBitrate < 500000) { + videoBitrate = 500000; + } else if (videoBitrate > 10000000) { + videoBitrate = 10000000; + } + + if (videoBitrate != converter->getVideoBitrate()) { + ALOGI("setting video bitrate to %d bps", videoBitrate); + + converter->setVideoBitrate(videoBitrate); + } + } + } + + sp repeaterSource = videoTrack->repeaterSource(); + if (repeaterSource != NULL) { + double rateHz = + Converter::GetInt32Property( + "media.wfd.video-framerate", -1); + + char val[PROPERTY_VALUE_MAX]; + if (rateHz < 0.0 + && property_get("media.wfd.video-framerate", val, NULL) + && !strcasecmp("adaptive", val)) { + rateHz = repeaterSource->getFrameRate(); + + if (avgLatencyUs > 300000ll) { + rateHz *= 0.9; + } else if (avgLatencyUs < 200000ll) { + rateHz *= 1.1; + } + } + + if (rateHz > 0) { + if (rateHz < 5.0) { + rateHz = 5.0; + } else if (rateHz > 30.0) { + rateHz = 30.0; + } + + if (rateHz != repeaterSource->getFrameRate()) { + ALOGI("setting frame rate to %.2f Hz", rateHz); + + repeaterSource->setFrameRate(rateHz); + } + } + } + } +} + status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( bool enableAudio, bool enableVideo) { DataSource::RegisterDefaultSniffers(); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 2b5bee9..4a49811 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -23,6 +23,7 @@ #include "Parameters.h" #include "ParsedMessage.h" #include "rtp/RTPSender.h" +#include "TimeSyncer.h" #include #include @@ -164,6 +165,14 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { } else { err = -EINVAL; } + } + + if (err == OK) { + sp notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + mTimeSyncer->startServer(8123); mState = AWAITING_CLIENT_CONNECTION; } @@ -539,6 +548,11 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { break; } + case kWhatTimeSyncerNotify: + { + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 44d3e4d..3efa0b4 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -30,6 +30,7 @@ namespace android { struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; +struct TimeSyncer; // Represents the RTSP server acting as a wifi display source. // Manages incoming connections, sets up Playback sessions as necessary. @@ -82,6 +83,7 @@ private: kWhatHDCPNotify, kWhatFinishStop2, kWhatTeardownTriggerTimedOut, + kWhatTimeSyncerNotify, }; struct ResponseID { @@ -118,6 +120,7 @@ private: sp mNetSession; sp mClient; AString mMediaPath; + sp mTimeSyncer; struct in_addr mInterfaceAddr; int32_t mSessionID; diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp new file mode 100644 index 0000000..111846d --- /dev/null +++ b/media/libstagefright/wifi-display/udptest.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 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_NEBUG 0 +#define LOG_TAG "udptest" +#include + +#include "ANetworkSession.h" +#include "TimeSyncer.h" + +#include +#include + +namespace android { + +} // namespace android + +static void usage(const char *me) { + fprintf(stderr, + "usage: %s -c host[:port]\tconnect to test server\n" + " -l \tcreate a test server\n", + me); +} + +int main(int argc, char **argv) { + using namespace android; + + ProcessState::self()->startThreadPool(); + + int32_t localPort = -1; + int32_t connectToPort = -1; + AString connectToHost; + + int res; + while ((res = getopt(argc, argv, "hc:l:")) >= 0) { + switch (res) { + case 'c': + { + const char *colonPos = strrchr(optarg, ':'); + + if (colonPos == NULL) { + connectToHost = optarg; + connectToPort = 49152; + } else { + connectToHost.setTo(optarg, colonPos - optarg); + + char *end; + connectToPort = strtol(colonPos + 1, &end, 10); + + if (*end != '\0' || end == colonPos + 1 + || connectToPort < 1 || connectToPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + } + break; + } + + case 'l': + { + char *end; + localPort = strtol(optarg, &end, 10); + + if (*end != '\0' || end == optarg + || localPort < 1 || localPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + break; + } + + case '?': + case 'h': + usage(argv[0]); + exit(1); + } + } + + if (localPort < 0 && connectToPort < 0) { + fprintf(stderr, + "You need to select either client or server mode.\n"); + exit(1); + } + + sp netSession = new ANetworkSession; + netSession->start(); + + sp looper = new ALooper; + + sp handler = new TimeSyncer(netSession, NULL /* notify */); + looper->registerHandler(handler); + + if (localPort >= 0) { + handler->startServer(localPort); + } else { + handler->startClient(connectToHost.c_str(), connectToPort); + } + + looper->start(true /* runOnCallingThread */); + + return 0; +} + diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index c947765..9fee4d0 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "wfd" #include +#include "sink/WifiDisplaySink.h" #include "source/WifiDisplaySource.h" #include @@ -38,8 +39,12 @@ namespace android { static void usage(const char *me) { fprintf(stderr, "usage:\n" - " %s -l iface[:port]\tcreate a wifi display source\n" - " -f(ilename) \tstream media\n", + " %s -c host[:port]\tconnect to wifi source\n" + " -u uri \tconnect to an rtsp uri\n" + " -l ip[:port] \tlisten on the specified port " + " -f(ilename) \tstream media " + "(create a sink)\n" + " -s(pecial) \trun in 'special' mode\n", me); } @@ -209,14 +214,48 @@ int main(int argc, char **argv) { DataSource::RegisterDefaultSniffers(); + AString connectToHost; + int32_t connectToPort = -1; + AString uri; + AString listenOnAddr; int32_t listenOnPort = -1; AString path; + bool specialMode = false; + int res; - while ((res = getopt(argc, argv, "hl:f:")) >= 0) { + while ((res = getopt(argc, argv, "hc:l:u:f:s")) >= 0) { switch (res) { + case 'c': + { + const char *colonPos = strrchr(optarg, ':'); + + if (colonPos == NULL) { + connectToHost = optarg; + connectToPort = WifiDisplaySource::kWifiDisplayDefaultPort; + } else { + connectToHost.setTo(optarg, colonPos - optarg); + + char *end; + connectToPort = strtol(colonPos + 1, &end, 10); + + if (*end != '\0' || end == colonPos + 1 + || connectToPort < 1 || connectToPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + } + break; + } + + case 'u': + { + uri = optarg; + break; + } + case 'f': { path = optarg; @@ -245,6 +284,12 @@ int main(int argc, char **argv) { break; } + case 's': + { + specialMode = true; + break; + } + case '?': case 'h': default: @@ -253,6 +298,13 @@ int main(int argc, char **argv) { } } + if (connectToPort >= 0 && listenOnPort >= 0) { + fprintf(stderr, + "You can connect to a source or create one, " + "but not both at the same time.\n"); + exit(1); + } + if (listenOnPort >= 0) { if (path.empty()) { createSource(listenOnAddr, listenOnPort); @@ -263,7 +315,72 @@ int main(int argc, char **argv) { exit(0); } - usage(argv[0]); + if (connectToPort < 0 && uri.empty()) { + fprintf(stderr, + "You need to select either source host or uri.\n"); + + exit(1); + } + + if (connectToPort >= 0 && !uri.empty()) { + fprintf(stderr, + "You need to either connect to a wfd host or an rtsp url, " + "not both.\n"); + exit(1); + } + + sp composerClient = new SurfaceComposerClient; + CHECK_EQ(composerClient->initCheck(), (status_t)OK); + + sp display(SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ssize_t displayWidth = info.w; + ssize_t displayHeight = info.h; + + ALOGV("display is %d x %d\n", displayWidth, displayHeight); + + sp control = + composerClient->createSurface( + String8("A Surface"), + displayWidth, + displayHeight, + PIXEL_FORMAT_RGB_565, + 0); + + CHECK(control != NULL); + CHECK(control->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); + CHECK_EQ(control->show(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); + + sp surface = control->getSurface(); + CHECK(surface != NULL); + + sp session = new ANetworkSession; + session->start(); + + sp looper = new ALooper; + + sp sink = new WifiDisplaySink( + specialMode ? WifiDisplaySink::FLAG_SPECIAL_MODE : 0 /* flags */, + session, + surface->getIGraphicBufferProducer()); + + looper->registerHandler(sink); + + if (connectToPort >= 0) { + sink->start(connectToHost.c_str(), connectToPort); + } else { + sink->start(uri.c_str()); + } + + looper->start(true /* runOnCallingThread */); + + composerClient->dispose(); return 0; } -- cgit v1.1 From 2ded8b53014602d25b20bade8ce46db95a8da4b5 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 26 Apr 2013 16:11:45 -0700 Subject: stagefright: Fix port-reconfiguration & output-buffer-filled race condition Remove the invalid assumption that when a port-reconfiguration event is received, buffers cannot be downstream (waiting to be rendered). Luckily, these buffers are properly handled (freed) after they are sent to be rendered. Also, the case where buffers have been sent onto the native window is already handled. Change-Id: I1df39c1ffc2bfb96f8b7b4ee5be07cae654f956f Signed-off-by: Lajos Molnar Bug: 8736466 --- media/libstagefright/ACodec.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 6d952c3..73bf6ba 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -748,12 +748,10 @@ status_t ACodec::freeOutputBuffersNotOwnedByComponent() { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); - if (info->mStatus != - BufferInfo::OWNED_BY_COMPONENT) { - // We shouldn't have sent out any buffers to the client at this - // point. - CHECK_NE((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM); - + // At this time some buffers may still be with the component + // or being drained. + if (info->mStatus != BufferInfo::OWNED_BY_COMPONENT && + info->mStatus != BufferInfo::OWNED_BY_DOWNSTREAM) { CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i)); } } -- cgit v1.1 From 4b4bb11b8747adeb2efe56c7df4ab6803dd7db41 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 29 Apr 2013 13:17:50 -0700 Subject: A reference to the psi section data could become invalid if more sections were added to the KeyedVector. Change-Id: I095b5452ccfad89d69fc502fb21ce39495e201c3 related-to-bug: 8754565 --- media/libstagefright/mpeg2ts/ATSParser.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index c12572f..9850a46 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -1059,7 +1059,7 @@ status_t ATSParser::parsePID( ssize_t sectionIndex = mPSISections.indexOfKey(PID); if (sectionIndex >= 0) { - const sp §ion = mPSISections.valueAt(sectionIndex); + sp section = mPSISections.valueAt(sectionIndex); if (payload_unit_start_indicator) { CHECK(section->isEmpty()); @@ -1068,7 +1068,6 @@ status_t ATSParser::parsePID( br->skipBits(skip * 8); } - CHECK((br->numBitsLeft() % 8) == 0); status_t err = section->append(br->data(), br->numBitsLeft() / 8); @@ -1103,10 +1102,13 @@ status_t ATSParser::parsePID( if (!handled) { mPSISections.removeItem(PID); + section.clear(); } } - section->clear(); + if (section != NULL) { + section->clear(); + } return OK; } -- cgit v1.1 From e96ee699aca0f711d41e6c0833e5de2341c4a36d Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 30 Apr 2013 16:08:47 -0700 Subject: Support MediaCodec::getOutputFormat for encoders codec specific data is provided as part of the MediaFormat if available. Change-Id: I5a79c936e2411fe66ebc694791071faefc33941e related-to-bug: 8616651 --- media/libstagefright/ACodec.cpp | 268 +++++++++++++++++++++++++----------- media/libstagefright/MediaCodec.cpp | 78 ++++++++++- 2 files changed, 261 insertions(+), 85 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 6d952c3..058852e 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1470,24 +1470,47 @@ status_t ACodec::setSupportedOutputFormat() { &format, sizeof(format)); } +static const struct VideoCodingMapEntry { + const char *mMime; + OMX_VIDEO_CODINGTYPE mVideoCodingType; +} kVideoCodingMapEntry[] = { + { MEDIA_MIMETYPE_VIDEO_AVC, OMX_VIDEO_CodingAVC }, + { MEDIA_MIMETYPE_VIDEO_MPEG4, OMX_VIDEO_CodingMPEG4 }, + { MEDIA_MIMETYPE_VIDEO_H263, OMX_VIDEO_CodingH263 }, + { MEDIA_MIMETYPE_VIDEO_MPEG2, OMX_VIDEO_CodingMPEG2 }, + { MEDIA_MIMETYPE_VIDEO_VPX, OMX_VIDEO_CodingVPX }, +}; + static status_t GetVideoCodingTypeFromMime( const char *mime, OMX_VIDEO_CODINGTYPE *codingType) { - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { - *codingType = OMX_VIDEO_CodingAVC; - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { - *codingType = OMX_VIDEO_CodingMPEG4; - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { - *codingType = OMX_VIDEO_CodingH263; - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { - *codingType = OMX_VIDEO_CodingMPEG2; - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) { - *codingType = OMX_VIDEO_CodingVPX; - } else { - *codingType = OMX_VIDEO_CodingUnused; - return ERROR_UNSUPPORTED; + for (size_t i = 0; + i < sizeof(kVideoCodingMapEntry) / sizeof(kVideoCodingMapEntry[0]); + ++i) { + if (!strcasecmp(mime, kVideoCodingMapEntry[i].mMime)) { + *codingType = kVideoCodingMapEntry[i].mVideoCodingType; + return OK; + } } - return OK; + *codingType = OMX_VIDEO_CodingUnused; + + return ERROR_UNSUPPORTED; +} + +static status_t GetMimeTypeForVideoCoding( + OMX_VIDEO_CODINGTYPE codingType, AString *mime) { + for (size_t i = 0; + i < sizeof(kVideoCodingMapEntry) / sizeof(kVideoCodingMapEntry[0]); + ++i) { + if (codingType == kVideoCodingMapEntry[i].mVideoCodingType) { + *mime = kVideoCodingMapEntry[i].mMime; + return OK; + } + } + + mime->clear(); + + return ERROR_UNSUPPORTED; } status_t ACodec::setupVideoDecoder( @@ -2227,49 +2250,61 @@ void ACodec::sendFormatChange() { { OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video; - notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW); + AString mime; + if (!mIsEncoder) { + notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW); + } else if (GetMimeTypeForVideoCoding( + videoDef->eCompressionFormat, &mime) != OK) { + notify->setString("mime", "application/octet-stream"); + } else { + notify->setString("mime", mime.c_str()); + } + notify->setInt32("width", videoDef->nFrameWidth); notify->setInt32("height", videoDef->nFrameHeight); - notify->setInt32("stride", videoDef->nStride); - notify->setInt32("slice-height", videoDef->nSliceHeight); - notify->setInt32("color-format", videoDef->eColorFormat); - - OMX_CONFIG_RECTTYPE rect; - InitOMXParams(&rect); - rect.nPortIndex = kPortIndexOutput; - - if (mOMX->getConfig( - mNode, OMX_IndexConfigCommonOutputCrop, - &rect, sizeof(rect)) != OK) { - rect.nLeft = 0; - rect.nTop = 0; - rect.nWidth = videoDef->nFrameWidth; - rect.nHeight = videoDef->nFrameHeight; - } - CHECK_GE(rect.nLeft, 0); - CHECK_GE(rect.nTop, 0); - CHECK_GE(rect.nWidth, 0u); - CHECK_GE(rect.nHeight, 0u); - CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth); - CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight); - - notify->setRect( - "crop", - rect.nLeft, - rect.nTop, - rect.nLeft + rect.nWidth - 1, - rect.nTop + rect.nHeight - 1); - - if (mNativeWindow != NULL) { - android_native_rect_t crop; - crop.left = rect.nLeft; - crop.top = rect.nTop; - crop.right = rect.nLeft + rect.nWidth; - crop.bottom = rect.nTop + rect.nHeight; - - CHECK_EQ(0, native_window_set_crop( - mNativeWindow.get(), &crop)); + if (!mIsEncoder) { + notify->setInt32("stride", videoDef->nStride); + notify->setInt32("slice-height", videoDef->nSliceHeight); + notify->setInt32("color-format", videoDef->eColorFormat); + + OMX_CONFIG_RECTTYPE rect; + InitOMXParams(&rect); + rect.nPortIndex = kPortIndexOutput; + + if (mOMX->getConfig( + mNode, OMX_IndexConfigCommonOutputCrop, + &rect, sizeof(rect)) != OK) { + rect.nLeft = 0; + rect.nTop = 0; + rect.nWidth = videoDef->nFrameWidth; + rect.nHeight = videoDef->nFrameHeight; + } + + CHECK_GE(rect.nLeft, 0); + CHECK_GE(rect.nTop, 0); + CHECK_GE(rect.nWidth, 0u); + CHECK_GE(rect.nHeight, 0u); + CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth); + CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight); + + notify->setRect( + "crop", + rect.nLeft, + rect.nTop, + rect.nLeft + rect.nWidth - 1, + rect.nTop + rect.nHeight - 1); + + if (mNativeWindow != NULL) { + android_native_rect_t crop; + crop.left = rect.nLeft; + crop.top = rect.nTop; + crop.right = rect.nLeft + rect.nWidth; + crop.bottom = rect.nTop + rect.nHeight; + + CHECK_EQ(0, native_window_set_crop( + mNativeWindow.get(), &crop)); + } } break; } @@ -2277,41 +2312,108 @@ void ACodec::sendFormatChange() { case OMX_PortDomainAudio: { OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio; - CHECK_EQ((int)audioDef->eEncoding, (int)OMX_AUDIO_CodingPCM); - OMX_AUDIO_PARAM_PCMMODETYPE params; - InitOMXParams(¶ms); - params.nPortIndex = kPortIndexOutput; + switch (audioDef->eEncoding) { + case OMX_AUDIO_CodingPCM: + { + OMX_AUDIO_PARAM_PCMMODETYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; - CHECK_EQ(mOMX->getParameter( - mNode, OMX_IndexParamAudioPcm, - ¶ms, sizeof(params)), - (status_t)OK); + CHECK_EQ(mOMX->getParameter( + mNode, OMX_IndexParamAudioPcm, + ¶ms, sizeof(params)), + (status_t)OK); + + CHECK(params.nChannels == 1 || params.bInterleaved); + CHECK_EQ(params.nBitPerSample, 16u); + CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned); + CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSamplingRate); + if (mEncoderDelay + mEncoderPadding) { + size_t frameSize = params.nChannels * sizeof(int16_t); + if (mSkipCutBuffer != NULL) { + size_t prevbufsize = mSkipCutBuffer->size(); + if (prevbufsize != 0) { + ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbufsize); + } + } + mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay * frameSize, + mEncoderPadding * frameSize); + } - CHECK(params.nChannels == 1 || params.bInterleaved); - CHECK_EQ(params.nBitPerSample, 16u); - CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned); - CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear); - - notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW); - notify->setInt32("channel-count", params.nChannels); - notify->setInt32("sample-rate", params.nSamplingRate); - if (mEncoderDelay + mEncoderPadding) { - size_t frameSize = params.nChannels * sizeof(int16_t); - if (mSkipCutBuffer != NULL) { - size_t prevbufsize = mSkipCutBuffer->size(); - if (prevbufsize != 0) { - ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbufsize); + if (mChannelMaskPresent) { + notify->setInt32("channel-mask", mChannelMask); } + break; } - mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay * frameSize, - mEncoderPadding * frameSize); - } - if (mChannelMaskPresent) { - notify->setInt32("channel-mask", mChannelMask); - } + case OMX_AUDIO_CodingAAC: + { + OMX_AUDIO_PARAM_AACPROFILETYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; + + CHECK_EQ(mOMX->getParameter( + mNode, OMX_IndexParamAudioAac, + ¶ms, sizeof(params)), + (status_t)OK); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + + case OMX_AUDIO_CodingAMR: + { + OMX_AUDIO_PARAM_AMRTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; + + CHECK_EQ(mOMX->getParameter( + mNode, OMX_IndexParamAudioAmr, + ¶ms, sizeof(params)), + (status_t)OK); + notify->setInt32("channel-count", 1); + if (params.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0) { + notify->setString( + "mime", MEDIA_MIMETYPE_AUDIO_AMR_WB); + + notify->setInt32("sample-rate", 16000); + } else { + notify->setString( + "mime", MEDIA_MIMETYPE_AUDIO_AMR_NB); + + notify->setInt32("sample-rate", 8000); + } + break; + } + + case OMX_AUDIO_CodingFLAC: + { + OMX_AUDIO_PARAM_FLACTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; + + CHECK_EQ(mOMX->getParameter( + mNode, OMX_IndexParamAudioFlac, + ¶ms, sizeof(params)), + (status_t)OK); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_FLAC); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + + default: + TRESPASS(); + } break; } @@ -2957,7 +3059,7 @@ bool ACodec::BaseState::onOMXFillBufferDone( break; } - if (!mCodec->mIsEncoder && !mCodec->mSentFormat) { + if (!mCodec->mSentFormat) { mCodec->sendFormatChange(); } diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index ae7bb17..714da55 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -31,10 +31,13 @@ #include #include #include +#include #include #include #include +#include "include/avc_utils.h" + namespace android { // static @@ -741,8 +744,16 @@ void MediaCodec::onMessageReceived(const sp &msg) { } mOutputFormat = msg; - mFlags |= kFlagOutputFormatChanged; - postActivityNotificationIfPossible(); + + if (mFlags & kFlagIsEncoder) { + // Before we announce the format change we should + // collect codec specific data and amend the output + // format as necessary. + mFlags |= kFlagGatherCodecSpecificData; + } else { + mFlags |= kFlagOutputFormatChanged; + postActivityNotificationIfPossible(); + } break; } @@ -812,6 +823,25 @@ void MediaCodec::onMessageReceived(const sp &msg) { buffer->meta()->setInt32("omxFlags", omxFlags); + if (mFlags & kFlagGatherCodecSpecificData) { + // This is the very first output buffer after a + // format change was signalled, it'll either contain + // the one piece of codec specific data we can expect + // or there won't be codec specific data. + if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) { + status_t err = + amendOutputFormatWithCodecSpecificData(buffer); + + if (err != OK) { + ALOGE("Codec spit out malformed codec " + "specific data!"); + } + } + + mFlags &= ~kFlagGatherCodecSpecificData; + mFlags |= kFlagOutputFormatChanged; + } + if (mFlags & kFlagDequeueOutputPending) { CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID)); @@ -955,6 +985,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { if (flags & CONFIGURE_FLAG_ENCODE) { format->setInt32("encoder", true); + mFlags |= kFlagIsEncoder; } extractCSD(format); @@ -1413,6 +1444,8 @@ void MediaCodec::setState(State newState) { mFlags &= ~kFlagOutputFormatChanged; mFlags &= ~kFlagOutputBuffersChanged; mFlags &= ~kFlagStickyError; + mFlags &= ~kFlagIsEncoder; + mFlags &= ~kFlagGatherCodecSpecificData; mActivityNotify.clear(); } @@ -1720,4 +1753,45 @@ status_t MediaCodec::onSetParameters(const sp ¶ms) { return OK; } +status_t MediaCodec::amendOutputFormatWithCodecSpecificData( + const sp &buffer) { + AString mime; + CHECK(mOutputFormat->findString("mime", &mime)); + + if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) { + // Codec specific data should be SPS and PPS in a single buffer, + // each prefixed by a startcode (0x00 0x00 0x00 0x01). + // We separate the two and put them into the output format + // under the keys "csd-0" and "csd-1". + + unsigned csdIndex = 0; + + const uint8_t *data = buffer->data(); + size_t size = buffer->size(); + + const uint8_t *nalStart; + size_t nalSize; + while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) { + sp csd = new ABuffer(nalSize + 4); + memcpy(csd->data(), "\x00\x00\x00\x01", 4); + memcpy(csd->data() + 4, nalStart, nalSize); + + mOutputFormat->setBuffer( + StringPrintf("csd-%u", csdIndex).c_str(), csd); + + ++csdIndex; + } + + if (csdIndex != 2) { + return ERROR_MALFORMED; + } + } else { + // For everything else we just stash the codec specific data into + // the output format as a single piece of csd under "csd-0". + mOutputFormat->setBuffer("csd-0", buffer); + } + + return OK; +} + } // namespace android -- cgit v1.1 From eb8709e3c65b59e85b882b5ca8710068708671be Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 1 May 2013 13:58:36 -0700 Subject: The software FLAC encoder now properly signals an error if client attempts to configure it with too large an input buffer size. Previously this would lead to memory corruption during encoding due to a typo. Change-Id: I229b07b7dbe87fb8424419706671b66a8d58ec6b related-to-bug: 8778893 --- .../codecs/flac/enc/SoftFlacEncoder.cpp | 21 +++++++++++++++++++-- .../codecs/flac/enc/SoftFlacEncoder.h | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp index 233aed3..e64fe72 100644 --- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp @@ -109,7 +109,7 @@ void SoftFlacEncoder::initPorts() { def.eDir = OMX_DirInput; def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t) * 2; + def.nBufferSize = kMaxInputBufferSize; def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainAudio; @@ -234,6 +234,22 @@ OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter( return OMX_ErrorNone; } + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *defParams = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + + if (defParams->nPortIndex == 0) { + if (defParams->nBufferSize > kMaxInputBufferSize) { + ALOGE("Input buffer size must be at most %zu bytes", + kMaxInputBufferSize); + return OMX_ErrorUnsupportedSetting; + } + } + + // fall through + } + default: ALOGV("SoftFlacEncoder::internalSetParameter(default)"); return SimpleSoftOMXComponent::internalSetParameter(index, params); @@ -273,7 +289,7 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { return; } - if (inHeader->nFilledLen > kMaxNumSamplesPerFrame * sizeof(FLAC__int32) * 2) { + if (inHeader->nFilledLen > kMaxInputBufferSize) { ALOGE("input buffer too large (%ld).", inHeader->nFilledLen); mSignalledError = true; notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); @@ -290,6 +306,7 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { const unsigned nbInputSamples = inHeader->nFilledLen / 2; const OMX_S16 * const pcm16 = reinterpret_cast(inHeader->pBuffer); + CHECK_LE(nbInputSamples, 2 * kMaxNumSamplesPerFrame); for (unsigned i=0 ; i < nbInputSamples ; i++) { mInputBufferPcm32[i] = (FLAC__int32) pcm16[i]; } diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h index 1e0148a..97361fa 100644 --- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h @@ -52,6 +52,7 @@ private: enum { kNumBuffers = 2, kMaxNumSamplesPerFrame = 1152, + kMaxInputBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t) * 2, kMaxOutputBufferSize = 65536, //TODO check if this can be reduced }; -- cgit v1.1 From 0182f9acca6f873ee127898e408cf75cc316c3ea Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 1 May 2013 14:13:26 -0700 Subject: Don't even try to verify the native resolution validity a miracast sink advertises, even if it were valid we couldn't use it since it's not consistently implemented by sinks. Change-Id: Ibee6b3e23b5a55270fc3c419a581e2626530e3af related-to-bug: 8772006 --- media/libstagefright/wifi-display/VideoFormats.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp index d171c6f..da557f7 100644 --- a/media/libstagefright/wifi-display/VideoFormats.cpp +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -249,11 +249,20 @@ bool VideoFormats::parseFormatSpec(const char *spec) { mNativeIndex = native >> 3; mNativeType = (ResolutionType)(native & 7); + bool success; if (mNativeType >= kNumResolutionTypes) { - return false; + success = false; + } else { + success = GetConfiguration( + mNativeType, mNativeIndex, NULL, NULL, NULL, NULL); } - return GetConfiguration(mNativeType, mNativeIndex, NULL, NULL, NULL, NULL); + if (!success) { + ALOGW("sink advertised an illegal native resolution, fortunately " + "this value is ignored for the time being..."); + } + + return true; } AString VideoFormats::getFormatSpec(bool forM4Message) const { -- cgit v1.1 From b489b1639c0c12fdd498def46d3f5be3e1fdf6b9 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 1 May 2013 16:03:28 -0700 Subject: Fix miracast source code to ignore the encoder output format change it doesn't care about. Change-Id: Iec1594775a98b0c1aba662cc9f08652d2f8d4805 related-to-bug: 8616651 --- media/libstagefright/wifi-display/source/Converter.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 0a8462c..5344623 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -649,6 +649,13 @@ status_t Converter::doMoreWork() { &bufferIndex, &offset, &size, &timeUs, &flags); if (err != OK) { + if (err == INFO_FORMAT_CHANGED) { + continue; + } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { + mEncoder->getOutputBuffers(&mEncoderOutputBuffers); + continue; + } + if (err == -EAGAIN) { err = OK; } -- cgit v1.1 From c92d6b0d491df675c6728cd4ffb7217469cc9d72 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 1 May 2013 16:15:49 -0700 Subject: Fix reverb at 48kHz The LVM reverb wrapper had a test to only accept input sampling rate of 44.1 kHz. As the LVM reberb engine supports multiple sampling rate we can remove this test. The fix for issue 8512027 (commit 2a9c5cd4) caused a regression because the framework now checks the return code of the effect configure command and ignores subsequent commands in case of error. Bug: 8630044 Change-Id: I3146871f1ad8f7945a2e63ea763dd7b87368337d --- media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index 87e2c85..8a96212 100644 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -616,10 +616,6 @@ int Reverb_setConfig(ReverbContext *pContext, effect_config_t *pConfig){ || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); CHECK_ARG(pConfig->inputCfg.format == AUDIO_FORMAT_PCM_16_BIT); - if(pConfig->inputCfg.samplingRate != 44100){ - return -EINVAL; - } - //ALOGV("\tReverb_setConfig calling memcpy"); pContext->config = *pConfig; @@ -648,7 +644,7 @@ int Reverb_setConfig(ReverbContext *pContext, effect_config_t *pConfig){ return -EINVAL; } - if(pContext->SampleRate != SampleRate){ + if (pContext->SampleRate != SampleRate) { LVREV_ControlParams_st ActiveParams; LVREV_ReturnStatus_en LvmStatus = LVREV_SUCCESS; @@ -662,11 +658,14 @@ int Reverb_setConfig(ReverbContext *pContext, effect_config_t *pConfig){ LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "Reverb_setConfig") if(LvmStatus != LVREV_SUCCESS) return -EINVAL; + ActiveParams.SampleRate = SampleRate; + LvmStatus = LVREV_SetControlParameters(pContext->hInstance, &ActiveParams); LVM_ERROR_CHECK(LvmStatus, "LVREV_SetControlParameters", "Reverb_setConfig") + if(LvmStatus != LVREV_SUCCESS) return -EINVAL; //ALOGV("\tReverb_setConfig Succesfully called LVREV_SetControlParameters\n"); - + pContext->SampleRate = SampleRate; }else{ //ALOGV("\tReverb_setConfig keep sampling rate at %d", SampleRate); } @@ -818,6 +817,7 @@ int Reverb_init(ReverbContext *pContext){ /* General parameters */ params.OperatingMode = LVM_MODE_ON; params.SampleRate = LVM_FS_44100; + pContext->SampleRate = LVM_FS_44100; if(pContext->config.inputCfg.channels == AUDIO_CHANNEL_OUT_MONO){ params.SourceFormat = LVM_MONO; -- cgit v1.1 From f2ae760602a948598a168ad43673bfbd9d50fc6b Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 29 Apr 2013 13:17:50 -0700 Subject: A reference to the psi section data could become invalid if more sections were added to the KeyedVector. Change-Id: I095b5452ccfad89d69fc502fb21ce39495e201c3 related-to-bug: 8754565 --- media/libstagefright/mpeg2ts/ATSParser.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index c12572f..9850a46 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -1059,7 +1059,7 @@ status_t ATSParser::parsePID( ssize_t sectionIndex = mPSISections.indexOfKey(PID); if (sectionIndex >= 0) { - const sp §ion = mPSISections.valueAt(sectionIndex); + sp section = mPSISections.valueAt(sectionIndex); if (payload_unit_start_indicator) { CHECK(section->isEmpty()); @@ -1068,7 +1068,6 @@ status_t ATSParser::parsePID( br->skipBits(skip * 8); } - CHECK((br->numBitsLeft() % 8) == 0); status_t err = section->append(br->data(), br->numBitsLeft() / 8); @@ -1103,10 +1102,13 @@ status_t ATSParser::parsePID( if (!handled) { mPSISections.removeItem(PID); + section.clear(); } } - section->clear(); + if (section != NULL) { + section->clear(); + } return OK; } -- cgit v1.1 From 1f7d356fa094b975ad2ebf9217be6abba2c70825 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 6 May 2013 20:20:16 -0700 Subject: libutils clean-up Change-Id: I3bf32d72aa8eec627249a675c130c91a8aff6710 --- media/libmedia/Android.mk | 3 +- media/libmedia/MediaScannerClient.cpp | 2 +- media/libmedia/StringArray.cpp | 113 ++++++++++++++++++++++++++++++++++ media/libmedia/StringArray.h | 83 +++++++++++++++++++++++++ 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 media/libmedia/StringArray.cpp create mode 100644 media/libmedia/StringArray.h (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 2c0c3a5..96755bb 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -53,7 +53,8 @@ LOCAL_SRC_FILES:= \ Visualizer.cpp \ MemoryLeakTrackUtil.cpp \ SoundPool.cpp \ - SoundPoolThread.cpp + SoundPoolThread.cpp \ + StringArray.cpp LOCAL_SRC_FILES += ../libnbaio/roundup.c diff --git a/media/libmedia/MediaScannerClient.cpp b/media/libmedia/MediaScannerClient.cpp index e1e3348..93a4a4c 100644 --- a/media/libmedia/MediaScannerClient.cpp +++ b/media/libmedia/MediaScannerClient.cpp @@ -16,7 +16,7 @@ #include -#include +#include "StringArray.h" #include "autodetect.h" #include "unicode/ucnv.h" diff --git a/media/libmedia/StringArray.cpp b/media/libmedia/StringArray.cpp new file mode 100644 index 0000000..5f5b57a --- /dev/null +++ b/media/libmedia/StringArray.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// + +#include +#include + +#include "StringArray.h" + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +StringArray::StringArray() + : mMax(0), mCurrent(0), mArray(NULL) +{ +} + +StringArray:: ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; +} + +// +// Add a string. A copy of the string is made. +// +bool StringArray::push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; +} + +// +// Delete an entry. +// +void StringArray::erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; +} + +// +// Sort the array. +// +void StringArray::sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); +} + +// +// Pass this to the sort routine to do an ascending alphabetical sort. +// +int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); +} + +// +// Set entry N to specified string. +// [should use operator[] here] +// +void StringArray::setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); +} + + +}; // namespace android diff --git a/media/libmedia/StringArray.h b/media/libmedia/StringArray.h new file mode 100644 index 0000000..ae47085 --- /dev/null +++ b/media/libmedia/StringArray.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// +#ifndef _LIBS_MEDIA_STRING_ARRAY_H +#define _LIBS_MEDIA_STRING_ARRAY_H + +#include +#include + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +class StringArray { +public: + StringArray(); + virtual ~StringArray(); + + // + // Add a string. A copy of the string is made. + // + bool push_back(const char* str); + + // + // Delete an entry. + // + void erase(int idx); + + // + // Sort the array. + // + void sort(int (*compare)(const void*, const void*)); + + // + // Pass this to the sort routine to do an ascending alphabetical sort. + // + static int cmpAscendingAlpha(const void* pstr1, const void* pstr2); + + // + // Get the #of items in the array. + // + inline int size(void) const { return mCurrent; } + + // + // Return entry N. + // [should use operator[] here] + // + const char* getEntry(int idx) const { + return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx]; + } + + // + // Set entry N to specified string. + // [should use operator[] here] + // + void setEntry(int idx, const char* str); + +private: + int mMax; + int mCurrent; + char** mArray; +}; + +}; // namespace android + +#endif // _LIBS_MEDIA_STRING_ARRAY_H -- cgit v1.1 From 6aade6058521b0dbd35a9a4620f4d04f02f90444 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 9 May 2013 09:15:34 -0700 Subject: Don't render buffers that have size 0 b/8857451 Change-Id: I12a31a2f85af76602db9e6f0ec80632954b3f7ed --- media/libstagefright/ACodec.cpp | 3 ++- media/libstagefright/MediaCodec.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 058852e..994d3f4 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3131,7 +3131,8 @@ void ACodec::BaseState::onOutputBufferDrained(const sp &msg) { int32_t render; if (mCodec->mNativeWindow != NULL - && msg->findInt32("render", &render) && render != 0) { + && msg->findInt32("render", &render) && render != 0 + && (info->mData == NULL || info->mData->size() != 0)) { // The client wants this buffer to be rendered. status_t err; diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 714da55..f412dc8 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1656,7 +1656,7 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp &msg) { return -EACCES; } - if (render) { + if (render && (info->mData == NULL || info->mData->size() != 0)) { info->mNotify->setInt32("render", true); if (mSoftRenderer != NULL) { -- cgit v1.1 From 082aa3f335ffeedacafeb8982684cbb371a18c32 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 10 May 2013 10:10:42 -0700 Subject: Don't purge effects we're about to start using By acquiring the new session id before releasing the old, we prevent purging existing effects with the new session id that aren't currently attached but will be once the player is switched to the new session id. b/8767565 Change-Id: I703881b69c5accd8832ac834246925a20ada4c21 --- media/libmedia/mediaplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index ecae3d3..4a34233 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -575,8 +575,8 @@ status_t MediaPlayer::setAudioSessionId(int sessionId) return BAD_VALUE; } if (sessionId != mAudioSessionId) { - AudioSystem::releaseAudioSessionId(mAudioSessionId); AudioSystem::acquireAudioSessionId(sessionId); + AudioSystem::releaseAudioSessionId(mAudioSessionId); mAudioSessionId = sessionId; } return NO_ERROR; -- cgit v1.1 From aef5c98cd3f67e0209e1fa28489078e9f40d6f46 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 9 May 2013 01:27:59 -0700 Subject: wifi-display: misc fixes for M3 response parsing - fix wfd_video_format parsing w/ multiple H264-Codec - fix handling of CRLF at end of M3 response - fix missing wfd_client_rtp_ports Bug: 8871667 Bug: 8875749 Change-Id: Idb0b5605313bba6ff067bcd765512bbed42e5202 --- media/libstagefright/wifi-display/Parameters.cpp | 4 +- media/libstagefright/wifi-display/VideoFormats.cpp | 279 ++++++++++++--------- media/libstagefright/wifi-display/VideoFormats.h | 23 ++ .../wifi-display/source/WifiDisplaySource.cpp | 5 +- 4 files changed, 186 insertions(+), 125 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/Parameters.cpp b/media/libstagefright/wifi-display/Parameters.cpp index f7118b3..d2a61ea 100644 --- a/media/libstagefright/wifi-display/Parameters.cpp +++ b/media/libstagefright/wifi-display/Parameters.cpp @@ -65,7 +65,9 @@ status_t Parameters::parse(const char *data, size_t size) { mDict.add(name, value); - i += 2; + while (i + 1 < size && data[i] == '\r' && data[i + 1] == '\n') { + i += 2; + } } return OK; diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp index da557f7..458b163 100644 --- a/media/libstagefright/wifi-display/VideoFormats.cpp +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -24,6 +24,114 @@ namespace android { +VideoFormats::config_t VideoFormats::mConfigs[][32] = { + { + // CEA Resolutions + { 640, 480, 60, false, 0, 0}, + { 720, 480, 60, false, 0, 0}, + { 720, 480, 60, true, 0, 0}, + { 720, 576, 50, false, 0, 0}, + { 720, 576, 50, true, 0, 0}, + { 1280, 720, 30, false, 0, 0}, + { 1280, 720, 60, false, 0, 0}, + { 1920, 1080, 30, false, 0, 0}, + { 1920, 1080, 60, false, 0, 0}, + { 1920, 1080, 60, true, 0, 0}, + { 1280, 720, 25, false, 0, 0}, + { 1280, 720, 50, false, 0, 0}, + { 1920, 1080, 25, false, 0, 0}, + { 1920, 1080, 50, false, 0, 0}, + { 1920, 1080, 50, true, 0, 0}, + { 1280, 720, 24, false, 0, 0}, + { 1920, 1080, 24, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + }, + { + // VESA Resolutions + { 800, 600, 30, false, 0, 0}, + { 800, 600, 60, false, 0, 0}, + { 1024, 768, 30, false, 0, 0}, + { 1024, 768, 60, false, 0, 0}, + { 1152, 864, 30, false, 0, 0}, + { 1152, 864, 60, false, 0, 0}, + { 1280, 768, 30, false, 0, 0}, + { 1280, 768, 60, false, 0, 0}, + { 1280, 800, 30, false, 0, 0}, + { 1280, 800, 60, false, 0, 0}, + { 1360, 768, 30, false, 0, 0}, + { 1360, 768, 60, false, 0, 0}, + { 1366, 768, 30, false, 0, 0}, + { 1366, 768, 60, false, 0, 0}, + { 1280, 1024, 30, false, 0, 0}, + { 1280, 1024, 60, false, 0, 0}, + { 1400, 1050, 30, false, 0, 0}, + { 1400, 1050, 60, false, 0, 0}, + { 1440, 900, 30, false, 0, 0}, + { 1440, 900, 60, false, 0, 0}, + { 1600, 900, 30, false, 0, 0}, + { 1600, 900, 60, false, 0, 0}, + { 1600, 1200, 30, false, 0, 0}, + { 1600, 1200, 60, false, 0, 0}, + { 1680, 1024, 30, false, 0, 0}, + { 1680, 1024, 60, false, 0, 0}, + { 1680, 1050, 30, false, 0, 0}, + { 1680, 1050, 60, false, 0, 0}, + { 1920, 1200, 30, false, 0, 0}, + { 1920, 1200, 60, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + }, + { + // HH Resolutions + { 800, 480, 30, false, 0, 0}, + { 800, 480, 60, false, 0, 0}, + { 854, 480, 30, false, 0, 0}, + { 854, 480, 60, false, 0, 0}, + { 864, 480, 30, false, 0, 0}, + { 864, 480, 60, false, 0, 0}, + { 640, 360, 30, false, 0, 0}, + { 640, 360, 60, false, 0, 0}, + { 960, 540, 30, false, 0, 0}, + { 960, 540, 60, false, 0, 0}, + { 848, 480, 30, false, 0, 0}, + { 848, 480, 60, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + { 0, 0, 0, false, 0, 0}, + } +}; + VideoFormats::VideoFormats() { for (size_t i = 0; i < kNumResolutionTypes; ++i) { mResolutionEnabled[i] = 0; @@ -51,12 +159,19 @@ void VideoFormats::getNativeResolution( void VideoFormats::disableAll() { for (size_t i = 0; i < kNumResolutionTypes; ++i) { mResolutionEnabled[i] = 0; + for (size_t j = 0; j < 32; j++) { + mConfigs[i][j].profile = mConfigs[i][j].level = 0; + } } } void VideoFormats::enableAll() { for (size_t i = 0; i < kNumResolutionTypes; ++i) { mResolutionEnabled[i] = 0xffffffff; + for (size_t j = 0; j < 32; j++) { + mConfigs[i][j].profile = (1ul << PROFILE_CBP); + mConfigs[i][j].level = (1ul << LEVEL_31); + } } } @@ -92,118 +207,7 @@ bool VideoFormats::GetConfiguration( return false; } - static const struct config_t { - size_t width, height, framesPerSecond; - bool interlaced; - } kConfigs[kNumResolutionTypes][32] = { - { - // CEA Resolutions - { 640, 480, 60, false }, - { 720, 480, 60, false }, - { 720, 480, 60, true }, - { 720, 576, 50, false }, - { 720, 576, 50, true }, - { 1280, 720, 30, false }, - { 1280, 720, 60, false }, - { 1920, 1080, 30, false }, - { 1920, 1080, 60, false }, - { 1920, 1080, 60, true }, - { 1280, 720, 25, false }, - { 1280, 720, 50, false }, - { 1920, 1080, 25, false }, - { 1920, 1080, 50, false }, - { 1920, 1080, 50, true }, - { 1280, 720, 24, false }, - { 1920, 1080, 24, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - }, - { - // VESA Resolutions - { 800, 600, 30, false }, - { 800, 600, 60, false }, - { 1024, 768, 30, false }, - { 1024, 768, 60, false }, - { 1152, 864, 30, false }, - { 1152, 864, 60, false }, - { 1280, 768, 30, false }, - { 1280, 768, 60, false }, - { 1280, 800, 30, false }, - { 1280, 800, 60, false }, - { 1360, 768, 30, false }, - { 1360, 768, 60, false }, - { 1366, 768, 30, false }, - { 1366, 768, 60, false }, - { 1280, 1024, 30, false }, - { 1280, 1024, 60, false }, - { 1400, 1050, 30, false }, - { 1400, 1050, 60, false }, - { 1440, 900, 30, false }, - { 1440, 900, 60, false }, - { 1600, 900, 30, false }, - { 1600, 900, 60, false }, - { 1600, 1200, 30, false }, - { 1600, 1200, 60, false }, - { 1680, 1024, 30, false }, - { 1680, 1024, 60, false }, - { 1680, 1050, 30, false }, - { 1680, 1050, 60, false }, - { 1920, 1200, 30, false }, - { 1920, 1200, 60, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - }, - { - // HH Resolutions - { 800, 480, 30, false }, - { 800, 480, 60, false }, - { 854, 480, 30, false }, - { 854, 480, 60, false }, - { 864, 480, 30, false }, - { 864, 480, 60, false }, - { 640, 360, 30, false }, - { 640, 360, 60, false }, - { 960, 540, 30, false }, - { 960, 540, 60, false }, - { 848, 480, 30, false }, - { 848, 480, 60, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - { 0, 0, 0, false }, - } - }; - - const config_t *config = &kConfigs[type][index]; + const config_t *config = &mConfigs[type][index]; if (config->width == 0) { return false; @@ -228,24 +232,55 @@ bool VideoFormats::GetConfiguration( return true; } +bool VideoFormats::parseH264Codec(const char *spec) { + unsigned profile, level, res[3]; + + if (sscanf( + spec, + "%02x %02x %08X %08X %08X", + &profile, + &level, + &res[0], + &res[1], + &res[2]) != 5) { + return false; + } + + for (size_t i = 0; i < kNumResolutionTypes; ++i) { + for (size_t j = 0; j < 32; ++j) { + if (res[i] & (1ul << j)){ + mResolutionEnabled[i] |= (1ul << j); + if (profile > mConfigs[i][j].profile) { + mConfigs[i][j].profile = profile; + if (level > mConfigs[i][j].level) + mConfigs[i][j].level = level; + } + } + } + } + + return true; +} + bool VideoFormats::parseFormatSpec(const char *spec) { CHECK_EQ(kNumResolutionTypes, 3); unsigned native, dummy; + unsigned res[3]; + size_t size = strlen(spec); + size_t offset = 0; - if (sscanf( - spec, - "%02x %02x %02x %02x %08X %08X %08X", - &native, - &dummy, - &dummy, - &dummy, - &mResolutionEnabled[0], - &mResolutionEnabled[1], - &mResolutionEnabled[2]) != 7) { + if (sscanf(spec, "%02x %02x ", &native, &dummy) != 2) { return false; } + offset += 6; // skip native and preferred-display-mode-supported + CHECK_LE(offset + 58, size); + while (offset < size) { + parseH264Codec(spec + offset); + offset += 60; // skip H.264-codec + ", " + } + mNativeIndex = native >> 3; mNativeType = (ResolutionType)(native & 7); diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h index 69e2197..01de246 100644 --- a/media/libstagefright/wifi-display/VideoFormats.h +++ b/media/libstagefright/wifi-display/VideoFormats.h @@ -36,6 +36,27 @@ struct AString; struct VideoFormats { VideoFormats(); + struct config_t { + size_t width, height, framesPerSecond; + bool interlaced; + unsigned char profile, level; + }; + + enum ProfileType { + PROFILE_CBP = 0, + PROFILE_CHP, + kNumProfileTypes, + }; + + enum LevelType { + LEVEL_31 = 0, + LEVEL_32, + LEVEL_40, + LEVEL_41, + LEVEL_42, + kNumLevelTypes, + }; + enum ResolutionType { RESOLUTION_CEA, RESOLUTION_VESA, @@ -69,10 +90,12 @@ struct VideoFormats { size_t *chosenIndex); private: + bool parseH264Codec(const char *spec); ResolutionType mNativeType; size_t mNativeIndex; uint32_t mResolutionEnabled[kNumResolutionTypes]; + static config_t mConfigs[kNumResolutionTypes][32]; DISALLOW_EVIL_CONSTRUCTORS(VideoFormats); }; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 2b5bee9..22dd0b1 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -635,8 +635,9 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n", mClientInfo.mLocalIP.c_str())); - body.append(mWfdClientRtpPorts); - body.append("\r\n"); + body.append( + StringPrintf( + "wfd_client_rtp_ports: %s\r\n", mWfdClientRtpPorts.c_str())); AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; AppendCommonResponse(&request, mNextCSeq); -- cgit v1.1 From 704455a5a6cd22f03bb8984e0c7f46108eb1afb7 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 13 May 2013 12:47:53 -0700 Subject: Fix unreleased mutex in ToneGenerator stopTone() Commit 09108ade introduced a regression by not releasing the ToneGenerator mutex before exiting in case of error. Bug: 8852855 Change-Id: I8ba2755b218842e2034ed8dbd54b18bf2a5fc571 --- media/libmedia/ToneGenerator.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'media') diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index f09ce75..f55b697 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -1012,6 +1012,7 @@ void ToneGenerator::stopTone() { if (lStatus == NO_ERROR) { // If the tone was restarted exit now before calling clearWaveGens(); if (mState != TONE_INIT) { + mLock.unlock(); return; } ALOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); -- cgit v1.1 From 614e95449a04ca495cddfa435fddca2945d03572 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 13 May 2013 13:29:46 -0700 Subject: Fix receiver report handling. Not adjusting the size of the buffer would in effect only add a single report block (the last one added would survive) and a whole lot of uninitialized data to the report. Change-Id: I5b4353d6d8c3becb1bc102afd42385b7851b1c3a --- media/libstagefright/wifi-display/rtp/RTPReceiver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp index 8fa1dae..2d22e79 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -489,6 +489,8 @@ void RTPReceiver::Source::addReportBlock( ptr[21] = 0x00; ptr[22] = 0x00; ptr[23] = 0x00; + + buf->setRange(buf->offset(), buf->size() + 24); } //////////////////////////////////////////////////////////////////////////////// @@ -1012,7 +1014,6 @@ void RTPReceiver::scheduleSendRR() { } void RTPReceiver::onSendRR() { -#if 0 sp buf = new ABuffer(kMaxUDPPacketSize); buf->setRange(0, 0); @@ -1053,7 +1054,6 @@ void RTPReceiver::onSendRR() { addSDES(buf); mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); -#endif scheduleSendRR(); } -- cgit v1.1 From 308bcaa44e578279e61be32b572fdb0b11b1e4c7 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 3 May 2013 21:54:17 -0700 Subject: wifi-display: add support for metadata mode on encoder output pass buffer_handle_t from encoder output to HDCP encryptor input Bug: 8968123 Change-Id: Iea8007ce568641e213fd2e3cf6947a6f7a95746c --- media/libmedia/IHDCP.cpp | 54 +++++++++ media/libmediaplayerservice/HDCP.cpp | 18 +++ media/libmediaplayerservice/HDCP.h | 5 + media/libstagefright/ACodec.cpp | 54 +++++++-- media/libstagefright/wifi-display/MediaSender.cpp | 37 ++++++- media/libstagefright/wifi-display/VideoFormats.cpp | 121 ++++++++++++++++++++- media/libstagefright/wifi-display/VideoFormats.h | 20 +++- .../wifi-display/source/Converter.cpp | 51 ++++++++- .../libstagefright/wifi-display/source/Converter.h | 1 + .../wifi-display/source/PlaybackSession.cpp | 44 ++++++-- .../wifi-display/source/PlaybackSession.h | 15 ++- .../wifi-display/source/TSPacketizer.cpp | 24 +++- .../wifi-display/source/WifiDisplaySource.cpp | 19 +++- .../wifi-display/source/WifiDisplaySource.h | 2 + 14 files changed, 416 insertions(+), 49 deletions(-) (limited to 'media') diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp index f13addc..a46ff91 100644 --- a/media/libmedia/IHDCP.cpp +++ b/media/libmedia/IHDCP.cpp @@ -31,6 +31,7 @@ enum { HDCP_INIT_ASYNC, HDCP_SHUTDOWN_ASYNC, HDCP_ENCRYPT, + HDCP_ENCRYPT_NATIVE, HDCP_DECRYPT, }; @@ -108,6 +109,31 @@ struct BpHDCP : public BpInterface { return err; } + virtual status_t encryptNative( + const sp &graphicBuffer, + size_t offset, size_t size, uint32_t streamCTR, + uint64_t *outInputCTR, void *outData) { + Parcel data, reply; + data.writeInterfaceToken(IHDCP::getInterfaceDescriptor()); + data.write(*graphicBuffer); + data.writeInt32(offset); + data.writeInt32(size); + data.writeInt32(streamCTR); + remote()->transact(HDCP_ENCRYPT_NATIVE, data, &reply); + + status_t err = reply.readInt32(); + + if (err != OK) { + *outInputCTR = 0; + return err; + } + + *outInputCTR = reply.readInt64(); + reply.read(outData, size); + + return err; + } + virtual status_t decrypt( const void *inData, size_t size, uint32_t streamCTR, uint64_t inputCTR, @@ -222,6 +248,34 @@ status_t BnHDCP::onTransact( return OK; } + case HDCP_ENCRYPT_NATIVE: + { + CHECK_INTERFACE(IHDCP, data, reply); + + sp graphicBuffer = new GraphicBuffer(); + data.read(*graphicBuffer); + size_t offset = data.readInt32(); + size_t size = data.readInt32(); + uint32_t streamCTR = data.readInt32(); + void *outData = malloc(size); + uint64_t inputCTR; + + status_t err = encryptNative(graphicBuffer, offset, size, + streamCTR, &inputCTR, outData); + + reply->writeInt32(err); + + if (err == OK) { + reply->writeInt64(inputCTR); + reply->write(outData, size); + } + + free(outData); + outData = NULL; + + return OK; + } + case HDCP_DECRYPT: { size_t size = data.readInt32(); diff --git a/media/libmediaplayerservice/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp index 469a02e..8a3188c 100644 --- a/media/libmediaplayerservice/HDCP.cpp +++ b/media/libmediaplayerservice/HDCP.cpp @@ -116,6 +116,24 @@ status_t HDCP::encrypt( return mHDCPModule->encrypt(inData, size, streamCTR, outInputCTR, outData); } +status_t HDCP::encryptNative( + const sp &graphicBuffer, + size_t offset, size_t size, uint32_t streamCTR, + uint64_t *outInputCTR, void *outData) { + Mutex::Autolock autoLock(mLock); + + CHECK(mIsEncryptionModule); + + if (mHDCPModule == NULL) { + *outInputCTR = 0; + + return NO_INIT; + } + + return mHDCPModule->encryptNative(graphicBuffer->handle, + offset, size, streamCTR, outInputCTR, outData); +} + status_t HDCP::decrypt( const void *inData, size_t size, uint32_t streamCTR, uint64_t outInputCTR, void *outData) { diff --git a/media/libmediaplayerservice/HDCP.h b/media/libmediaplayerservice/HDCP.h index 42e6467..c60c2e0 100644 --- a/media/libmediaplayerservice/HDCP.h +++ b/media/libmediaplayerservice/HDCP.h @@ -35,6 +35,11 @@ struct HDCP : public BnHDCP { const void *inData, size_t size, uint32_t streamCTR, uint64_t *outInputCTR, void *outData); + virtual status_t encryptNative( + const sp &graphicBuffer, + size_t offset, size_t size, uint32_t streamCTR, + uint64_t *outInputCTR, void *outData); + virtual status_t decrypt( const void *inData, size_t size, uint32_t streamCTR, uint64_t outInputCTR, void *outData); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 64e3885..d3ac734 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -359,6 +359,7 @@ ACodec::ACodec() mNode(NULL), mSentFormat(false), mIsEncoder(false), + mUseMetadataOnEncoderOutput(false), mShutdownInProgress(false), mEncoderDelay(0), mEncoderPadding(0), @@ -483,7 +484,8 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { ? OMXCodec::kRequiresAllocateBufferOnInputPorts : OMXCodec::kRequiresAllocateBufferOnOutputPorts; - if (portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) { + if ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) + || mUseMetadataOnEncoderOutput) { mem.clear(); void *ptr; @@ -491,7 +493,10 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { mNode, portIndex, def.nBufferSize, &info.mBufferID, &ptr); - info.mData = new ABuffer(ptr, def.nBufferSize); + int32_t bufSize = mUseMetadataOnEncoderOutput ? + (4 + sizeof(buffer_handle_t)) : def.nBufferSize; + + info.mData = new ABuffer(ptr, bufSize); } else if (mQuirks & requiresAllocateBufferBit) { err = mOMX->allocateBufferWithBackup( mNode, portIndex, mem, &info.mBufferID); @@ -912,14 +917,14 @@ status_t ACodec::configureCodec( err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexInput, OMX_TRUE); if (err != OK) { - ALOGE("[%s] storeMetaDataInBuffers failed w/ err %d", - mComponentName.c_str(), err); + ALOGE("[%s] storeMetaDataInBuffers (input) failed w/ err %d", + mComponentName.c_str(), err); - return err; - } - } + return err; + } + } - int32_t prependSPSPPS; + int32_t prependSPSPPS = 0; if (encoder && msg->findInt32("prepend-sps-pps-to-idr-frames", &prependSPSPPS) && prependSPSPPS != 0) { @@ -946,7 +951,27 @@ status_t ACodec::configureCodec( } } - if (!strncasecmp(mime, "video/", 6)) { + // Only enable metadata mode on encoder output if encoder can prepend + // sps/pps to idr frames, since in metadata mode the bitstream is in an + // opaque handle, to which we don't have access. + int32_t video = !strncasecmp(mime, "video/", 6); + if (encoder && video) { + OMX_BOOL enable = (OMX_BOOL) (prependSPSPPS + && msg->findInt32("store-metadata-in-buffers-output", &storeMeta) + && storeMeta != 0); + + err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexOutput, enable); + + if (err != OK) { + ALOGE("[%s] storeMetaDataInBuffers (output) failed w/ err %d", + mComponentName.c_str(), err); + mUseMetadataOnEncoderOutput = 0; + } else { + mUseMetadataOnEncoderOutput = enable; + } + } + + if (video) { if (encoder) { err = setupVideoEncoder(mime, msg); } else { @@ -3061,7 +3086,15 @@ bool ACodec::BaseState::onOMXFillBufferDone( mCodec->sendFormatChange(); } - info->mData->setRange(rangeOffset, rangeLength); + if (mCodec->mUseMetadataOnEncoderOutput) { + native_handle_t* handle = + *(native_handle_t**)(info->mData->data() + 4); + info->mData->meta()->setPointer("handle", handle); + info->mData->meta()->setInt32("rangeOffset", rangeOffset); + info->mData->meta()->setInt32("rangeLength", rangeLength); + } else { + info->mData->setRange(rangeOffset, rangeLength); + } #if 0 if (mCodec->mNativeWindow == NULL) { if (IsIDR(info->mData)) { @@ -3215,6 +3248,7 @@ void ACodec::UninitializedState::stateEntered() { mCodec->mOMX.clear(); mCodec->mQuirks = 0; mCodec->mFlags = 0; + mCodec->mUseMetadataOnEncoderOutput = 0; mCodec->mComponentName.clear(); } diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 33af66d..a230cd8 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -27,9 +27,11 @@ #include "include/avc_utils.h" #include +#include #include #include #include +#include namespace android { @@ -408,11 +410,36 @@ status_t MediaSender::packetizeAccessUnit( info.mPacketizerTrackIndex, accessUnit); } - status_t err = mHDCP->encrypt( - accessUnit->data(), accessUnit->size(), - trackIndex /* streamCTR */, - &inputCTR, - accessUnit->data()); + status_t err; + native_handle_t* handle; + if (accessUnit->meta()->findPointer("handle", (void**)&handle) + && handle != NULL) { + int32_t rangeLength, rangeOffset; + sp notify; + CHECK(accessUnit->meta()->findInt32("rangeOffset", &rangeOffset)); + CHECK(accessUnit->meta()->findInt32("rangeLength", &rangeLength)); + CHECK(accessUnit->meta()->findMessage("notify", ¬ify) + && notify != NULL); + CHECK_GE(accessUnit->size(), rangeLength); + + sp grbuf(new GraphicBuffer( + rangeOffset + rangeLength, 1, HAL_PIXEL_FORMAT_Y8, + GRALLOC_USAGE_HW_VIDEO_ENCODER, rangeOffset + rangeLength, + handle, false)); + + err = mHDCP->encryptNative( + grbuf, rangeOffset, rangeLength, + trackIndex /* streamCTR */, + &inputCTR, + accessUnit->data()); + notify->post(); + } else { + err = mHDCP->encrypt( + accessUnit->data(), accessUnit->size(), + trackIndex /* streamCTR */, + &inputCTR, + accessUnit->data()); + } if (err != OK) { ALOGE("Failed to HDCP-encrypt media data (err %d)", diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp index 458b163..c368c38 100644 --- a/media/libstagefright/wifi-display/VideoFormats.cpp +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -24,7 +24,8 @@ namespace android { -VideoFormats::config_t VideoFormats::mConfigs[][32] = { +// static +const VideoFormats::config_t VideoFormats::mResolutionTable[][32] = { { // CEA Resolutions { 640, 480, 60, false, 0, 0}, @@ -133,6 +134,8 @@ VideoFormats::config_t VideoFormats::mConfigs[][32] = { }; VideoFormats::VideoFormats() { + memcpy(mConfigs, mResolutionTable, sizeof(mConfigs)); + for (size_t i = 0; i < kNumResolutionTypes; ++i) { mResolutionEnabled[i] = 0; } @@ -182,11 +185,56 @@ void VideoFormats::setResolutionEnabled( if (enabled) { mResolutionEnabled[type] |= (1ul << index); + mConfigs[type][index].profile = (1ul << PROFILE_CBP); + mConfigs[type][index].level = (1ul << LEVEL_31); } else { mResolutionEnabled[type] &= ~(1ul << index); + mConfigs[type][index].profile = 0; + mConfigs[type][index].level = 0; } } +void VideoFormats::setProfileLevel( + ResolutionType type, size_t index, + ProfileType profile, LevelType level) { + CHECK_LT(type, kNumResolutionTypes); + CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL)); + + mConfigs[type][index].profile = (1ul << profile); + mConfigs[type][index].level = (1ul << level); +} + +void VideoFormats::getProfileLevel( + ResolutionType type, size_t index, + ProfileType *profile, LevelType *level) const{ + CHECK_LT(type, kNumResolutionTypes); + CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL)); + + int i, bestProfile = -1, bestLevel = -1; + + for (i = 0; i < kNumProfileTypes; ++i) { + if (mConfigs[type][index].profile & (1ul << i)) { + bestProfile = i; + } + } + + for (i = 0; i < kNumLevelTypes; ++i) { + if (mConfigs[type][index].level & (1ul << i)) { + bestLevel = i; + } + } + + if (bestProfile == -1 || bestLevel == -1) { + ALOGE("Profile or level not set for resolution type %d, index %d", + type, index); + bestProfile = PROFILE_CBP; + bestLevel = LEVEL_31; + } + + *profile = (ProfileType) bestProfile; + *level = (LevelType) bestLevel; +} + bool VideoFormats::isResolutionEnabled( ResolutionType type, size_t index) const { CHECK_LT(type, kNumResolutionTypes); @@ -207,7 +255,7 @@ bool VideoFormats::GetConfiguration( return false; } - const config_t *config = &mConfigs[type][index]; + const config_t *config = &mResolutionTable[type][index]; if (config->width == 0) { return false; @@ -251,9 +299,12 @@ bool VideoFormats::parseH264Codec(const char *spec) { if (res[i] & (1ul << j)){ mResolutionEnabled[i] |= (1ul << j); if (profile > mConfigs[i][j].profile) { + // prefer higher profile (even if level is lower) mConfigs[i][j].profile = profile; - if (level > mConfigs[i][j].level) - mConfigs[i][j].level = level; + mConfigs[i][j].level = level; + } else if (profile == mConfigs[i][j].profile && + level > mConfigs[i][j].level) { + mConfigs[i][j].level = level; } } } @@ -262,9 +313,51 @@ bool VideoFormats::parseH264Codec(const char *spec) { return true; } +// static +bool VideoFormats::GetProfileLevel( + ProfileType profile, LevelType level, unsigned *profileIdc, + unsigned *levelIdc, unsigned *constraintSet) { + CHECK_LT(profile, kNumProfileTypes); + CHECK_LT(level, kNumLevelTypes); + + static const unsigned kProfileIDC[kNumProfileTypes] = { + 66, // PROFILE_CBP + 100, // PROFILE_CHP + }; + + static const unsigned kLevelIDC[kNumLevelTypes] = { + 31, // LEVEL_31 + 32, // LEVEL_32 + 40, // LEVEL_40 + 41, // LEVEL_41 + 42, // LEVEL_42 + }; + + static const unsigned kConstraintSet[kNumProfileTypes] = { + 0xc0, // PROFILE_CBP + 0x0c, // PROFILE_CHP + }; + + if (profileIdc) { + *profileIdc = kProfileIDC[profile]; + } + + if (levelIdc) { + *levelIdc = kLevelIDC[level]; + } + + if (constraintSet) { + *constraintSet = kConstraintSet[profile]; + } + + return true; +} + bool VideoFormats::parseFormatSpec(const char *spec) { CHECK_EQ(kNumResolutionTypes, 3); + disableAll(); + unsigned native, dummy; unsigned res[3]; size_t size = strlen(spec); @@ -320,8 +413,10 @@ AString VideoFormats::getFormatSpec(bool forM4Message) const { // max-vres (none or 2 byte) return StringPrintf( - "%02x 00 02 02 %08x %08x %08x 00 0000 0000 00 none none", + "%02x 00 %02x %02x %08x %08x %08x 00 0000 0000 00 none none", forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType), + mConfigs[mNativeType][mNativeIndex].profile, + mConfigs[mNativeType][mNativeIndex].level, mResolutionEnabled[0], mResolutionEnabled[1], mResolutionEnabled[2]); @@ -332,7 +427,9 @@ bool VideoFormats::PickBestFormat( const VideoFormats &sinkSupported, const VideoFormats &sourceSupported, ResolutionType *chosenType, - size_t *chosenIndex) { + size_t *chosenIndex, + ProfileType *chosenProfile, + LevelType *chosenLevel) { #if 0 // Support for the native format is a great idea, the spec includes // these features, but nobody supports it and the tests don't validate it. @@ -412,6 +509,18 @@ bool VideoFormats::PickBestFormat( *chosenType = (ResolutionType)bestType; *chosenIndex = bestIndex; + // Pick the best profile/level supported by both sink and source. + ProfileType srcProfile, sinkProfile; + LevelType srcLevel, sinkLevel; + sourceSupported.getProfileLevel( + (ResolutionType)bestType, bestIndex, + &srcProfile, &srcLevel); + sinkSupported.getProfileLevel( + (ResolutionType)bestType, bestIndex, + &sinkProfile, &sinkLevel); + *chosenProfile = srcProfile < sinkProfile ? srcProfile : sinkProfile; + *chosenLevel = srcLevel < sinkLevel ? srcLevel : sinkLevel; + return true; } diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h index 01de246..b918652 100644 --- a/media/libstagefright/wifi-display/VideoFormats.h +++ b/media/libstagefright/wifi-display/VideoFormats.h @@ -75,11 +75,24 @@ struct VideoFormats { bool isResolutionEnabled(ResolutionType type, size_t index) const; + void setProfileLevel( + ResolutionType type, size_t index, + ProfileType profile, LevelType level); + + void getProfileLevel( + ResolutionType type, size_t index, + ProfileType *profile, LevelType *level) const; + static bool GetConfiguration( ResolutionType type, size_t index, size_t *width, size_t *height, size_t *framesPerSecond, bool *interlaced); + static bool GetProfileLevel( + ProfileType profile, LevelType level, + unsigned *profileIdc, unsigned *levelIdc, + unsigned *constraintSet); + bool parseFormatSpec(const char *spec); AString getFormatSpec(bool forM4Message = false) const; @@ -87,7 +100,9 @@ struct VideoFormats { const VideoFormats &sinkSupported, const VideoFormats &sourceSupported, ResolutionType *chosenType, - size_t *chosenIndex); + size_t *chosenIndex, + ProfileType *chosenProfile, + LevelType *chosenLevel); private: bool parseH264Codec(const char *spec); @@ -95,7 +110,8 @@ private: size_t mNativeIndex; uint32_t mResolutionEnabled[kNumResolutionTypes]; - static config_t mConfigs[kNumResolutionTypes][32]; + static const config_t mResolutionTable[kNumResolutionTypes][32]; + config_t mConfigs[kNumResolutionTypes][32]; DISALLOW_EVIL_CONSTRUCTORS(VideoFormats); }; diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 5344623..e62505d 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -438,6 +438,17 @@ void Converter::onMessageReceived(const sp &msg) { break; } + case kWhatReleaseOutputBuffer: + { + if (mEncoder != NULL) { + size_t bufferIndex; + CHECK(msg->findInt32("bufferIndex", (int32_t*)&bufferIndex)); + CHECK(bufferIndex < mEncoderOutputBuffers.size()); + mEncoder->releaseOutputBuffer(bufferIndex); + } + break; + } + default: TRESPASS(); } @@ -645,6 +656,7 @@ status_t Converter::doMoreWork() { size_t size; int64_t timeUs; uint32_t flags; + native_handle_t* handle = NULL; err = mEncoder->dequeueOutputBuffer( &bufferIndex, &offset, &size, &timeUs, &flags); @@ -667,18 +679,43 @@ status_t Converter::doMoreWork() { notify->setInt32("what", kWhatEOS); notify->post(); } else { - sp buffer = new ABuffer(size); + sp buffer; + sp outbuf = mEncoderOutputBuffers.itemAt(bufferIndex); + + if (outbuf->meta()->findPointer("handle", (void**)&handle) && + handle != NULL) { + int32_t rangeLength, rangeOffset; + CHECK(outbuf->meta()->findInt32("rangeOffset", &rangeOffset)); + CHECK(outbuf->meta()->findInt32("rangeLength", &rangeLength)); + outbuf->meta()->setPointer("handle", NULL); + + // MediaSender will post the following message when HDCP + // is done, to release the output buffer back to encoder. + sp notify(new AMessage( + kWhatReleaseOutputBuffer, id())); + notify->setInt32("bufferIndex", bufferIndex); + + buffer = new ABuffer( + rangeLength > (int32_t)size ? rangeLength : size); + buffer->meta()->setPointer("handle", handle); + buffer->meta()->setInt32("rangeOffset", rangeOffset); + buffer->meta()->setInt32("rangeLength", rangeLength); + buffer->meta()->setMessage("notify", notify); + } else { + buffer = new ABuffer(size); + } + buffer->meta()->setInt64("timeUs", timeUs); ALOGV("[%s] time %lld us (%.2f secs)", mIsVideo ? "video" : "audio", timeUs, timeUs / 1E6); - memcpy(buffer->data(), - mEncoderOutputBuffers.itemAt(bufferIndex)->base() + offset, - size); + memcpy(buffer->data(), outbuf->base() + offset, size); if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) { - mOutputFormat->setBuffer("csd-0", buffer); + if (!handle) { + mOutputFormat->setBuffer("csd-0", buffer); + } } else { sp notify = mNotify->dup(); notify->setInt32("what", kWhatAccessUnit); @@ -687,7 +724,9 @@ status_t Converter::doMoreWork() { } } - mEncoder->releaseOutputBuffer(bufferIndex); + if (!handle) { + mEncoder->releaseOutputBuffer(bufferIndex); + } if (flags & MediaCodec::BUFFER_FLAG_EOS) { break; diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index ba297c4..fceef55 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -66,6 +66,7 @@ struct Converter : public AHandler { kWhatMediaPullerNotify, kWhatEncoderActivity, kWhatDropAFrame, + kWhatReleaseOutputBuffer, }; void shutdownAsync(); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index cacfcca..7f0ba96 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -378,7 +378,9 @@ status_t WifiDisplaySource::PlaybackSession::init( bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex) { + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType) { sp notify = new AMessage(kWhatMediaSenderNotify, id()); mMediaSender = new MediaSender(mNetSession, notify); looper()->registerHandler(mMediaSender); @@ -390,7 +392,9 @@ status_t WifiDisplaySource::PlaybackSession::init( usePCMAudio, enableVideo, videoResolutionType, - videoResolutionIndex); + videoResolutionIndex, + videoProfileType, + videoLevelType); if (err == OK) { err = mMediaSender->initAsync( @@ -870,7 +874,9 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex) { + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType) { CHECK(enableAudio || enableVideo); if (!mMediaPath.empty()) { @@ -879,7 +885,8 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( if (enableVideo) { status_t err = addVideoSource( - videoResolutionType, videoResolutionIndex); + videoResolutionType, videoResolutionIndex, videoProfileType, + videoLevelType); if (err != OK) { return err; @@ -895,9 +902,13 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( status_t WifiDisplaySource::PlaybackSession::addSource( bool isVideo, const sp &source, bool isRepeaterSource, - bool usePCMAudio, size_t *numInputBuffers) { + bool usePCMAudio, unsigned profileIdc, unsigned levelIdc, + unsigned constraintSet, size_t *numInputBuffers) { CHECK(!usePCMAudio || !isVideo); CHECK(!isRepeaterSource || isVideo); + CHECK(!profileIdc || isVideo); + CHECK(!levelIdc || isVideo); + CHECK(!constraintSet || isVideo); sp pullLooper = new ALooper; pullLooper->setName("pull_looper"); @@ -927,9 +938,12 @@ status_t WifiDisplaySource::PlaybackSession::addSource( if (isVideo) { format->setInt32("store-metadata-in-buffers", true); - + format->setInt32("store-metadata-in-buffers-output", (mHDCP != NULL)); format->setInt32( "color-format", OMX_COLOR_FormatAndroidOpaque); + format->setInt32("profile-idc", profileIdc); + format->setInt32("level-idc", levelIdc); + format->setInt32("constraint-set", constraintSet); } notify = new AMessage(kWhatConverterNotify, id()); @@ -990,7 +1004,9 @@ status_t WifiDisplaySource::PlaybackSession::addSource( status_t WifiDisplaySource::PlaybackSession::addVideoSource( VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex) { + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType) { size_t width, height, framesPerSecond; bool interlaced; CHECK(VideoFormats::GetConfiguration( @@ -1001,6 +1017,14 @@ status_t WifiDisplaySource::PlaybackSession::addVideoSource( &framesPerSecond, &interlaced)); + unsigned profileIdc, levelIdc, constraintSet; + CHECK(VideoFormats::GetProfileLevel( + videoProfileType, + videoLevelType, + &profileIdc, + &levelIdc, + &constraintSet)); + sp source = new SurfaceMediaSource(width, height); source->setUseAbsoluteTimestamps(); @@ -1011,7 +1035,8 @@ status_t WifiDisplaySource::PlaybackSession::addVideoSource( size_t numInputBuffers; status_t err = addSource( true /* isVideo */, videoSource, true /* isRepeaterSource */, - false /* usePCMAudio */, &numInputBuffers); + false /* usePCMAudio */, profileIdc, levelIdc, constraintSet, + &numInputBuffers); if (err != OK) { return err; @@ -1034,7 +1059,8 @@ status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) { if (audioSource->initCheck() == OK) { return addSource( false /* isVideo */, audioSource, false /* isRepeaterSource */, - usePCMAudio, NULL /* numInputBuffers */); + usePCMAudio, 0 /* profileIdc */, 0 /* levelIdc */, + 0 /* constraintSet */, NULL /* numInputBuffers */); } ALOGW("Unable to instantiate audio source"); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index 39086a1..5c8ee94 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -53,7 +53,9 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex); + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType); void destroyAsync(); @@ -130,18 +132,25 @@ private: bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex); + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType); status_t addSource( bool isVideo, const sp &source, bool isRepeaterSource, bool usePCMAudio, + unsigned profileIdc, + unsigned levelIdc, + unsigned contraintSet, size_t *numInputBuffers); status_t addVideoSource( VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex); + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType); status_t addAudioSource(bool usePCMAudio); diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index 2c4a373..c674700 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -261,12 +261,24 @@ void TSPacketizer::Track::finalize() { data[0] = 40; // descriptor_tag data[1] = 4; // descriptor_length - CHECK_GE(mCSD.size(), 1u); - const sp &sps = mCSD.itemAt(0); - CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4)); - CHECK_GE(sps->size(), 7u); - // profile_idc, constraint_set*, level_idc - memcpy(&data[2], sps->data() + 4, 3); + if (mCSD.size() > 0) { + CHECK_GE(mCSD.size(), 1u); + const sp &sps = mCSD.itemAt(0); + CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4)); + CHECK_GE(sps->size(), 7u); + // profile_idc, constraint_set*, level_idc + memcpy(&data[2], sps->data() + 4, 3); + } else { + int32_t profileIdc, levelIdc, constraintSet; + CHECK(mFormat->findInt32("profile-idc", &profileIdc)); + CHECK(mFormat->findInt32("level-idc", &levelIdc)); + CHECK(mFormat->findInt32("constraint-set", &constraintSet)); + CHECK_GE(profileIdc, 0u); + CHECK_GE(levelIdc, 0u); + data[2] = profileIdc; // profile_idc + data[3] = constraintSet; // constraint_set* + data[4] = levelIdc; // level_idc + } // AVC_still_present=0, AVC_24_hour_picture_flag=0, reserved data[5] = 0x3f; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index b2cc66c..0b714f0 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -74,6 +74,11 @@ WifiDisplaySource::WifiDisplaySource( mSupportedSourceVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30 + + mSupportedSourceVideoFormats.setProfileLevel( + VideoFormats::RESOLUTION_CEA, 5, + VideoFormats::PROFILE_CHP, // Constrained High Profile + VideoFormats::LEVEL_32); // Level 3.2 } WifiDisplaySource::~WifiDisplaySource() { @@ -631,6 +636,9 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { chosenVideoFormat.disableAll(); chosenVideoFormat.setNativeResolution( mChosenVideoResolutionType, mChosenVideoResolutionIndex); + chosenVideoFormat.setProfileLevel( + mChosenVideoResolutionType, mChosenVideoResolutionIndex, + mChosenVideoProfile, mChosenVideoLevel); body.append(chosenVideoFormat.getFormatSpec(true /* forM4Message */)); body.append("\r\n"); @@ -859,7 +867,9 @@ status_t WifiDisplaySource::onReceiveM3Response( mSupportedSinkVideoFormats, mSupportedSourceVideoFormats, &mChosenVideoResolutionType, - &mChosenVideoResolutionIndex)) { + &mChosenVideoResolutionIndex, + &mChosenVideoProfile, + &mChosenVideoLevel)) { ALOGE("Sink and source share no commonly supported video " "formats."); @@ -878,6 +888,9 @@ status_t WifiDisplaySource::onReceiveM3Response( ALOGI("Picked video resolution %u x %u %c%u", width, height, interlaced ? 'i' : 'p', framesPerSecond); + + ALOGI("Picked AVC profile %d, level %d", + mChosenVideoProfile, mChosenVideoLevel); } else { ALOGI("Sink doesn't support video at all."); } @@ -1271,7 +1284,9 @@ status_t WifiDisplaySource::onSetupRequest( mUsingPCMAudio, mSinkSupportsVideo, mChosenVideoResolutionType, - mChosenVideoResolutionIndex); + mChosenVideoResolutionIndex, + mChosenVideoProfile, + mChosenVideoLevel); if (err != OK) { looper()->unregisterHandler(playbackSession->id()); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 3efa0b4..64186fc 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -134,6 +134,8 @@ private: VideoFormats::ResolutionType mChosenVideoResolutionType; size_t mChosenVideoResolutionIndex; + VideoFormats::ProfileType mChosenVideoProfile; + VideoFormats::LevelType mChosenVideoLevel; bool mSinkSupportsAudio; -- cgit v1.1 From a02eae5e911f3bdc3f84f39c0ef223261b646128 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 22 May 2013 14:36:06 -0700 Subject: stagefright: SoftVP8: Handle EOS flag on frames with content. SoftVP8 decoder ignored frame content if EOS flag was set on input frame. Now, decode the frame first, unless it is empty. Change-Id: Id105a9eb86103a61390af3de60cae2507028e2d1 Signed-off-by: Lajos Molnar Bug: 9091495 --- media/libstagefright/codecs/on2/dec/SoftVPX.cpp | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index a400b4c..866e5b0 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -226,6 +226,7 @@ void SoftVPX::onQueueFilled(OMX_U32 portIndex) { List &inQueue = getPortQueue(0); List &outQueue = getPortQueue(1); + bool EOSseen = false; while (!inQueue.empty() && !outQueue.empty()) { BufferInfo *inInfo = *inQueue.begin(); @@ -235,17 +236,20 @@ void SoftVPX::onQueueFilled(OMX_U32 portIndex) { OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inQueue.erase(inQueue.begin()); - inInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inHeader); - - outHeader->nFilledLen = 0; - outHeader->nFlags = OMX_BUFFERFLAG_EOS; - - outQueue.erase(outQueue.begin()); - outInfo->mOwnedByUs = false; - notifyFillBufferDone(outHeader); - return; + EOSseen = true; + if (inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } } if (vpx_codec_decode( @@ -282,7 +286,7 @@ void SoftVPX::onQueueFilled(OMX_U32 portIndex) { outHeader->nOffset = 0; outHeader->nFilledLen = (width * height * 3) / 2; - outHeader->nFlags = 0; + outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0; outHeader->nTimeStamp = inHeader->nTimeStamp; const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; -- cgit v1.1 From d030447b617105b31bf3013e5e4b39d422b53b77 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 15 May 2013 12:59:19 -0700 Subject: stagefright: BufferProducer updates Update BufferQueue and ConsumerBase users to new BufferQueue API, to allow BufferQueue slots to be reused. Buffer consumers generally now need to track the unique frameNumber belonging to each frame acquired if they are using BufferQueue directly. Otherwise, they can simply track the graphicBuffer. Change-Id: I30ee3158cf40fb10bbd085241646d5f1128ee480 Signed-off-by: Lajos Molnar Related-to-bug: 7093648 --- media/libstagefright/SurfaceMediaSource.cpp | 24 +++++++----- media/libstagefright/omx/GraphicBufferSource.cpp | 47 ++++++++++-------------- media/libstagefright/omx/GraphicBufferSource.h | 10 ++++- 3 files changed, 42 insertions(+), 39 deletions(-) (limited to 'media') diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 409038a..71b6569 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -305,8 +305,9 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer, // First time seeing the buffer? Added it to the SMS slot if (item.mGraphicBuffer != NULL) { - mBufferSlot[item.mBuf] = item.mGraphicBuffer; + mSlots[item.mBuf].mGraphicBuffer = item.mGraphicBuffer; } + mSlots[item.mBuf].mFrameNumber = item.mFrameNumber; // check for the timing of this buffer if (mNumFramesReceived == 0 && !mUseAbsoluteTimestamps) { @@ -315,7 +316,8 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer, if (mStartTimeNs > 0) { if (item.mTimestamp < mStartTimeNs) { // This frame predates start of record, discard - mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, + mBufferQueue->releaseBuffer( + item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); continue; } @@ -345,17 +347,18 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer, // First time seeing the buffer? Added it to the SMS slot if (item.mGraphicBuffer != NULL) { - mBufferSlot[mCurrentSlot] = item.mGraphicBuffer; + mSlots[item.mBuf].mGraphicBuffer = item.mGraphicBuffer; } + mSlots[item.mBuf].mFrameNumber = item.mFrameNumber; - mCurrentBuffers.push_back(mBufferSlot[mCurrentSlot]); + mCurrentBuffers.push_back(mSlots[mCurrentSlot].mGraphicBuffer); int64_t prevTimeStamp = mCurrentTimestamp; mCurrentTimestamp = item.mTimestamp; mNumFramesEncoded++; // Pass the data to the MediaBuffer. Pass in only the metadata - passMetadataBuffer(buffer, mBufferSlot[mCurrentSlot]->handle); + passMetadataBuffer(buffer, mSlots[mCurrentSlot].mGraphicBuffer->handle); (*buffer)->setObserver(this); (*buffer)->add_ref(); @@ -405,15 +408,16 @@ void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { } for (int id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) { - if (mBufferSlot[id] == NULL) { + if (mSlots[id].mGraphicBuffer == NULL) { continue; } - if (bufferHandle == mBufferSlot[id]->handle) { + if (bufferHandle == mSlots[id].mGraphicBuffer->handle) { ALOGV("Slot %d returned, matches handle = %p", id, - mBufferSlot[id]->handle); + mSlots[id].mGraphicBuffer->handle); - mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, + mBufferQueue->releaseBuffer(id, mSlots[id].mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); buffer->setObserver(0); @@ -469,7 +473,7 @@ void SurfaceMediaSource::onBuffersReleased() { mFrameAvailableCondition.signal(); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - mBufferSlot[i] = 0; + mSlots[i].mGraphicBuffer = 0; } } diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index ef27879..b3a8463 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -206,24 +206,15 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // Find matching entry in our cached copy of the BufferQueue slots. // If we find a match, release that slot. If we don't, the BufferQueue // has dropped that GraphicBuffer, and there's nothing for us to release. - // - // (We could store "id" in CodecBuffer and avoid the slot search.) - int id; - for (id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) { - if (mBufferSlot[id] == NULL) { - continue; - } - - if (mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { - ALOGV("cbi %d matches bq slot %d, handle=%p", - cbi, id, mBufferSlot[id]->handle); - - mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - Fence::NO_FENCE); - break; - } - } - if (id == BufferQueue::NUM_BUFFER_SLOTS) { + int id = codecBuffer.mBuf; + if (mBufferSlot[id] != NULL && + mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { + ALOGV("cbi %d matches bq slot %d, handle=%p", + cbi, id, mBufferSlot[id]->handle); + + mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + } else { ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", cbi); } @@ -287,11 +278,11 @@ bool GraphicBufferSource::fillCodecBuffer_l() { mBufferSlot[item.mBuf] = item.mGraphicBuffer; } - err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp / 1000, cbi); + err = submitBuffer_l(item, cbi); if (err != OK) { ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); - mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR, Fence::NO_FENCE); + mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); } else { ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); } @@ -326,11 +317,13 @@ status_t GraphicBufferSource::signalEndOfInputStream() { return OK; } -status_t GraphicBufferSource::submitBuffer_l(sp& graphicBuffer, - int64_t timestampUsec, int cbi) { +status_t GraphicBufferSource::submitBuffer_l( + const BufferQueue::BufferItem &item, int cbi) { ALOGV("submitBuffer_l cbi=%d", cbi); CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); - codecBuffer.mGraphicBuffer = graphicBuffer; + codecBuffer.mGraphicBuffer = mBufferSlot[item.mBuf]; + codecBuffer.mBuf = item.mBuf; + codecBuffer.mFrameNumber = item.mFrameNumber; OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t)); @@ -342,7 +335,7 @@ status_t GraphicBufferSource::submitBuffer_l(sp& graphicBuffer, status_t err = mNodeInstance->emptyDirectBuffer(header, 0, 4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME, - timestampUsec); + item.mTimestamp / 1000); if (err != OK) { ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err); codecBuffer.mGraphicBuffer = NULL; @@ -431,8 +424,8 @@ void GraphicBufferSource::onFrameAvailable() { BufferQueue::BufferItem item; status_t err = mBufferQueue->acquireBuffer(&item); if (err == OK) { - mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR, item.mFence); + mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } return; } diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 562d342..8c6b470 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -104,6 +104,13 @@ private: // (mGraphicBuffer == NULL) or in use by the codec. struct CodecBuffer { OMX_BUFFERHEADERTYPE* mHeader; + + // buffer producer's frame-number for buffer + uint64_t mFrameNumber; + + // buffer producer's buffer slot for buffer + int mBuf; + sp mGraphicBuffer; }; @@ -130,8 +137,7 @@ private: // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer // reference into the codec buffer, and submits the data to the codec. - status_t submitBuffer_l(sp& graphicBuffer, - int64_t timestampUsec, int cbi); + status_t submitBuffer_l(const BufferQueue::BufferItem &item, int cbi); // Submits an empty buffer, with the EOS flag set. Returns without // doing anything if we don't have a codec buffer available. -- cgit v1.1 From 0a69bd281c76bf777ddb51d0c6c08519634b192d Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 23 May 2013 15:17:24 -0700 Subject: Add support for MPEG editlist gapless info Change-Id: I862d89c805d738db9bdf81a8f1c5b317ff968dff --- media/libstagefright/MPEG4Extractor.cpp | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'media') diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 145869e..7697d55 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -817,6 +817,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('i', 'l', 's', 't'): case FOURCC('s', 'i', 'n', 'f'): case FOURCC('s', 'c', 'h', 'i'): + case FOURCC('e', 'd', 't', 's'): { if (chunk_type == FOURCC('s', 't', 'b', 'l')) { ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size); @@ -904,6 +905,66 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('e', 'l', 's', 't'): + { + // See 14496-12 8.6.6 + uint8_t version; + if (mDataSource->readAt(data_offset, &version, 1) < 1) { + return ERROR_IO; + } + + uint32_t entry_count; + if (!mDataSource->getUInt32(data_offset + 4, &entry_count)) { + return ERROR_IO; + } + + if (entry_count != 1) { + // we only support a single entry at the moment, for gapless playback + ALOGW("ignoring edit list with %d entries", entry_count); + } else { + off64_t entriesoffset = data_offset + 8; + uint64_t segment_duration; + int64_t media_time; + + if (version == 1) { + if (!mDataSource->getUInt64(entriesoffset, &segment_duration) || + !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) { + return ERROR_IO; + } + } else if (version == 0) { + uint32_t sd; + int32_t mt; + if (!mDataSource->getUInt32(entriesoffset, &sd) || + !mDataSource->getUInt32(entriesoffset + 4, (uint32_t*)&mt)) { + return ERROR_IO; + } + segment_duration = sd; + media_time = mt; + } else { + return ERROR_IO; + } + + uint64_t halfscale = mLastTrack->timescale / 2; + segment_duration = (segment_duration * 1000000 + halfscale)/ mLastTrack->timescale; + media_time = (media_time * 1000000 + halfscale) / mLastTrack->timescale; + + int64_t duration; + int32_t samplerate; + if (mLastTrack->meta->findInt64(kKeyDuration, &duration) && + mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) { + + int64_t delay = (media_time * samplerate + 500000) / 1000000; + mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); + + int64_t paddingus = duration - (segment_duration + media_time); + int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000; + mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples); + } + } + *offset += chunk_size; + break; + } + case FOURCC('f', 'r', 'm', 'a'): { uint32_t original_fourcc; -- cgit v1.1 From 776a0023f5146423e88474c35691eb0e20fc8102 Mon Sep 17 00:00:00 2001 From: Ajay Dudani Date: Wed, 22 May 2013 22:16:33 -0700 Subject: libstagefright: Check for duration > 0 to avoid divide-by-zero exception Change-Id: I58ccacbf7ede892dff9626715162ea7b1f2ddbc6 --- media/libstagefright/AwesomePlayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index bd28118..6c197e2 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -597,7 +597,7 @@ void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { bool AwesomePlayer::getBitrate(int64_t *bitrate) { off64_t size; - if (mDurationUs >= 0 && mCachedSource != NULL + if (mDurationUs > 0 && mCachedSource != NULL && mCachedSource->getSize(&size) == OK) { *bitrate = size * 8000000ll / mDurationUs; // in bits/sec return true; -- cgit v1.1 From 210efd48ed21ca0084d9440f9a1db7d9878f8094 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 29 May 2013 10:58:08 -0700 Subject: Don't crash if no timescale was given b/9175577 Change-Id: Ie159a9c9b42e6c8d9366d0ef6a607234af569e36 --- media/libstagefright/MPEG4Extractor.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'media') diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 7697d55..919766c 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -921,6 +921,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (entry_count != 1) { // we only support a single entry at the moment, for gapless playback ALOGW("ignoring edit list with %d entries", entry_count); + } else if (mLastTrack->timescale == 0) { + ALOGW("ignoring edit list because timescale is 0"); } else { off64_t entriesoffset = data_offset + 8; uint64_t segment_duration; -- cgit v1.1 From bd25dacce1187c827dde3fb72036c044c8106719 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 30 May 2013 09:46:20 -0700 Subject: wifi-display: fixes for PAUSE/PLAY state PAUSE could be initiated by either source (via trigger method) or sink, in latter case we have to allow PAUSE from PLAYING state. Similarly PLAY should be allowed from PAUSED state. Bug: 8922515 Change-Id: I475534aa4ffa6dc6844f59c5868d8f88291019ae --- media/libstagefright/wifi-display/source/WifiDisplaySource.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 0b714f0..dee95eb 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -1369,7 +1369,9 @@ status_t WifiDisplaySource::onPlayRequest( return ERROR_MALFORMED; } - if (mState != AWAITING_CLIENT_PLAY) { + if (mState != AWAITING_CLIENT_PLAY + && mState != PAUSED_TO_PLAYING + && mState != PAUSED) { ALOGW("Received PLAY request but we're in state %d", mState); sendErrorResponse( @@ -1396,7 +1398,7 @@ status_t WifiDisplaySource::onPlayRequest( return err; } - if (mState == PAUSED_TO_PLAYING) { + if (mState == PAUSED_TO_PLAYING || mPlaybackSessionEstablished) { mState = PLAYING; return OK; } @@ -1430,7 +1432,7 @@ status_t WifiDisplaySource::onPauseRequest( ALOGI("Received PAUSE request."); - if (mState != PLAYING_TO_PAUSED) { + if (mState != PLAYING_TO_PAUSED && mState != PLAYING) { return INVALID_OPERATION; } -- cgit v1.1 From fc80e9ec5582770cb5a7fef172af3b52625ecce7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 30 May 2013 11:00:47 -0700 Subject: Edit list uses timescale from movie header not from track media header Change-Id: I24063183f44027b999782cc9006e9a1b56e87355 --- media/libstagefright/MPEG4Extractor.cpp | 17 ++++++++++------- media/libstagefright/include/MPEG4Extractor.h | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 919766c..42a9c7a 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -341,6 +341,7 @@ MPEG4Extractor::MPEG4Extractor(const sp &source) mDataSource(source), mInitCheck(NO_INIT), mHasVideo(false), + mHeaderTimescale(0), mFirstTrack(NULL), mLastTrack(NULL), mFileMetaData(new MetaData), @@ -921,7 +922,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (entry_count != 1) { // we only support a single entry at the moment, for gapless playback ALOGW("ignoring edit list with %d entries", entry_count); - } else if (mLastTrack->timescale == 0) { + } else if (mHeaderTimescale == 0) { ALOGW("ignoring edit list because timescale is 0"); } else { off64_t entriesoffset = data_offset + 8; @@ -946,9 +947,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } - uint64_t halfscale = mLastTrack->timescale / 2; - segment_duration = (segment_duration * 1000000 + halfscale)/ mLastTrack->timescale; - media_time = (media_time * 1000000 + halfscale) / mLastTrack->timescale; + uint64_t halfscale = mHeaderTimescale / 2; + segment_duration = (segment_duration * 1000000 + halfscale)/ mHeaderTimescale; + media_time = (media_time * 1000000 + halfscale) / mHeaderTimescale; int64_t duration; int32_t samplerate; @@ -1627,24 +1628,26 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('m', 'v', 'h', 'd'): { - if (chunk_data_size < 12) { + if (chunk_data_size < 24) { return ERROR_MALFORMED; } - uint8_t header[12]; + uint8_t header[24]; if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } - int64_t creationTime; + uint64_t creationTime; if (header[0] == 1) { creationTime = U64_AT(&header[4]); + mHeaderTimescale = U32_AT(&header[20]); } else if (header[0] != 0) { return ERROR_MALFORMED; } else { creationTime = U32_AT(&header[4]); + mHeaderTimescale = U32_AT(&header[12]); } String8 s; diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 35eff96..bbec1c4 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -82,6 +82,7 @@ private: sp mDataSource; status_t mInitCheck; bool mHasVideo; + uint32_t mHeaderTimescale; Track *mFirstTrack, *mLastTrack; -- cgit v1.1 From 7c027248e1a4ccd5b22bc4deafb03e2d87ac8f38 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 26 Dec 2012 14:43:16 -0800 Subject: Consistent whitespace Change-Id: I118cce68d3b777f9ec9b6bfb70367496422a40f2 --- media/libmedia/AudioRecord.cpp | 2 +- media/libmedia/AudioTrack.cpp | 4 +++- media/libmedia/IMediaDeathNotifier.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 40ff1bf..a2b8ae2 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -563,7 +563,7 @@ create_new_record: } } // read the server count again - start_loop_here: +start_loop_here: framesReady = mProxy->framesReady(); } cblk->lock.unlock(); diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 5ed8e3b..ff52b28 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -861,7 +861,9 @@ status_t AudioTrack::createTrack_l( // Ensure that buffer depth covers at least audio hardware latency uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); - if (minBufCount < 2) minBufCount = 2; + if (minBufCount < 2) { + minBufCount = 2; + } size_t minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; ALOGV("minFrameCount: %u, afFrameCount=%d, minBufCount=%d, sampleRate=%u, afSampleRate=%u" diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp index 9199db6..9db5b1b 100644 --- a/media/libmedia/IMediaDeathNotifier.cpp +++ b/media/libmedia/IMediaDeathNotifier.cpp @@ -49,10 +49,10 @@ IMediaDeathNotifier::getMediaPlayerService() } while (true); if (sDeathNotifier == NULL) { - sDeathNotifier = new DeathNotifier(); - } - binder->linkToDeath(sDeathNotifier); - sMediaPlayerService = interface_cast(binder); + sDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(sDeathNotifier); + sMediaPlayerService = interface_cast(binder); } ALOGE_IF(sMediaPlayerService == 0, "no media player service!?"); return sMediaPlayerService; -- cgit v1.1 From 1ad3eb9441eb509c792c61aa0181b0e74dbe9984 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 30 May 2013 21:51:38 -0700 Subject: wifi-display: fix resolution list and keepalive interval - add all resolutions lower than 1280x720p30 - schedule next keepalive when sending M16 bug 9116665 Change-Id: I7b3fea2101d3d882c0af5c153af5c502b8ce98f6 --- media/libstagefright/wifi-display/VideoFormats.cpp | 23 ++++++++++++++++++++++ media/libstagefright/wifi-display/VideoFormats.h | 3 +++ .../wifi-display/source/WifiDisplaySource.cpp | 7 ++++--- 3 files changed, 30 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp index c368c38..04e02c1 100644 --- a/media/libstagefright/wifi-display/VideoFormats.cpp +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -178,6 +178,29 @@ void VideoFormats::enableAll() { } } +void VideoFormats::enableResolutionUpto( + ResolutionType type, size_t index, + ProfileType profile, LevelType level) { + size_t width, height, fps, score; + bool interlaced; + if (!GetConfiguration(type, index, &width, &height, + &fps, &interlaced)) { + ALOGE("Maximum resolution not found!"); + return; + } + score = width * height * fps * (!interlaced + 1); + for (size_t i = 0; i < kNumResolutionTypes; ++i) { + for (size_t j = 0; j < 32; j++) { + if (GetConfiguration((ResolutionType)i, j, + &width, &height, &fps, &interlaced) + && score >= width * height * fps * (!interlaced + 1)) { + setResolutionEnabled((ResolutionType)i, j); + setProfileLevel((ResolutionType)i, j, profile, level); + } + } + } +} + void VideoFormats::setResolutionEnabled( ResolutionType type, size_t index, bool enabled) { CHECK_LT(type, kNumResolutionTypes); diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h index b918652..fd38fd1 100644 --- a/media/libstagefright/wifi-display/VideoFormats.h +++ b/media/libstagefright/wifi-display/VideoFormats.h @@ -69,6 +69,9 @@ struct VideoFormats { void disableAll(); void enableAll(); + void enableResolutionUpto( + ResolutionType type, size_t index, + ProfileType profile, LevelType level); void setResolutionEnabled( ResolutionType type, size_t index, bool enabled = true); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index dee95eb..b421b35 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -75,7 +75,8 @@ WifiDisplaySource::WifiDisplaySource( mSupportedSourceVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30 - mSupportedSourceVideoFormats.setProfileLevel( + // Enable all resolutions up to 1280x720p30 + mSupportedSourceVideoFormats.enableResolutionUpto( VideoFormats::RESOLUTION_CEA, 5, VideoFormats::PROFILE_CHP, // Constrained High Profile VideoFormats::LEVEL_32); // Level 3.2 @@ -751,6 +752,8 @@ status_t WifiDisplaySource::sendM16(int32_t sessionID) { ++mNextCSeq; + scheduleKeepAlive(sessionID); + return OK; } @@ -1021,8 +1024,6 @@ status_t WifiDisplaySource::onReceiveM16Response( if (mClientInfo.mPlaybackSession != NULL) { mClientInfo.mPlaybackSession->updateLiveness(); - - scheduleKeepAlive(sessionID); } return OK; -- cgit v1.1 From 14f7672b5d450ed26a06fd3bb3ce045ea78b11b2 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 15 Jan 2013 09:04:18 -0800 Subject: New HLS implementation supporting independent stream sources, audio-only streams and more. Change-Id: Icfc45a0100243b2f7a14a9e65696be45b67d6495 --- media/libmediaplayerservice/Android.mk | 1 + .../nuplayer/HTTPLiveSource.cpp | 140 +-- .../nuplayer/HTTPLiveSource.h | 8 +- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 88 +- media/libmediaplayerservice/nuplayer/NuPlayer.h | 7 +- .../nuplayer/NuPlayerRenderer.cpp | 6 +- .../nuplayer/NuPlayerSource.h | 1 + media/libstagefright/ACodec.cpp | 18 +- media/libstagefright/Android.mk | 2 - .../foundation/AHierarchicalStateMachine.cpp | 4 + media/libstagefright/httplive/Android.mk | 11 +- media/libstagefright/httplive/LiveSession.cpp | 1216 +++++++++++--------- media/libstagefright/httplive/LiveSession.h | 172 +++ media/libstagefright/httplive/M3UParser.cpp | 493 +++++++- media/libstagefright/httplive/M3UParser.h | 104 ++ media/libstagefright/httplive/PlaylistFetcher.cpp | 969 ++++++++++++++++ media/libstagefright/httplive/PlaylistFetcher.h | 155 +++ media/libstagefright/id3/ID3.cpp | 48 +- media/libstagefright/include/ID3.h | 7 + media/libstagefright/include/LiveSession.h | 165 --- media/libstagefright/include/M3UParser.h | 89 -- media/libstagefright/include/MPEG2TSExtractor.h | 5 - .../libstagefright/mpeg2ts/AnotherPacketSource.cpp | 29 +- media/libstagefright/mpeg2ts/AnotherPacketSource.h | 2 + media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 39 +- 25 files changed, 2757 insertions(+), 1022 deletions(-) create mode 100644 media/libstagefright/httplive/LiveSession.h create mode 100644 media/libstagefright/httplive/M3UParser.h create mode 100644 media/libstagefright/httplive/PlaylistFetcher.cpp create mode 100644 media/libstagefright/httplive/PlaylistFetcher.h delete mode 100644 media/libstagefright/include/LiveSession.h delete mode 100644 media/libstagefright/include/M3UParser.h (limited to 'media') diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index d87bc7f..8f21632 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -34,6 +34,7 @@ LOCAL_SHARED_LIBRARIES := \ libsonivox \ libstagefright \ libstagefright_foundation \ + libstagefright_httplive \ libstagefright_omx \ libstagefright_wfd \ libutils \ diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index 655ee55..c8901ce 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -20,7 +20,6 @@ #include "HTTPLiveSource.h" -#include "ATSParser.h" #include "AnotherPacketSource.h" #include "LiveDataSource.h" #include "LiveSession.h" @@ -62,7 +61,10 @@ NuPlayer::HTTPLiveSource::HTTPLiveSource( NuPlayer::HTTPLiveSource::~HTTPLiveSource() { if (mLiveSession != NULL) { mLiveSession->disconnect(); + mLiveSession.clear(); + mLiveLooper->stop(); + mLiveLooper.clear(); } } @@ -76,112 +78,42 @@ void NuPlayer::HTTPLiveSource::prepareAsync() { mLiveSession = new LiveSession( notify, (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0, - mUIDValid, mUID); + mUIDValid, + mUID); mLiveLooper->registerHandler(mLiveSession); - mLiveSession->connect( + mLiveSession->connectAsync( mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); - - mTSParser = new ATSParser; } void NuPlayer::HTTPLiveSource::start() { } -sp NuPlayer::HTTPLiveSource::getFormatMeta(bool audio) { - ATSParser::SourceType type = - audio ? ATSParser::AUDIO : ATSParser::VIDEO; - - sp source = - static_cast(mTSParser->getSource(type).get()); +sp NuPlayer::HTTPLiveSource::getFormat(bool audio) { + sp format; + status_t err = mLiveSession->getStreamFormat( + audio ? LiveSession::STREAMTYPE_AUDIO + : LiveSession::STREAMTYPE_VIDEO, + &format); - if (source == NULL) { + if (err != OK) { return NULL; } - return source->getFormat(); + return format; } status_t NuPlayer::HTTPLiveSource::feedMoreTSData() { - if (mFinalResult != OK) { - return mFinalResult; - } - - sp source = - static_cast(mLiveSession->getDataSource().get()); - - for (int32_t i = 0; i < 50; ++i) { - char buffer[188]; - ssize_t n = source->readAtNonBlocking(mOffset, buffer, sizeof(buffer)); - - if (n == -EWOULDBLOCK) { - break; - } else if (n < 0) { - if (n != ERROR_END_OF_STREAM) { - ALOGI("input data EOS reached, error %ld", n); - } else { - ALOGI("input data EOS reached."); - } - mTSParser->signalEOS(n); - mFinalResult = n; - break; - } else { - if (buffer[0] == 0x00) { - // XXX legacy - - uint8_t type = buffer[1]; - - sp extra = new AMessage; - - if (type & 2) { - int64_t mediaTimeUs; - memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs)); - - extra->setInt64(IStreamListener::kKeyMediaTimeUs, mediaTimeUs); - } - - mTSParser->signalDiscontinuity( - ((type & 1) == 0) - ? ATSParser::DISCONTINUITY_SEEK - : ATSParser::DISCONTINUITY_FORMATCHANGE, - extra); - } else { - status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer)); - - if (err != OK) { - ALOGE("TS Parser returned error %d", err); - mTSParser->signalEOS(err); - mFinalResult = err; - break; - } - } - - mOffset += n; - } - } - return OK; } status_t NuPlayer::HTTPLiveSource::dequeueAccessUnit( bool audio, sp *accessUnit) { - ATSParser::SourceType type = - audio ? ATSParser::AUDIO : ATSParser::VIDEO; - - sp source = - static_cast(mTSParser->getSource(type).get()); - - if (source == NULL) { - return -EWOULDBLOCK; - } - - status_t finalResult; - if (!source->hasBufferAvailable(&finalResult)) { - return finalResult == OK ? -EWOULDBLOCK : finalResult; - } - - return source->dequeueAccessUnit(accessUnit); + return mLiveSession->dequeueAccessUnit( + audio ? LiveSession::STREAMTYPE_AUDIO + : LiveSession::STREAMTYPE_VIDEO, + accessUnit); } status_t NuPlayer::HTTPLiveSource::getDuration(int64_t *durationUs) { @@ -189,15 +121,7 @@ status_t NuPlayer::HTTPLiveSource::getDuration(int64_t *durationUs) { } status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) { - // We need to make sure we're not seeking until we have seen the very first - // PTS timestamp in the whole stream (from the beginning of the stream). - while (!mTSParser->PTSTimeDeltaEstablished() && feedMoreTSData() == OK) { - usleep(100000); - } - - mLiveSession->seekTo(seekTimeUs); - - return OK; + return mLiveSession->seekTo(seekTimeUs); } void NuPlayer::HTTPLiveSource::onMessageReceived(const sp &msg) { @@ -249,6 +173,32 @@ void NuPlayer::HTTPLiveSource::onSessionNotify(const sp &msg) { break; } + case LiveSession::kWhatStreamsChanged: + { + uint32_t changedMask; + CHECK(msg->findInt32( + "changedMask", (int32_t *)&changedMask)); + + bool audio = changedMask & LiveSession::STREAMTYPE_AUDIO; + bool video = changedMask & LiveSession::STREAMTYPE_VIDEO; + + sp reply; + CHECK(msg->findMessage("reply", &reply)); + + sp notify = dupNotify(); + notify->setInt32("what", kWhatQueueDecoderShutdown); + notify->setInt32("audio", audio); + notify->setInt32("video", video); + notify->setMessage("reply", reply); + notify->post(); + break; + } + + case LiveSession::kWhatError: + { + break; + } + default: TRESPASS(); } diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 067d1da..aa9434b 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -23,7 +23,6 @@ namespace android { -struct ATSParser; struct LiveSession; struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { @@ -37,18 +36,16 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { virtual void prepareAsync(); virtual void start(); - virtual status_t feedMoreTSData(); - virtual status_t dequeueAccessUnit(bool audio, sp *accessUnit); + virtual sp getFormat(bool audio); + virtual status_t feedMoreTSData(); virtual status_t getDuration(int64_t *durationUs); virtual status_t seekTo(int64_t seekTimeUs); protected: virtual ~HTTPLiveSource(); - virtual sp getFormatMeta(bool audio); - virtual void onMessageReceived(const sp &msg); private: @@ -70,7 +67,6 @@ private: off64_t mOffset; sp mLiveLooper; sp mLiveSession; - sp mTSParser; void onSessionNotify(const sp &msg); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index b89b1c8..7e81035 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -89,6 +89,38 @@ private: DISALLOW_EVIL_CONSTRUCTORS(SetSurfaceAction); }; +struct NuPlayer::ShutdownDecoderAction : public Action { + ShutdownDecoderAction(bool audio, bool video) + : mAudio(audio), + mVideo(video) { + } + + virtual void execute(NuPlayer *player) { + player->performDecoderShutdown(mAudio, mVideo); + } + +private: + bool mAudio; + bool mVideo; + + DISALLOW_EVIL_CONSTRUCTORS(ShutdownDecoderAction); +}; + +struct NuPlayer::PostMessageAction : public Action { + PostMessageAction(const sp &msg) + : mMessage(msg) { + } + + virtual void execute(NuPlayer *) { + mMessage->post(); + } + +private: + sp mMessage; + + DISALLOW_EVIL_CONSTRUCTORS(PostMessageAction); +}; + // Use this if there's no state necessary to save in order to execute // the action. struct NuPlayer::SimpleAction : public Action { @@ -335,7 +367,8 @@ void NuPlayer::onMessageReceived(const sp &msg) { ALOGV("kWhatSetVideoNativeWindow"); mDeferredActions.push_back( - new SimpleAction(&NuPlayer::performDecoderShutdown)); + new ShutdownDecoderAction( + false /* audio */, true /* video */)); sp obj; CHECK(msg->findObject("native-window", &obj)); @@ -712,7 +745,8 @@ void NuPlayer::onMessageReceived(const sp &msg) { ALOGV("kWhatReset"); mDeferredActions.push_back( - new SimpleAction(&NuPlayer::performDecoderShutdown)); + new ShutdownDecoderAction( + true /* audio */, true /* video */)); mDeferredActions.push_back( new SimpleAction(&NuPlayer::performReset)); @@ -1023,6 +1057,9 @@ void NuPlayer::notifyListener(int msg, int ext1, int ext2) { } void NuPlayer::flushDecoder(bool audio, bool needShutdown) { + ALOGV("[%s] flushDecoder needShutdown=%d", + audio ? "audio" : "video", needShutdown); + if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) { ALOGI("flushDecoder %s without decoder present", audio ? "audio" : "video"); @@ -1173,20 +1210,29 @@ void NuPlayer::performDecoderFlush() { } } -void NuPlayer::performDecoderShutdown() { - ALOGV("performDecoderShutdown"); +void NuPlayer::performDecoderShutdown(bool audio, bool video) { + ALOGV("performDecoderShutdown audio=%d, video=%d", audio, video); - if (mAudioDecoder == NULL && mVideoDecoder == NULL) { + if ((!audio || mAudioDecoder == NULL) + && (!video || mVideoDecoder == NULL)) { return; } mTimeDiscontinuityPending = true; - if (mAudioDecoder != NULL) { + if (mFlushingAudio == NONE && (!audio || mAudioDecoder == NULL)) { + mFlushingAudio = FLUSHED; + } + + if (mFlushingVideo == NONE && (!video || mVideoDecoder == NULL)) { + mFlushingVideo = FLUSHED; + } + + if (audio && mAudioDecoder != NULL) { flushDecoder(true /* audio */, true /* needShutdown */); } - if (mVideoDecoder != NULL) { + if (video && mVideoDecoder != NULL) { flushDecoder(false /* audio */, true /* needShutdown */); } } @@ -1322,6 +1368,19 @@ void NuPlayer::onSourceNotify(const sp &msg) { break; } + case Source::kWhatQueueDecoderShutdown: + { + int32_t audio, video; + CHECK(msg->findInt32("audio", &audio)); + CHECK(msg->findInt32("video", &video)); + + sp reply; + CHECK(msg->findMessage("reply", &reply)); + + queueDecoderShutdown(audio, video, reply); + break; + } + default: TRESPASS(); } @@ -1355,4 +1414,19 @@ void NuPlayer::Source::onMessageReceived(const sp &msg) { TRESPASS(); } +void NuPlayer::queueDecoderShutdown( + bool audio, bool video, const sp &reply) { + ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video); + + mDeferredActions.push_back( + new ShutdownDecoderAction(audio, video)); + + mDeferredActions.push_back( + new SimpleAction(&NuPlayer::performScanSources)); + + mDeferredActions.push_back(new PostMessageAction(reply)); + + processDeferredActions(); +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 50d0462..8b6c8c1 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -80,6 +80,8 @@ private: struct Action; struct SeekAction; struct SetSurfaceAction; + struct ShutdownDecoderAction; + struct PostMessageAction; struct SimpleAction; enum { @@ -172,13 +174,16 @@ private: void performSeek(int64_t seekTimeUs); void performDecoderFlush(); - void performDecoderShutdown(); + void performDecoderShutdown(bool audio, bool video); void performReset(); void performScanSources(); void performSetSurface(const sp &wrapper); void onSourceNotify(const sp &msg); + void queueDecoderShutdown( + bool audio, bool video, const sp &reply); + DISALLOW_EVIL_CONSTRUCTORS(NuPlayer); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 404b56f..b543d9d 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -95,11 +95,11 @@ void NuPlayer::Renderer::flush(bool audio) { } void NuPlayer::Renderer::signalTimeDiscontinuity() { - CHECK(mAudioQueue.empty()); - CHECK(mVideoQueue.empty()); + // CHECK(mAudioQueue.empty()); + // CHECK(mVideoQueue.empty()); mAnchorTimeMediaUs = -1; mAnchorTimeRealUs = -1; - mSyncQueues = mHasAudio && mHasVideo; + mSyncQueues = false; } void NuPlayer::Renderer::pause() { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 1cbf575..81ffd21 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -42,6 +42,7 @@ struct NuPlayer::Source : public AHandler { kWhatVideoSizeChanged, kWhatBufferingStart, kWhatBufferingEnd, + kWhatQueueDecoderShutdown, }; // The provides message is used to notify the player about various diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index d3ac734..a60c320 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2348,10 +2348,15 @@ void ACodec::sendFormatChange() { ¶ms, sizeof(params)), (status_t)OK); + CHECK_GT(params.nChannels, 0); CHECK(params.nChannels == 1 || params.bInterleaved); CHECK_EQ(params.nBitPerSample, 16u); - CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned); - CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear); + + CHECK_EQ((int)params.eNumData, + (int)OMX_NumericalDataSigned); + + CHECK_EQ((int)params.ePCMMode, + (int)OMX_AUDIO_PCMModeLinear); notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW); notify->setInt32("channel-count", params.nChannels); @@ -2361,11 +2366,14 @@ void ACodec::sendFormatChange() { if (mSkipCutBuffer != NULL) { size_t prevbufsize = mSkipCutBuffer->size(); if (prevbufsize != 0) { - ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbufsize); + ALOGW("Replacing SkipCutBuffer holding %d " + "bytes", + prevbufsize); } } - mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay * frameSize, - mEncoderPadding * frameSize); + mSkipCutBuffer = new SkipCutBuffer( + mEncoderDelay * frameSize, + mEncoderPadding * frameSize); } if (mChannelMaskPresent) { diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index acc3abf..9544dbc 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -69,7 +69,6 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcamera_client \ - libcrypto \ libcutils \ libdl \ libdrmframework \ @@ -97,7 +96,6 @@ LOCAL_STATIC_LIBRARIES := \ libvpx \ libwebm \ libstagefright_mpeg2ts \ - libstagefright_httplive \ libstagefright_id3 \ libFLAC \ diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp index 40c5a3c..f7a00d8 100644 --- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp +++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "AHierarchicalStateMachine" +#include + #include #include diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk index a3fa7a3..85bd492 100644 --- a/media/libstagefright/httplive/Android.mk +++ b/media/libstagefright/httplive/Android.mk @@ -6,16 +6,25 @@ LOCAL_SRC_FILES:= \ LiveDataSource.cpp \ LiveSession.cpp \ M3UParser.cpp \ + PlaylistFetcher.cpp \ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/external/openssl/include +LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + libcutils \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libutils \ + LOCAL_MODULE:= libstagefright_httplive ifeq ($(TARGET_ARCH),arm) LOCAL_CFLAGS += -Wno-psabi endif -include $(BUILD_STATIC_LIBRARY) +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 505bdb3..fff13eb 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -18,12 +18,13 @@ #define LOG_TAG "LiveSession" #include -#include "include/LiveSession.h" +#include "LiveSession.h" -#include "LiveDataSource.h" +#include "M3UParser.h" +#include "PlaylistFetcher.h" -#include "include/M3UParser.h" #include "include/HTTPBase.h" +#include "mpeg2ts/AnotherPacketSource.h" #include #include @@ -33,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -47,37 +50,107 @@ LiveSession::LiveSession( mUIDValid(uidValid), mUID(uid), mInPreparationPhase(true), - mDataSource(new LiveDataSource), mHTTPDataSource( HTTPBase::Create( (mFlags & kFlagIncognito) ? HTTPBase::kFlagIncognito : 0)), mPrevBandwidthIndex(-1), - mLastPlaylistFetchTimeUs(-1), - mSeqNumber(-1), - mSeekTimeUs(-1), - mNumRetries(0), - mStartOfPlayback(true), - mDurationUs(-1), - mDurationFixed(false), - mSeekDone(false), - mDisconnectPending(false), - mMonitorQueueGeneration(0), - mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY) { + mStreamMask(0), + mCheckBandwidthGeneration(0), + mLastDequeuedTimeUs(0ll), + mReconfigurationInProgress(false), + mDisconnectReplyID(0) { if (mUIDValid) { mHTTPDataSource->setUID(mUID); } + + mPacketSources.add( + STREAMTYPE_AUDIO, new AnotherPacketSource(NULL /* meta */)); + + mPacketSources.add( + STREAMTYPE_VIDEO, new AnotherPacketSource(NULL /* meta */)); + + mPacketSources.add( + STREAMTYPE_SUBTITLES, new AnotherPacketSource(NULL /* meta */)); } LiveSession::~LiveSession() { } -sp LiveSession::getDataSource() { - return mDataSource; +status_t LiveSession::dequeueAccessUnit( + StreamType stream, sp *accessUnit) { + if (!(mStreamMask & stream)) { + return UNKNOWN_ERROR; + } + + sp packetSource = mPacketSources.valueFor(stream); + + status_t finalResult; + if (!packetSource->hasBufferAvailable(&finalResult)) { + return finalResult == OK ? -EAGAIN : finalResult; + } + + status_t err = packetSource->dequeueAccessUnit(accessUnit); + + const char *streamStr; + switch (stream) { + case STREAMTYPE_AUDIO: + streamStr = "audio"; + break; + case STREAMTYPE_VIDEO: + streamStr = "video"; + break; + case STREAMTYPE_SUBTITLES: + streamStr = "subs"; + break; + default: + TRESPASS(); + } + + if (err == INFO_DISCONTINUITY) { + int32_t type; + CHECK((*accessUnit)->meta()->findInt32("discontinuity", &type)); + + sp extra; + if (!(*accessUnit)->meta()->findMessage("extra", &extra)) { + extra.clear(); + } + + ALOGI("[%s] read discontinuity of type %d, extra = %s", + streamStr, + type, + extra == NULL ? "NULL" : extra->debugString().c_str()); + } else if (err == OK) { + int64_t timeUs; + CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); + ALOGV("[%s] read buffer at time %lld us", streamStr, timeUs); + + mLastDequeuedTimeUs = timeUs; + } else { + ALOGI("[%s] encountered error %d", streamStr, err); + } + + return err; +} + +status_t LiveSession::getStreamFormat(StreamType stream, sp *format) { + if (!(mStreamMask & stream)) { + return UNKNOWN_ERROR; + } + + sp packetSource = mPacketSources.valueFor(stream); + + sp meta = packetSource->getFormat(); + + if (meta == NULL) { + return -EAGAIN; + } + + return convertMetaDataToMessage(meta, format); } -void LiveSession::connect( +void LiveSession::connectAsync( const char *url, const KeyedVector *headers) { sp msg = new AMessage(kWhatConnect, id()); msg->setString("url", url); @@ -91,55 +164,184 @@ void LiveSession::connect( msg->post(); } -void LiveSession::disconnect() { - Mutex::Autolock autoLock(mLock); - mDisconnectPending = true; +status_t LiveSession::disconnect() { + sp msg = new AMessage(kWhatDisconnect, id()); - mHTTPDataSource->disconnect(); + sp response; + status_t err = msg->postAndAwaitResponse(&response); - (new AMessage(kWhatDisconnect, id()))->post(); + return err; } -void LiveSession::seekTo(int64_t timeUs) { - Mutex::Autolock autoLock(mLock); - mSeekDone = false; - +status_t LiveSession::seekTo(int64_t timeUs) { sp msg = new AMessage(kWhatSeek, id()); msg->setInt64("timeUs", timeUs); - msg->post(); - while (!mSeekDone) { - mCondition.wait(mLock); - } + sp response; + status_t err = msg->postAndAwaitResponse(&response); + + return err; } void LiveSession::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatConnect: + { onConnect(msg); break; + } case kWhatDisconnect: - onDisconnect(); + { + CHECK(msg->senderAwaitsResponse(&mDisconnectReplyID)); + + if (mReconfigurationInProgress) { + break; + } + + finishDisconnect(); break; + } - case kWhatMonitorQueue: + case kWhatSeek: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + status_t err = onSeek(msg); + + sp response = new AMessage; + response->setInt32("err", err); + + response->postReply(replyID); + break; + } + + case kWhatFetcherNotify: + { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case PlaylistFetcher::kWhatStarted: + break; + case PlaylistFetcher::kWhatPaused: + case PlaylistFetcher::kWhatStopped: + { + if (what == PlaylistFetcher::kWhatStopped) { + AString uri; + CHECK(msg->findString("uri", &uri)); + mFetcherInfos.removeItem(uri); + } + + if (mContinuation != NULL) { + CHECK_GT(mContinuationCounter, 0); + if (--mContinuationCounter == 0) { + mContinuation->post(); + } + } + break; + } + + case PlaylistFetcher::kWhatDurationUpdate: + { + AString uri; + CHECK(msg->findString("uri", &uri)); + + int64_t durationUs; + CHECK(msg->findInt64("durationUs", &durationUs)); + + FetcherInfo *info = &mFetcherInfos.editValueFor(uri); + info->mDurationUs = durationUs; + break; + } + + case PlaylistFetcher::kWhatError: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGE("XXX Received error %d from PlaylistFetcher.", err); + + if (mInPreparationPhase) { + postPrepared(err); + } + + mPacketSources.valueFor(STREAMTYPE_AUDIO)->signalEOS(err); + + mPacketSources.valueFor(STREAMTYPE_VIDEO)->signalEOS(err); + + mPacketSources.valueFor( + STREAMTYPE_SUBTITLES)->signalEOS(err); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); + break; + } + + case PlaylistFetcher::kWhatTemporarilyDoneFetching: + { + AString uri; + CHECK(msg->findString("uri", &uri)); + + FetcherInfo *info = &mFetcherInfos.editValueFor(uri); + info->mIsPrepared = true; + + if (mInPreparationPhase) { + bool allFetchersPrepared = true; + for (size_t i = 0; i < mFetcherInfos.size(); ++i) { + if (!mFetcherInfos.valueAt(i).mIsPrepared) { + allFetchersPrepared = false; + break; + } + } + + if (allFetchersPrepared) { + postPrepared(OK); + } + } + break; + } + + default: + TRESPASS(); + } + + break; + } + + case kWhatCheckBandwidth: { int32_t generation; CHECK(msg->findInt32("generation", &generation)); - if (generation != mMonitorQueueGeneration) { - // Stale event + if (generation != mCheckBandwidthGeneration) { break; } - onMonitorQueue(); + onCheckBandwidth(); break; } - case kWhatSeek: - onSeek(msg); + case kWhatChangeConfiguration2: + { + onChangeConfiguration2(msg); + break; + } + + case kWhatChangeConfiguration3: + { + onChangeConfiguration3(msg); + break; + } + + case kWhatFinishDisconnect2: + { + onFinishDisconnect2(); break; + } default: TRESPASS(); @@ -172,48 +374,127 @@ void LiveSession::onConnect(const sp &msg) { headers = NULL; } +#if 1 ALOGI("onConnect "); +#else + ALOGI("onConnect %s", url.c_str()); +#endif mMasterURL = url; bool dummy; - sp playlist = fetchPlaylist(url.c_str(), &dummy); + mPlaylist = fetchPlaylist(url.c_str(), NULL /* curPlaylistHash */, &dummy); - if (playlist == NULL) { + if (mPlaylist == NULL) { ALOGE("unable to fetch master playlist '%s'.", url.c_str()); - signalEOS(ERROR_IO); + postPrepared(ERROR_IO); return; } - if (playlist->isVariantPlaylist()) { - for (size_t i = 0; i < playlist->size(); ++i) { + // We trust the content provider to make a reasonable choice of preferred + // initial bandwidth by listing it first in the variant playlist. + // At startup we really don't have a good estimate on the available + // network bandwidth since we haven't tranferred any data yet. Once + // we have we can make a better informed choice. + size_t initialBandwidth = 0; + size_t initialBandwidthIndex = 0; + + if (mPlaylist->isVariantPlaylist()) { + for (size_t i = 0; i < mPlaylist->size(); ++i) { BandwidthItem item; + item.mPlaylistIndex = i; + sp meta; - playlist->itemAt(i, &item.mURI, &meta); + AString uri; + mPlaylist->itemAt(i, &uri, &meta); unsigned long bandwidth; CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth)); + if (initialBandwidth == 0) { + initialBandwidth = item.mBandwidth; + } + mBandwidthItems.push(item); } CHECK_GT(mBandwidthItems.size(), 0u); mBandwidthItems.sort(SortByBandwidth); + + for (size_t i = 0; i < mBandwidthItems.size(); ++i) { + if (mBandwidthItems.itemAt(i).mBandwidth == initialBandwidth) { + initialBandwidthIndex = i; + break; + } + } + } else { + // dummy item. + BandwidthItem item; + item.mPlaylistIndex = 0; + item.mBandwidth = 0; + mBandwidthItems.push(item); } - postMonitorQueue(); + changeConfiguration(0ll /* timeUs */, initialBandwidthIndex); } -void LiveSession::onDisconnect() { - ALOGI("onDisconnect"); +void LiveSession::finishDisconnect() { + // No reconfiguration is currently pending, make sure none will trigger + // during disconnection either. + cancelCheckBandwidthEvent(); + + for (size_t i = 0; i < mFetcherInfos.size(); ++i) { + mFetcherInfos.valueAt(i).mFetcher->stopAsync(); + } + + sp msg = new AMessage(kWhatFinishDisconnect2, id()); - signalEOS(ERROR_END_OF_STREAM); + mContinuationCounter = mFetcherInfos.size(); + mContinuation = msg; - Mutex::Autolock autoLock(mLock); - mDisconnectPending = false; + if (mContinuationCounter == 0) { + msg->post(); + } +} + +void LiveSession::onFinishDisconnect2() { + mContinuation.clear(); + + mPacketSources.valueFor(STREAMTYPE_AUDIO)->signalEOS(ERROR_END_OF_STREAM); + mPacketSources.valueFor(STREAMTYPE_VIDEO)->signalEOS(ERROR_END_OF_STREAM); + + mPacketSources.valueFor( + STREAMTYPE_SUBTITLES)->signalEOS(ERROR_END_OF_STREAM); + + sp response = new AMessage; + response->setInt32("err", OK); + + response->postReply(mDisconnectReplyID); + mDisconnectReplyID = 0; +} + +sp LiveSession::addFetcher(const char *uri) { + ssize_t index = mFetcherInfos.indexOfKey(uri); + + if (index >= 0) { + return NULL; + } + + sp notify = new AMessage(kWhatFetcherNotify, id()); + notify->setString("uri", uri); + + FetcherInfo info; + info.mFetcher = new PlaylistFetcher(notify, this, uri); + info.mDurationUs = -1ll; + info.mIsPrepared = false; + looper()->registerHandler(info.mFetcher); + + mFetcherInfos.add(uri, info); + + return info.mFetcher; } status_t LiveSession::fetchFile( @@ -229,14 +510,6 @@ status_t LiveSession::fetchFile( && strncasecmp(url, "https://", 8)) { return ERROR_UNSUPPORTED; } else { - { - Mutex::Autolock autoLock(mLock); - - if (mDisconnectPending) { - return ERROR_IO; - } - } - KeyedVector headers = mExtraHeaders; if (range_offset > 0 || range_length >= 0) { headers.add( @@ -315,7 +588,8 @@ status_t LiveSession::fetchFile( return OK; } -sp LiveSession::fetchPlaylist(const char *url, bool *unchanged) { +sp LiveSession::fetchPlaylist( + const char *url, uint8_t *curPlaylistHash, bool *unchanged) { ALOGV("fetchPlaylist '%s'", url); *unchanged = false; @@ -339,13 +613,8 @@ sp LiveSession::fetchPlaylist(const char *url, bool *unchanged) { MD5_Final(hash, &m); - if (mPlaylist != NULL && !memcmp(hash, mPlaylistHash, 16)) { + if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) { // playlist unchanged - - if (mRefreshState != THIRD_UNCHANGED_RELOAD_ATTEMPT) { - mRefreshState = (RefreshState)(mRefreshState + 1); - } - *unchanged = true; ALOGV("Playlist unchanged, refresh state is now %d", @@ -354,9 +623,9 @@ sp LiveSession::fetchPlaylist(const char *url, bool *unchanged) { return NULL; } - memcpy(mPlaylistHash, hash, sizeof(hash)); - - mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY; + if (curPlaylistHash != NULL) { + memcpy(curPlaylistHash, hash, sizeof(hash)); + } #endif sp playlist = @@ -371,37 +640,6 @@ sp LiveSession::fetchPlaylist(const char *url, bool *unchanged) { return playlist; } -int64_t LiveSession::getSegmentStartTimeUs(int32_t seqNumber) const { - CHECK(mPlaylist != NULL); - - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } - - int32_t lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; - - CHECK_GE(seqNumber, firstSeqNumberInPlaylist); - CHECK_LE(seqNumber, lastSeqNumberInPlaylist); - - int64_t segmentStartUs = 0ll; - for (int32_t index = 0; - index < seqNumber - firstSeqNumberInPlaylist; ++index) { - sp itemMeta; - CHECK(mPlaylist->itemAt( - index, NULL /* uri */, &itemMeta)); - - int64_t itemDurationUs; - CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); - - segmentStartUs += itemDurationUs; - } - - return segmentStartUs; -} - static double uniformRand() { return (double)rand() / RAND_MAX; } @@ -412,36 +650,50 @@ size_t LiveSession::getBandwidthIndex() { } #if 1 - int32_t bandwidthBps; - if (mHTTPDataSource != NULL - && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) { - ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); - } else { - ALOGV("no bandwidth estimate."); - return 0; // Pick the lowest bandwidth stream by default. - } - char value[PROPERTY_VALUE_MAX]; - if (property_get("media.httplive.max-bw", value, NULL)) { + ssize_t index; + if (property_get("media.httplive.bw-index", value, NULL)) { char *end; - long maxBw = strtoul(value, &end, 10); - if (end > value && *end == '\0') { - if (maxBw > 0 && bandwidthBps > maxBw) { - ALOGV("bandwidth capped to %ld bps", maxBw); - bandwidthBps = maxBw; - } + index = strtol(value, &end, 10); + CHECK(end > value && *end == '\0'); + + if (index >= 0 && (size_t)index >= mBandwidthItems.size()) { + index = mBandwidthItems.size() - 1; } } - // Consider only 80% of the available bandwidth usable. - bandwidthBps = (bandwidthBps * 8) / 10; + if (index < 0) { + int32_t bandwidthBps; + if (mHTTPDataSource != NULL + && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) { + ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); + } else { + ALOGV("no bandwidth estimate."); + return 0; // Pick the lowest bandwidth stream by default. + } - // Pick the highest bandwidth stream below or equal to estimated bandwidth. + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.httplive.max-bw", value, NULL)) { + char *end; + long maxBw = strtoul(value, &end, 10); + if (end > value && *end == '\0') { + if (maxBw > 0 && bandwidthBps > maxBw) { + ALOGV("bandwidth capped to %ld bps", maxBw); + bandwidthBps = maxBw; + } + } + } - size_t index = mBandwidthItems.size() - 1; - while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth - > (size_t)bandwidthBps) { - --index; + // Consider only 80% of the available bandwidth usable. + bandwidthBps = (bandwidthBps * 8) / 10; + + // Pick the highest bandwidth stream below or equal to estimated bandwidth. + + index = mBandwidthItems.size() - 1; + while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth + > (size_t)bandwidthBps) { + --index; + } } #elif 0 // Change bandwidth at random() @@ -452,6 +704,8 @@ size_t LiveSession::getBandwidthIndex() { // to lowest) const size_t kMinIndex = 0; + static ssize_t mPrevBandwidthIndex = -1; + size_t index; if (mPrevBandwidthIndex < 0) { index = kMinIndex; @@ -463,6 +717,7 @@ size_t LiveSession::getBandwidthIndex() { index = kMinIndex; } } + mPrevBandwidthIndex = index; #elif 0 // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec @@ -470,570 +725,381 @@ size_t LiveSession::getBandwidthIndex() { while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) { --index; } +#elif 1 + char value[PROPERTY_VALUE_MAX]; + size_t index; + if (property_get("media.httplive.bw-index", value, NULL)) { + char *end; + index = strtoul(value, &end, 10); + CHECK(end > value && *end == '\0'); + + if (index >= mBandwidthItems.size()) { + index = mBandwidthItems.size() - 1; + } + } else { + index = 0; + } #else size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream #endif + CHECK_GE(index, 0); + return index; } -bool LiveSession::timeToRefreshPlaylist(int64_t nowUs) const { - if (mPlaylist == NULL) { - CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY); - return true; - } - - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); - - int64_t targetDurationUs = targetDurationSecs * 1000000ll; - - int64_t minPlaylistAgeUs; - - switch (mRefreshState) { - case INITIAL_MINIMUM_RELOAD_DELAY: - { - size_t n = mPlaylist->size(); - if (n > 0) { - sp itemMeta; - CHECK(mPlaylist->itemAt(n - 1, NULL /* uri */, &itemMeta)); - - int64_t itemDurationUs; - CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); - - minPlaylistAgeUs = itemDurationUs; - break; - } - - // fall through - } - - case FIRST_UNCHANGED_RELOAD_ATTEMPT: - { - minPlaylistAgeUs = targetDurationUs / 2; - break; - } - - case SECOND_UNCHANGED_RELOAD_ATTEMPT: - { - minPlaylistAgeUs = (targetDurationUs * 3) / 2; - break; - } - - case THIRD_UNCHANGED_RELOAD_ATTEMPT: - { - minPlaylistAgeUs = targetDurationUs * 3; - break; - } +status_t LiveSession::onSeek(const sp &msg) { + int64_t timeUs; + CHECK(msg->findInt64("timeUs", &timeUs)); - default: - TRESPASS(); - break; + if (!mReconfigurationInProgress) { + changeConfiguration(timeUs, getBandwidthIndex()); } - return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs; + return OK; } -void LiveSession::onDownloadNext() { - size_t bandwidthIndex = getBandwidthIndex(); - -rinse_repeat: - int64_t nowUs = ALooper::GetNowUs(); - - if (mLastPlaylistFetchTimeUs < 0 - || (ssize_t)bandwidthIndex != mPrevBandwidthIndex - || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) { - AString url; - if (mBandwidthItems.size() > 0) { - url = mBandwidthItems.editItemAt(bandwidthIndex).mURI; - } else { - url = mMasterURL; - } - - if ((ssize_t)bandwidthIndex != mPrevBandwidthIndex) { - // If we switch bandwidths, do not pay any heed to whether - // playlists changed since the last time... - mPlaylist.clear(); - } - - bool unchanged; - sp playlist = fetchPlaylist(url.c_str(), &unchanged); - if (playlist == NULL) { - if (unchanged) { - // We succeeded in fetching the playlist, but it was - // unchanged from the last time we tried. - } else { - ALOGE("failed to load playlist at url '%s'", url.c_str()); - signalEOS(ERROR_IO); - - return; - } - } else { - mPlaylist = playlist; - } - - if (!mDurationFixed) { - Mutex::Autolock autoLock(mLock); - - if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) { - mDurationUs = -1; - mDurationFixed = true; - } else { - mDurationUs = 0; - for (size_t i = 0; i < mPlaylist->size(); ++i) { - sp itemMeta; - CHECK(mPlaylist->itemAt( - i, NULL /* uri */, &itemMeta)); - - int64_t itemDurationUs; - CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); - - mDurationUs += itemDurationUs; - } +status_t LiveSession::getDuration(int64_t *durationUs) const { + int64_t maxDurationUs = 0ll; + for (size_t i = 0; i < mFetcherInfos.size(); ++i) { + int64_t fetcherDurationUs = mFetcherInfos.valueAt(i).mDurationUs; - mDurationFixed = mPlaylist->isComplete(); - } + if (fetcherDurationUs >= 0ll && fetcherDurationUs > maxDurationUs) { + maxDurationUs = fetcherDurationUs; } - - mLastPlaylistFetchTimeUs = ALooper::GetNowUs(); } - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } + *durationUs = maxDurationUs; - bool seekDiscontinuity = false; - bool explicitDiscontinuity = false; - bool bandwidthChanged = false; - - if (mSeekTimeUs >= 0) { - if (mPlaylist->isComplete() || mPlaylist->isEvent()) { - size_t index = 0; - int64_t segmentStartUs = 0; - while (index < mPlaylist->size()) { - sp itemMeta; - CHECK(mPlaylist->itemAt( - index, NULL /* uri */, &itemMeta)); - - int64_t itemDurationUs; - CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + return OK; +} - if (mSeekTimeUs < segmentStartUs + itemDurationUs) { - break; - } +bool LiveSession::isSeekable() const { + int64_t durationUs; + return getDuration(&durationUs) == OK && durationUs >= 0; +} - segmentStartUs += itemDurationUs; - ++index; - } +bool LiveSession::hasDynamicDuration() const { + return false; +} - if (index < mPlaylist->size()) { - int32_t newSeqNumber = firstSeqNumberInPlaylist + index; +void LiveSession::changeConfiguration(int64_t timeUs, size_t bandwidthIndex) { + CHECK(!mReconfigurationInProgress); + mReconfigurationInProgress = true; - ALOGI("seeking to seq no %d", newSeqNumber); + mPrevBandwidthIndex = bandwidthIndex; - mSeqNumber = newSeqNumber; + ALOGV("changeConfiguration => timeUs:%lld us, bwIndex:%d", + timeUs, bandwidthIndex); - mDataSource->reset(); + mPlaylist->pickRandomMediaItems(); - // reseting the data source will have had the - // side effect of discarding any previously queued - // bandwidth change discontinuity. - // Therefore we'll need to treat these seek - // discontinuities as involving a bandwidth change - // even if they aren't directly. - seekDiscontinuity = true; - bandwidthChanged = true; - } - } + CHECK_LT(bandwidthIndex, mBandwidthItems.size()); + const BandwidthItem &item = mBandwidthItems.itemAt(bandwidthIndex); - mSeekTimeUs = -1; + uint32_t streamMask = 0; - Mutex::Autolock autoLock(mLock); - mSeekDone = true; - mCondition.broadcast(); + AString audioURI; + if (mPlaylist->getAudioURI(item.mPlaylistIndex, &audioURI)) { + streamMask |= STREAMTYPE_AUDIO; } - const int32_t lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; - - if (mSeqNumber < 0) { - if (mPlaylist->isComplete()) { - mSeqNumber = firstSeqNumberInPlaylist; - } else { - // If this is a live session, start 3 segments from the end. - mSeqNumber = lastSeqNumberInPlaylist - 3; - if (mSeqNumber < firstSeqNumberInPlaylist) { - mSeqNumber = firstSeqNumberInPlaylist; - } - } + AString videoURI; + if (mPlaylist->getVideoURI(item.mPlaylistIndex, &videoURI)) { + streamMask |= STREAMTYPE_VIDEO; } - if (mSeqNumber < firstSeqNumberInPlaylist - || mSeqNumber > lastSeqNumberInPlaylist) { - if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) { - // Go back to the previous bandwidth. + AString subtitleURI; + if (mPlaylist->getSubtitleURI(item.mPlaylistIndex, &subtitleURI)) { + streamMask |= STREAMTYPE_SUBTITLES; + } - ALOGI("new bandwidth does not have the sequence number " - "we're looking for, switching back to previous bandwidth"); + // Step 1, stop and discard fetchers that are no longer needed. + // Pause those that we'll reuse. + for (size_t i = 0; i < mFetcherInfos.size(); ++i) { + const AString &uri = mFetcherInfos.keyAt(i); - mLastPlaylistFetchTimeUs = -1; - bandwidthIndex = mPrevBandwidthIndex; - goto rinse_repeat; - } + bool discardFetcher = true; - if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) { - ++mNumRetries; - - if (mSeqNumber > lastSeqNumberInPlaylist) { - mLastPlaylistFetchTimeUs = -1; - postMonitorQueue(3000000ll); - return; + // If we're seeking all current fetchers are discarded. + if (timeUs < 0ll) { + if (((streamMask & STREAMTYPE_AUDIO) && uri == audioURI) + || ((streamMask & STREAMTYPE_VIDEO) && uri == videoURI) + || ((streamMask & STREAMTYPE_SUBTITLES) && uri == subtitleURI)) { + discardFetcher = false; } + } - // we've missed the boat, let's start from the lowest sequence - // number available and signal a discontinuity. - - ALOGI("We've missed the boat, restarting playback."); - mSeqNumber = lastSeqNumberInPlaylist; - explicitDiscontinuity = true; - - // fall through + if (discardFetcher) { + mFetcherInfos.valueAt(i).mFetcher->stopAsync(); } else { - ALOGE("Cannot find sequence number %d in playlist " - "(contains %d - %d)", - mSeqNumber, firstSeqNumberInPlaylist, - firstSeqNumberInPlaylist + mPlaylist->size() - 1); - - signalEOS(ERROR_END_OF_STREAM); - return; + mFetcherInfos.valueAt(i).mFetcher->pauseAsync(); } } - mNumRetries = 0; - - AString uri; - sp itemMeta; - CHECK(mPlaylist->itemAt( - mSeqNumber - firstSeqNumberInPlaylist, - &uri, - &itemMeta)); - - int32_t val; - if (itemMeta->findInt32("discontinuity", &val) && val != 0) { - explicitDiscontinuity = true; + sp msg = new AMessage(kWhatChangeConfiguration2, id()); + msg->setInt32("streamMask", streamMask); + msg->setInt64("timeUs", timeUs); + if (streamMask & STREAMTYPE_AUDIO) { + msg->setString("audioURI", audioURI.c_str()); } - - int64_t range_offset, range_length; - if (!itemMeta->findInt64("range-offset", &range_offset) - || !itemMeta->findInt64("range-length", &range_length)) { - range_offset = 0; - range_length = -1; + if (streamMask & STREAMTYPE_VIDEO) { + msg->setString("videoURI", videoURI.c_str()); } - - ALOGV("fetching segment %d from (%d .. %d)", - mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); - - sp buffer; - status_t err = fetchFile(uri.c_str(), &buffer, range_offset, range_length); - if (err != OK) { - ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); - signalEOS(err); - return; + if (streamMask & STREAMTYPE_SUBTITLES) { + msg->setString("subtitleURI", subtitleURI.c_str()); } - CHECK(buffer != NULL); - - err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer); + // Every time a fetcher acknowledges the stopAsync or pauseAsync request + // we'll decrement mContinuationCounter, once it reaches zero, i.e. all + // fetchers have completed their asynchronous operation, we'll post + // mContinuation, which then is handled below in onChangeConfiguration2. + mContinuationCounter = mFetcherInfos.size(); + mContinuation = msg; - if (err != OK) { - ALOGE("decryptBuffer failed w/ error %d", err); - - signalEOS(err); - return; + if (mContinuationCounter == 0) { + msg->post(); } +} - if (buffer->size() == 0 || buffer->data()[0] != 0x47) { - // Not a transport stream??? - - ALOGE("This doesn't look like a transport stream..."); - - mBandwidthItems.removeAt(bandwidthIndex); - - if (mBandwidthItems.isEmpty()) { - signalEOS(ERROR_UNSUPPORTED); - return; - } +void LiveSession::onChangeConfiguration2(const sp &msg) { + mContinuation.clear(); - ALOGI("Retrying with a different bandwidth stream."); + // All fetchers are either suspended or have been removed now. - mLastPlaylistFetchTimeUs = -1; - bandwidthIndex = getBandwidthIndex(); - mPrevBandwidthIndex = bandwidthIndex; - mSeqNumber = -1; + uint32_t streamMask; + CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask)); - goto rinse_repeat; + AString audioURI, videoURI, subtitleURI; + if (streamMask & STREAMTYPE_AUDIO) { + CHECK(msg->findString("audioURI", &audioURI)); + ALOGV("audioURI = '%s'", audioURI.c_str()); } - - if ((size_t)mPrevBandwidthIndex != bandwidthIndex) { - bandwidthChanged = true; + if (streamMask & STREAMTYPE_VIDEO) { + CHECK(msg->findString("videoURI", &videoURI)); + ALOGV("videoURI = '%s'", videoURI.c_str()); } - - if (mPrevBandwidthIndex < 0) { - // Don't signal a bandwidth change at the very beginning of - // playback. - bandwidthChanged = false; + if (streamMask & STREAMTYPE_SUBTITLES) { + CHECK(msg->findString("subtitleURI", &subtitleURI)); + ALOGV("subtitleURI = '%s'", subtitleURI.c_str()); } - if (mStartOfPlayback) { - seekDiscontinuity = true; - mStartOfPlayback = false; + // Determine which decoders to shutdown on the player side, + // a decoder has to be shutdown if either + // 1) its streamtype was active before but now longer isn't. + // or + // 2) its streamtype was already active and still is but the URI + // has changed. + uint32_t changedMask = 0; + if (((mStreamMask & streamMask & STREAMTYPE_AUDIO) + && !(audioURI == mAudioURI)) + || (mStreamMask & ~streamMask & STREAMTYPE_AUDIO)) { + changedMask |= STREAMTYPE_AUDIO; + } + if (((mStreamMask & streamMask & STREAMTYPE_VIDEO) + && !(videoURI == mVideoURI)) + || (mStreamMask & ~streamMask & STREAMTYPE_VIDEO)) { + changedMask |= STREAMTYPE_VIDEO; } - if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) { - // Signal discontinuity. - - ALOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)", - seekDiscontinuity, explicitDiscontinuity, bandwidthChanged); + if (changedMask == 0) { + // If nothing changed as far as the audio/video decoders + // are concerned we can proceed. + onChangeConfiguration3(msg); + return; + } - sp tmp = new ABuffer(188); - memset(tmp->data(), 0, tmp->size()); + // Something changed, inform the player which will shutdown the + // corresponding decoders and will post the reply once that's done. + // Handling the reply will continue executing below in + // onChangeConfiguration3. + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatStreamsChanged); + notify->setInt32("changedMask", changedMask); - // signal a 'hard' discontinuity for explicit or bandwidthChanged. - uint8_t type = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0; + msg->setWhat(kWhatChangeConfiguration3); + msg->setTarget(id()); - if (mPlaylist->isComplete() || mPlaylist->isEvent()) { - // If this was a live event this made no sense since - // we don't have access to all the segment before the current - // one. - int64_t segmentStartTimeUs = getSegmentStartTimeUs(mSeqNumber); - memcpy(tmp->data() + 2, &segmentStartTimeUs, sizeof(segmentStartTimeUs)); + notify->setMessage("reply", msg); + notify->post(); +} - type |= 2; - } +void LiveSession::onChangeConfiguration3(const sp &msg) { + // All remaining fetchers are still suspended, the player has shutdown + // any decoders that needed it. - tmp->data()[1] = type; + uint32_t streamMask; + CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask)); - mDataSource->queueBuffer(tmp); + AString audioURI, videoURI, subtitleURI; + if (streamMask & STREAMTYPE_AUDIO) { + CHECK(msg->findString("audioURI", &audioURI)); + } + if (streamMask & STREAMTYPE_VIDEO) { + CHECK(msg->findString("videoURI", &videoURI)); + } + if (streamMask & STREAMTYPE_SUBTITLES) { + CHECK(msg->findString("subtitleURI", &subtitleURI)); } - mDataSource->queueBuffer(buffer); + int64_t timeUs; + CHECK(msg->findInt64("timeUs", &timeUs)); - mPrevBandwidthIndex = bandwidthIndex; - ++mSeqNumber; + if (timeUs < 0ll) { + timeUs = mLastDequeuedTimeUs; + } - postMonitorQueue(); -} + mStreamMask = streamMask; + mAudioURI = audioURI; + mVideoURI = videoURI; + mSubtitleURI = subtitleURI; -void LiveSession::signalEOS(status_t err) { - if (mInPreparationPhase && mNotify != NULL) { - sp notify = mNotify->dup(); + // Resume all existing fetchers and assign them packet sources. + for (size_t i = 0; i < mFetcherInfos.size(); ++i) { + const AString &uri = mFetcherInfos.keyAt(i); - notify->setInt32( - "what", - err == ERROR_END_OF_STREAM - ? kWhatPrepared : kWhatPreparationFailed); + uint32_t resumeMask = 0; - if (err != ERROR_END_OF_STREAM) { - notify->setInt32("err", err); + sp audioSource; + if ((streamMask & STREAMTYPE_AUDIO) && uri == audioURI) { + audioSource = mPacketSources.valueFor(STREAMTYPE_AUDIO); + resumeMask |= STREAMTYPE_AUDIO; } - notify->post(); - - mInPreparationPhase = false; - } - - mDataSource->queueEOS(err); -} - -void LiveSession::onMonitorQueue() { - if (mSeekTimeUs >= 0 - || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) { - onDownloadNext(); - } else { - if (mInPreparationPhase) { - if (mNotify != NULL) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatPrepared); - notify->post(); - } - - mInPreparationPhase = false; + sp videoSource; + if ((streamMask & STREAMTYPE_VIDEO) && uri == videoURI) { + videoSource = mPacketSources.valueFor(STREAMTYPE_VIDEO); + resumeMask |= STREAMTYPE_VIDEO; } - postMonitorQueue(1000000ll); - } -} + sp subtitleSource; + if ((streamMask & STREAMTYPE_SUBTITLES) && uri == subtitleURI) { + subtitleSource = mPacketSources.valueFor(STREAMTYPE_SUBTITLES); + resumeMask |= STREAMTYPE_SUBTITLES; + } -status_t LiveSession::decryptBuffer( - size_t playlistIndex, const sp &buffer) { - sp itemMeta; - bool found = false; - AString method; + CHECK_NE(resumeMask, 0u); - for (ssize_t i = playlistIndex; i >= 0; --i) { - AString uri; - CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); + ALOGV("resuming fetchers for mask 0x%08x", resumeMask); - if (itemMeta->findString("cipher-method", &method)) { - found = true; - break; - } - } + streamMask &= ~resumeMask; - if (!found) { - method = "NONE"; + mFetcherInfos.valueAt(i).mFetcher->startAsync( + audioSource, videoSource, subtitleSource); } - if (method == "NONE") { - return OK; - } else if (!(method == "AES-128")) { - ALOGE("Unsupported cipher method '%s'", method.c_str()); - return ERROR_UNSUPPORTED; - } + // streamMask now only contains the types that need a new fetcher created. - AString keyURI; - if (!itemMeta->findString("cipher-uri", &keyURI)) { - ALOGE("Missing key uri"); - return ERROR_MALFORMED; + if (streamMask != 0) { + ALOGV("creating new fetchers for mask 0x%08x", streamMask); } - ssize_t index = mAESKeyForURI.indexOfKey(keyURI); - - sp key; - if (index >= 0) { - key = mAESKeyForURI.valueAt(index); - } else { - key = new ABuffer(16); - - sp keySource = - HTTPBase::Create( - (mFlags & kFlagIncognito) - ? HTTPBase::kFlagIncognito - : 0); + while (streamMask != 0) { + StreamType streamType = (StreamType)(streamMask & ~(streamMask - 1)); - if (mUIDValid) { - keySource->setUID(mUID); + AString uri; + switch (streamType) { + case STREAMTYPE_AUDIO: + uri = audioURI; + break; + case STREAMTYPE_VIDEO: + uri = videoURI; + break; + case STREAMTYPE_SUBTITLES: + uri = subtitleURI; + break; + default: + TRESPASS(); } - status_t err = - keySource->connect( - keyURI.c_str(), - mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); - - if (err == OK) { - size_t offset = 0; - while (offset < 16) { - ssize_t n = keySource->readAt( - offset, key->data() + offset, 16 - offset); - if (n <= 0) { - err = ERROR_IO; - break; - } + sp fetcher = addFetcher(uri.c_str()); + CHECK(fetcher != NULL); - offset += n; - } - } + sp audioSource; + if ((streamMask & STREAMTYPE_AUDIO) && uri == audioURI) { + audioSource = mPacketSources.valueFor(STREAMTYPE_AUDIO); + audioSource->clear(); - if (err != OK) { - ALOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); - return ERROR_IO; + streamMask &= ~STREAMTYPE_AUDIO; } - mAESKeyForURI.add(keyURI, key); - } + sp videoSource; + if ((streamMask & STREAMTYPE_VIDEO) && uri == videoURI) { + videoSource = mPacketSources.valueFor(STREAMTYPE_VIDEO); + videoSource->clear(); - AES_KEY aes_key; - if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { - ALOGE("failed to set AES decryption key."); - return UNKNOWN_ERROR; - } - - unsigned char aes_ivec[16]; - - AString iv; - if (itemMeta->findString("cipher-iv", &iv)) { - if ((!iv.startsWith("0x") && !iv.startsWith("0X")) - || iv.size() != 16 * 2 + 2) { - ALOGE("malformed cipher IV '%s'.", iv.c_str()); - return ERROR_MALFORMED; + streamMask &= ~STREAMTYPE_VIDEO; } - memset(aes_ivec, 0, sizeof(aes_ivec)); - for (size_t i = 0; i < 16; ++i) { - char c1 = tolower(iv.c_str()[2 + 2 * i]); - char c2 = tolower(iv.c_str()[3 + 2 * i]); - if (!isxdigit(c1) || !isxdigit(c2)) { - ALOGE("malformed cipher IV '%s'.", iv.c_str()); - return ERROR_MALFORMED; - } - uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; - uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; + sp subtitleSource; + if ((streamMask & STREAMTYPE_SUBTITLES) && uri == subtitleURI) { + subtitleSource = mPacketSources.valueFor(STREAMTYPE_SUBTITLES); + subtitleSource->clear(); - aes_ivec[i] = nibble1 << 4 | nibble2; + streamMask &= ~STREAMTYPE_SUBTITLES; } - } else { - memset(aes_ivec, 0, sizeof(aes_ivec)); - aes_ivec[15] = mSeqNumber & 0xff; - aes_ivec[14] = (mSeqNumber >> 8) & 0xff; - aes_ivec[13] = (mSeqNumber >> 16) & 0xff; - aes_ivec[12] = (mSeqNumber >> 24) & 0xff; + + fetcher->startAsync(audioSource, videoSource, subtitleSource, timeUs); } - AES_cbc_encrypt( - buffer->data(), buffer->data(), buffer->size(), - &aes_key, aes_ivec, AES_DECRYPT); + // All fetchers have now been started, the configuration change + // has completed. - // hexdump(buffer->data(), buffer->size()); + scheduleCheckBandwidthEvent(); - size_t n = buffer->size(); - CHECK_GT(n, 0u); + ALOGV("XXX configuration change completed."); - size_t pad = buffer->data()[n - 1]; + mReconfigurationInProgress = false; - CHECK_GT(pad, 0u); - CHECK_LE(pad, 16u); - CHECK_GE((size_t)n, pad); - for (size_t i = 0; i < pad; ++i) { - CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad); + if (mDisconnectReplyID != 0) { + finishDisconnect(); } +} - n -= pad; - - buffer->setRange(buffer->offset(), n); - - return OK; +void LiveSession::scheduleCheckBandwidthEvent() { + sp msg = new AMessage(kWhatCheckBandwidth, id()); + msg->setInt32("generation", mCheckBandwidthGeneration); + msg->post(10000000ll); } -void LiveSession::postMonitorQueue(int64_t delayUs) { - sp msg = new AMessage(kWhatMonitorQueue, id()); - msg->setInt32("generation", ++mMonitorQueueGeneration); - msg->post(delayUs); +void LiveSession::cancelCheckBandwidthEvent() { + ++mCheckBandwidthGeneration; } -void LiveSession::onSeek(const sp &msg) { - int64_t timeUs; - CHECK(msg->findInt64("timeUs", &timeUs)); +void LiveSession::onCheckBandwidth() { + if (mReconfigurationInProgress) { + scheduleCheckBandwidthEvent(); + return; + } + + size_t bandwidthIndex = getBandwidthIndex(); + if (mPrevBandwidthIndex < 0 + || bandwidthIndex != (size_t)mPrevBandwidthIndex) { + changeConfiguration(-1ll /* timeUs */, bandwidthIndex); + } - mSeekTimeUs = timeUs; - postMonitorQueue(); + // Handling the kWhatCheckBandwidth even here does _not_ automatically + // schedule another one on return, only an explicit call to + // scheduleCheckBandwidthEvent will do that. + // This ensures that only one configuration change is ongoing at any + // one time, once that completes it'll schedule another check bandwidth + // event. } -status_t LiveSession::getDuration(int64_t *durationUs) const { - Mutex::Autolock autoLock(mLock); - *durationUs = mDurationUs; +void LiveSession::postPrepared(status_t err) { + CHECK(mInPreparationPhase); - return OK; -} + sp notify = mNotify->dup(); + if (err == OK || err == ERROR_END_OF_STREAM) { + notify->setInt32("what", kWhatPrepared); + } else { + notify->setInt32("what", kWhatPreparationFailed); + notify->setInt32("err", err); + } -bool LiveSession::isSeekable() const { - int64_t durationUs; - return getDuration(&durationUs) == OK && durationUs >= 0; -} + notify->post(); -bool LiveSession::hasDynamicDuration() const { - return !mDurationFixed; + mInPreparationPhase = false; } } // namespace android diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h new file mode 100644 index 0000000..b134725 --- /dev/null +++ b/media/libstagefright/httplive/LiveSession.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIVE_SESSION_H_ + +#define LIVE_SESSION_H_ + +#include + +#include + +namespace android { + +struct ABuffer; +struct AnotherPacketSource; +struct DataSource; +struct HTTPBase; +struct LiveDataSource; +struct M3UParser; +struct PlaylistFetcher; + +struct LiveSession : public AHandler { + enum Flags { + // Don't log any URLs. + kFlagIncognito = 1, + }; + LiveSession( + const sp ¬ify, + uint32_t flags = 0, bool uidValid = false, uid_t uid = 0); + + enum StreamType { + STREAMTYPE_AUDIO = 1, + STREAMTYPE_VIDEO = 2, + STREAMTYPE_SUBTITLES = 4, + }; + status_t dequeueAccessUnit(StreamType stream, sp *accessUnit); + + status_t getStreamFormat(StreamType stream, sp *format); + + void connectAsync( + const char *url, + const KeyedVector *headers = NULL); + + status_t disconnect(); + + // Blocks until seek is complete. + status_t seekTo(int64_t timeUs); + + status_t getDuration(int64_t *durationUs) const; + + bool isSeekable() const; + bool hasDynamicDuration() const; + + enum { + kWhatStreamsChanged, + kWhatError, + kWhatPrepared, + kWhatPreparationFailed, + }; + +protected: + virtual ~LiveSession(); + + virtual void onMessageReceived(const sp &msg); + +private: + friend struct PlaylistFetcher; + + enum { + kWhatConnect = 'conn', + kWhatDisconnect = 'disc', + kWhatSeek = 'seek', + kWhatFetcherNotify = 'notf', + kWhatCheckBandwidth = 'bndw', + kWhatChangeConfiguration2 = 'chC2', + kWhatChangeConfiguration3 = 'chC3', + kWhatFinishDisconnect2 = 'fin2', + }; + + struct BandwidthItem { + size_t mPlaylistIndex; + unsigned long mBandwidth; + }; + + struct FetcherInfo { + sp mFetcher; + int64_t mDurationUs; + bool mIsPrepared; + }; + + sp mNotify; + uint32_t mFlags; + bool mUIDValid; + uid_t mUID; + + bool mInPreparationPhase; + + sp mHTTPDataSource; + KeyedVector mExtraHeaders; + + AString mMasterURL; + + Vector mBandwidthItems; + ssize_t mPrevBandwidthIndex; + + sp mPlaylist; + + KeyedVector mFetcherInfos; + AString mAudioURI, mVideoURI, mSubtitleURI; + uint32_t mStreamMask; + + KeyedVector > mPacketSources; + + int32_t mCheckBandwidthGeneration; + + size_t mContinuationCounter; + sp mContinuation; + + int64_t mLastDequeuedTimeUs; + + bool mReconfigurationInProgress; + uint32_t mDisconnectReplyID; + + sp addFetcher(const char *uri); + + void onConnect(const sp &msg); + status_t onSeek(const sp &msg); + void onFinishDisconnect2(); + + status_t fetchFile( + const char *url, sp *out, + int64_t range_offset = 0, int64_t range_length = -1); + + sp fetchPlaylist( + const char *url, uint8_t *curPlaylistHash, bool *unchanged); + + size_t getBandwidthIndex(); + + static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); + + void changeConfiguration(int64_t timeUs, size_t bandwidthIndex); + void onChangeConfiguration2(const sp &msg); + void onChangeConfiguration3(const sp &msg); + + void scheduleCheckBandwidthEvent(); + void cancelCheckBandwidthEvent(); + + void onCheckBandwidth(); + + void finishDisconnect(); + + void postPrepared(status_t err); + + DISALLOW_EVIL_CONSTRUCTORS(LiveSession); +}; + +} // namespace android + +#endif // LIVE_SESSION_H_ diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 68bbca2..be66252 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -18,14 +18,153 @@ #define LOG_TAG "M3UParser" #include -#include "include/M3UParser.h" +#include "M3UParser.h" +#include #include #include #include namespace android { +struct M3UParser::MediaGroup : public RefBase { + enum Type { + TYPE_AUDIO, + TYPE_VIDEO, + TYPE_SUBS, + }; + + enum FlagBits { + FLAG_AUTOSELECT = 1, + FLAG_DEFAULT = 2, + FLAG_FORCED = 4, + FLAG_HAS_LANGUAGE = 8, + FLAG_HAS_URI = 16, + }; + + MediaGroup(Type type); + + Type type() const; + + status_t addMedia( + const char *name, + const char *uri, + const char *language, + uint32_t flags); + + bool getActiveURI(AString *uri) const; + + void pickRandomMediaItems(); + +protected: + virtual ~MediaGroup(); + +private: + struct Media { + AString mName; + AString mURI; + AString mLanguage; + uint32_t mFlags; + }; + + Type mType; + Vector mMediaItems; + + ssize_t mSelectedIndex; + + DISALLOW_EVIL_CONSTRUCTORS(MediaGroup); +}; + +M3UParser::MediaGroup::MediaGroup(Type type) + : mType(type), + mSelectedIndex(-1) { +} + +M3UParser::MediaGroup::~MediaGroup() { +} + +M3UParser::MediaGroup::Type M3UParser::MediaGroup::type() const { + return mType; +} + +status_t M3UParser::MediaGroup::addMedia( + const char *name, + const char *uri, + const char *language, + uint32_t flags) { + mMediaItems.push(); + Media &item = mMediaItems.editItemAt(mMediaItems.size() - 1); + + item.mName = name; + + if (uri) { + item.mURI = uri; + } + + if (language) { + item.mLanguage = language; + } + + item.mFlags = flags; + + return OK; +} + +void M3UParser::MediaGroup::pickRandomMediaItems() { +#if 1 + switch (mType) { + case TYPE_AUDIO: + { + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.httplive.audio-index", value, NULL)) { + char *end; + mSelectedIndex = strtoul(value, &end, 10); + CHECK(end > value && *end == '\0'); + + if (mSelectedIndex >= mMediaItems.size()) { + mSelectedIndex = mMediaItems.size() - 1; + } + } else { + mSelectedIndex = 0; + } + break; + } + + case TYPE_VIDEO: + { + mSelectedIndex = 0; + break; + } + + case TYPE_SUBS: + { + mSelectedIndex = -1; + break; + } + + default: + TRESPASS(); + } +#else + mSelectedIndex = (rand() * mMediaItems.size()) / RAND_MAX; +#endif +} + +bool M3UParser::MediaGroup::getActiveURI(AString *uri) const { + for (size_t i = 0; i < mMediaItems.size(); ++i) { + if (mSelectedIndex >= 0 && i == (size_t)mSelectedIndex) { + const Media &item = mMediaItems.itemAt(i); + + *uri = item.mURI; + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// + M3UParser::M3UParser( const char *baseURI, const void *data, size_t size) : mInitCheck(NO_INIT), @@ -92,6 +231,58 @@ bool M3UParser::itemAt(size_t index, AString *uri, sp *meta) { return true; } +void M3UParser::pickRandomMediaItems() { + for (size_t i = 0; i < mMediaGroups.size(); ++i) { + mMediaGroups.valueAt(i)->pickRandomMediaItems(); + } +} + +bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const { + if (!mIsVariantPlaylist) { + *uri = mBaseURI; + + // Assume media without any more specific attribute contains + // audio and video, but no subtitles. + return !strcmp("audio", key) || !strcmp("video", key); + } + + CHECK_LT(index, mItems.size()); + + sp meta = mItems.itemAt(index).mMeta; + + AString groupID; + if (!meta->findString(key, &groupID)) { + *uri = mItems.itemAt(index).mURI; + + // Assume media without any more specific attribute contains + // audio and video, but no subtitles. + return !strcmp("audio", key) || !strcmp("video", key); + } + + sp group = mMediaGroups.valueFor(groupID); + if (!group->getActiveURI(uri)) { + return false; + } + + if ((*uri).empty()) { + *uri = mItems.itemAt(index).mURI; + } + + return true; +} + +bool M3UParser::getAudioURI(size_t index, AString *uri) const { + return getTypeURI(index, "audio", uri); +} + +bool M3UParser::getVideoURI(size_t index, AString *uri) const { + return getTypeURI(index, "video", uri); +} + +bool M3UParser::getSubtitleURI(size_t index, AString *uri) const { + return getTypeURI(index, "subtitles", uri); +} + static bool MakeURL(const char *baseURL, const char *url, AString *out) { out->clear(); @@ -241,6 +432,8 @@ status_t M3UParser::parse(const void *_data, size_t size) { segmentRangeOffset = offset + length; } + } else if (line.startsWith("#EXT-X-MEDIA")) { + err = parseMedia(line); } if (err != OK) { @@ -322,9 +515,31 @@ status_t M3UParser::parseMetaDataDuration( return OK; } -// static +// Find the next occurence of the character "what" at or after "offset", +// but ignore occurences between quotation marks. +// Return the index of the occurrence or -1 if not found. +static ssize_t FindNextUnquoted( + const AString &line, char what, size_t offset) { + CHECK_NE((int)what, (int)'"'); + + bool quoted = false; + while (offset < line.size()) { + char c = line.c_str()[offset]; + + if (c == '"') { + quoted = !quoted; + } else if (c == what && !quoted) { + return offset; + } + + ++offset; + } + + return -1; +} + status_t M3UParser::parseStreamInf( - const AString &line, sp *meta) { + const AString &line, sp *meta) const { ssize_t colonPos = line.find(":"); if (colonPos < 0) { @@ -334,7 +549,7 @@ status_t M3UParser::parseStreamInf( size_t offset = colonPos + 1; while (offset < line.size()) { - ssize_t end = line.find(",", offset); + ssize_t end = FindNextUnquoted(line, ',', offset); if (end < 0) { end = line.size(); } @@ -371,33 +586,35 @@ status_t M3UParser::parseStreamInf( *meta = new AMessage; } (*meta)->setInt32("bandwidth", x); - } - } + } else if (!strcasecmp("audio", key.c_str()) + || !strcasecmp("video", key.c_str()) + || !strcasecmp("subtitles", key.c_str())) { + if (val.size() < 2 + || val.c_str()[0] != '"' + || val.c_str()[val.size() - 1] != '"') { + ALOGE("Expected quoted string for %s attribute, " + "got '%s' instead.", + key.c_str(), val.c_str()); + + return ERROR_MALFORMED; + } - return OK; -} + AString groupID(val, 1, val.size() - 2); + ssize_t groupIndex = mMediaGroups.indexOfKey(groupID); -// Find the next occurence of the character "what" at or after "offset", -// but ignore occurences between quotation marks. -// Return the index of the occurrence or -1 if not found. -static ssize_t FindNextUnquoted( - const AString &line, char what, size_t offset) { - CHECK_NE((int)what, (int)'"'); + if (groupIndex < 0) { + ALOGE("Undefined media group '%s' referenced in stream info.", + groupID.c_str()); - bool quoted = false; - while (offset < line.size()) { - char c = line.c_str()[offset]; + return ERROR_MALFORMED; + } - if (c == '"') { - quoted = !quoted; - } else if (c == what && !quoted) { - return offset; + key.tolower(); + (*meta)->setString(key.c_str(), groupID.c_str()); } - - ++offset; } - return -1; + return OK; } // static @@ -515,6 +732,234 @@ status_t M3UParser::parseByteRange( return OK; } +status_t M3UParser::parseMedia(const AString &line) { + ssize_t colonPos = line.find(":"); + + if (colonPos < 0) { + return ERROR_MALFORMED; + } + + bool haveGroupType = false; + MediaGroup::Type groupType = MediaGroup::TYPE_AUDIO; + + bool haveGroupID = false; + AString groupID; + + bool haveGroupLanguage = false; + AString groupLanguage; + + bool haveGroupName = false; + AString groupName; + + bool haveGroupAutoselect = false; + bool groupAutoselect = false; + + bool haveGroupDefault = false; + bool groupDefault = false; + + bool haveGroupForced = false; + bool groupForced = false; + + bool haveGroupURI = false; + AString groupURI; + + size_t offset = colonPos + 1; + + while (offset < line.size()) { + ssize_t end = FindNextUnquoted(line, ',', offset); + if (end < 0) { + end = line.size(); + } + + AString attr(line, offset, end - offset); + attr.trim(); + + offset = end + 1; + + ssize_t equalPos = attr.find("="); + if (equalPos < 0) { + continue; + } + + AString key(attr, 0, equalPos); + key.trim(); + + AString val(attr, equalPos + 1, attr.size() - equalPos - 1); + val.trim(); + + ALOGV("key=%s value=%s", key.c_str(), val.c_str()); + + if (!strcasecmp("type", key.c_str())) { + if (!strcasecmp("subtitles", val.c_str())) { + groupType = MediaGroup::TYPE_SUBS; + } else if (!strcasecmp("audio", val.c_str())) { + groupType = MediaGroup::TYPE_AUDIO; + } else if (!strcasecmp("video", val.c_str())) { + groupType = MediaGroup::TYPE_VIDEO; + } else { + ALOGE("Invalid media group type '%s'", val.c_str()); + return ERROR_MALFORMED; + } + + haveGroupType = true; + } else if (!strcasecmp("group-id", key.c_str())) { + if (val.size() < 2 + || val.c_str()[0] != '"' + || val.c_str()[val.size() - 1] != '"') { + ALOGE("Expected quoted string for GROUP-ID, got '%s' instead.", + val.c_str()); + + return ERROR_MALFORMED; + } + + groupID.setTo(val, 1, val.size() - 2); + haveGroupID = true; + } else if (!strcasecmp("language", key.c_str())) { + if (val.size() < 2 + || val.c_str()[0] != '"' + || val.c_str()[val.size() - 1] != '"') { + ALOGE("Expected quoted string for LANGUAGE, got '%s' instead.", + val.c_str()); + + return ERROR_MALFORMED; + } + + groupLanguage.setTo(val, 1, val.size() - 2); + haveGroupLanguage = true; + } else if (!strcasecmp("name", key.c_str())) { + if (val.size() < 2 + || val.c_str()[0] != '"' + || val.c_str()[val.size() - 1] != '"') { + ALOGE("Expected quoted string for NAME, got '%s' instead.", + val.c_str()); + + return ERROR_MALFORMED; + } + + groupName.setTo(val, 1, val.size() - 2); + haveGroupName = true; + } else if (!strcasecmp("autoselect", key.c_str())) { + groupAutoselect = false; + if (!strcasecmp("YES", val.c_str())) { + groupAutoselect = true; + } else if (!strcasecmp("NO", val.c_str())) { + groupAutoselect = false; + } else { + ALOGE("Expected YES or NO for AUTOSELECT attribute, " + "got '%s' instead.", + val.c_str()); + + return ERROR_MALFORMED; + } + + haveGroupAutoselect = true; + } else if (!strcasecmp("default", key.c_str())) { + groupDefault = false; + if (!strcasecmp("YES", val.c_str())) { + groupDefault = true; + } else if (!strcasecmp("NO", val.c_str())) { + groupDefault = false; + } else { + ALOGE("Expected YES or NO for DEFAULT attribute, " + "got '%s' instead.", + val.c_str()); + + return ERROR_MALFORMED; + } + + haveGroupDefault = true; + } else if (!strcasecmp("forced", key.c_str())) { + groupForced = false; + if (!strcasecmp("YES", val.c_str())) { + groupForced = true; + } else if (!strcasecmp("NO", val.c_str())) { + groupForced = false; + } else { + ALOGE("Expected YES or NO for FORCED attribute, " + "got '%s' instead.", + val.c_str()); + + return ERROR_MALFORMED; + } + + haveGroupForced = true; + } else if (!strcasecmp("uri", key.c_str())) { + if (val.size() < 2 + || val.c_str()[0] != '"' + || val.c_str()[val.size() - 1] != '"') { + ALOGE("Expected quoted string for URI, got '%s' instead.", + val.c_str()); + + return ERROR_MALFORMED; + } + + AString tmp(val, 1, val.size() - 2); + + if (!MakeURL(mBaseURI.c_str(), tmp.c_str(), &groupURI)) { + ALOGI("Failed to make absolute URI from '%s'.", tmp.c_str()); + } + + haveGroupURI = true; + } + } + + if (!haveGroupType || !haveGroupID || !haveGroupName) { + ALOGE("Incomplete EXT-X-MEDIA element."); + return ERROR_MALFORMED; + } + + uint32_t flags = 0; + if (haveGroupAutoselect && groupAutoselect) { + flags |= MediaGroup::FLAG_AUTOSELECT; + } + if (haveGroupDefault && groupDefault) { + flags |= MediaGroup::FLAG_DEFAULT; + } + if (haveGroupForced) { + if (groupType != MediaGroup::TYPE_SUBS) { + ALOGE("The FORCED attribute MUST not be present on anything " + "but SUBS media."); + + return ERROR_MALFORMED; + } + + if (groupForced) { + flags |= MediaGroup::FLAG_FORCED; + } + } + if (haveGroupLanguage) { + flags |= MediaGroup::FLAG_HAS_LANGUAGE; + } + if (haveGroupURI) { + flags |= MediaGroup::FLAG_HAS_URI; + } + + ssize_t groupIndex = mMediaGroups.indexOfKey(groupID); + sp group; + + if (groupIndex < 0) { + group = new MediaGroup(groupType); + mMediaGroups.add(groupID, group); + } else { + group = mMediaGroups.valueAt(groupIndex); + + if (group->type() != groupType) { + ALOGE("Attempt to put media item under group of different type " + "(groupType = %d, item type = %d", + group->type(), + groupType); + + return ERROR_MALFORMED; + } + } + + return group->addMedia( + groupName.c_str(), + haveGroupURI ? groupURI.c_str() : NULL, + haveGroupLanguage ? groupLanguage.c_str() : NULL, + flags); +} + // static status_t M3UParser::ParseInt32(const char *s, int32_t *x) { char *end; diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h new file mode 100644 index 0000000..abea286 --- /dev/null +++ b/media/libstagefright/httplive/M3UParser.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef M3U_PARSER_H_ + +#define M3U_PARSER_H_ + +#include +#include +#include +#include + +namespace android { + +struct M3UParser : public RefBase { + M3UParser(const char *baseURI, const void *data, size_t size); + + status_t initCheck() const; + + bool isExtM3U() const; + bool isVariantPlaylist() const; + bool isComplete() const; + bool isEvent() const; + + sp meta(); + + size_t size(); + bool itemAt(size_t index, AString *uri, sp *meta = NULL); + + void pickRandomMediaItems(); + + bool getAudioURI(size_t index, AString *uri) const; + bool getVideoURI(size_t index, AString *uri) const; + bool getSubtitleURI(size_t index, AString *uri) const; + +protected: + virtual ~M3UParser(); + +private: + struct MediaGroup; + + struct Item { + AString mURI; + sp mMeta; + }; + + status_t mInitCheck; + + AString mBaseURI; + bool mIsExtM3U; + bool mIsVariantPlaylist; + bool mIsComplete; + bool mIsEvent; + + sp mMeta; + Vector mItems; + + // Media groups keyed by group ID. + KeyedVector > mMediaGroups; + + status_t parse(const void *data, size_t size); + + static status_t parseMetaData( + const AString &line, sp *meta, const char *key); + + static status_t parseMetaDataDuration( + const AString &line, sp *meta, const char *key); + + status_t parseStreamInf( + const AString &line, sp *meta) const; + + static status_t parseCipherInfo( + const AString &line, sp *meta, const AString &baseURI); + + static status_t parseByteRange( + const AString &line, uint64_t curOffset, + uint64_t *length, uint64_t *offset); + + status_t parseMedia(const AString &line); + + bool getTypeURI(size_t index, const char *key, AString *uri) const; + + static status_t ParseInt32(const char *s, int32_t *x); + static status_t ParseDouble(const char *s, double *x); + + DISALLOW_EVIL_CONSTRUCTORS(M3UParser); +}; + +} // namespace android + +#endif // M3U_PARSER_H_ diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp new file mode 100644 index 0000000..8ae70b7 --- /dev/null +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -0,0 +1,969 @@ +/* + * 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 "PlaylistFetcher" +#include + +#include "PlaylistFetcher.h" + +#include "LiveDataSource.h" +#include "LiveSession.h" +#include "M3UParser.h" + +#include "include/avc_utils.h" +#include "include/HTTPBase.h" +#include "include/ID3.h" +#include "mpeg2ts/AnotherPacketSource.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace android { + +// static +const int64_t PlaylistFetcher::kMinBufferedDurationUs = 10000000ll; + +PlaylistFetcher::PlaylistFetcher( + const sp ¬ify, + const sp &session, + const char *uri) + : mNotify(notify), + mSession(session), + mURI(uri), + mStreamTypeMask(0), + mStartTimeUs(-1ll), + mLastPlaylistFetchTimeUs(-1ll), + mSeqNumber(-1), + mNumRetries(0), + mStartup(true), + mNextPTSTimeUs(-1ll), + mMonitorQueueGeneration(0), + mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY), + mFirstPTSValid(false), + mAbsoluteTimeAnchorUs(0ll) { + memset(mPlaylistHash, 0, sizeof(mPlaylistHash)); +} + +PlaylistFetcher::~PlaylistFetcher() { +} + +int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const { + CHECK(mPlaylist != NULL); + + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } + + int32_t lastSeqNumberInPlaylist = + firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + + CHECK_GE(seqNumber, firstSeqNumberInPlaylist); + CHECK_LE(seqNumber, lastSeqNumberInPlaylist); + + int64_t segmentStartUs = 0ll; + for (int32_t index = 0; + index < seqNumber - firstSeqNumberInPlaylist; ++index) { + sp itemMeta; + CHECK(mPlaylist->itemAt( + index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + segmentStartUs += itemDurationUs; + } + + return segmentStartUs; +} + +bool PlaylistFetcher::timeToRefreshPlaylist(int64_t nowUs) const { + if (mPlaylist == NULL) { + CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY); + return true; + } + + int32_t targetDurationSecs; + CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); + + int64_t targetDurationUs = targetDurationSecs * 1000000ll; + + int64_t minPlaylistAgeUs; + + switch (mRefreshState) { + case INITIAL_MINIMUM_RELOAD_DELAY: + { + size_t n = mPlaylist->size(); + if (n > 0) { + sp itemMeta; + CHECK(mPlaylist->itemAt(n - 1, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + minPlaylistAgeUs = itemDurationUs; + break; + } + + // fall through + } + + case FIRST_UNCHANGED_RELOAD_ATTEMPT: + { + minPlaylistAgeUs = targetDurationUs / 2; + break; + } + + case SECOND_UNCHANGED_RELOAD_ATTEMPT: + { + minPlaylistAgeUs = (targetDurationUs * 3) / 2; + break; + } + + case THIRD_UNCHANGED_RELOAD_ATTEMPT: + { + minPlaylistAgeUs = targetDurationUs * 3; + break; + } + + default: + TRESPASS(); + break; + } + + return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs; +} + +status_t PlaylistFetcher::decryptBuffer( + size_t playlistIndex, const sp &buffer) { + sp itemMeta; + bool found = false; + AString method; + + for (ssize_t i = playlistIndex; i >= 0; --i) { + AString uri; + CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); + + if (itemMeta->findString("cipher-method", &method)) { + found = true; + break; + } + } + + if (!found) { + method = "NONE"; + } + + if (method == "NONE") { + return OK; + } else if (!(method == "AES-128")) { + ALOGE("Unsupported cipher method '%s'", method.c_str()); + return ERROR_UNSUPPORTED; + } + + AString keyURI; + if (!itemMeta->findString("cipher-uri", &keyURI)) { + ALOGE("Missing key uri"); + return ERROR_MALFORMED; + } + + ssize_t index = mAESKeyForURI.indexOfKey(keyURI); + + sp key; + if (index >= 0) { + key = mAESKeyForURI.valueAt(index); + } else { + status_t err = mSession->fetchFile(keyURI.c_str(), &key); + + if (err != OK) { + ALOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); + return ERROR_IO; + } else if (key->size() != 16) { + ALOGE("key file '%s' wasn't 16 bytes in size.", keyURI.c_str()); + return ERROR_MALFORMED; + } + + mAESKeyForURI.add(keyURI, key); + } + + AES_KEY aes_key; + if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { + ALOGE("failed to set AES decryption key."); + return UNKNOWN_ERROR; + } + + unsigned char aes_ivec[16]; + + AString iv; + if (itemMeta->findString("cipher-iv", &iv)) { + if ((!iv.startsWith("0x") && !iv.startsWith("0X")) + || iv.size() != 16 * 2 + 2) { + ALOGE("malformed cipher IV '%s'.", iv.c_str()); + return ERROR_MALFORMED; + } + + memset(aes_ivec, 0, sizeof(aes_ivec)); + for (size_t i = 0; i < 16; ++i) { + char c1 = tolower(iv.c_str()[2 + 2 * i]); + char c2 = tolower(iv.c_str()[3 + 2 * i]); + if (!isxdigit(c1) || !isxdigit(c2)) { + ALOGE("malformed cipher IV '%s'.", iv.c_str()); + return ERROR_MALFORMED; + } + uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; + uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; + + aes_ivec[i] = nibble1 << 4 | nibble2; + } + } else { + memset(aes_ivec, 0, sizeof(aes_ivec)); + aes_ivec[15] = mSeqNumber & 0xff; + aes_ivec[14] = (mSeqNumber >> 8) & 0xff; + aes_ivec[13] = (mSeqNumber >> 16) & 0xff; + aes_ivec[12] = (mSeqNumber >> 24) & 0xff; + } + + AES_cbc_encrypt( + buffer->data(), buffer->data(), buffer->size(), + &aes_key, aes_ivec, AES_DECRYPT); + + // hexdump(buffer->data(), buffer->size()); + + size_t n = buffer->size(); + CHECK_GT(n, 0u); + + size_t pad = buffer->data()[n - 1]; + + CHECK_GT(pad, 0u); + CHECK_LE(pad, 16u); + CHECK_GE((size_t)n, pad); + for (size_t i = 0; i < pad; ++i) { + CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad); + } + + n -= pad; + + buffer->setRange(buffer->offset(), n); + + return OK; +} + +void PlaylistFetcher::postMonitorQueue(int64_t delayUs) { + sp msg = new AMessage(kWhatMonitorQueue, id()); + msg->setInt32("generation", mMonitorQueueGeneration); + msg->post(delayUs); +} + +void PlaylistFetcher::cancelMonitorQueue() { + ++mMonitorQueueGeneration; +} + +void PlaylistFetcher::startAsync( + const sp &audioSource, + const sp &videoSource, + const sp &subtitleSource, + int64_t startTimeUs) { + sp msg = new AMessage(kWhatStart, id()); + + uint32_t streamTypeMask = 0ul; + + if (audioSource != NULL) { + msg->setPointer("audioSource", audioSource.get()); + streamTypeMask |= LiveSession::STREAMTYPE_AUDIO; + } + + if (videoSource != NULL) { + msg->setPointer("videoSource", videoSource.get()); + streamTypeMask |= LiveSession::STREAMTYPE_VIDEO; + } + + if (subtitleSource != NULL) { + msg->setPointer("subtitleSource", subtitleSource.get()); + streamTypeMask |= LiveSession::STREAMTYPE_SUBTITLES; + } + + msg->setInt32("streamTypeMask", streamTypeMask); + msg->setInt64("startTimeUs", startTimeUs); + msg->post(); +} + +void PlaylistFetcher::pauseAsync() { + (new AMessage(kWhatPause, id()))->post(); +} + +void PlaylistFetcher::stopAsync() { + (new AMessage(kWhatStop, id()))->post(); +} + +void PlaylistFetcher::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatStart: + { + status_t err = onStart(msg); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatStarted); + notify->setInt32("err", err); + notify->post(); + break; + } + + case kWhatPause: + { + onPause(); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatPaused); + notify->post(); + break; + } + + case kWhatStop: + { + onStop(); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatStopped); + notify->post(); + break; + } + + case kWhatMonitorQueue: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mMonitorQueueGeneration) { + // Stale event + break; + } + + onMonitorQueue(); + break; + } + + default: + TRESPASS(); + } +} + +status_t PlaylistFetcher::onStart(const sp &msg) { + mPacketSources.clear(); + + uint32_t streamTypeMask; + CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask)); + + int64_t startTimeUs; + CHECK(msg->findInt64("startTimeUs", &startTimeUs)); + + if (streamTypeMask & LiveSession::STREAMTYPE_AUDIO) { + void *ptr; + CHECK(msg->findPointer("audioSource", &ptr)); + + mPacketSources.add( + LiveSession::STREAMTYPE_AUDIO, + static_cast(ptr)); + } + + if (streamTypeMask & LiveSession::STREAMTYPE_VIDEO) { + void *ptr; + CHECK(msg->findPointer("videoSource", &ptr)); + + mPacketSources.add( + LiveSession::STREAMTYPE_VIDEO, + static_cast(ptr)); + } + + if (streamTypeMask & LiveSession::STREAMTYPE_SUBTITLES) { + void *ptr; + CHECK(msg->findPointer("subtitleSource", &ptr)); + + mPacketSources.add( + LiveSession::STREAMTYPE_SUBTITLES, + static_cast(ptr)); + } + + mStreamTypeMask = streamTypeMask; + mStartTimeUs = startTimeUs; + + if (mStartTimeUs >= 0ll) { + mSeqNumber = -1; + mStartup = true; + } + + postMonitorQueue(); + + return OK; +} + +void PlaylistFetcher::onPause() { + cancelMonitorQueue(); + + mPacketSources.clear(); + mStreamTypeMask = 0; +} + +void PlaylistFetcher::onStop() { + cancelMonitorQueue(); + + for (size_t i = 0; i < mPacketSources.size(); ++i) { + mPacketSources.valueAt(i)->clear(); + } + + mPacketSources.clear(); + mStreamTypeMask = 0; +} + +void PlaylistFetcher::notifyError(status_t err) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +void PlaylistFetcher::queueDiscontinuity( + ATSParser::DiscontinuityType type, const sp &extra) { + for (size_t i = 0; i < mPacketSources.size(); ++i) { + mPacketSources.valueAt(i)->queueDiscontinuity(type, extra); + } +} + +void PlaylistFetcher::onMonitorQueue() { + bool downloadMore = false; + + status_t finalResult; + if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) { + sp packetSource = + mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES); + + downloadMore = packetSource->hasBufferAvailable(&finalResult); + } else { + bool first = true; + int64_t minBufferedDurationUs = 0ll; + + for (size_t i = 0; i < mPacketSources.size(); ++i) { + if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0) { + continue; + } + + int64_t bufferedDurationUs = + mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult); + + if (first || bufferedDurationUs < minBufferedDurationUs) { + minBufferedDurationUs = bufferedDurationUs; + first = false; + } + } + + downloadMore = + !first && (minBufferedDurationUs < kMinBufferedDurationUs); + } + + if (finalResult == OK && downloadMore) { + onDownloadNext(); + } else { + // Nothing to do yet, try again in a second. + + sp msg = mNotify->dup(); + msg->setInt32("what", kWhatTemporarilyDoneFetching); + msg->post(); + + postMonitorQueue(1000000ll); + } +} + +void PlaylistFetcher::onDownloadNext() { + int64_t nowUs = ALooper::GetNowUs(); + + if (mLastPlaylistFetchTimeUs < 0ll + || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) { + bool unchanged; + sp playlist = mSession->fetchPlaylist( + mURI.c_str(), mPlaylistHash, &unchanged); + + if (playlist == NULL) { + if (unchanged) { + // We succeeded in fetching the playlist, but it was + // unchanged from the last time we tried. + + if (mRefreshState != THIRD_UNCHANGED_RELOAD_ATTEMPT) { + mRefreshState = (RefreshState)(mRefreshState + 1); + } + } else { + ALOGE("failed to load playlist at url '%s'", mURI.c_str()); + notifyError(ERROR_IO); + return; + } + } else { + mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY; + mPlaylist = playlist; + + if (mPlaylist->isComplete() || mPlaylist->isEvent()) { + updateDuration(); + } + } + + mLastPlaylistFetchTimeUs = ALooper::GetNowUs(); + } + + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } + + bool seekDiscontinuity = false; + bool explicitDiscontinuity = false; + + const int32_t lastSeqNumberInPlaylist = + firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + + if (mSeqNumber < 0) { + CHECK_GE(mStartTimeUs, 0ll); + + if (mPlaylist->isComplete() || mPlaylist->isEvent()) { + mSeqNumber = getSeqNumberForTime(mStartTimeUs); + } else { + // If this is a live session, start 3 segments from the end. + mSeqNumber = lastSeqNumberInPlaylist - 3; + if (mSeqNumber < firstSeqNumberInPlaylist) { + mSeqNumber = firstSeqNumberInPlaylist; + } + } + + mStartTimeUs = -1ll; + } + + if (mSeqNumber < firstSeqNumberInPlaylist + || mSeqNumber > lastSeqNumberInPlaylist) { + if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) { + ++mNumRetries; + + if (mSeqNumber > lastSeqNumberInPlaylist) { + mLastPlaylistFetchTimeUs = -1; + postMonitorQueue(3000000ll); + return; + } + + // we've missed the boat, let's start from the lowest sequence + // number available and signal a discontinuity. + + ALOGI("We've missed the boat, restarting playback."); + mSeqNumber = lastSeqNumberInPlaylist; + explicitDiscontinuity = true; + + // fall through + } else { + ALOGE("Cannot find sequence number %d in playlist " + "(contains %d - %d)", + mSeqNumber, firstSeqNumberInPlaylist, + firstSeqNumberInPlaylist + mPlaylist->size() - 1); + + notifyError(ERROR_END_OF_STREAM); + return; + } + } + + mNumRetries = 0; + + AString uri; + sp itemMeta; + CHECK(mPlaylist->itemAt( + mSeqNumber - firstSeqNumberInPlaylist, + &uri, + &itemMeta)); + + int32_t val; + if (itemMeta->findInt32("discontinuity", &val) && val != 0) { + explicitDiscontinuity = true; + } + + int64_t range_offset, range_length; + if (!itemMeta->findInt64("range-offset", &range_offset) + || !itemMeta->findInt64("range-length", &range_length)) { + range_offset = 0; + range_length = -1; + } + + ALOGV("fetching segment %d from (%d .. %d)", + mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); + + ALOGV("fetching '%s'", uri.c_str()); + + sp buffer; + status_t err = mSession->fetchFile( + uri.c_str(), &buffer, range_offset, range_length); + + if (err != OK) { + ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); + notifyError(err); + return; + } + + CHECK(buffer != NULL); + + err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer); + + if (err != OK) { + ALOGE("decryptBuffer failed w/ error %d", err); + + notifyError(err); + return; + } + + if (mStartup || seekDiscontinuity || explicitDiscontinuity) { + // Signal discontinuity. + + if (mPlaylist->isComplete() || mPlaylist->isEvent()) { + // If this was a live event this made no sense since + // we don't have access to all the segment before the current + // one. + mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber); + } + + if (seekDiscontinuity || explicitDiscontinuity) { + ALOGI("queueing discontinuity (seek=%d, explicit=%d)", + seekDiscontinuity, explicitDiscontinuity); + + queueDiscontinuity( + explicitDiscontinuity + ? ATSParser::DISCONTINUITY_FORMATCHANGE + : ATSParser::DISCONTINUITY_SEEK, + NULL /* extra */); + } + } + + err = extractAndQueueAccessUnits(buffer); + + if (err != OK) { + notifyError(err); + return; + } + + ++mSeqNumber; + + postMonitorQueue(); + + mStartup = false; +} + +int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const { + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } + + size_t index = 0; + int64_t segmentStartUs = 0; + while (index < mPlaylist->size()) { + sp itemMeta; + CHECK(mPlaylist->itemAt( + index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + if (timeUs < segmentStartUs + itemDurationUs) { + break; + } + + segmentStartUs += itemDurationUs; + ++index; + } + + if (index >= mPlaylist->size()) { + index = mPlaylist->size() - 1; + } + + return firstSeqNumberInPlaylist + index; +} + +status_t PlaylistFetcher::extractAndQueueAccessUnits( + const sp &buffer) { + if (buffer->size() > 0 && buffer->data()[0] == 0x47) { + // Let's assume this is an MPEG2 transport stream. + + if ((buffer->size() % 188) != 0) { + ALOGE("MPEG2 transport stream is not an even multiple of 188 " + "bytes in length."); + return ERROR_MALFORMED; + } + + if (mTSParser == NULL) { + mTSParser = new ATSParser; + } + + if (mNextPTSTimeUs >= 0ll) { + sp extra = new AMessage; + extra->setInt64(IStreamListener::kKeyMediaTimeUs, mNextPTSTimeUs); + + mTSParser->signalDiscontinuity( + ATSParser::DISCONTINUITY_SEEK, extra); + + mNextPTSTimeUs = -1ll; + } + + size_t offset = 0; + while (offset < buffer->size()) { + status_t err = mTSParser->feedTSPacket(buffer->data() + offset, 188); + + if (err != OK) { + return err; + } + + offset += 188; + } + + for (size_t i = mPacketSources.size(); i-- > 0;) { + sp packetSource = mPacketSources.valueAt(i); + + ATSParser::SourceType type; + switch (mPacketSources.keyAt(i)) { + case LiveSession::STREAMTYPE_VIDEO: + type = ATSParser::VIDEO; + break; + + case LiveSession::STREAMTYPE_AUDIO: + type = ATSParser::AUDIO; + break; + + case LiveSession::STREAMTYPE_SUBTITLES: + { + ALOGE("MPEG2 Transport streams do not contain subtitles."); + return ERROR_MALFORMED; + break; + } + + default: + TRESPASS(); + } + + sp source = + static_cast( + mTSParser->getSource(type).get()); + + if (source == NULL) { + ALOGW("MPEG2 Transport stream does not contain %s data.", + type == ATSParser::VIDEO ? "video" : "audio"); + + mStreamTypeMask &= ~mPacketSources.keyAt(i); + mPacketSources.removeItemsAt(i); + continue; + } + + sp accessUnit; + status_t finalResult; + while (source->hasBufferAvailable(&finalResult) + && source->dequeueAccessUnit(&accessUnit) == OK) { + // Note that we do NOT dequeue any discontinuities. + + packetSource->queueAccessUnit(accessUnit); + } + + if (packetSource->getFormat() == NULL) { + packetSource->setFormat(source->getFormat()); + } + } + + return OK; + } else if (buffer->size() >= 7 && !memcmp("WEBVTT\n", buffer->data(), 7)) { + if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES) { + ALOGE("This stream only contains subtitles."); + return ERROR_MALFORMED; + } + + const sp packetSource = + mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES); + + buffer->meta()->setInt64("timeUs", 0ll); + + packetSource->queueAccessUnit(buffer); + return OK; + } + + if (mNextPTSTimeUs >= 0ll) { + mFirstPTSValid = false; + mAbsoluteTimeAnchorUs = mNextPTSTimeUs; + mNextPTSTimeUs = -1ll; + } + + // This better be an ISO 13818-7 (AAC) or ISO 13818-1 (MPEG) audio + // stream prefixed by an ID3 tag. + + bool firstID3Tag = true; + uint64_t PTS = 0; + + for (;;) { + // Make sure to skip all ID3 tags preceding the audio data. + // At least one must be present to provide the PTS timestamp. + + ID3 id3(buffer->data(), buffer->size(), true /* ignoreV1 */); + if (!id3.isValid()) { + if (firstID3Tag) { + ALOGE("Unable to parse ID3 tag."); + return ERROR_MALFORMED; + } else { + break; + } + } + + if (firstID3Tag) { + bool found = false; + + ID3::Iterator it(id3, "PRIV"); + while (!it.done()) { + size_t length; + const uint8_t *data = it.getData(&length); + + static const char *kMatchName = + "com.apple.streaming.transportStreamTimestamp"; + static const size_t kMatchNameLen = strlen(kMatchName); + + if (length == kMatchNameLen + 1 + 8 + && !strncmp((const char *)data, kMatchName, kMatchNameLen)) { + found = true; + PTS = U64_AT(&data[kMatchNameLen + 1]); + } + + it.next(); + } + + if (!found) { + ALOGE("Unable to extract transportStreamTimestamp from ID3 tag."); + return ERROR_MALFORMED; + } + } + + // skip the ID3 tag + buffer->setRange( + buffer->offset() + id3.rawSize(), buffer->size() - id3.rawSize()); + + firstID3Tag = false; + } + + if (!mFirstPTSValid) { + mFirstPTSValid = true; + mFirstPTS = PTS; + } + PTS -= mFirstPTS; + + int64_t timeUs = (PTS * 100ll) / 9ll + mAbsoluteTimeAnchorUs; + + if (mStreamTypeMask != LiveSession::STREAMTYPE_AUDIO) { + ALOGW("This stream only contains audio data!"); + + mStreamTypeMask &= LiveSession::STREAMTYPE_AUDIO; + + if (mStreamTypeMask == 0) { + return OK; + } + } + + sp packetSource = + mPacketSources.valueFor(LiveSession::STREAMTYPE_AUDIO); + + if (packetSource->getFormat() == NULL && buffer->size() >= 7) { + ABitReader bits(buffer->data(), buffer->size()); + + // adts_fixed_header + + CHECK_EQ(bits.getBits(12), 0xfffu); + bits.skipBits(3); // ID, layer + bool protection_absent = bits.getBits(1) != 0; + + unsigned profile = bits.getBits(2); + CHECK_NE(profile, 3u); + unsigned sampling_freq_index = bits.getBits(4); + bits.getBits(1); // private_bit + unsigned channel_configuration = bits.getBits(3); + CHECK_NE(channel_configuration, 0u); + bits.skipBits(2); // original_copy, home + + sp meta = MakeAACCodecSpecificData( + profile, sampling_freq_index, channel_configuration); + + meta->setInt32(kKeyIsADTS, true); + + packetSource->setFormat(meta); + } + + int64_t numSamples = 0ll; + int32_t sampleRate; + CHECK(packetSource->getFormat()->findInt32(kKeySampleRate, &sampleRate)); + + size_t offset = 0; + while (offset < buffer->size()) { + const uint8_t *adtsHeader = buffer->data() + offset; + CHECK_LT(offset + 5, buffer->size()); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + CHECK_LE(offset + aac_frame_length, buffer->size()); + + sp unit = new ABuffer(aac_frame_length); + memcpy(unit->data(), adtsHeader, aac_frame_length); + + int64_t unitTimeUs = timeUs + numSamples * 1000000ll / sampleRate; + unit->meta()->setInt64("timeUs", unitTimeUs); + + // Each AAC frame encodes 1024 samples. + numSamples += 1024; + + packetSource->queueAccessUnit(unit); + + offset += aac_frame_length; + } + + return OK; +} + +void PlaylistFetcher::updateDuration() { + int64_t durationUs = 0ll; + for (size_t index = 0; index < mPlaylist->size(); ++index) { + sp itemMeta; + CHECK(mPlaylist->itemAt( + index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + durationUs += itemDurationUs; + } + + sp msg = mNotify->dup(); + msg->setInt32("what", kWhatDurationUpdate); + msg->setInt64("durationUs", durationUs); + msg->post(); +} + +} // namespace android diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h new file mode 100644 index 0000000..5a2b901 --- /dev/null +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -0,0 +1,155 @@ +/* + * 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 PLAYLIST_FETCHER_H_ + +#define PLAYLIST_FETCHER_H_ + +#include + +#include "mpeg2ts/ATSParser.h" +#include "LiveSession.h" + +namespace android { + +struct ABuffer; +struct AnotherPacketSource; +struct DataSource; +struct HTTPBase; +struct LiveDataSource; +struct M3UParser; +struct String8; + +struct PlaylistFetcher : public AHandler { + enum { + kWhatStarted, + kWhatPaused, + kWhatStopped, + kWhatError, + kWhatDurationUpdate, + kWhatTemporarilyDoneFetching, + kWhatPrepared, + kWhatPreparationFailed, + }; + + PlaylistFetcher( + const sp ¬ify, + const sp &session, + const char *uri); + + sp getDataSource(); + + void startAsync( + const sp &audioSource, + const sp &videoSource, + const sp &subtitleSource, + int64_t startTimeUs = -1ll); + + void pauseAsync(); + + void stopAsync(); + +protected: + virtual ~PlaylistFetcher(); + virtual void onMessageReceived(const sp &msg); + +private: + enum { + kMaxNumRetries = 5, + }; + + enum { + kWhatStart = 'strt', + kWhatPause = 'paus', + kWhatStop = 'stop', + kWhatMonitorQueue = 'moni', + }; + + static const int64_t kMinBufferedDurationUs; + + sp mNotify; + sp mSession; + AString mURI; + + uint32_t mStreamTypeMask; + int64_t mStartTimeUs; + + KeyedVector > + mPacketSources; + + KeyedVector > mAESKeyForURI; + + int64_t mLastPlaylistFetchTimeUs; + sp mPlaylist; + int32_t mSeqNumber; + int32_t mNumRetries; + bool mStartup; + int64_t mNextPTSTimeUs; + + int32_t mMonitorQueueGeneration; + + enum RefreshState { + INITIAL_MINIMUM_RELOAD_DELAY, + FIRST_UNCHANGED_RELOAD_ATTEMPT, + SECOND_UNCHANGED_RELOAD_ATTEMPT, + THIRD_UNCHANGED_RELOAD_ATTEMPT + }; + RefreshState mRefreshState; + + uint8_t mPlaylistHash[16]; + + sp mTSParser; + + bool mFirstPTSValid; + uint64_t mFirstPTS; + int64_t mAbsoluteTimeAnchorUs; + + status_t decryptBuffer( + size_t playlistIndex, const sp &buffer); + + void postMonitorQueue(int64_t delayUs = 0); + void cancelMonitorQueue(); + + bool timeToRefreshPlaylist(int64_t nowUs) const; + + // Returns the media time in us of the segment specified by seqNumber. + // This is computed by summing the durations of all segments before it. + int64_t getSegmentStartTimeUs(int32_t seqNumber) const; + + status_t onStart(const sp &msg); + void onPause(); + void onStop(); + void onMonitorQueue(); + void onDownloadNext(); + + status_t extractAndQueueAccessUnits(const sp &buffer); + + void notifyError(status_t err); + + void queueDiscontinuity( + ATSParser::DiscontinuityType type, const sp &extra); + + int32_t getSeqNumberForTime(int64_t timeUs) const; + + void updateDuration(); + + DISALLOW_EVIL_CONSTRUCTORS(PlaylistFetcher); +}; + +} // namespace android + +#endif // PLAYLIST_FETCHER_H_ + diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 22c2f5a..8d3013b 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -30,12 +30,55 @@ namespace android { static const size_t kMaxMetadataSize = 3 * 1024 * 1024; +struct MemorySource : public DataSource { + MemorySource(const uint8_t *data, size_t size) + : mData(data), + mSize(size) { + } + + virtual status_t initCheck() const { + return OK; + } + + virtual ssize_t readAt(off64_t offset, void *data, size_t size) { + off64_t available = (offset >= mSize) ? 0ll : mSize - offset; + + size_t copy = (available > size) ? size : available; + memcpy(data, mData + offset, copy); + + return copy; + } + +private: + const uint8_t *mData; + size_t mSize; + + DISALLOW_EVIL_CONSTRUCTORS(MemorySource); +}; + ID3::ID3(const sp &source, bool ignoreV1) : mIsValid(false), mData(NULL), mSize(0), mFirstFrameOffset(0), - mVersion(ID3_UNKNOWN) { + mVersion(ID3_UNKNOWN), + mRawSize(0) { + mIsValid = parseV2(source); + + if (!mIsValid && !ignoreV1) { + mIsValid = parseV1(source); + } +} + +ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1) + : mIsValid(false), + mData(NULL), + mSize(0), + mFirstFrameOffset(0), + mVersion(ID3_UNKNOWN), + mRawSize(0) { + sp source = new MemorySource(data, size); + mIsValid = parseV2(source); if (!mIsValid && !ignoreV1) { @@ -140,6 +183,7 @@ struct id3_header { } mSize = size; + mRawSize = mSize + sizeof(header); if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) { free(mData); @@ -505,7 +549,7 @@ void ID3::Iterator::getstring(String8 *id, bool otherdata) const { int32_t i = n - 4; while(--i >= 0 && *++frameData != 0) ; int skipped = (frameData - mFrameData); - if (skipped >= n) { + if (skipped >= (int)n) { return; } n -= skipped; diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index 3028f56..cca83ab 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -36,6 +36,7 @@ struct ID3 { }; ID3(const sp &source, bool ignoreV1 = false); + ID3(const uint8_t *data, size_t size, bool ignoreV1 = false); ~ID3(); bool isValid() const; @@ -71,6 +72,8 @@ struct ID3 { Iterator &operator=(const Iterator &); }; + size_t rawSize() const { return mRawSize; } + private: bool mIsValid; uint8_t *mData; @@ -78,6 +81,10 @@ private: size_t mFirstFrameOffset; Version mVersion; + // size of the ID3 tag including header before any unsynchronization. + // only valid for IDV2+ + size_t mRawSize; + bool parseV1(const sp &source); bool parseV2(const sp &source); void removeUnsynchronization(); diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h deleted file mode 100644 index db44a33..0000000 --- a/media/libstagefright/include/LiveSession.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LIVE_SESSION_H_ - -#define LIVE_SESSION_H_ - -#include - -#include - -namespace android { - -struct ABuffer; -struct DataSource; -struct LiveDataSource; -struct M3UParser; -struct HTTPBase; - -struct LiveSession : public AHandler { - enum Flags { - // Don't log any URLs. - kFlagIncognito = 1, - }; - LiveSession( - const sp ¬ify, - uint32_t flags = 0, bool uidValid = false, uid_t uid = 0); - - sp getDataSource(); - - void connect( - const char *url, - const KeyedVector *headers = NULL); - - void disconnect(); - - // Blocks until seek is complete. - void seekTo(int64_t timeUs); - - status_t getDuration(int64_t *durationUs) const; - - bool isSeekable() const; - bool hasDynamicDuration() const; - - // Posted notification's "what" field will carry one of the following: - enum { - kWhatPrepared, - kWhatPreparationFailed, - }; - -protected: - virtual ~LiveSession(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kMaxNumQueuedFragments = 3, - kMaxNumRetries = 5, - }; - - enum { - kWhatConnect = 'conn', - kWhatDisconnect = 'disc', - kWhatMonitorQueue = 'moni', - kWhatSeek = 'seek', - }; - - struct BandwidthItem { - AString mURI; - unsigned long mBandwidth; - }; - - sp mNotify; - uint32_t mFlags; - bool mUIDValid; - uid_t mUID; - - bool mInPreparationPhase; - - sp mDataSource; - - sp mHTTPDataSource; - - AString mMasterURL; - KeyedVector mExtraHeaders; - - Vector mBandwidthItems; - - KeyedVector > mAESKeyForURI; - - ssize_t mPrevBandwidthIndex; - int64_t mLastPlaylistFetchTimeUs; - sp mPlaylist; - int32_t mSeqNumber; - int64_t mSeekTimeUs; - int32_t mNumRetries; - bool mStartOfPlayback; - - mutable Mutex mLock; - Condition mCondition; - int64_t mDurationUs; - bool mDurationFixed; // Duration has been determined once and for all. - bool mSeekDone; - bool mDisconnectPending; - - int32_t mMonitorQueueGeneration; - - enum RefreshState { - INITIAL_MINIMUM_RELOAD_DELAY, - FIRST_UNCHANGED_RELOAD_ATTEMPT, - SECOND_UNCHANGED_RELOAD_ATTEMPT, - THIRD_UNCHANGED_RELOAD_ATTEMPT - }; - RefreshState mRefreshState; - - uint8_t mPlaylistHash[16]; - - void onConnect(const sp &msg); - void onDisconnect(); - void onDownloadNext(); - void onMonitorQueue(); - void onSeek(const sp &msg); - - status_t fetchFile( - const char *url, sp *out, - int64_t range_offset = 0, int64_t range_length = -1); - - sp fetchPlaylist(const char *url, bool *unchanged); - size_t getBandwidthIndex(); - - status_t decryptBuffer( - size_t playlistIndex, const sp &buffer); - - void postMonitorQueue(int64_t delayUs = 0); - - bool timeToRefreshPlaylist(int64_t nowUs) const; - - static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); - - // Returns the media time in us of the segment specified by seqNumber. - // This is computed by summing the durations of all segments before it. - int64_t getSegmentStartTimeUs(int32_t seqNumber) const; - - void signalEOS(status_t err); - - DISALLOW_EVIL_CONSTRUCTORS(LiveSession); -}; - -} // namespace android - -#endif // LIVE_SESSION_H_ diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h deleted file mode 100644 index 2d2f50f..0000000 --- a/media/libstagefright/include/M3UParser.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef M3U_PARSER_H_ - -#define M3U_PARSER_H_ - -#include -#include -#include -#include - -namespace android { - -struct M3UParser : public RefBase { - M3UParser(const char *baseURI, const void *data, size_t size); - - status_t initCheck() const; - - bool isExtM3U() const; - bool isVariantPlaylist() const; - bool isComplete() const; - bool isEvent() const; - - sp meta(); - - size_t size(); - bool itemAt(size_t index, AString *uri, sp *meta = NULL); - -protected: - virtual ~M3UParser(); - -private: - struct Item { - AString mURI; - sp mMeta; - }; - - status_t mInitCheck; - - AString mBaseURI; - bool mIsExtM3U; - bool mIsVariantPlaylist; - bool mIsComplete; - bool mIsEvent; - - sp mMeta; - Vector mItems; - - status_t parse(const void *data, size_t size); - - static status_t parseMetaData( - const AString &line, sp *meta, const char *key); - - static status_t parseMetaDataDuration( - const AString &line, sp *meta, const char *key); - - static status_t parseStreamInf( - const AString &line, sp *meta); - - static status_t parseCipherInfo( - const AString &line, sp *meta, const AString &baseURI); - - static status_t parseByteRange( - const AString &line, uint64_t curOffset, - uint64_t *length, uint64_t *offset); - - static status_t ParseInt32(const char *s, int32_t *x); - static status_t ParseDouble(const char *s, double *x); - - DISALLOW_EVIL_CONSTRUCTORS(M3UParser); -}; - -} // namespace android - -#endif // M3U_PARSER_H_ diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h index fe74a42..c5e86a6 100644 --- a/media/libstagefright/include/MPEG2TSExtractor.h +++ b/media/libstagefright/include/MPEG2TSExtractor.h @@ -31,7 +31,6 @@ struct ATSParser; struct DataSource; struct MPEG2TSSource; struct String8; -struct LiveSession; struct MPEG2TSExtractor : public MediaExtractor { MPEG2TSExtractor(const sp &source); @@ -44,16 +43,12 @@ struct MPEG2TSExtractor : public MediaExtractor { virtual uint32_t flags() const; - void setLiveSession(const sp &liveSession); - void seekTo(int64_t seekTimeUs); - private: friend struct MPEG2TSSource; mutable Mutex mLock; sp mDataSource; - sp mLiveSession; sp mParser; diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 3de3a61..3153c8b 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -32,9 +32,22 @@ const int64_t kNearEOSMarkUs = 2000000ll; // 2 secs AnotherPacketSource::AnotherPacketSource(const sp &meta) : mIsAudio(false), - mFormat(meta), + mFormat(NULL), mLastQueuedTimeUs(0), mEOSResult(OK) { + setFormat(meta); +} + +void AnotherPacketSource::setFormat(const sp &meta) { + CHECK(mFormat == NULL); + + mIsAudio = false; + + if (meta == NULL) { + return; + } + + mFormat = meta; const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); @@ -45,11 +58,6 @@ AnotherPacketSource::AnotherPacketSource(const sp &meta) } } -void AnotherPacketSource::setFormat(const sp &meta) { - CHECK(mFormat == NULL); - mFormat = meta; -} - AnotherPacketSource::~AnotherPacketSource() { } @@ -152,6 +160,15 @@ void AnotherPacketSource::queueAccessUnit(const sp &buffer) { mCondition.signal(); } +void AnotherPacketSource::clear() { + Mutex::Autolock autoLock(mLock); + + mBuffers.clear(); + mEOSResult = OK; + + mFormat = NULL; +} + void AnotherPacketSource::queueDiscontinuity( ATSParser::DiscontinuityType type, const sp &extra) { diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h index 1db4068..e16cf78 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -41,6 +41,8 @@ struct AnotherPacketSource : public MediaSource { virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); + void clear(); + bool hasBufferAvailable(status_t *finalResult); // Returns the difference between the last and the first queued diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index e1589b4..d449c34 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -19,7 +19,6 @@ #include #include "include/MPEG2TSExtractor.h" -#include "include/LiveSession.h" #include "include/NuCachedSource2.h" #include @@ -79,15 +78,7 @@ status_t MPEG2TSSource::stop() { } sp MPEG2TSSource::getFormat() { - sp meta = mImpl->getFormat(); - - int64_t durationUs; - if (mExtractor->mLiveSession != NULL - && mExtractor->mLiveSession->getDuration(&durationUs) == OK) { - meta->setInt64(kKeyDuration, durationUs); - } - - return meta; + return mImpl->getFormat(); } status_t MPEG2TSSource::read( @@ -97,7 +88,7 @@ status_t MPEG2TSSource::read( int64_t seekTimeUs; ReadOptions::SeekMode seekMode; if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) { - mExtractor->seekTo(seekTimeUs); + return ERROR_UNSUPPORTED; } status_t finalResult; @@ -216,32 +207,8 @@ status_t MPEG2TSExtractor::feedMore() { return mParser->feedTSPacket(packet, kTSPacketSize); } -void MPEG2TSExtractor::setLiveSession(const sp &liveSession) { - Mutex::Autolock autoLock(mLock); - - mLiveSession = liveSession; -} - -void MPEG2TSExtractor::seekTo(int64_t seekTimeUs) { - Mutex::Autolock autoLock(mLock); - - if (mLiveSession == NULL) { - return; - } - - mLiveSession->seekTo(seekTimeUs); -} - uint32_t MPEG2TSExtractor::flags() const { - Mutex::Autolock autoLock(mLock); - - uint32_t flags = CAN_PAUSE; - - if (mLiveSession != NULL && mLiveSession->isSeekable()) { - flags |= CAN_SEEK_FORWARD | CAN_SEEK_BACKWARD | CAN_SEEK; - } - - return flags; + return CAN_PAUSE; } //////////////////////////////////////////////////////////////////////////////// -- cgit v1.1 From 2799d743ee2ae5a25fe869a7f9c052acc029559f Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 30 May 2013 14:33:29 -0700 Subject: Use sp instead of raw AudioTrack * This change prepares for the new implementation of AudioTrack client, which will require clients to use only sp, not raw AudioTrack *. A raw delete will cause a race condition during AudioTrack destruction. AudioTrack was made a RefBase by commit b68a91a70bc8d0d18e7404e14443d4e4020b3635 on 2011/11/15, when it was needed by OpenSL ES (for the callback protector). At that time, the only other client that was also converted from AudioTrack * to sp was android.media.AudioTrack JNI in project frameworks/base (file android_media_AudioTrack.cpp). Details: * Use .clear() instead of delete followed by = NULL. * ALOG %p need .get(). * sp<> don't need to be listed in constructor initializer, if initially 0. * Use == 0 for sp<> vs == NULL for raw pointers. * Use if (sp != 0) instead of if (raw). Change-Id: Ic7cad25795d6e862e112abdc227b6d33afdfce17 --- media/libmedia/JetPlayer.cpp | 6 ++-- media/libmedia/SoundPool.cpp | 14 ++++---- media/libmedia/ToneGenerator.cpp | 22 ++++--------- media/libmediaplayerservice/MediaPlayerService.cpp | 37 +++++++++------------- media/libmediaplayerservice/MediaPlayerService.h | 6 ++-- media/libstagefright/AudioPlayer.cpp | 11 +++---- 6 files changed, 37 insertions(+), 59 deletions(-) (limited to 'media') diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index 59e538f..8fe5bb3 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -39,7 +39,6 @@ JetPlayer::JetPlayer(void *javaJetPlayer, int maxTracks, int trackBufferSize) : mMaxTracks(maxTracks), mEasData(NULL), mEasJetFileLoc(NULL), - mAudioTrack(NULL), mTrackBufferSize(trackBufferSize) { ALOGV("JetPlayer constructor"); @@ -140,11 +139,10 @@ int JetPlayer::release() free(mEasJetFileLoc); mEasJetFileLoc = NULL; } - if (mAudioTrack) { + if (mAudioTrack != 0) { mAudioTrack->stop(); mAudioTrack->flush(); - delete mAudioTrack; - mAudioTrack = NULL; + mAudioTrack.clear(); } if (mAudioBuffer) { delete mAudioBuffer; diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index ee70ef7..e1e88ec 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -547,8 +547,8 @@ void SoundChannel::init(SoundPool* soundPool) void SoundChannel::play(const sp& sample, int nextChannelID, float leftVolume, float rightVolume, int priority, int loop, float rate) { - AudioTrack* oldTrack; - AudioTrack* newTrack; + sp oldTrack; + sp newTrack; status_t status; { // scope for the lock @@ -620,7 +620,7 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV ALOGE("Error creating AudioTrack"); goto exit; } - ALOGV("setVolume %p", newTrack); + ALOGV("setVolume %p", newTrack.get()); newTrack->setVolume(leftVolume, rightVolume); newTrack->setLoop(0, frameCount, loop); @@ -643,11 +643,9 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV } exit: - ALOGV("delete oldTrack %p", oldTrack); - delete oldTrack; + ALOGV("delete oldTrack %p", oldTrack.get()); if (status != NO_ERROR) { - delete newTrack; - mAudioTrack = NULL; + mAudioTrack.clear(); } } @@ -884,7 +882,7 @@ SoundChannel::~SoundChannel() } // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack // callback thread to exit which may need to execute process() and acquire the mLock. - delete mAudioTrack; + mAudioTrack.clear(); } void SoundChannel::dump() diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index f55b697..ebe1ba1 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -803,7 +803,6 @@ ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool ALOGV("ToneGenerator constructor: streamType=%d, volume=%f", streamType, volume); mState = TONE_IDLE; - mpAudioTrack = NULL; if (AudioSystem::getOutputSamplingRate(&mSamplingRate, streamType) != NO_ERROR) { ALOGE("Unable to marshal AudioFlinger"); @@ -855,10 +854,10 @@ ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool ToneGenerator::~ToneGenerator() { ALOGV("ToneGenerator destructor"); - if (mpAudioTrack != NULL) { + if (mpAudioTrack != 0) { stopTone(); - ALOGV("Delete Track: %p", mpAudioTrack); - delete mpAudioTrack; + ALOGV("Delete Track: %p", mpAudioTrack.get()); + mpAudioTrack.clear(); } } @@ -1047,14 +1046,9 @@ void ToneGenerator::stopTone() { //////////////////////////////////////////////////////////////////////////////// bool ToneGenerator::initAudioTrack() { - if (mpAudioTrack) { - delete mpAudioTrack; - mpAudioTrack = NULL; - } - // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size mpAudioTrack = new AudioTrack(); - ALOGV("Create Track: %p", mpAudioTrack); + ALOGV("Create Track: %p", mpAudioTrack.get()); mpAudioTrack->set(mStreamType, 0, // sampleRate @@ -1081,12 +1075,10 @@ bool ToneGenerator::initAudioTrack() { initAudioTrack_exit: + ALOGV("Init failed: %p", mpAudioTrack.get()); + // Cleanup - if (mpAudioTrack != NULL) { - ALOGV("Delete Track I: %p", mpAudioTrack); - delete mpAudioTrack; - mpAudioTrack = NULL; - } + mpAudioTrack.clear(); return false; } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index e600a3f..fa1ff36 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1295,8 +1295,6 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId) mSessionId(sessionId), mFlags(AUDIO_OUTPUT_FLAG_NONE) { ALOGV("AudioOutput(%d)", sessionId); - mTrack = 0; - mRecycledTrack = 0; mStreamType = AUDIO_STREAM_MUSIC; mLeftVolume = 1.0; mRightVolume = 1.0; @@ -1311,7 +1309,6 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId) MediaPlayerService::AudioOutput::~AudioOutput() { close(); - delete mRecycledTrack; delete mCallbackData; } @@ -1422,7 +1419,7 @@ status_t MediaPlayerService::AudioOutput::open( } } - AudioTrack *t; + sp t; CallbackData *newcbd = NULL; if (mCallback != NULL) { newcbd = new CallbackData(this); @@ -1453,13 +1450,12 @@ status_t MediaPlayerService::AudioOutput::open( if ((t == 0) || (t->initCheck() != NO_ERROR)) { ALOGE("Unable to create audio track"); - delete t; delete newcbd; return NO_INIT; } - if (mRecycledTrack) { + if (mRecycledTrack != 0) { // check if the existing track can be reused as-is, or if a new track needs to be created. bool reuse = true; @@ -1484,11 +1480,10 @@ status_t MediaPlayerService::AudioOutput::open( ALOGV("chaining to next output"); close(); mTrack = mRecycledTrack; - mRecycledTrack = NULL; + mRecycledTrack.clear(); if (mCallbackData != NULL) { mCallbackData->setOutput(this); } - delete t; delete newcbd; return OK; } @@ -1499,8 +1494,7 @@ status_t MediaPlayerService::AudioOutput::open( mCallbackData->endTrackSwitch(); } mRecycledTrack->flush(); - delete mRecycledTrack; - mRecycledTrack = NULL; + mRecycledTrack.clear(); delete mCallbackData; mCallbackData = NULL; close(); @@ -1533,7 +1527,7 @@ void MediaPlayerService::AudioOutput::start() if (mCallbackData != NULL) { mCallbackData->endTrackSwitch(); } - if (mTrack) { + if (mTrack != 0) { mTrack->setVolume(mLeftVolume, mRightVolume); mTrack->setAuxEffectSendLevel(mSendLevel); mTrack->start(); @@ -1555,7 +1549,7 @@ void MediaPlayerService::AudioOutput::switchToNextOutput() { mNextOutput->mCallbackData = mCallbackData; mCallbackData = NULL; mNextOutput->mRecycledTrack = mTrack; - mTrack = NULL; + mTrack.clear(); mNextOutput->mSampleRateHz = mSampleRateHz; mNextOutput->mMsecsPerFrame = mMsecsPerFrame; mNextOutput->mBytesWritten = mBytesWritten; @@ -1568,7 +1562,7 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback."); //ALOGV("write(%p, %u)", buffer, size); - if (mTrack) { + if (mTrack != 0) { ssize_t ret = mTrack->write(buffer, size); mBytesWritten += ret; return ret; @@ -1579,26 +1573,25 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) void MediaPlayerService::AudioOutput::stop() { ALOGV("stop"); - if (mTrack) mTrack->stop(); + if (mTrack != 0) mTrack->stop(); } void MediaPlayerService::AudioOutput::flush() { ALOGV("flush"); - if (mTrack) mTrack->flush(); + if (mTrack != 0) mTrack->flush(); } void MediaPlayerService::AudioOutput::pause() { ALOGV("pause"); - if (mTrack) mTrack->pause(); + if (mTrack != 0) mTrack->pause(); } void MediaPlayerService::AudioOutput::close() { ALOGV("close"); - delete mTrack; - mTrack = 0; + mTrack.clear(); } void MediaPlayerService::AudioOutput::setVolume(float left, float right) @@ -1606,7 +1599,7 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right) ALOGV("setVolume(%f, %f)", left, right); mLeftVolume = left; mRightVolume = right; - if (mTrack) { + if (mTrack != 0) { mTrack->setVolume(left, right); } } @@ -1615,7 +1608,7 @@ status_t MediaPlayerService::AudioOutput::setPlaybackRatePermille(int32_t ratePe { ALOGV("setPlaybackRatePermille(%d)", ratePermille); status_t res = NO_ERROR; - if (mTrack) { + if (mTrack != 0) { res = mTrack->setSampleRate(ratePermille * mSampleRateHz / 1000); } else { res = NO_INIT; @@ -1631,7 +1624,7 @@ status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level) { ALOGV("setAuxEffectSendLevel(%f)", level); mSendLevel = level; - if (mTrack) { + if (mTrack != 0) { return mTrack->setAuxEffectSendLevel(level); } return NO_ERROR; @@ -1641,7 +1634,7 @@ status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId) { ALOGV("attachAuxEffect(%d)", effectId); mAuxEffectId = effectId; - if (mTrack) { + if (mTrack != 0) { return mTrack->attachAuxEffect(effectId); } return NO_ERROR; diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index b33805d..e586156 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -78,7 +78,7 @@ class MediaPlayerService : public BnMediaPlayerService AudioOutput(int sessionId); virtual ~AudioOutput(); - virtual bool ready() const { return mTrack != NULL; } + virtual bool ready() const { return mTrack != 0; } virtual bool realtime() const { return true; } virtual ssize_t bufferSize() const; virtual ssize_t frameCount() const; @@ -120,8 +120,8 @@ class MediaPlayerService : public BnMediaPlayerService static void CallbackWrapper( int event, void *me, void *info); - AudioTrack* mTrack; - AudioTrack* mRecycledTrack; + sp mTrack; + sp mRecycledTrack; sp mNextOutput; AudioCallback mCallback; void * mCallbackCookie; diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 4208019..92efae8 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -36,8 +36,7 @@ AudioPlayer::AudioPlayer( const sp &audioSink, bool allowDeepBuffering, AwesomePlayer *observer) - : mAudioTrack(NULL), - mInputBuffer(NULL), + : mInputBuffer(NULL), mSampleRate(0), mLatencyUs(0), mFrameSize(0), @@ -166,8 +165,7 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { 0, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, 0); if ((err = mAudioTrack->initCheck()) != OK) { - delete mAudioTrack; - mAudioTrack = NULL; + mAudioTrack.clear(); if (mFirstBuffer != NULL) { mFirstBuffer->release(); @@ -235,8 +233,7 @@ void AudioPlayer::reset() { } else { mAudioTrack->stop(); - delete mAudioTrack; - mAudioTrack = NULL; + mAudioTrack.clear(); } // Make sure to release any buffer we hold onto so that the @@ -297,7 +294,7 @@ bool AudioPlayer::reachedEOS(status_t *finalStatus) { status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) { if (mAudioSink.get() != NULL) { return mAudioSink->setPlaybackRatePermille(ratePermille); - } else if (mAudioTrack != NULL){ + } else if (mAudioTrack != 0){ return mAudioTrack->setSampleRate(ratePermille * mSampleRate / 1000); } else { return NO_INIT; -- cgit v1.1 From d94e716af0e49d775f0c0c4f36dd2c136ba5f2b2 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 3 Jun 2013 15:48:11 -0700 Subject: Fix our software decoders to reset (more of) their internal state properly on a transition from idle->loaded. Change-Id: I56ccfeef24c391e50e42b522194206e35c7ab700 related-to-bug: 9105408 --- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 3 +++ media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp | 5 +++++ media/libstagefright/codecs/amrnb/dec/SoftAMR.h | 1 + media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp | 5 +++++ media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h | 1 + media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 2 ++ media/libstagefright/codecs/on2/dec/SoftVPX.cpp | 4 ++++ media/libstagefright/codecs/on2/dec/SoftVPX.h | 1 + media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp | 5 +++++ media/libstagefright/codecs/on2/h264dec/SoftAVC.h | 1 + media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp | 2 ++ 11 files changed, 30 insertions(+) (limited to 'media') diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index cf50dc9..1b20cbb 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -604,6 +604,9 @@ void SoftAAC2::onReset() { // To make the codec behave the same before and after a reset, we need to invalidate the // streaminfo struct. This does that: mStreamInfo->sampleRate = 0; + + mSignalledError = false; + mOutputPortSettingsChange = NONE; } void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp index 4d4212f..3320688 100644 --- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp @@ -457,6 +457,11 @@ void SoftAMR::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { } } +void SoftAMR::onReset() { + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + } // namespace android android::SoftOMXComponent *createSoftOMXComponent( diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h index 9a596e5..758d6ac 100644 --- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h @@ -40,6 +40,7 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); private: enum { diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index 020cc0a..3c15adc 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -571,6 +571,11 @@ void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { } } +void SoftMPEG4::onReset() { + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + void SoftMPEG4::updatePortDefinitions() { OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; def->format.video.nFrameWidth = mWidth; diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h index dff08a7..6df4c92 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h @@ -44,6 +44,7 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); private: enum { diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index 9f25536..7c382fb 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -361,6 +361,8 @@ void SoftMP3::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { void SoftMP3::onReset() { pvmp3_InitDecoder(mConfig, mDecoderBuf); mIsFirst = true; + mSignalledError = false; + mOutputPortSettingsChange = NONE; } } // namespace android diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index 866e5b0..fe76036 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -358,6 +358,10 @@ void SoftVPX::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { } } +void SoftVPX::onReset() { + mOutputPortSettingsChange = NONE; +} + void SoftVPX::updatePortDefinitions() { OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; def->format.video.nFrameWidth = mWidth; diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h index 3e814a2..4cb05cf 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.h +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -40,6 +40,7 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); private: enum { diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp index 6e36651..5e299d5 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp @@ -530,6 +530,11 @@ void SoftAVC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { } } +void SoftAVC::onReset() { + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + void SoftAVC::updatePortDefinitions() { OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; def->format.video.nFrameWidth = mWidth; diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h index 879b014..8c104c5 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h @@ -46,6 +46,7 @@ protected: virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); private: enum { diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index 4115324..51bb958 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -424,6 +424,8 @@ void SoftVorbis::onReset() { delete mVi; mVi = NULL; } + + mOutputPortSettingsChange = NONE; } void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { -- cgit v1.1 From 673158582c9589cee1d5e4d7c79622609938b8f8 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 3 Jun 2013 16:00:13 -0700 Subject: Fix uninitialized variable error in HLS bandwidth determination. Change-Id: I647e79443f2a06e5b1b4f9068c02b424b0e57989 --- media/libstagefright/httplive/LiveSession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index fff13eb..e91c60b 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -651,7 +651,7 @@ size_t LiveSession::getBandwidthIndex() { #if 1 char value[PROPERTY_VALUE_MAX]; - ssize_t index; + ssize_t index = -1; if (property_get("media.httplive.bw-index", value, NULL)) { char *end; index = strtol(value, &end, 10); -- cgit v1.1 From bb6f0a0bb413817d707cfb4c4a30847fda520787 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 3 Jun 2013 15:00:29 -0700 Subject: Fix underruns when sample rate != native sample rate This forces a minimum of 3 application buffers when the sample rates don't match, using the normal mixer and low latency HAL. There is still an issue that the latency() varies depending on whether screen was off or on at the time of creating the AudioTrack. With screen on: I/AudioTrack( 2028): afFrameCount=960, minBufCount=2, afSampleRate=48000, afLatency=50 I/AudioTrack( 2028): minFrameCount: 2646, afFrameCount=960, minBufCount=3, sampleRate=44100, afSampleRate=48000, afLatency=50 With screen off: I/AudioTrack( 2817): afFrameCount=960, minBufCount=4, afSampleRate=48000, afLatency=84 I/AudioTrack( 2817): minFrameCount: 3528, afFrameCount=960, minBufCount=4, sampleRate=44100, afSampleRate=48000, afLatency=84 Change-Id: Ib45515edff2afcd672dda34881b658c800ffc25a --- media/libmedia/AudioTrack.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ff52b28..77fc6f6 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -861,8 +861,10 @@ status_t AudioTrack::createTrack_l( // Ensure that buffer depth covers at least audio hardware latency uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); - if (minBufCount < 2) { - minBufCount = 2; + ALOGV("afFrameCount=%d, minBufCount=%d, afSampleRate=%u, afLatency=%d", + afFrameCount, minBufCount, afSampleRate, afLatency); + if (minBufCount <= 2) { + minBufCount = sampleRate == afSampleRate ? 2 : 3; } size_t minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; -- cgit v1.1 From 53b0a2b1f9cb6b99b3f0d1a639921d1b24bc30b7 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 4 Jun 2013 18:43:08 -0700 Subject: Reset PV decoder on SoftMPEG4 decoder reset Otherwise, state may be undefined for subsequent frames. Change-Id: Icdc0126247e1422eba21f2008a04cf7867d93f5d Signed-off-by: Lajos Molnar Bug: 9284771 (cherry picked from commit 0f15875b8e80fb49a3b18d88964b063326f307b9) --- media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'media') diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index 3c15adc..875674b 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -574,6 +574,11 @@ void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { void SoftMPEG4::onReset() { mSignalledError = false; mOutputPortSettingsChange = NONE; + mFramesConfigured = false; + if (mInitialized) { + PVCleanUpVideoDecoder(mHandle); + mInitialized = false; + } } void SoftMPEG4::updatePortDefinitions() { -- cgit v1.1 From 269a355679fce6a71523faeefc2ff575abbd1a8e Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 4 Jun 2013 19:35:03 -0700 Subject: Track exact timestamps in SoftMPEG4/H263 decoders Change-Id: I7772e3afec020f889dea80fd6372afbc36cd68d6 Signed-off-by: Lajos Molnar Bug: 9285553 (cherry picked from commit e113aa1f078cb3d5f8182058e144fd14ce945fca) --- media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp | 16 ++++++++++++---- media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index 875674b..bb5625f 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -76,6 +76,7 @@ SoftMPEG4::SoftMPEG4( mInitialized(false), mFramesConfigured(false), mNumSamplesOutput(0), + mPvTime(0), mOutputPortSettingsChange(NONE) { if (!strcmp(name, "OMX.google.h263.decoder")) { mMode = MODE_H263; @@ -415,9 +416,14 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { uint32_t useExtTimestamp = (inHeader->nOffset == 0); - // decoder deals in ms, OMX in us. - uint32_t timestamp = - useExtTimestamp ? (inHeader->nTimeStamp + 500) / 1000 : 0xFFFFFFFF; + // decoder deals in ms (int32_t), OMX in us (int64_t) + // so use fake timestamp instead + uint32_t timestamp = 0xFFFFFFFF; + if (useExtTimestamp) { + mPvToOmxTimeMap.add(mPvTime, inHeader->nTimeStamp); + timestamp = mPvTime; + mPvTime++; + } int32_t bufferSize = inHeader->nFilledLen; int32_t tmp = bufferSize; @@ -441,7 +447,8 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { } // decoder deals in ms, OMX in us. - outHeader->nTimeStamp = timestamp * 1000; + outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp); + mPvToOmxTimeMap.removeItem(timestamp); inHeader->nOffset += bufferSize; inHeader->nFilledLen = 0; @@ -572,6 +579,7 @@ void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { } void SoftMPEG4::onReset() { + mPvToOmxTimeMap.clear(); mSignalledError = false; mOutputPortSettingsChange = NONE; mFramesConfigured = false; diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h index 6df4c92..f71ccef 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h @@ -19,6 +19,7 @@ #define SOFT_MPEG4_H_ #include "SimpleSoftOMXComponent.h" +#include struct tagvideoDecControls; @@ -70,6 +71,8 @@ private: bool mFramesConfigured; int32_t mNumSamplesOutput; + int32_t mPvTime; + KeyedVector mPvToOmxTimeMap; enum { NONE, -- cgit v1.1 From 7f616d3cc5366a4b8af20d3d0c768e3de1df0666 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 4 Jun 2013 19:35:18 -0700 Subject: stagefright: created SoftVideoDecoderOMXComponent Created common base class for all software video decoders to make adding new features easier. Change-Id: Id89964e572d5cc5add02662273e6ae96c6b7eb12 Signed-off-by: Lajos Molnar --- .../codecs/m4v_h263/dec/SoftMPEG4.cpp | 322 +++------------------ .../libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h | 31 +- media/libstagefright/codecs/on2/dec/SoftVPX.cpp | 211 +------------- media/libstagefright/codecs/on2/dec/SoftVPX.h | 25 +- .../libstagefright/codecs/on2/h264dec/SoftAVC.cpp | 249 +--------------- media/libstagefright/codecs/on2/h264dec/SoftAVC.h | 28 +- .../include/SoftVideoDecoderOMXComponent.h | 93 ++++++ media/libstagefright/omx/Android.mk | 1 + .../omx/SoftVideoDecoderOMXComponent.cpp | 290 +++++++++++++++++++ 9 files changed, 456 insertions(+), 794 deletions(-) create mode 100644 media/libstagefright/include/SoftVideoDecoderOMXComponent.h create mode 100644 media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp (limited to 'media') diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index bb5625f..fb2a430 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -48,43 +48,32 @@ static const CodecProfileLevel kH263ProfileLevels[] = { { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level45 }, }; -template -static void InitOMXParams(T *params) { - params->nSize = sizeof(T); - params->nVersion.s.nVersionMajor = 1; - params->nVersion.s.nVersionMinor = 0; - params->nVersion.s.nRevision = 0; - params->nVersion.s.nStep = 0; -} - SoftMPEG4::SoftMPEG4( const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) - : SimpleSoftOMXComponent(name, callbacks, appData, component), - mMode(MODE_MPEG4), + : SoftVideoDecoderOMXComponent( + name, componentRole, codingType, profileLevels, numProfileLevels, + 352 /* width */, 288 /* height */, callbacks, appData, component), + mMode(codingType == OMX_VIDEO_CodingH263 ? MODE_H263 : MODE_MPEG4), mHandle(new tagvideoDecControls), mInputBufferCount(0), - mWidth(352), - mHeight(288), - mCropLeft(0), - mCropTop(0), - mCropRight(mWidth - 1), - mCropBottom(mHeight - 1), mSignalledError(false), mInitialized(false), mFramesConfigured(false), mNumSamplesOutput(0), - mPvTime(0), - mOutputPortSettingsChange(NONE) { - if (!strcmp(name, "OMX.google.h263.decoder")) { - mMode = MODE_H263; - } else { - CHECK(!strcmp(name, "OMX.google.mpeg4.decoder")); - } - - initPorts(); + mPvTime(0) { + initPorts( + kNumInputBuffers, + 8192 /* inputBufferSize */, + kNumOutputBuffers, + (mMode == MODE_MPEG4) + ? MEDIA_MIMETYPE_VIDEO_MPEG4 : MEDIA_MIMETYPE_VIDEO_H263); CHECK_EQ(initDecoder(), (status_t)OK); } @@ -97,219 +86,11 @@ SoftMPEG4::~SoftMPEG4() { mHandle = NULL; } -void SoftMPEG4::initPorts() { - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - - def.nPortIndex = 0; - def.eDir = OMX_DirInput; - def.nBufferCountMin = kNumInputBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = 8192; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 1; - - def.format.video.cMIMEType = - (mMode == MODE_MPEG4) - ? const_cast(MEDIA_MIMETYPE_VIDEO_MPEG4) - : const_cast(MEDIA_MIMETYPE_VIDEO_H263); - - def.format.video.pNativeRender = NULL; - def.format.video.nFrameWidth = mWidth; - def.format.video.nFrameHeight = mHeight; - def.format.video.nStride = def.format.video.nFrameWidth; - def.format.video.nSliceHeight = def.format.video.nFrameHeight; - def.format.video.nBitrate = 0; - def.format.video.xFramerate = 0; - def.format.video.bFlagErrorConcealment = OMX_FALSE; - - def.format.video.eCompressionFormat = - mMode == MODE_MPEG4 ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263; - - def.format.video.eColorFormat = OMX_COLOR_FormatUnused; - def.format.video.pNativeWindow = NULL; - - addPort(def); - - def.nPortIndex = 1; - def.eDir = OMX_DirOutput; - def.nBufferCountMin = kNumOutputBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 2; - - def.format.video.cMIMEType = const_cast(MEDIA_MIMETYPE_VIDEO_RAW); - def.format.video.pNativeRender = NULL; - def.format.video.nFrameWidth = mWidth; - def.format.video.nFrameHeight = mHeight; - def.format.video.nStride = def.format.video.nFrameWidth; - def.format.video.nSliceHeight = def.format.video.nFrameHeight; - def.format.video.nBitrate = 0; - def.format.video.xFramerate = 0; - def.format.video.bFlagErrorConcealment = OMX_FALSE; - def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; - def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; - def.format.video.pNativeWindow = NULL; - - def.nBufferSize = - (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2; - - addPort(def); -} - status_t SoftMPEG4::initDecoder() { memset(mHandle, 0, sizeof(tagvideoDecControls)); return OK; } -OMX_ERRORTYPE SoftMPEG4::internalGetParameter( - OMX_INDEXTYPE index, OMX_PTR params) { - switch (index) { - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex != 0) { - return OMX_ErrorNoMore; - } - - if (formatParams->nPortIndex == 0) { - formatParams->eCompressionFormat = - (mMode == MODE_MPEG4) - ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263; - - formatParams->eColorFormat = OMX_COLOR_FormatUnused; - formatParams->xFramerate = 0; - } else { - CHECK_EQ(formatParams->nPortIndex, 1u); - - formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; - formatParams->xFramerate = 0; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoProfileLevelQuerySupported: - { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; - - if (profileLevel->nPortIndex != 0) { // Input port only - ALOGE("Invalid port index: %ld", profileLevel->nPortIndex); - return OMX_ErrorUnsupportedIndex; - } - - size_t index = profileLevel->nProfileIndex; - if (mMode == MODE_H263) { - size_t nProfileLevels = - sizeof(kH263ProfileLevels) / sizeof(kH263ProfileLevels[0]); - if (index >= nProfileLevels) { - return OMX_ErrorNoMore; - } - - profileLevel->eProfile = kH263ProfileLevels[index].mProfile; - profileLevel->eLevel = kH263ProfileLevels[index].mLevel; - } else { - size_t nProfileLevels = - sizeof(kM4VProfileLevels) / sizeof(kM4VProfileLevels[0]); - if (index >= nProfileLevels) { - return OMX_ErrorNoMore; - } - - profileLevel->eProfile = kM4VProfileLevels[index].mProfile; - profileLevel->eLevel = kM4VProfileLevels[index].mLevel; - } - return OMX_ErrorNone; - } - - default: - return SimpleSoftOMXComponent::internalGetParameter(index, params); - } -} - -OMX_ERRORTYPE SoftMPEG4::internalSetParameter( - OMX_INDEXTYPE index, const OMX_PTR params) { - switch (index) { - case OMX_IndexParamStandardComponentRole: - { - const OMX_PARAM_COMPONENTROLETYPE *roleParams = - (const OMX_PARAM_COMPONENTROLETYPE *)params; - - if (mMode == MODE_MPEG4) { - if (strncmp((const char *)roleParams->cRole, - "video_decoder.mpeg4", - OMX_MAX_STRINGNAME_SIZE - 1)) { - return OMX_ErrorUndefined; - } - } else { - if (strncmp((const char *)roleParams->cRole, - "video_decoder.h263", - OMX_MAX_STRINGNAME_SIZE - 1)) { - return OMX_ErrorUndefined; - } - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex != 0) { - return OMX_ErrorNoMore; - } - - return OMX_ErrorNone; - } - - default: - return SimpleSoftOMXComponent::internalSetParameter(index, params); - } -} - -OMX_ERRORTYPE SoftMPEG4::getConfig( - OMX_INDEXTYPE index, OMX_PTR params) { - switch (index) { - case OMX_IndexConfigCommonOutputCrop: - { - OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; - - if (rectParams->nPortIndex != 1) { - return OMX_ErrorUndefined; - } - - rectParams->nLeft = mCropLeft; - rectParams->nTop = mCropTop; - rectParams->nWidth = mCropRight - mCropLeft + 1; - rectParams->nHeight = mCropBottom - mCropTop + 1; - - return OMX_ErrorNone; - } - - default: - return OMX_ErrorUnsupportedIndex; - } -} - void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { if (mSignalledError || mOutputPortSettingsChange != NONE) { return; @@ -489,11 +270,11 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { } bool SoftMPEG4::portSettingsChanged() { - int32_t disp_width, disp_height; - PVGetVideoDimensions(mHandle, &disp_width, &disp_height); + uint32_t disp_width, disp_height; + PVGetVideoDimensions(mHandle, (int32 *)&disp_width, (int32 *)&disp_height); - int32_t buf_width, buf_height; - PVGetBufferDimensions(mHandle, &buf_width, &buf_height); + uint32_t buf_width, buf_height; + PVGetBufferDimensions(mHandle, (int32 *)&buf_width, (int32 *)&buf_height); CHECK_LE(disp_width, buf_width); CHECK_LE(disp_height, buf_height); @@ -501,12 +282,12 @@ bool SoftMPEG4::portSettingsChanged() { ALOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d", disp_width, disp_height, buf_width, buf_height); - if (mCropRight != disp_width - 1 - || mCropBottom != disp_height - 1) { + if (mCropWidth != disp_width + || mCropHeight != disp_height) { mCropLeft = 0; mCropTop = 0; - mCropRight = disp_width - 1; - mCropBottom = disp_height - 1; + mCropWidth = disp_width; + mCropHeight = disp_height; notify(OMX_EventPortSettingsChanged, 1, @@ -552,36 +333,10 @@ void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) { } } -void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { - if (portIndex != 1) { - return; - } - - switch (mOutputPortSettingsChange) { - case NONE: - break; - - case AWAITING_DISABLED: - { - CHECK(!enabled); - mOutputPortSettingsChange = AWAITING_ENABLED; - break; - } - - default: - { - CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); - CHECK(enabled); - mOutputPortSettingsChange = NONE; - break; - } - } -} - void SoftMPEG4::onReset() { + SoftVideoDecoderOMXComponent::onReset(); mPvToOmxTimeMap.clear(); mSignalledError = false; - mOutputPortSettingsChange = NONE; mFramesConfigured = false; if (mInitialized) { PVCleanUpVideoDecoder(mHandle); @@ -590,18 +345,10 @@ void SoftMPEG4::onReset() { } void SoftMPEG4::updatePortDefinitions() { - OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - - def = &editPortInfo(1)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; + SoftVideoDecoderOMXComponent::updatePortDefinitions(); + /* We have to align our width and height - this should affect stride! */ + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; def->nBufferSize = (((def->format.video.nFrameWidth + 15) & -16) * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2; @@ -612,6 +359,19 @@ void SoftMPEG4::updatePortDefinitions() { android::SoftOMXComponent *createSoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { - return new android::SoftMPEG4(name, callbacks, appData, component); + using namespace android; + if (!strcmp(name, "OMX.google.h263.decoder")) { + return new android::SoftMPEG4( + name, "video_decoder.h263", OMX_VIDEO_CodingH263, + kH263ProfileLevels, ARRAY_SIZE(kH263ProfileLevels), + callbacks, appData, component); + } else if (!strcmp(name, "OMX.google.mpeg4.decoder")) { + return new android::SoftMPEG4( + name, "video_decoder.mpeg4", OMX_VIDEO_CodingMPEG4, + kM4VProfileLevels, ARRAY_SIZE(kM4VProfileLevels), + callbacks, appData, component); + } else { + CHECK(!"Unknown component"); + } } diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h index f71ccef..de14aaf 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h @@ -18,15 +18,18 @@ #define SOFT_MPEG4_H_ -#include "SimpleSoftOMXComponent.h" -#include +#include "SoftVideoDecoderOMXComponent.h" struct tagvideoDecControls; namespace android { -struct SoftMPEG4 : public SimpleSoftOMXComponent { +struct SoftMPEG4 : public SoftVideoDecoderOMXComponent { SoftMPEG4(const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component); @@ -34,17 +37,8 @@ struct SoftMPEG4 : public SimpleSoftOMXComponent { protected: virtual ~SoftMPEG4(); - virtual OMX_ERRORTYPE internalGetParameter( - OMX_INDEXTYPE index, OMX_PTR params); - - virtual OMX_ERRORTYPE internalSetParameter( - OMX_INDEXTYPE index, const OMX_PTR params); - - virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params); - virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); - virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); virtual void onReset(); private: @@ -56,16 +50,12 @@ private: enum { MODE_MPEG4, MODE_H263, - } mMode; tagvideoDecControls *mHandle; size_t mInputBufferCount; - int32_t mWidth, mHeight; - int32_t mCropLeft, mCropTop, mCropRight, mCropBottom; - bool mSignalledError; bool mInitialized; bool mFramesConfigured; @@ -74,16 +64,9 @@ private: int32_t mPvTime; KeyedVector mPvToOmxTimeMap; - enum { - NONE, - AWAITING_DISABLED, - AWAITING_ENABLED - } mOutputPortSettingsChange; - - void initPorts(); status_t initDecoder(); - void updatePortDefinitions(); + virtual void updatePortDefinitions(); bool portSettingsChanged(); DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4); diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index fe76036..43d0263 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -29,26 +29,19 @@ namespace android { -template -static void InitOMXParams(T *params) { - params->nSize = sizeof(T); - params->nVersion.s.nVersionMajor = 1; - params->nVersion.s.nVersionMinor = 0; - params->nVersion.s.nRevision = 0; - params->nVersion.s.nStep = 0; -} - SoftVPX::SoftVPX( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) - : SimpleSoftOMXComponent(name, callbacks, appData, component), - mCtx(NULL), - mWidth(320), - mHeight(240), - mOutputPortSettingsChange(NONE) { - initPorts(); + : SoftVideoDecoderOMXComponent( + name, "video_decoder.vpx", OMX_VIDEO_CodingVPX, + NULL /* profileLevels */, 0 /* numProfileLevels */, + 320 /* width */, 240 /* height */, callbacks, appData, component), + mCtx(NULL) { + initPorts(kNumBuffers, 768 * 1024 /* inputBufferSize */, + kNumBuffers, MEDIA_MIMETYPE_VIDEO_VPX); + CHECK_EQ(initDecoder(), (status_t)OK); } @@ -58,65 +51,6 @@ SoftVPX::~SoftVPX() { mCtx = NULL; } -void SoftVPX::initPorts() { - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - - def.nPortIndex = 0; - def.eDir = OMX_DirInput; - def.nBufferCountMin = kNumBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = 768 * 1024; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 1; - - def.format.video.cMIMEType = const_cast(MEDIA_MIMETYPE_VIDEO_VPX); - def.format.video.pNativeRender = NULL; - def.format.video.nFrameWidth = mWidth; - def.format.video.nFrameHeight = mHeight; - def.format.video.nStride = def.format.video.nFrameWidth; - def.format.video.nSliceHeight = def.format.video.nFrameHeight; - def.format.video.nBitrate = 0; - def.format.video.xFramerate = 0; - def.format.video.bFlagErrorConcealment = OMX_FALSE; - def.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX; - def.format.video.eColorFormat = OMX_COLOR_FormatUnused; - def.format.video.pNativeWindow = NULL; - - addPort(def); - - def.nPortIndex = 1; - def.eDir = OMX_DirOutput; - def.nBufferCountMin = kNumBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 2; - - def.format.video.cMIMEType = const_cast(MEDIA_MIMETYPE_VIDEO_RAW); - def.format.video.pNativeRender = NULL; - def.format.video.nFrameWidth = mWidth; - def.format.video.nFrameHeight = mHeight; - def.format.video.nStride = def.format.video.nFrameWidth; - def.format.video.nSliceHeight = def.format.video.nFrameHeight; - def.format.video.nBitrate = 0; - def.format.video.xFramerate = 0; - def.format.video.bFlagErrorConcealment = OMX_FALSE; - def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; - def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; - def.format.video.pNativeWindow = NULL; - - def.nBufferSize = - (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2; - - addPort(def); -} - static int GetCPUCoreCount() { int cpuCoreCount = 1; #if defined(_SC_NPROCESSORS_ONLN) @@ -145,80 +79,6 @@ status_t SoftVPX::initDecoder() { return OK; } -OMX_ERRORTYPE SoftVPX::internalGetParameter( - OMX_INDEXTYPE index, OMX_PTR params) { - switch (index) { - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex != 0) { - return OMX_ErrorNoMore; - } - - if (formatParams->nPortIndex == 0) { - formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX; - formatParams->eColorFormat = OMX_COLOR_FormatUnused; - formatParams->xFramerate = 0; - } else { - CHECK_EQ(formatParams->nPortIndex, 1u); - - formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; - formatParams->xFramerate = 0; - } - - return OMX_ErrorNone; - } - - default: - return SimpleSoftOMXComponent::internalGetParameter(index, params); - } -} - -OMX_ERRORTYPE SoftVPX::internalSetParameter( - OMX_INDEXTYPE index, const OMX_PTR params) { - switch (index) { - case OMX_IndexParamStandardComponentRole: - { - const OMX_PARAM_COMPONENTROLETYPE *roleParams = - (const OMX_PARAM_COMPONENTROLETYPE *)params; - - if (strncmp((const char *)roleParams->cRole, - "video_decoder.vpx", - OMX_MAX_STRINGNAME_SIZE - 1)) { - return OMX_ErrorUndefined; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > 1) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex != 0) { - return OMX_ErrorNoMore; - } - - return OMX_ErrorNone; - } - - default: - return SimpleSoftOMXComponent::internalSetParameter(index, params); - } -} - void SoftVPX::onQueueFilled(OMX_U32 portIndex) { if (mOutputPortSettingsChange != NONE) { return; @@ -270,8 +130,8 @@ void SoftVPX::onQueueFilled(OMX_U32 portIndex) { if (img != NULL) { CHECK_EQ(img->fmt, IMG_FMT_I420); - int32_t width = img->d_w; - int32_t height = img->d_h; + uint32_t width = img->d_w; + uint32_t height = img->d_h; if (width != mWidth || height != mHeight) { mWidth = width; @@ -329,57 +189,6 @@ void SoftVPX::onQueueFilled(OMX_U32 portIndex) { } } -void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { -} - -void SoftVPX::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { - if (portIndex != 1) { - return; - } - - switch (mOutputPortSettingsChange) { - case NONE: - break; - - case AWAITING_DISABLED: - { - CHECK(!enabled); - mOutputPortSettingsChange = AWAITING_ENABLED; - break; - } - - default: - { - CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); - CHECK(enabled); - mOutputPortSettingsChange = NONE; - break; - } - } -} - -void SoftVPX::onReset() { - mOutputPortSettingsChange = NONE; -} - -void SoftVPX::updatePortDefinitions() { - OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - - def = &editPortInfo(1)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - - def->nBufferSize = - (def->format.video.nFrameWidth - * def->format.video.nFrameHeight * 3) / 2; -} - } // namespace android android::SoftOMXComponent *createSoftOMXComponent( diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h index 4cb05cf..626307b 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.h +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -18,11 +18,11 @@ #define SOFT_VPX_H_ -#include "SimpleSoftOMXComponent.h" +#include "SoftVideoDecoderOMXComponent.h" namespace android { -struct SoftVPX : public SimpleSoftOMXComponent { +struct SoftVPX : public SoftVideoDecoderOMXComponent { SoftVPX(const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, @@ -31,16 +31,7 @@ struct SoftVPX : public SimpleSoftOMXComponent { protected: virtual ~SoftVPX(); - virtual OMX_ERRORTYPE internalGetParameter( - OMX_INDEXTYPE index, OMX_PTR params); - - virtual OMX_ERRORTYPE internalSetParameter( - OMX_INDEXTYPE index, const OMX_PTR params); - virtual void onQueueFilled(OMX_U32 portIndex); - virtual void onPortFlushCompleted(OMX_U32 portIndex); - virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); - virtual void onReset(); private: enum { @@ -49,20 +40,8 @@ private: void *mCtx; - int32_t mWidth; - int32_t mHeight; - - enum { - NONE, - AWAITING_DISABLED, - AWAITING_ENABLED - } mOutputPortSettingsChange; - - void initPorts(); status_t initDecoder(); - void updatePortDefinitions(); - DISALLOW_EVIL_CONSTRUCTORS(SoftVPX); }; diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp index 5e299d5..3bd9f47 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp @@ -47,38 +47,28 @@ static const CodecProfileLevel kProfileLevels[] = { { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 }, }; -template -static void InitOMXParams(T *params) { - params->nSize = sizeof(T); - params->nVersion.s.nVersionMajor = 1; - params->nVersion.s.nVersionMinor = 0; - params->nVersion.s.nRevision = 0; - params->nVersion.s.nStep = 0; -} - SoftAVC::SoftAVC( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) - : SimpleSoftOMXComponent(name, callbacks, appData, component), + : SoftVideoDecoderOMXComponent( + name, "video_decoder.avc", OMX_VIDEO_CodingAVC, + kProfileLevels, ARRAY_SIZE(kProfileLevels), + 320 /* width */, 240 /* height */, callbacks, appData, component), mHandle(NULL), mInputBufferCount(0), - mWidth(320), - mHeight(240), mPictureSize(mWidth * mHeight * 3 / 2), - mCropLeft(0), - mCropTop(0), - mCropWidth(mWidth), - mCropHeight(mHeight), mFirstPicture(NULL), mFirstPictureId(-1), mPicId(0), mHeadersDecoded(false), mEOSStatus(INPUT_DATA_AVAILABLE), - mOutputPortSettingsChange(NONE), mSignalledError(false) { - initPorts(); + initPorts( + kNumInputBuffers, 8192 /* inputBufferSize */, + kNumOutputBuffers, MEDIA_MIMETYPE_VIDEO_AVC); + CHECK_EQ(initDecoder(), (status_t)OK); } @@ -100,65 +90,6 @@ SoftAVC::~SoftAVC() { delete[] mFirstPicture; } -void SoftAVC::initPorts() { - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - - def.nPortIndex = kInputPortIndex; - def.eDir = OMX_DirInput; - def.nBufferCountMin = kNumInputBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = 8192; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 1; - - def.format.video.cMIMEType = const_cast(MEDIA_MIMETYPE_VIDEO_AVC); - def.format.video.pNativeRender = NULL; - def.format.video.nFrameWidth = mWidth; - def.format.video.nFrameHeight = mHeight; - def.format.video.nStride = def.format.video.nFrameWidth; - def.format.video.nSliceHeight = def.format.video.nFrameHeight; - def.format.video.nBitrate = 0; - def.format.video.xFramerate = 0; - def.format.video.bFlagErrorConcealment = OMX_FALSE; - def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; - def.format.video.eColorFormat = OMX_COLOR_FormatUnused; - def.format.video.pNativeWindow = NULL; - - addPort(def); - - def.nPortIndex = kOutputPortIndex; - def.eDir = OMX_DirOutput; - def.nBufferCountMin = kNumOutputBuffers; - def.nBufferCountActual = def.nBufferCountMin; - def.bEnabled = OMX_TRUE; - def.bPopulated = OMX_FALSE; - def.eDomain = OMX_PortDomainVideo; - def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 2; - - def.format.video.cMIMEType = const_cast(MEDIA_MIMETYPE_VIDEO_RAW); - def.format.video.pNativeRender = NULL; - def.format.video.nFrameWidth = mWidth; - def.format.video.nFrameHeight = mHeight; - def.format.video.nStride = def.format.video.nFrameWidth; - def.format.video.nSliceHeight = def.format.video.nFrameHeight; - def.format.video.nBitrate = 0; - def.format.video.xFramerate = 0; - def.format.video.bFlagErrorConcealment = OMX_FALSE; - def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; - def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; - def.format.video.pNativeWindow = NULL; - - def.nBufferSize = - (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2; - - addPort(def); -} - status_t SoftAVC::initDecoder() { // Force decoder to output buffers in display order. if (H264SwDecInit(&mHandle, 0) == H264SWDEC_OK) { @@ -167,126 +98,6 @@ status_t SoftAVC::initDecoder() { return UNKNOWN_ERROR; } -OMX_ERRORTYPE SoftAVC::internalGetParameter( - OMX_INDEXTYPE index, OMX_PTR params) { - switch (index) { - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > kOutputPortIndex) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex != 0) { - return OMX_ErrorNoMore; - } - - if (formatParams->nPortIndex == kInputPortIndex) { - formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC; - formatParams->eColorFormat = OMX_COLOR_FormatUnused; - formatParams->xFramerate = 0; - } else { - CHECK(formatParams->nPortIndex == kOutputPortIndex); - - formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; - formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; - formatParams->xFramerate = 0; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoProfileLevelQuerySupported: - { - OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = - (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; - - if (profileLevel->nPortIndex != kInputPortIndex) { - ALOGE("Invalid port index: %ld", profileLevel->nPortIndex); - return OMX_ErrorUnsupportedIndex; - } - - size_t index = profileLevel->nProfileIndex; - size_t nProfileLevels = - sizeof(kProfileLevels) / sizeof(kProfileLevels[0]); - if (index >= nProfileLevels) { - return OMX_ErrorNoMore; - } - - profileLevel->eProfile = kProfileLevels[index].mProfile; - profileLevel->eLevel = kProfileLevels[index].mLevel; - return OMX_ErrorNone; - } - - default: - return SimpleSoftOMXComponent::internalGetParameter(index, params); - } -} - -OMX_ERRORTYPE SoftAVC::internalSetParameter( - OMX_INDEXTYPE index, const OMX_PTR params) { - switch (index) { - case OMX_IndexParamStandardComponentRole: - { - const OMX_PARAM_COMPONENTROLETYPE *roleParams = - (const OMX_PARAM_COMPONENTROLETYPE *)params; - - if (strncmp((const char *)roleParams->cRole, - "video_decoder.avc", - OMX_MAX_STRINGNAME_SIZE - 1)) { - return OMX_ErrorUndefined; - } - - return OMX_ErrorNone; - } - - case OMX_IndexParamVideoPortFormat: - { - OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = - (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; - - if (formatParams->nPortIndex > kOutputPortIndex) { - return OMX_ErrorUndefined; - } - - if (formatParams->nIndex != 0) { - return OMX_ErrorNoMore; - } - - return OMX_ErrorNone; - } - - default: - return SimpleSoftOMXComponent::internalSetParameter(index, params); - } -} - -OMX_ERRORTYPE SoftAVC::getConfig( - OMX_INDEXTYPE index, OMX_PTR params) { - switch (index) { - case OMX_IndexConfigCommonOutputCrop: - { - OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; - - if (rectParams->nPortIndex != 1) { - return OMX_ErrorUndefined; - } - - rectParams->nLeft = mCropLeft; - rectParams->nTop = mCropTop; - rectParams->nWidth = mCropWidth; - rectParams->nHeight = mCropHeight; - - return OMX_ErrorNone; - } - - default: - return OMX_ErrorUnsupportedIndex; - } -} - void SoftAVC::onQueueFilled(OMX_U32 portIndex) { if (mSignalledError || mOutputPortSettingsChange != NONE) { return; @@ -409,8 +220,6 @@ bool SoftAVC::handlePortSettingChangeEvent(const H264SwDecInfo *info) { mWidth = info->picWidth; mHeight = info->picHeight; mPictureSize = mWidth * mHeight * 3 / 2; - mCropWidth = mWidth; - mCropHeight = mHeight; updatePortDefinitions(); notify(OMX_EventPortSettingsChanged, 1, 0, NULL); mOutputPortSettingsChange = AWAITING_DISABLED; @@ -508,49 +317,9 @@ void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) { } } -void SoftAVC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { - switch (mOutputPortSettingsChange) { - case NONE: - break; - - case AWAITING_DISABLED: - { - CHECK(!enabled); - mOutputPortSettingsChange = AWAITING_ENABLED; - break; - } - - default: - { - CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); - CHECK(enabled); - mOutputPortSettingsChange = NONE; - break; - } - } -} - void SoftAVC::onReset() { + SoftVideoDecoderOMXComponent::onReset(); mSignalledError = false; - mOutputPortSettingsChange = NONE; -} - -void SoftAVC::updatePortDefinitions() { - OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - - def = &editPortInfo(1)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; - def->format.video.nStride = def->format.video.nFrameWidth; - def->format.video.nSliceHeight = def->format.video.nFrameHeight; - - def->nBufferSize = - (def->format.video.nFrameWidth - * def->format.video.nFrameHeight * 3) / 2; } } // namespace android diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h index 8c104c5..0ed7ebe 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h @@ -18,7 +18,7 @@ #define SOFT_AVC_H_ -#include "SimpleSoftOMXComponent.h" +#include "SoftVideoDecoderOMXComponent.h" #include #include "H264SwDecApi.h" @@ -26,7 +26,7 @@ namespace android { -struct SoftAVC : public SimpleSoftOMXComponent { +struct SoftAVC : public SoftVideoDecoderOMXComponent { SoftAVC(const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, @@ -35,23 +35,12 @@ struct SoftAVC : public SimpleSoftOMXComponent { protected: virtual ~SoftAVC(); - virtual OMX_ERRORTYPE internalGetParameter( - OMX_INDEXTYPE index, OMX_PTR params); - - virtual OMX_ERRORTYPE internalSetParameter( - OMX_INDEXTYPE index, const OMX_PTR params); - - virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params); - virtual void onQueueFilled(OMX_U32 portIndex); virtual void onPortFlushCompleted(OMX_U32 portIndex); - virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); virtual void onReset(); private: enum { - kInputPortIndex = 0, - kOutputPortIndex = 1, kNumInputBuffers = 8, kNumOutputBuffers = 2, }; @@ -66,9 +55,7 @@ private: size_t mInputBufferCount; - uint32_t mWidth, mHeight, mPictureSize; - uint32_t mCropLeft, mCropTop; - uint32_t mCropWidth, mCropHeight; + uint32_t mPictureSize; uint8_t *mFirstPicture; int32_t mFirstPictureId; @@ -82,18 +69,9 @@ private: EOSStatus mEOSStatus; - enum OutputPortSettingChange { - NONE, - AWAITING_DISABLED, - AWAITING_ENABLED - }; - OutputPortSettingChange mOutputPortSettingsChange; - bool mSignalledError; - void initPorts(); status_t initDecoder(); - void updatePortDefinitions(); bool drainAllOutputBuffers(); void drainOneOutputBuffer(int32_t picId, uint8_t *data); void saveFirstOutputBuffer(int32_t pidId, uint8_t *data); diff --git a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h new file mode 100644 index 0000000..d050fa6 --- /dev/null +++ b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 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 SOFT_VIDEO_DECODER_OMX_COMPONENT_H_ + +#define SOFT_VIDEO_DECODER_OMX_COMPONENT_H_ + +#include "SimpleSoftOMXComponent.h" + +#include +#include + +#include +#include +#include + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +namespace android { + +struct SoftVideoDecoderOMXComponent : public SimpleSoftOMXComponent { + SoftVideoDecoderOMXComponent( + const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + int32_t width, + int32_t height, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE getConfig( + OMX_INDEXTYPE index, OMX_PTR params); + + void initPorts(OMX_U32 numInputBuffers, + OMX_U32 inputBufferSize, + OMX_U32 numOutputBuffers, + const char *mimeType); + + virtual void updatePortDefinitions(); + + enum { + kInputPortIndex = 0, + kOutputPortIndex = 1, + kMaxPortIndex = 1, + }; + + uint32_t mWidth, mHeight; + uint32_t mCropLeft, mCropTop, mCropWidth, mCropHeight; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + +private: + const char *mComponentRole; + OMX_VIDEO_CODINGTYPE mCodingType; + const CodecProfileLevel *mProfileLevels; + size_t mNumProfileLevels; + + DISALLOW_EVIL_CONSTRUCTORS(SoftVideoDecoderOMXComponent); +}; + +} // namespace android + +#endif // SOFT_VIDEO_DECODER_OMX_COMPONENT_H_ diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index a8b4939..cd912e7 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \ SimpleSoftOMXComponent.cpp \ SoftOMXComponent.cpp \ SoftOMXPlugin.cpp \ + SoftVideoDecoderOMXComponent.cpp \ LOCAL_C_INCLUDES += \ $(TOP)/frameworks/av/media/libstagefright \ diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp new file mode 100644 index 0000000..08a3d42 --- /dev/null +++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2013 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 "SoftVideoDecoderOMXComponent" +#include + +#include "include/SoftVideoDecoderOMXComponent.h" + +#include +#include +#include +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftVideoDecoderOMXComponent::SoftVideoDecoderOMXComponent( + const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + int32_t width, + int32_t height, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mWidth(width), + mHeight(height), + mCropLeft(0), + mCropTop(0), + mCropWidth(width), + mCropHeight(height), + mOutputPortSettingsChange(NONE), + mComponentRole(componentRole), + mCodingType(codingType), + mProfileLevels(profileLevels), + mNumProfileLevels(numProfileLevels) { +} + +void SoftVideoDecoderOMXComponent::initPorts( + OMX_U32 numInputBuffers, + OMX_U32 inputBufferSize, + OMX_U32 numOutputBuffers, + const char *mimeType) { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = kInputPortIndex; + def.eDir = OMX_DirInput; + def.nBufferCountMin = numInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = inputBufferSize; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.video.cMIMEType = const_cast(mimeType); + def.format.video.pNativeRender = NULL; + /* size is initialized in updatePortDefinitions() */ + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.format.video.eCompressionFormat = mCodingType; + def.format.video.eColorFormat = OMX_COLOR_FormatUnused; + def.format.video.pNativeWindow = NULL; + + addPort(def); + + def.nPortIndex = kOutputPortIndex; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = numOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainVideo; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.video.cMIMEType = const_cast("video/raw"); + def.format.video.pNativeRender = NULL; + /* size is initialized in updatePortDefinitions() */ + def.format.video.nBitrate = 0; + def.format.video.xFramerate = 0; + def.format.video.bFlagErrorConcealment = OMX_FALSE; + def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar; + def.format.video.pNativeWindow = NULL; + + addPort(def); + + updatePortDefinitions(); +} + +void SoftVideoDecoderOMXComponent::updatePortDefinitions() { + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def = &editPortInfo(kOutputPortIndex)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + + def->nBufferSize = + (def->format.video.nFrameWidth * + def->format.video.nFrameHeight * 3) / 2; + + mCropLeft = 0; + mCropTop = 0; + mCropWidth = mWidth; + mCropHeight = mHeight; +} + +OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > kMaxPortIndex) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + if (formatParams->nPortIndex == kInputPortIndex) { + formatParams->eCompressionFormat = mCodingType; + formatParams->eColorFormat = OMX_COLOR_FormatUnused; + formatParams->xFramerate = 0; + } else { + CHECK_EQ(formatParams->nPortIndex, 1u); + + formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused; + formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar; + formatParams->xFramerate = 0; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoProfileLevelQuerySupported: + { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; + + if (profileLevel->nPortIndex != kInputPortIndex) { + ALOGE("Invalid port index: %ld", profileLevel->nPortIndex); + return OMX_ErrorUnsupportedIndex; + } + + if (index >= mNumProfileLevels) { + return OMX_ErrorNoMore; + } + + profileLevel->eProfile = mProfileLevels[index].mProfile; + profileLevel->eLevel = mProfileLevels[index].mLevel; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (strncmp((const char *)roleParams->cRole, + mComponentRole, + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params; + + if (formatParams->nPortIndex > kMaxPortIndex) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexConfigCommonOutputCrop: + { + OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params; + + if (rectParams->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUndefined; + } + + rectParams->nLeft = mCropLeft; + rectParams->nTop = mCropTop; + rectParams->nWidth = mCropWidth; + rectParams->nHeight = mCropHeight; + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +void SoftVideoDecoderOMXComponent::onReset() { + mOutputPortSettingsChange = NONE; +} + +void SoftVideoDecoderOMXComponent::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != kOutputPortIndex) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android -- cgit v1.1 From d1fffa24d9b5d0d6f5ff9eda372befe114ceefb6 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 6 Jun 2013 15:00:14 -0700 Subject: Remove "LOCAL_MODULE_TAGS := debug" for stagefright tests Change-Id: I53815d2f6d7dfe7eebb26c3802eb3d195244aab1 --- media/libstagefright/wifi-display/Android.mk | 8 -------- 1 file changed, 8 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index f99ef60..404b41e 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -64,8 +64,6 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= wfd -LOCAL_MODULE_TAGS := debug - include $(BUILD_EXECUTABLE) ################################################################################ @@ -87,8 +85,6 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= udptest -LOCAL_MODULE_TAGS := debug - include $(BUILD_EXECUTABLE) ################################################################################ @@ -110,8 +106,6 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= rtptest -LOCAL_MODULE_TAGS := debug - include $(BUILD_EXECUTABLE) ################################################################################ @@ -133,6 +127,4 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= nettest -LOCAL_MODULE_TAGS := debug - include $(BUILD_EXECUTABLE) -- cgit v1.1 From 9fef8d453b15a91a2b748faac2bfaff713bcf1e1 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 12 Jun 2013 10:26:19 -0700 Subject: Converter now takes the desired _output_ format instead of the input format, allowing control over the type of encoding. Change-Id: Iaaa1a825f447ea130e373bbd8e5dc96f2762db18 --- .../wifi-display/source/Converter.cpp | 47 +++++++++------------- .../libstagefright/wifi-display/source/Converter.h | 11 ++--- .../wifi-display/source/PlaybackSession.cpp | 9 ++++- 3 files changed, 30 insertions(+), 37 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index e62505d..0214520 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -40,14 +40,13 @@ namespace android { Converter::Converter( const sp ¬ify, const sp &codecLooper, - const sp &format, - bool usePCMAudio) + const sp &outputFormat) : mInitCheck(NO_INIT), mNotify(notify), mCodecLooper(codecLooper), - mInputFormat(format), + mOutputFormat(outputFormat), mIsVideo(false), - mIsPCMAudio(usePCMAudio), + mIsPCMAudio(false), mNeedToManuallyPrependSPSPPS(false), mDoMoreWorkPending(false) #if ENABLE_SILENCE_DETECTION @@ -58,14 +57,14 @@ Converter::Converter( ,mNumFramesToDrop(0) { AString mime; - CHECK(mInputFormat->findString("mime", &mime)); + CHECK(mOutputFormat->findString("mime", &mime)); if (!strncasecmp("video/", mime.c_str(), 6)) { mIsVideo = true; + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime.c_str())) { + mIsPCMAudio = true; } - CHECK(!usePCMAudio || !mIsVideo); - mInitCheck = initEncoder(); if (mInitCheck != OK) { @@ -152,23 +151,10 @@ int32_t Converter::GetInt32Property( } status_t Converter::initEncoder() { - AString inputMIME; - CHECK(mInputFormat->findString("mime", &inputMIME)); - AString outputMIME; - bool isAudio = false; - if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) { - if (mIsPCMAudio) { - outputMIME = MEDIA_MIMETYPE_AUDIO_RAW; - } else { - outputMIME = MEDIA_MIMETYPE_AUDIO_AAC; - } - isAudio = true; - } else if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_VIDEO_RAW)) { - outputMIME = MEDIA_MIMETYPE_VIDEO_AVC; - } else { - TRESPASS(); - } + CHECK(mOutputFormat->findString("mime", &outputMIME)); + + bool isAudio = !strncasecmp(outputMIME.c_str(), "audio/", 6); if (!mIsPCMAudio) { mEncoder = MediaCodec::CreateByType( @@ -179,14 +165,10 @@ status_t Converter::initEncoder() { } } - mOutputFormat = mInputFormat->dup(); - if (mIsPCMAudio) { return OK; } - mOutputFormat->setString("mime", outputMIME.c_str()); - int32_t audioBitrate = GetInt32Property("media.wfd.audio-bitrate", 128000); int32_t videoBitrate = GetInt32Property("media.wfd.video-bitrate", 5000000); mPrevVideoBitrate = videoBitrate; @@ -427,7 +409,7 @@ void Converter::onMessageReceived(const sp &msg) { releaseEncoder(); AString mime; - CHECK(mInputFormat->findString("mime", &mime)); + CHECK(mOutputFormat->findString("mime", &mime)); ALOGI("encoder (%s) shut down.", mime.c_str()); break; } @@ -679,6 +661,15 @@ status_t Converter::doMoreWork() { notify->setInt32("what", kWhatEOS); notify->post(); } else { +#if 0 + if (mIsVideo) { + int32_t videoBitrate = GetInt32Property( + "media.wfd.video-bitrate", 5000000); + + setVideoBitrate(videoBitrate); + } +#endif + sp buffer; sp outbuf = mEncoderOutputBuffers.itemAt(bufferIndex); diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index fceef55..76c8b19 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -33,11 +33,9 @@ struct MediaCodec; // media access unit of a different format. // Right now this'll convert raw video into H.264 and raw audio into AAC. struct Converter : public AHandler { - Converter( - const sp ¬ify, - const sp &codecLooper, - const sp &format, - bool usePCMAudio); + Converter(const sp ¬ify, + const sp &codecLooper, + const sp &outputFormat); status_t initCheck() const; @@ -84,10 +82,9 @@ private: status_t mInitCheck; sp mNotify; sp mCodecLooper; - sp mInputFormat; + sp mOutputFormat; bool mIsVideo; bool mIsPCMAudio; - sp mOutputFormat; bool mNeedToManuallyPrependSPSPPS; sp mEncoder; diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 7f0ba96..a15fbac 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -937,6 +937,7 @@ status_t WifiDisplaySource::PlaybackSession::addSource( CHECK_EQ(err, (status_t)OK); if (isVideo) { + format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC); format->setInt32("store-metadata-in-buffers", true); format->setInt32("store-metadata-in-buffers-output", (mHDCP != NULL)); format->setInt32( @@ -944,13 +945,17 @@ status_t WifiDisplaySource::PlaybackSession::addSource( format->setInt32("profile-idc", profileIdc); format->setInt32("level-idc", levelIdc); format->setInt32("constraint-set", constraintSet); + } else { + format->setString( + "mime", + usePCMAudio + ? MEDIA_MIMETYPE_AUDIO_RAW : MEDIA_MIMETYPE_AUDIO_AAC); } notify = new AMessage(kWhatConverterNotify, id()); notify->setSize("trackIndex", trackIndex); - sp converter = - new Converter(notify, codecLooper, format, usePCMAudio); + sp converter = new Converter(notify, codecLooper, format); err = converter->initCheck(); if (err != OK) { -- cgit v1.1 From 9f80dd223d83d9bb9077fb6baee056cee4eaf7e5 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 18 Dec 2012 15:57:32 -0800 Subject: New control block for AudioTrack and AudioRecord Main differences between old and new control block: - removes the mutex, which was a potential source of priority inversion - circular indices into shared buffer, which is now always a power-of-2 size Change-Id: I4e9b7fa99858b488ac98a441fa70e31dbba1b865 --- media/libmedia/AudioRecord.cpp | 832 ++++++++++++++---------- media/libmedia/AudioTrack.cpp | 1219 +++++++++++++++++++---------------- media/libmedia/AudioTrackShared.cpp | 716 ++++++++++++++++---- media/libmedia/ToneGenerator.cpp | 4 +- 4 files changed, 1726 insertions(+), 1045 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index a2b8ae2..9faa497 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -19,18 +19,13 @@ #define LOG_TAG "AudioRecord" #include -#include - #include -#include -#include #include -#include -#include #include - #include +#define WAIT_PERIOD_MS 10 + namespace android { // --------------------------------------------------------------------------- @@ -41,7 +36,9 @@ status_t AudioRecord::getMinFrameCount( audio_format_t format, audio_channel_mask_t channelMask) { - if (frameCount == NULL) return BAD_VALUE; + if (frameCount == NULL) { + return BAD_VALUE; + } // default to 0 in case of error *frameCount = 0; @@ -75,8 +72,7 @@ status_t AudioRecord::getMinFrameCount( AudioRecord::AudioRecord() : mStatus(NO_INIT), mSessionId(0), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), - mProxy(NULL) + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) { } @@ -89,14 +85,15 @@ AudioRecord::AudioRecord( callback_t cbf, void* user, int notificationFrames, - int sessionId) + int sessionId, + transfer_type transferType) : mStatus(NO_INIT), mSessionId(0), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mProxy(NULL) { - mStatus = set(inputSource, sampleRate, format, channelMask, - frameCount, cbf, user, notificationFrames, false /*threadCanCallJava*/, sessionId); + mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user, + notificationFrames, false /*threadCanCallJava*/, sessionId, transferType); } AudioRecord::~AudioRecord() @@ -111,11 +108,13 @@ AudioRecord::~AudioRecord() mAudioRecordThread->requestExitAndWait(); mAudioRecordThread.clear(); } - mAudioRecord.clear(); + if (mAudioRecord != 0) { + mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this); + mAudioRecord.clear(); + } IPCThreadState::self()->flushCommands(); AudioSystem::releaseAudioSessionId(mSessionId); } - delete mProxy; } status_t AudioRecord::set( @@ -128,8 +127,32 @@ status_t AudioRecord::set( void* user, int notificationFrames, bool threadCanCallJava, - int sessionId) + int sessionId, + transfer_type transferType) { + switch (transferType) { + case TRANSFER_DEFAULT: + if (cbf == NULL || threadCanCallJava) { + transferType = TRANSFER_SYNC; + } else { + transferType = TRANSFER_CALLBACK; + } + break; + case TRANSFER_CALLBACK: + if (cbf == NULL) { + ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL"); + return BAD_VALUE; + } + break; + case TRANSFER_OBTAIN: + case TRANSFER_SYNC: + break; + default: + ALOGE("Invalid transfer type %d", transferType); + return BAD_VALUE; + } + mTransfer = transferType; + // FIXME "int" here is legacy and will be replaced by size_t later if (frameCountInt < 0) { ALOGE("Invalid frame count %d", frameCountInt); @@ -143,6 +166,7 @@ status_t AudioRecord::set( AutoMutex lock(mLock); if (mAudioRecord != 0) { + ALOGE("Track already in use"); return INVALID_OPERATION; } @@ -159,14 +183,16 @@ status_t AudioRecord::set( if (format == AUDIO_FORMAT_DEFAULT) { format = AUDIO_FORMAT_PCM_16_BIT; } + // validate parameters if (!audio_is_valid_format(format)) { - ALOGE("Invalid format"); + ALOGE("Invalid format %d", format); return BAD_VALUE; } mFormat = format; if (!audio_is_input_channel(channelMask)) { + ALOGE("Invalid channel mask %#x", channelMask); return BAD_VALUE; } mChannelMask = channelMask; @@ -200,6 +226,7 @@ status_t AudioRecord::set( size_t minFrameCount = 0; status_t status = getMinFrameCount(&minFrameCount, sampleRate, format, channelMask); if (status != NO_ERROR) { + ALOGE("getMinFrameCount() failed; status %d", status); return status; } ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount); @@ -207,6 +234,7 @@ status_t AudioRecord::set( if (frameCount == 0) { frameCount = minFrameCount; } else if (frameCount < minFrameCount) { + ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount); return BAD_VALUE; } @@ -215,7 +243,7 @@ status_t AudioRecord::set( } // create the IAudioRecord - status = openRecord_l(sampleRate, format, frameCount, input); + status = openRecord_l(sampleRate, format, frameCount, input, 0 /*epoch*/); if (status != NO_ERROR) { return status; } @@ -233,7 +261,7 @@ status_t AudioRecord::set( mActive = false; mCbf = cbf; mNotificationFrames = notificationFrames; - mRemainingFrames = notificationFrames; + mRefreshRemaining = true; mUserData = user; // TODO: add audio hardware input latency here mLatency = (1000*mFrameCount) / sampleRate; @@ -244,117 +272,78 @@ status_t AudioRecord::set( mInputSource = inputSource; mInput = input; AudioSystem::acquireAudioSessionId(mSessionId); + mSequence = 1; + mObservedSequence = mSequence; + mInOverrun = false; return NO_ERROR; } -status_t AudioRecord::initCheck() const -{ - return mStatus; -} - -// ------------------------------------------------------------------------- - -uint32_t AudioRecord::latency() const -{ - return mLatency; -} - -audio_format_t AudioRecord::format() const -{ - return mFormat; -} - -uint32_t AudioRecord::channelCount() const -{ - return mChannelCount; -} - -size_t AudioRecord::frameCount() const -{ - return mFrameCount; -} - -audio_source_t AudioRecord::inputSource() const -{ - return mInputSource; -} - // ------------------------------------------------------------------------- status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) { - status_t ret = NO_ERROR; - sp t = mAudioRecordThread; - ALOGV("start, sync event %d trigger session %d", event, triggerSession); AutoMutex lock(mLock); - // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed - // while we are accessing the cblk - sp audioRecord = mAudioRecord; - sp iMem = mCblkMemory; - audio_track_cblk_t* cblk = mCblk; + if (mActive) { + return NO_ERROR; + } - if (!mActive) { - mActive = true; + // reset current position as seen by client to 0 + mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition()); - cblk->lock.lock(); - if (!(cblk->flags & CBLK_INVALID)) { - cblk->lock.unlock(); - ALOGV("mAudioRecord->start()"); - ret = mAudioRecord->start(event, triggerSession); - cblk->lock.lock(); - if (ret == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID, &cblk->flags); - } - } - if (cblk->flags & CBLK_INVALID) { - audio_track_cblk_t* temp = cblk; - ret = restoreRecord_l(temp); - cblk = temp; + mNewPosition = mProxy->getPosition() + mUpdatePeriod; + int32_t flags = android_atomic_acquire_load(&mCblk->flags); + + status_t status = NO_ERROR; + if (!(flags & CBLK_INVALID)) { + ALOGV("mAudioRecord->start()"); + status = mAudioRecord->start(event, triggerSession); + if (status == DEAD_OBJECT) { + flags |= CBLK_INVALID; } - cblk->lock.unlock(); - if (ret == NO_ERROR) { - mNewPosition = cblk->user + mUpdatePeriod; - cblk->bufferTimeoutMs = (event == AudioSystem::SYNC_EVENT_NONE) ? MAX_RUN_TIMEOUT_MS : - AudioSystem::kSyncRecordStartTimeOutMs; - cblk->waitTimeMs = 0; - if (t != 0) { - t->resume(); - } else { - mPreviousPriority = getpriority(PRIO_PROCESS, 0); - get_sched_policy(0, &mPreviousSchedulingGroup); - androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); - } + } + if (flags & CBLK_INVALID) { + status = restoreRecord_l("start"); + } + + if (status != NO_ERROR) { + ALOGE("start() status %d", status); + } else { + mActive = true; + sp t = mAudioRecordThread; + if (t != 0) { + t->resume(); } else { - mActive = false; + mPreviousPriority = getpriority(PRIO_PROCESS, 0); + get_sched_policy(0, &mPreviousSchedulingGroup); + androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); } } - return ret; + return status; } void AudioRecord::stop() { - sp t = mAudioRecordThread; - - ALOGV("stop"); - AutoMutex lock(mLock); - if (mActive) { - mActive = false; - mCblk->cv.signal(); - mAudioRecord->stop(); - // the record head position will reset to 0, so if a marker is set, we need - // to activate it again - mMarkerReached = false; - if (t != 0) { - t->pause(); - } else { - setpriority(PRIO_PROCESS, 0, mPreviousPriority); - set_sched_policy(0, mPreviousSchedulingGroup); - } + if (!mActive) { + return; + } + + mActive = false; + mProxy->interrupt(); + mAudioRecord->stop(); + // the record head position will reset to 0, so if a marker is set, we need + // to activate it again + mMarkerReached = false; + sp t = mAudioRecordThread; + if (t != 0) { + t->pause(); + } else { + setpriority(PRIO_PROCESS, 0, mPreviousPriority); + set_sched_policy(0, mPreviousSchedulingGroup); } } @@ -364,14 +353,11 @@ bool AudioRecord::stopped() const return !mActive; } -uint32_t AudioRecord::getSampleRate() const -{ - return mSampleRate; -} - status_t AudioRecord::setMarkerPosition(uint32_t marker) { - if (mCbf == NULL) return INVALID_OPERATION; + if (mCbf == NULL) { + return INVALID_OPERATION; + } AutoMutex lock(mLock); mMarkerPosition = marker; @@ -382,7 +368,9 @@ status_t AudioRecord::setMarkerPosition(uint32_t marker) status_t AudioRecord::getMarkerPosition(uint32_t *marker) const { - if (marker == NULL) return BAD_VALUE; + if (marker == NULL) { + return BAD_VALUE; + } AutoMutex lock(mLock); *marker = mMarkerPosition; @@ -392,13 +380,12 @@ status_t AudioRecord::getMarkerPosition(uint32_t *marker) const status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) { - if (mCbf == NULL) return INVALID_OPERATION; - - uint32_t curPosition; - getPosition(&curPosition); + if (mCbf == NULL) { + return INVALID_OPERATION; + } AutoMutex lock(mLock); - mNewPosition = curPosition + updatePeriod; + mNewPosition = mProxy->getPosition() + updatePeriod; mUpdatePeriod = updatePeriod; return NO_ERROR; @@ -406,7 +393,9 @@ status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const { - if (updatePeriod == NULL) return BAD_VALUE; + if (updatePeriod == NULL) { + return BAD_VALUE; + } AutoMutex lock(mLock); *updatePeriod = mUpdatePeriod; @@ -416,10 +405,12 @@ status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const status_t AudioRecord::getPosition(uint32_t *position) const { - if (position == NULL) return BAD_VALUE; + if (position == NULL) { + return BAD_VALUE; + } AutoMutex lock(mLock); - *position = mCblk->user; + *position = mProxy->getPosition(); return NO_ERROR; } @@ -427,7 +418,7 @@ status_t AudioRecord::getPosition(uint32_t *position) const unsigned int AudioRecord::getInputFramesLost() const { // no need to check mActive, because if inactive this will return 0, which is what we want - return AudioSystem::getInputFramesLost(mInput); + return AudioSystem::getInputFramesLost(getInput()); } // ------------------------------------------------------------------------- @@ -437,7 +428,8 @@ status_t AudioRecord::openRecord_l( uint32_t sampleRate, audio_format_t format, size_t frameCount, - audio_io_handle_t input) + audio_io_handle_t input, + size_t epoch) { status_t status; const sp& audioFlinger = AudioSystem::get_audio_flinger(); @@ -447,7 +439,7 @@ status_t AudioRecord::openRecord_l( } pid_t tid = -1; - // FIXME see similar logic at AudioTrack + // FIXME see similar logic at AudioTrack for tid int originalSessionId = mSessionId; sp record = audioFlinger->openRecord(input, @@ -470,133 +462,138 @@ status_t AudioRecord::openRecord_l( ALOGE("Could not get control block"); return NO_INIT; } - mAudioRecord.clear(); + if (mAudioRecord != 0) { + mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this); + mDeathNotifier.clear(); + } mAudioRecord = record; - mCblkMemory.clear(); mCblkMemory = iMem; audio_track_cblk_t* cblk = static_cast(iMem->pointer()); mCblk = cblk; - mBuffers = (char*)cblk + sizeof(audio_track_cblk_t); - cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - cblk->waitTimeMs = 0; + + // starting address of buffers in shared memory + void *buffers = (char*)cblk + sizeof(audio_track_cblk_t); // update proxy - delete mProxy; - mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize); + mProxy = new AudioRecordClientProxy(cblk, buffers, frameCount, mFrameSize); + mProxy->setEpoch(epoch); + mProxy->setMinimum(mNotificationFrames); + + mDeathNotifier = new DeathNotifier(this); + mAudioRecord->asBinder()->linkToDeath(mDeathNotifier, this); return NO_ERROR; } status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { - ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + if (audioBuffer == NULL) { + return BAD_VALUE; + } + if (mTransfer != TRANSFER_OBTAIN) { + audioBuffer->frameCount = 0; + audioBuffer->size = 0; + audioBuffer->raw = NULL; + return INVALID_OPERATION; + } - AutoMutex lock(mLock); - bool active; - status_t result = NO_ERROR; - audio_track_cblk_t* cblk = mCblk; - uint32_t framesReq = audioBuffer->frameCount; - uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS; - - audioBuffer->frameCount = 0; - audioBuffer->size = 0; - - size_t framesReady = mProxy->framesReady(); - - if (framesReady == 0) { - cblk->lock.lock(); - goto start_loop_here; - while (framesReady == 0) { - active = mActive; - if (CC_UNLIKELY(!active)) { - cblk->lock.unlock(); - return NO_MORE_BUFFERS; - } - if (CC_UNLIKELY(!waitCount)) { - cblk->lock.unlock(); - return WOULD_BLOCK; - } - if (!(cblk->flags & CBLK_INVALID)) { - mLock.unlock(); - // this condition is in shared memory, so if IAudioRecord and control block - // are replaced due to mediaserver death or IAudioRecord invalidation then - // cv won't be signalled, but fortunately the timeout will limit the wait - result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); - cblk->lock.unlock(); - mLock.lock(); - if (!mActive) { - return status_t(STOPPED); - } - // IAudioRecord may have been re-created while mLock was unlocked - cblk = mCblk; - cblk->lock.lock(); - } - if (cblk->flags & CBLK_INVALID) { - goto create_new_record; - } - if (CC_UNLIKELY(result != NO_ERROR)) { - cblk->waitTimeMs += waitTimeMs; - if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { - ALOGW( "obtainBuffer timed out (is the CPU pegged?) " - "user=%08x, server=%08x", cblk->user, cblk->server); - cblk->lock.unlock(); - // callback thread or sync event hasn't changed - result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0); - cblk->lock.lock(); - if (result == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID, &cblk->flags); -create_new_record: - audio_track_cblk_t* temp = cblk; - result = AudioRecord::restoreRecord_l(temp); - cblk = temp; - } - if (result != NO_ERROR) { - ALOGW("obtainBuffer create Track error %d", result); - cblk->lock.unlock(); - return result; + const struct timespec *requested; + if (waitCount == -1) { + requested = &ClientProxy::kForever; + } else if (waitCount == 0) { + requested = &ClientProxy::kNonBlocking; + } else if (waitCount > 0) { + long long ms = WAIT_PERIOD_MS * (long long) waitCount; + struct timespec timeout; + timeout.tv_sec = ms / 1000; + timeout.tv_nsec = (int) (ms % 1000) * 1000000; + requested = &timeout; + } else { + ALOGE("%s invalid waitCount %d", __func__, waitCount); + requested = NULL; + } + return obtainBuffer(audioBuffer, requested); +} + +status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, + struct timespec *elapsed, size_t *nonContig) +{ + // previous and new IAudioRecord sequence numbers are used to detect track re-creation + uint32_t oldSequence = 0; + uint32_t newSequence; + + Proxy::Buffer buffer; + status_t status = NO_ERROR; + + static const int32_t kMaxTries = 5; + int32_t tryCounter = kMaxTries; + + do { + // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to + // keep them from going away if another thread re-creates the track during obtainBuffer() + sp proxy; + sp iMem; + { + // start of lock scope + AutoMutex lock(mLock); + + newSequence = mSequence; + // did previous obtainBuffer() fail due to media server death or voluntary invalidation? + if (status == DEAD_OBJECT) { + // re-create track, unless someone else has already done so + if (newSequence == oldSequence) { + status = restoreRecord_l("obtainBuffer"); + if (status != NO_ERROR) { + break; } - cblk->waitTimeMs = 0; - } - if (--waitCount == 0) { - cblk->lock.unlock(); - return TIMED_OUT; } } - // read the server count again -start_loop_here: - framesReady = mProxy->framesReady(); - } - cblk->lock.unlock(); - } + oldSequence = newSequence; - cblk->waitTimeMs = 0; - // reset time out to running value after obtaining a buffer - cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + // Keep the extra references + proxy = mProxy; + iMem = mCblkMemory; - if (framesReq > framesReady) { - framesReq = framesReady; - } + // Non-blocking if track is stopped + if (!mActive) { + requested = &ClientProxy::kNonBlocking; + } - uint32_t u = cblk->user; - uint32_t bufferEnd = cblk->userBase + mFrameCount; + } // end of lock scope - if (framesReq > bufferEnd - u) { - framesReq = bufferEnd - u; - } + buffer.mFrameCount = audioBuffer->frameCount; + // FIXME starts the requested timeout and elapsed over from scratch + status = proxy->obtainBuffer(&buffer, requested, elapsed); + + } while ((status == DEAD_OBJECT) && (tryCounter-- > 0)); - audioBuffer->frameCount = framesReq; - audioBuffer->size = framesReq * mFrameSize; - audioBuffer->raw = mProxy->buffer(u); - active = mActive; - return active ? status_t(NO_ERROR) : status_t(STOPPED); + audioBuffer->frameCount = buffer.mFrameCount; + audioBuffer->size = buffer.mFrameCount * mFrameSize; + audioBuffer->raw = buffer.mRaw; + if (nonContig != NULL) { + *nonContig = buffer.mNonContig; + } + return status; } void AudioRecord::releaseBuffer(Buffer* audioBuffer) { - ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + // all TRANSFER_* are valid + + size_t stepCount = audioBuffer->size / mFrameSize; + if (stepCount == 0) { + return; + } + + Proxy::Buffer buffer; + buffer.mFrameCount = stepCount; + buffer.mRaw = audioBuffer->raw; AutoMutex lock(mLock); - (void) mProxy->stepUser(audioBuffer->frameCount); + mInOverrun = false; + mProxy->releaseBuffer(&buffer); + + // the server does not automatically disable recorder on overrun, so no need to restart } audio_io_handle_t AudioRecord::getInput() const @@ -616,215 +613,304 @@ audio_io_handle_t AudioRecord::getInput_l() return mInput; } -int AudioRecord::getSessionId() const -{ - // no lock needed because session ID doesn't change after first set() - return mSessionId; -} - // ------------------------------------------------------------------------- ssize_t AudioRecord::read(void* buffer, size_t userSize) { - ssize_t read = 0; - Buffer audioBuffer; - int8_t *dst = static_cast(buffer); + if (mTransfer != TRANSFER_SYNC) { + return INVALID_OPERATION; + } - if (ssize_t(userSize) < 0) { - // sanity-check. user is most-likely passing an error code. - ALOGE("AudioRecord::read(buffer=%p, size=%u (%d)", - buffer, userSize, userSize); + if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) { + // sanity-check. user is most-likely passing an error code, and it would + // make the return value ambiguous (actualSize vs error). + ALOGE("AudioRecord::read(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } - mLock.lock(); - // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed - // while we are accessing the cblk - sp audioRecord = mAudioRecord; - sp iMem = mCblkMemory; - mLock.unlock(); - - do { + ssize_t read = 0; + Buffer audioBuffer; - audioBuffer.frameCount = userSize/frameSize(); + while (userSize >= mFrameSize) { + audioBuffer.frameCount = userSize / mFrameSize; - // By using a wait count corresponding to twice the timeout period in - // obtainBuffer() we give a chance to recover once for a read timeout - // (if media_server crashed for instance) before returning a length of - // 0 bytes read to the client - status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS)); + status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); if (err < 0) { - // out of buffers, return #bytes written - if (err == status_t(NO_MORE_BUFFERS)) { + if (read > 0) { break; } - if (err == status_t(TIMED_OUT)) { - // return partial transfer count - return read; - } return ssize_t(err); } size_t bytesRead = audioBuffer.size; - memcpy(dst, audioBuffer.i8, bytesRead); - - dst += bytesRead; + memcpy(buffer, audioBuffer.i8, bytesRead); + buffer = ((char *) buffer) + bytesRead; userSize -= bytesRead; read += bytesRead; releaseBuffer(&audioBuffer); - } while (userSize); + } return read; } // ------------------------------------------------------------------------- -bool AudioRecord::processAudioBuffer(const sp& thread) +nsecs_t AudioRecord::processAudioBuffer(const sp& thread) { - Buffer audioBuffer; - uint32_t frames = mRemainingFrames; - size_t readSize; - mLock.lock(); - // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed - // while we are accessing the cblk - sp audioRecord = mAudioRecord; - sp iMem = mCblkMemory; - audio_track_cblk_t* cblk = mCblk; + + // Can only reference mCblk while locked + int32_t flags = android_atomic_and(~CBLK_OVERRUN, &mCblk->flags); + + // Check for track invalidation + if (flags & CBLK_INVALID) { + (void) restoreRecord_l("processAudioBuffer"); + mLock.unlock(); + // Run again immediately, but with a new IAudioRecord + return 0; + } + bool active = mActive; - uint32_t markerPosition = mMarkerPosition; - uint32_t newPosition = mNewPosition; - uint32_t user = cblk->user; - // determine whether a marker callback will be needed, while locked - bool needMarker = !mMarkerReached && (mMarkerPosition > 0) && (user >= mMarkerPosition); - if (needMarker) { - mMarkerReached = true; - } - // determine the number of new position callback(s) that will be needed, while locked + + // Manage overrun callback, must be done under lock to avoid race with releaseBuffer() + bool newOverrun = false; + if (flags & CBLK_OVERRUN) { + if (!mInOverrun) { + mInOverrun = true; + newOverrun = true; + } + } + + // Get current position of server + size_t position = mProxy->getPosition(); + + // Manage marker callback + bool markerReached = false; + size_t markerPosition = mMarkerPosition; + // FIXME fails for wraparound, need 64 bits + if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition)) { + mMarkerReached = markerReached = true; + } + + // Determine the number of new position callback(s) that will be needed, while locked + size_t newPosCount = 0; + size_t newPosition = mNewPosition; uint32_t updatePeriod = mUpdatePeriod; - uint32_t needNewPos = updatePeriod > 0 && user >= newPosition ? - ((user - newPosition) / updatePeriod) + 1 : 0; - mNewPosition = newPosition + updatePeriod * needNewPos; + // FIXME fails for wraparound, need 64 bits + if (updatePeriod > 0 && position >= newPosition) { + newPosCount = ((position - newPosition) / updatePeriod) + 1; + mNewPosition += updatePeriod * newPosCount; + } + + // Cache other fields that will be needed soon + size_t notificationFrames = mNotificationFrames; + if (mRefreshRemaining) { + mRefreshRemaining = false; + mRemainingFrames = notificationFrames; + mRetryOnPartialBuffer = false; + } + size_t misalignment = mProxy->getMisalignment(); + int32_t sequence = mSequence; + + // These fields don't need to be cached, because they are assigned only by set(): + // mTransfer, mCbf, mUserData, mSampleRate + mLock.unlock(); - // perform marker callback, while unlocked - if (needMarker) { + // perform callbacks while unlocked + if (newOverrun) { + mCbf(EVENT_OVERRUN, mUserData, NULL); + } + if (markerReached) { mCbf(EVENT_MARKER, mUserData, &markerPosition); } - - // perform new position callback(s), while unlocked - for (; needNewPos > 0; --needNewPos) { - uint32_t temp = newPosition; + while (newPosCount > 0) { + size_t temp = newPosition; mCbf(EVENT_NEW_POS, mUserData, &temp); newPosition += updatePeriod; + newPosCount--; + } + if (mObservedSequence != sequence) { + mObservedSequence = sequence; + mCbf(EVENT_NEW_IAUDIORECORD, mUserData, NULL); } - do { - audioBuffer.frameCount = frames; - // Calling obtainBuffer() with a wait count of 1 - // limits wait time to WAIT_PERIOD_MS. This prevents from being - // stuck here not being able to handle timed events (position, markers). - status_t err = obtainBuffer(&audioBuffer, 1); - if (err < NO_ERROR) { - if (err != TIMED_OUT) { - ALOGE_IF(err != status_t(NO_MORE_BUFFERS), - "Error obtaining an audio buffer, giving up."); - return false; + // if inactive, then don't run me again until re-started + if (!active) { + return NS_INACTIVE; + } + + // Compute the estimated time until the next timed event (position, markers) + uint32_t minFrames = ~0; + if (!markerReached && position < markerPosition) { + minFrames = markerPosition - position; + } + if (updatePeriod > 0 && updatePeriod < minFrames) { + minFrames = updatePeriod; + } + + // If > 0, poll periodically to recover from a stuck server. A good value is 2. + static const uint32_t kPoll = 0; + if (kPoll > 0 && mTransfer == TRANSFER_CALLBACK && kPoll * notificationFrames < minFrames) { + minFrames = kPoll * notificationFrames; + } + + // Convert frame units to time units + nsecs_t ns = NS_WHENEVER; + if (minFrames != (uint32_t) ~0) { + // This "fudge factor" avoids soaking CPU, and compensates for late progress by server + static const nsecs_t kFudgeNs = 10000000LL; // 10 ms + ns = ((minFrames * 1000000000LL) / mSampleRate) + kFudgeNs; + } + + // If not supplying data by EVENT_MORE_DATA, then we're done + if (mTransfer != TRANSFER_CALLBACK) { + return ns; + } + + struct timespec timeout; + const struct timespec *requested = &ClientProxy::kForever; + if (ns != NS_WHENEVER) { + timeout.tv_sec = ns / 1000000000LL; + timeout.tv_nsec = ns % 1000000000LL; + ALOGV("timeout %ld.%03d", timeout.tv_sec, (int) timeout.tv_nsec / 1000000); + requested = &timeout; + } + + while (mRemainingFrames > 0) { + + Buffer audioBuffer; + audioBuffer.frameCount = mRemainingFrames; + size_t nonContig; + status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig); + LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0), + "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount); + requested = &ClientProxy::kNonBlocking; + size_t avail = audioBuffer.frameCount + nonContig; + ALOGV("obtainBuffer(%u) returned %u = %u + %u", + mRemainingFrames, avail, audioBuffer.frameCount, nonContig); + if (err != NO_ERROR) { + if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) { + break; + } + ALOGE("Error %d obtaining an audio buffer, giving up.", err); + return NS_NEVER; + } + + if (mRetryOnPartialBuffer) { + mRetryOnPartialBuffer = false; + if (avail < mRemainingFrames) { + int64_t myns = ((mRemainingFrames - avail) * + 1100000000LL) / mSampleRate; + if (ns < 0 || myns < ns) { + ns = myns; + } + return ns; } - break; } - if (err == status_t(STOPPED)) return false; size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); - readSize = audioBuffer.size; + size_t readSize = audioBuffer.size; // Sanity check on returned size - if (ssize_t(readSize) <= 0) { - // The callback is done filling buffers + if (ssize_t(readSize) < 0 || readSize > reqSize) { + ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes", + reqSize, (int) readSize); + return NS_NEVER; + } + + if (readSize == 0) { + // The callback is done consuming buffers // Keep this thread going to handle timed events and - // still try to get more data in intervals of WAIT_PERIOD_MS + // still try to provide more data in intervals of WAIT_PERIOD_MS // but don't just loop and block the CPU, so wait - usleep(WAIT_PERIOD_MS*1000); - break; + return WAIT_PERIOD_MS * 1000000LL; } - if (readSize > reqSize) readSize = reqSize; - audioBuffer.size = readSize; - audioBuffer.frameCount = readSize/frameSize(); - frames -= audioBuffer.frameCount; + size_t releasedFrames = readSize / mFrameSize; + audioBuffer.frameCount = releasedFrames; + mRemainingFrames -= releasedFrames; + if (misalignment >= releasedFrames) { + misalignment -= releasedFrames; + } else { + misalignment = 0; + } releaseBuffer(&audioBuffer); - } while (frames); + // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer + // if callback doesn't like to accept the full chunk + if (readSize < reqSize) { + continue; + } + // There could be enough non-contiguous frames available to satisfy the remaining request + if (mRemainingFrames <= nonContig) { + continue; + } - // Manage overrun callback - if (active && (mProxy->framesAvailable() == 0)) { - // The value of active is stale, but we are almost sure to be active here because - // otherwise we would have exited when obtainBuffer returned STOPPED earlier. - ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); - if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) { - mCbf(EVENT_OVERRUN, mUserData, NULL); +#if 0 + // This heuristic tries to collapse a series of EVENT_MORE_DATA that would total to a + // sum <= notificationFrames. It replaces that series by at most two EVENT_MORE_DATA + // that total to a sum == notificationFrames. + if (0 < misalignment && misalignment <= mRemainingFrames) { + mRemainingFrames = misalignment; + return (mRemainingFrames * 1100000000LL) / mSampleRate; } - } +#endif - if (frames == 0) { - mRemainingFrames = mNotificationFrames; - } else { - mRemainingFrames = frames; } - return true; + mRemainingFrames = notificationFrames; + mRetryOnPartialBuffer = true; + + // A lot has transpired since ns was calculated, so run again immediately and re-calculate + return 0; } -// must be called with mLock and cblk.lock held. Callers must also hold strong references on -// the IAudioRecord and IMemory in case they are recreated here. -// If the IAudioRecord is successfully restored, the cblk pointer is updated -status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk) +status_t AudioRecord::restoreRecord_l(const char *from) { + ALOGW("dead IAudioRecord, creating a new one from %s()", from); + ++mSequence; status_t result; - audio_track_cblk_t* cblk = refCblk; - audio_track_cblk_t* newCblk = cblk; - ALOGW("dead IAudioRecord, creating a new one"); - - // signal old cblk condition so that other threads waiting for available buffers stop - // waiting now - cblk->cv.broadcast(); - cblk->lock.unlock(); - // if the new IAudioRecord is created, openRecord_l() will modify the // following member variables: mAudioRecord, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioRecord and IMemory - result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l()); + size_t position = mProxy->getPosition(); + mNewPosition = position + mUpdatePeriod; + result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l(), position); if (result == NO_ERROR) { - newCblk = mCblk; - // callback thread or sync event hasn't changed - result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0); + if (mActive) { + // callback thread or sync event hasn't changed + // FIXME this fails if we have a new AudioFlinger instance + result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0); + } } if (result != NO_ERROR) { + ALOGW("restoreRecord_l() failed status %d", result); mActive = false; } - ALOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", - result, mActive, newCblk, cblk, newCblk->flags, cblk->flags); - - if (result == NO_ERROR) { - // from now on we switch to the newly created cblk - refCblk = newCblk; - } - newCblk->lock.lock(); + return result; +} - ALOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result); +// ========================================================================= - return result; +void AudioRecord::DeathNotifier::binderDied(const wp& who) +{ + sp audioRecord = mAudioRecord.promote(); + if (audioRecord != 0) { + AutoMutex lock(audioRecord->mLock); + audioRecord->mProxy->binderDied(); + } } // ========================================================================= AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver), mPaused(true) + : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false) { } @@ -842,10 +928,26 @@ bool AudioRecord::AudioRecordThread::threadLoop() return true; } } - if (!mReceiver.processAudioBuffer(this)) { - pause(); + nsecs_t ns = mReceiver.processAudioBuffer(this); + switch (ns) { + case 0: + return true; + case NS_WHENEVER: + sleep(1); + return true; + case NS_INACTIVE: + pauseConditional(); + return true; + case NS_NEVER: + return false; + default: + LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns); + struct timespec req; + req.tv_sec = ns / 1000000000LL; + req.tv_nsec = ns % 1000000000LL; + nanosleep(&req, NULL /*rem*/); + return true; } - return true; } void AudioRecord::AudioRecordThread::requestExit() @@ -859,6 +961,17 @@ void AudioRecord::AudioRecordThread::pause() { AutoMutex _l(mMyLock); mPaused = true; + mResumeLatch = false; +} + +void AudioRecord::AudioRecordThread::pauseConditional() +{ + AutoMutex _l(mMyLock); + if (mResumeLatch) { + mResumeLatch = false; + } else { + mPaused = true; + } } void AudioRecord::AudioRecordThread::resume() @@ -866,7 +979,10 @@ void AudioRecord::AudioRecordThread::resume() AutoMutex _l(mMyLock); if (mPaused) { mPaused = false; + mResumeLatch = false; mMyCond.signal(); + } else { + mResumeLatch = true; } } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 77fc6f6..faca054 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -19,31 +19,14 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioTrack" -#include -#include -#include - -#include #include - -#include - -#include +#include +#include #include - #include -#include -#include -#include -#include - -#include -#include +#include -#include -#include - -#include +#define WAIT_PERIOD_MS 10 namespace android { // --------------------------------------------------------------------------- @@ -82,7 +65,9 @@ status_t AudioTrack::getMinFrameCount( // Ensure that buffer depth covers at least audio hardware latency uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate); - if (minBufCount < 2) minBufCount = 2; + if (minBufCount < 2) { + minBufCount = 2; + } *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount : afFrameCount * minBufCount * sampleRate / afSampleRate; @@ -97,8 +82,7 @@ AudioTrack::AudioTrack() : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), - mPreviousSchedulingGroup(SP_DEFAULT), - mProxy(NULL) + mPreviousSchedulingGroup(SP_DEFAULT) { } @@ -112,16 +96,16 @@ AudioTrack::AudioTrack( callback_t cbf, void* user, int notificationFrames, - int sessionId) + int sessionId, + transfer_type transferType) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), - mPreviousSchedulingGroup(SP_DEFAULT), - mProxy(NULL) + mPreviousSchedulingGroup(SP_DEFAULT) { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, - 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId); + 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType); } AudioTrack::AudioTrack( @@ -134,27 +118,20 @@ AudioTrack::AudioTrack( callback_t cbf, void* user, int notificationFrames, - int sessionId) + int sessionId, + transfer_type transferType) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), - mPreviousSchedulingGroup(SP_DEFAULT), - mProxy(NULL) + mPreviousSchedulingGroup(SP_DEFAULT) { - if (sharedBuffer == 0) { - ALOGE("sharedBuffer must be non-0"); - mStatus = BAD_VALUE; - return; - } mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, - sharedBuffer, false /*threadCanCallJava*/, sessionId); + sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType); } AudioTrack::~AudioTrack() { - ALOGV_IF(mSharedBuffer != 0, "Destructor sharedBuffer: %p", mSharedBuffer->pointer()); - if (mStatus == NO_ERROR) { // Make sure that callback function exits in the case where // it is looping on buffer full condition in obtainBuffer(). @@ -165,11 +142,13 @@ AudioTrack::~AudioTrack() mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } - mAudioTrack.clear(); + if (mAudioTrack != 0) { + mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this); + mAudioTrack.clear(); + } IPCThreadState::self()->flushCommands(); AudioSystem::releaseAudioSessionId(mSessionId); } - delete mProxy; } status_t AudioTrack::set( @@ -184,8 +163,44 @@ status_t AudioTrack::set( int notificationFrames, const sp& sharedBuffer, bool threadCanCallJava, - int sessionId) + int sessionId, + transfer_type transferType) { + switch (transferType) { + case TRANSFER_DEFAULT: + if (sharedBuffer != 0) { + transferType = TRANSFER_SHARED; + } else if (cbf == NULL || threadCanCallJava) { + transferType = TRANSFER_SYNC; + } else { + transferType = TRANSFER_CALLBACK; + } + break; + case TRANSFER_CALLBACK: + if (cbf == NULL || sharedBuffer != 0) { + ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL || sharedBuffer != 0"); + return BAD_VALUE; + } + break; + case TRANSFER_OBTAIN: + case TRANSFER_SYNC: + if (sharedBuffer != 0) { + ALOGE("Transfer type TRANSFER_OBTAIN but sharedBuffer != 0"); + return BAD_VALUE; + } + break; + case TRANSFER_SHARED: + if (sharedBuffer == 0) { + ALOGE("Transfer type TRANSFER_SHARED but sharedBuffer == 0"); + return BAD_VALUE; + } + break; + default: + ALOGE("Invalid transfer type %d", transferType); + return BAD_VALUE; + } + mTransfer = transferType; + // FIXME "int" here is legacy and will be replaced by size_t later if (frameCountInt < 0) { ALOGE("Invalid frame count %d", frameCountInt); @@ -199,6 +214,7 @@ status_t AudioTrack::set( ALOGV("set() streamType %d frameCount %u flags %04x", streamType, frameCount, flags); AutoMutex lock(mLock); + if (mAudioTrack != 0) { ALOGE("Track already in use"); return INVALID_OPERATION; @@ -228,7 +244,7 @@ status_t AudioTrack::set( // validate parameters if (!audio_is_valid_format(format)) { - ALOGE("Invalid format"); + ALOGE("Invalid format %d", format); return BAD_VALUE; } @@ -281,6 +297,7 @@ status_t AudioTrack::set( mFrameCount = frameCount; mReqFrameCount = frameCount; mNotificationFramesReq = notificationFrames; + mNotificationFramesAct = 0; mSessionId = sessionId; mAuxEffectId = 0; mFlags = flags; @@ -298,7 +315,8 @@ status_t AudioTrack::set( frameCount, flags, sharedBuffer, - output); + output, + 0 /*epoch*/); if (status != NO_ERROR) { if (mAudioTrackThread != 0) { @@ -309,20 +327,21 @@ status_t AudioTrack::set( } mStatus = NO_ERROR; - mStreamType = streamType; mFormat = format; - mSharedBuffer = sharedBuffer; - mActive = false; + mState = STATE_STOPPED; mUserData = user; - mLoopCount = 0; + mLoopPeriod = 0; mMarkerPosition = 0; mMarkerReached = false; mNewPosition = 0; mUpdatePeriod = 0; - mFlushed = false; AudioSystem::acquireAudioSessionId(mSessionId); + mSequence = 1; + mObservedSequence = mSequence; + mInUnderrun = false; + return NO_ERROR; } @@ -330,87 +349,45 @@ status_t AudioTrack::set( void AudioTrack::start() { - sp t = mAudioTrackThread; - - ALOGV("start %p", this); - AutoMutex lock(mLock); - // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed - // while we are accessing the cblk - sp audioTrack = mAudioTrack; - sp iMem = mCblkMemory; - audio_track_cblk_t* cblk = mCblk; + if (mState == STATE_ACTIVE) { + return; + } - if (!mActive) { - mFlushed = false; - mActive = true; - mNewPosition = cblk->server + mUpdatePeriod; - cblk->lock.lock(); - cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; - cblk->waitTimeMs = 0; - android_atomic_and(~CBLK_DISABLED, &cblk->flags); - if (t != 0) { - t->resume(); - } else { - mPreviousPriority = getpriority(PRIO_PROCESS, 0); - get_sched_policy(0, &mPreviousSchedulingGroup); - androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); - } + mInUnderrun = true; - ALOGV("start %p before lock cblk %p", this, cblk); - status_t status = NO_ERROR; - if (!(cblk->flags & CBLK_INVALID)) { - cblk->lock.unlock(); - ALOGV("mAudioTrack->start()"); - status = mAudioTrack->start(); - cblk->lock.lock(); - if (status == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID, &cblk->flags); - } - } - if (cblk->flags & CBLK_INVALID) { - audio_track_cblk_t* temp = cblk; - status = restoreTrack_l(temp, true /*fromStart*/); - cblk = temp; - } - cblk->lock.unlock(); - if (status != NO_ERROR) { - ALOGV("start() failed"); - mActive = false; - if (t != 0) { - t->pause(); - } else { - setpriority(PRIO_PROCESS, 0, mPreviousPriority); - set_sched_policy(0, mPreviousSchedulingGroup); - } - } + State previousState = mState; + mState = STATE_ACTIVE; + if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) { + // reset current position as seen by client to 0 + mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition()); } + mNewPosition = mProxy->getPosition() + mUpdatePeriod; + int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->flags); -} - -void AudioTrack::stop() -{ sp t = mAudioTrackThread; + if (t != 0) { + t->resume(); + } else { + mPreviousPriority = getpriority(PRIO_PROCESS, 0); + get_sched_policy(0, &mPreviousSchedulingGroup); + androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); + } - ALOGV("stop %p", this); - - AutoMutex lock(mLock); - if (mActive) { - mActive = false; - mCblk->cv.signal(); - mAudioTrack->stop(); - // Cancel loops (If we are in the middle of a loop, playback - // would not stop until loopCount reaches 0). - setLoop_l(0, 0, 0); - // the playback head position will reset to 0, so if a marker is set, we need - // to activate it again - mMarkerReached = false; - // Force flush if a shared buffer is used otherwise audioflinger - // will not stop before end of buffer is reached. - // It may be needed to make sure that we stop playback, likely in case looping is on. - if (mSharedBuffer != 0) { - flush_l(); + status_t status = NO_ERROR; + if (!(flags & CBLK_INVALID)) { + status = mAudioTrack->start(); + if (status == DEAD_OBJECT) { + flags |= CBLK_INVALID; } + } + if (flags & CBLK_INVALID) { + status = restoreTrack_l("start"); + } + + if (status != NO_ERROR) { + ALOGE("start() status %d", status); + mState = previousState; if (t != 0) { t->pause(); } else { @@ -419,57 +396,85 @@ void AudioTrack::stop() } } + // FIXME discarding status +} + +void AudioTrack::stop() +{ + AutoMutex lock(mLock); + // FIXME pause then stop should not be a nop + if (mState != STATE_ACTIVE) { + return; + } + + mState = STATE_STOPPED; + mProxy->interrupt(); + mAudioTrack->stop(); + // the playback head position will reset to 0, so if a marker is set, we need + // to activate it again + mMarkerReached = false; +#if 0 + // Force flush if a shared buffer is used otherwise audioflinger + // will not stop before end of buffer is reached. + // It may be needed to make sure that we stop playback, likely in case looping is on. + if (mSharedBuffer != 0) { + flush_l(); + } +#endif + sp t = mAudioTrackThread; + if (t != 0) { + t->pause(); + } else { + setpriority(PRIO_PROCESS, 0, mPreviousPriority); + set_sched_policy(0, mPreviousSchedulingGroup); + } } bool AudioTrack::stopped() const { AutoMutex lock(mLock); - return stopped_l(); + return mState != STATE_ACTIVE; } void AudioTrack::flush() { + if (mSharedBuffer != 0) { + return; + } AutoMutex lock(mLock); - if (!mActive && mSharedBuffer == 0) { - flush_l(); + if (mState == STATE_ACTIVE || mState == STATE_FLUSHED) { + return; } + flush_l(); } void AudioTrack::flush_l() { - ALOGV("flush"); - ALOG_ASSERT(!mActive); + ALOG_ASSERT(mState != STATE_ACTIVE); // clear playback marker and periodic update counter mMarkerPosition = 0; mMarkerReached = false; mUpdatePeriod = 0; - mFlushed = true; + mState = STATE_FLUSHED; + mProxy->flush(); mAudioTrack->flush(); - // Release AudioTrack callback thread in case it was waiting for new buffers - // in AudioTrack::obtainBuffer() - mCblk->cv.signal(); } void AudioTrack::pause() { - ALOGV("pause"); AutoMutex lock(mLock); - if (mActive) { - mActive = false; - mCblk->cv.signal(); - mAudioTrack->pause(); + if (mState != STATE_ACTIVE) { + return; } + mState = STATE_PAUSED; + mProxy->interrupt(); + mAudioTrack->pause(); } status_t AudioTrack::setVolume(float left, float right) { - if (mStatus != NO_ERROR) { - return mStatus; - } - ALOG_ASSERT(mProxy != NULL); - if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) { return BAD_VALUE; } @@ -490,18 +495,11 @@ status_t AudioTrack::setVolume(float volume) status_t AudioTrack::setAuxEffectSendLevel(float level) { - ALOGV("setAuxEffectSendLevel(%f)", level); - - if (mStatus != NO_ERROR) { - return mStatus; - } - ALOG_ASSERT(mProxy != NULL); - if (level < 0.0f || level > 1.0f) { return BAD_VALUE; } - AutoMutex lock(mLock); + AutoMutex lock(mLock); mSendLevel = level; mProxy->setSendLevel(level); @@ -511,18 +509,17 @@ status_t AudioTrack::setAuxEffectSendLevel(float level) void AudioTrack::getAuxEffectSendLevel(float* level) const { if (level != NULL) { - *level = mSendLevel; + *level = mSendLevel; } } status_t AudioTrack::setSampleRate(uint32_t rate) { - uint32_t afSamplingRate; - if (mIsTimed) { return INVALID_OPERATION; } + uint32_t afSamplingRate; if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) { return NO_INIT; } @@ -550,78 +547,44 @@ uint32_t AudioTrack::getSampleRate() const status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) { - AutoMutex lock(mLock); - return setLoop_l(loopStart, loopEnd, loopCount); -} - -// must be called with mLock held -status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) -{ if (mSharedBuffer == 0 || mIsTimed) { return INVALID_OPERATION; } - if (loopCount < 0 && loopCount != -1) { - return BAD_VALUE; - } - -#if 0 - // This will be for the new interpretation of loopStart and loopEnd - - if (loopCount != 0) { - if (loopStart >= mFrameCount || loopEnd >= mFrameCount || loopStart >= loopEnd) { - return BAD_VALUE; - } - uint32_t periodFrames = loopEnd - loopStart; - if (periodFrames < PERIOD_FRAMES_MIN) { - return BAD_VALUE; - } - } - - // The remainder of this code still uses the old interpretation -#endif - - audio_track_cblk_t* cblk = mCblk; - - Mutex::Autolock _l(cblk->lock); - if (loopCount == 0) { - cblk->loopStart = UINT_MAX; - cblk->loopEnd = UINT_MAX; - cblk->loopCount = 0; - mLoopCount = 0; - return NO_ERROR; - } - - if (loopStart >= loopEnd || - loopEnd - loopStart > mFrameCount || - cblk->server > loopStart) { - ALOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, " - "user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user); + ; + } else if (loopCount >= -1 && loopStart < loopEnd && loopEnd <= mFrameCount && + loopEnd - loopStart >= MIN_LOOP) { + ; + } else { return BAD_VALUE; } - if ((mSharedBuffer != 0) && (loopEnd > mFrameCount)) { - ALOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, " - "framecount %d", - loopStart, loopEnd, mFrameCount); - return BAD_VALUE; + AutoMutex lock(mLock); + // See setPosition() regarding setting parameters such as loop points or position while active + if (mState == STATE_ACTIVE) { + return INVALID_OPERATION; } - - cblk->loopStart = loopStart; - cblk->loopEnd = loopEnd; - cblk->loopCount = loopCount; - mLoopCount = loopCount; - + setLoop_l(loopStart, loopEnd, loopCount); return NO_ERROR; } +void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) +{ + // FIXME If setting a loop also sets position to start of loop, then + // this is correct. Otherwise it should be removed. + mNewPosition = mProxy->getPosition() + mUpdatePeriod; + mLoopPeriod = loopCount != 0 ? loopEnd - loopStart : 0; + mStaticProxy->setLoop(loopStart, loopEnd, loopCount); +} + status_t AudioTrack::setMarkerPosition(uint32_t marker) { if (mCbf == NULL) { return INVALID_OPERATION; } + AutoMutex lock(mLock); mMarkerPosition = marker; mMarkerReached = false; @@ -634,6 +597,7 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const return BAD_VALUE; } + AutoMutex lock(mLock); *marker = mMarkerPosition; return NO_ERROR; @@ -645,9 +609,8 @@ status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) return INVALID_OPERATION; } - uint32_t curPosition; - getPosition(&curPosition); - mNewPosition = curPosition + updatePeriod; + AutoMutex lock(mLock); + mNewPosition = mProxy->getPosition() + updatePeriod; mUpdatePeriod = updatePeriod; return NO_ERROR; @@ -659,6 +622,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const return BAD_VALUE; } + AutoMutex lock(mLock); *updatePeriod = mUpdatePeriod; return NO_ERROR; @@ -669,49 +633,44 @@ status_t AudioTrack::setPosition(uint32_t position) if (mSharedBuffer == 0 || mIsTimed) { return INVALID_OPERATION; } - - AutoMutex lock(mLock); - - if (!stopped_l()) { - return INVALID_OPERATION; - } - -#if 0 - // This will be for the new interpretation of position - - if (position >= mFrameCount) { + if (position > mFrameCount) { return BAD_VALUE; } - // The remainder of this code still uses the old interpretation -#endif - - audio_track_cblk_t* cblk = mCblk; - Mutex::Autolock _l(cblk->lock); - - if (position > cblk->user) { - return BAD_VALUE; + AutoMutex lock(mLock); + // Currently we require that the player is inactive before setting parameters such as position + // or loop points. Otherwise, there could be a race condition: the application could read the + // current position, compute a new position or loop parameters, and then set that position or + // loop parameters but it would do the "wrong" thing since the position has continued to advance + // in the mean time. If we ever provide a sequencer in server, we could allow a way for the app + // to specify how it wants to handle such scenarios. + if (mState == STATE_ACTIVE) { + return INVALID_OPERATION; } - - cblk->server = position; - android_atomic_or(CBLK_FORCEREADY, &cblk->flags); + mNewPosition = mProxy->getPosition() + mUpdatePeriod; + mLoopPeriod = 0; + // FIXME Check whether loops and setting position are incompatible in old code. + // If we use setLoop for both purposes we lose the capability to set the position while looping. + mStaticProxy->setLoop(position, mFrameCount, 0); return NO_ERROR; } -status_t AudioTrack::getPosition(uint32_t *position) +status_t AudioTrack::getPosition(uint32_t *position) const { if (position == NULL) { return BAD_VALUE; } + AutoMutex lock(mLock); - *position = mFlushed ? 0 : mCblk->server; + // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes + *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 : + mProxy->getPosition(); return NO_ERROR; } -#if 0 -status_t AudioTrack::getBufferPosition(uint32_t *position) +status_t AudioTrack::getBufferPosition(size_t *position) { if (mSharedBuffer == 0 || mIsTimed) { return INVALID_OPERATION; @@ -719,33 +678,28 @@ status_t AudioTrack::getBufferPosition(uint32_t *position) if (position == NULL) { return BAD_VALUE; } - *position = 0; + AutoMutex lock(mLock); + *position = mStaticProxy->getBufferPosition(); return NO_ERROR; } -#endif status_t AudioTrack::reload() { - if (mStatus != NO_ERROR) { - return mStatus; - } - ALOG_ASSERT(mProxy != NULL); - if (mSharedBuffer == 0 || mIsTimed) { return INVALID_OPERATION; } AutoMutex lock(mLock); - - if (!stopped_l()) { + // See setPosition() regarding setting parameters such as loop points or position while active + if (mState == STATE_ACTIVE) { return INVALID_OPERATION; } - - flush_l(); - - (void) mProxy->stepUser(mFrameCount); - + mNewPosition = mUpdatePeriod; + mLoopPeriod = 0; + // FIXME The new code cannot reload while keeping a loop specified. + // Need to check how the old code handled this, and whether it's a significant change. + mStaticProxy->setLoop(0, mFrameCount, 0); return NO_ERROR; } @@ -764,7 +718,7 @@ audio_io_handle_t AudioTrack::getOutput_l() status_t AudioTrack::attachAuxEffect(int effectId) { - ALOGV("attachAuxEffect(%d)", effectId); + AutoMutex lock(mLock); status_t status = mAudioTrack->attachAuxEffect(effectId); if (status == NO_ERROR) { mAuxEffectId = effectId; @@ -782,7 +736,8 @@ status_t AudioTrack::createTrack_l( size_t frameCount, audio_output_flags_t flags, const sp& sharedBuffer, - audio_io_handle_t output) + audio_io_handle_t output, + size_t epoch) { status_t status; const sp& audioFlinger = AudioSystem::get_audio_flinger(); @@ -792,7 +747,8 @@ status_t AudioTrack::createTrack_l( } uint32_t afLatency; - if (AudioSystem::getLatency(output, streamType, &afLatency) != NO_ERROR) { + if ((status = AudioSystem::getLatency(output, streamType, &afLatency)) != NO_ERROR) { + ALOGE("getLatency(%d) failed status %d", output, status); return NO_INIT; } @@ -820,7 +776,10 @@ status_t AudioTrack::createTrack_l( frameCount = sharedBuffer->size(); } else if (frameCount == 0) { size_t afFrameCount; - if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) { + status = AudioSystem::getFrameCount(output, streamType, &afFrameCount); + if (status != NO_ERROR) { + ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, + status); return NO_INIT; } frameCount = afFrameCount; @@ -851,11 +810,16 @@ status_t AudioTrack::createTrack_l( // FIXME move these calculations and associated checks to server uint32_t afSampleRate; - if (AudioSystem::getSamplingRate(output, streamType, &afSampleRate) != NO_ERROR) { + status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate); + if (status != NO_ERROR) { + ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, streamType, + status); return NO_INIT; } size_t afFrameCount; - if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) { + status = AudioSystem::getFrameCount(output, streamType, &afFrameCount); + if (status != NO_ERROR) { + ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, status); return NO_INIT; } @@ -875,12 +839,9 @@ status_t AudioTrack::createTrack_l( if (frameCount == 0) { frameCount = minFrameCount; } - if (mNotificationFramesAct == 0) { - mNotificationFramesAct = frameCount/2; - } // Make sure that application is notified with sufficient margin // before underrun - if (mNotificationFramesAct > frameCount/2) { + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { mNotificationFramesAct = frameCount/2; } if (frameCount < minFrameCount) { @@ -930,6 +891,10 @@ status_t AudioTrack::createTrack_l( ALOGE("Could not get control block"); return NO_INIT; } + if (mAudioTrack != 0) { + mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this); + mDeathNotifier.clear(); + } mAudioTrack = track; mCblkMemory = iMem; audio_track_cblk_t* cblk = static_cast(iMem->pointer()); @@ -947,26 +912,38 @@ status_t AudioTrack::createTrack_l( if (trackFlags & IAudioFlinger::TRACK_FAST) { ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount); mAwaitBoost = true; + if (sharedBuffer == 0) { + // double-buffering is not required for fast tracks, due to tighter scheduling + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) { + mNotificationFramesAct = frameCount; + } + } } else { ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", frameCount); // once denied, do not request again if IAudioTrack is re-created flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST); mFlags = flags; - } - if (sharedBuffer == 0) { - mNotificationFramesAct = frameCount/2; + if (sharedBuffer == 0) { + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { + mNotificationFramesAct = frameCount/2; + } + } } } + mRefreshRemaining = true; + + // Starting address of buffers in shared memory. If there is a shared buffer, buffers + // is the value of pointer() for the shared buffer, otherwise buffers points + // immediately after the control block. This address is for the mapping within client + // address space. AudioFlinger::TrackBase::mBuffer is for the server address space. + void* buffers; if (sharedBuffer == 0) { - mBuffers = (char*)cblk + sizeof(audio_track_cblk_t); + buffers = (char*)cblk + sizeof(audio_track_cblk_t); } else { - mBuffers = sharedBuffer->pointer(); + buffers = sharedBuffer->pointer(); } mAudioTrack->attachAuxEffect(mAuxEffectId); - cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; - cblk->waitTimeMs = 0; - mRemainingFrames = mNotificationFramesAct; // FIXME don't believe this lie mLatency = afLatency + (1000*frameCount) / sampleRate; mFrameCount = frameCount; @@ -977,147 +954,143 @@ status_t AudioTrack::createTrack_l( } // update proxy - delete mProxy; - mProxy = new AudioTrackClientProxy(cblk, mBuffers, frameCount, mFrameSizeAF); + if (sharedBuffer == 0) { + mStaticProxy.clear(); + mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); + } else { + mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF); + mProxy = mStaticProxy; + } mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000)); mProxy->setSendLevel(mSendLevel); mProxy->setSampleRate(mSampleRate); - if (sharedBuffer != 0) { - // Force buffer full condition as data is already present in shared memory - mProxy->stepUser(frameCount); - } + mProxy->setEpoch(epoch); + mProxy->setMinimum(mNotificationFramesAct); + + mDeathNotifier = new DeathNotifier(this); + mAudioTrack->asBinder()->linkToDeath(mDeathNotifier, this); return NO_ERROR; } status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { - ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + if (audioBuffer == NULL) { + return BAD_VALUE; + } + if (mTransfer != TRANSFER_OBTAIN) { + audioBuffer->frameCount = 0; + audioBuffer->size = 0; + audioBuffer->raw = NULL; + return INVALID_OPERATION; + } + + const struct timespec *requested; + if (waitCount == -1) { + requested = &ClientProxy::kForever; + } else if (waitCount == 0) { + requested = &ClientProxy::kNonBlocking; + } else if (waitCount > 0) { + long long ms = WAIT_PERIOD_MS * (long long) waitCount; + struct timespec timeout; + timeout.tv_sec = ms / 1000; + timeout.tv_nsec = (int) (ms % 1000) * 1000000; + requested = &timeout; + } else { + ALOGE("%s invalid waitCount %d", __func__, waitCount); + requested = NULL; + } + return obtainBuffer(audioBuffer, requested); +} + +status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, + struct timespec *elapsed, size_t *nonContig) +{ + // previous and new IAudioTrack sequence numbers are used to detect track re-creation + uint32_t oldSequence = 0; + uint32_t newSequence; - AutoMutex lock(mLock); - bool active; - status_t result = NO_ERROR; - audio_track_cblk_t* cblk = mCblk; - uint32_t framesReq = audioBuffer->frameCount; - uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS; + Proxy::Buffer buffer; + status_t status = NO_ERROR; - audioBuffer->frameCount = 0; - audioBuffer->size = 0; + static const int32_t kMaxTries = 5; + int32_t tryCounter = kMaxTries; - size_t framesAvail = mProxy->framesAvailable(); + do { + // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to + // keep them from going away if another thread re-creates the track during obtainBuffer() + sp proxy; + sp iMem; - cblk->lock.lock(); - if (cblk->flags & CBLK_INVALID) { - goto create_new_track; - } - cblk->lock.unlock(); - - if (framesAvail == 0) { - cblk->lock.lock(); - goto start_loop_here; - while (framesAvail == 0) { - active = mActive; - if (CC_UNLIKELY(!active)) { - ALOGV("Not active and NO_MORE_BUFFERS"); - cblk->lock.unlock(); - return NO_MORE_BUFFERS; - } - if (CC_UNLIKELY(!waitCount)) { - cblk->lock.unlock(); - return WOULD_BLOCK; - } - if (!(cblk->flags & CBLK_INVALID)) { - mLock.unlock(); - // this condition is in shared memory, so if IAudioTrack and control block - // are replaced due to mediaserver death or IAudioTrack invalidation then - // cv won't be signalled, but fortunately the timeout will limit the wait - result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); - cblk->lock.unlock(); - mLock.lock(); - if (!mActive) { - return status_t(STOPPED); - } - // IAudioTrack may have been re-created while mLock was unlocked - cblk = mCblk; - cblk->lock.lock(); - } + { // start of lock scope + AutoMutex lock(mLock); - if (cblk->flags & CBLK_INVALID) { - goto create_new_track; - } - if (CC_UNLIKELY(result != NO_ERROR)) { - cblk->waitTimeMs += waitTimeMs; - if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { - // timing out when a loop has been set and we have already written upto loop end - // is a normal condition: no need to wake AudioFlinger up. - if (cblk->user < cblk->loopEnd) { - ALOGW("obtainBuffer timed out (is the CPU pegged?) %p name=%#x user=%08x, " - "server=%08x", this, cblk->mName, cblk->user, cblk->server); - //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140) - cblk->lock.unlock(); - result = mAudioTrack->start(); - cblk->lock.lock(); - if (result == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID, &cblk->flags); -create_new_track: - audio_track_cblk_t* temp = cblk; - result = restoreTrack_l(temp, false /*fromStart*/); - cblk = temp; - } - if (result != NO_ERROR) { - ALOGW("obtainBuffer create Track error %d", result); - cblk->lock.unlock(); - return result; - } + newSequence = mSequence; + // did previous obtainBuffer() fail due to media server death or voluntary invalidation? + if (status == DEAD_OBJECT) { + // re-create track, unless someone else has already done so + if (newSequence == oldSequence) { + status = restoreTrack_l("obtainBuffer"); + if (status != NO_ERROR) { + break; } - cblk->waitTimeMs = 0; } + } + oldSequence = newSequence; - if (--waitCount == 0) { - cblk->lock.unlock(); - return TIMED_OUT; - } + // Keep the extra references + proxy = mProxy; + iMem = mCblkMemory; + + // Non-blocking if track is stopped or paused + if (mState != STATE_ACTIVE) { + requested = &ClientProxy::kNonBlocking; } - // read the server count again - start_loop_here: - framesAvail = mProxy->framesAvailable_l(); - } - cblk->lock.unlock(); - } - cblk->waitTimeMs = 0; + } // end of lock scope - if (framesReq > framesAvail) { - framesReq = framesAvail; - } + buffer.mFrameCount = audioBuffer->frameCount; + // FIXME starts the requested timeout and elapsed over from scratch + status = proxy->obtainBuffer(&buffer, requested, elapsed); - uint32_t u = cblk->user; - uint32_t bufferEnd = cblk->userBase + mFrameCount; + } while ((status == DEAD_OBJECT) && (tryCounter-- > 0)); - if (framesReq > bufferEnd - u) { - framesReq = bufferEnd - u; + audioBuffer->frameCount = buffer.mFrameCount; + audioBuffer->size = buffer.mFrameCount * mFrameSizeAF; + audioBuffer->raw = buffer.mRaw; + if (nonContig != NULL) { + *nonContig = buffer.mNonContig; } - - audioBuffer->frameCount = framesReq; - audioBuffer->size = framesReq * mFrameSizeAF; - audioBuffer->raw = mProxy->buffer(u); - active = mActive; - return active ? status_t(NO_ERROR) : status_t(STOPPED); + return status; } void AudioTrack::releaseBuffer(Buffer* audioBuffer) { - ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL); + if (mTransfer == TRANSFER_SHARED) { + return; + } + + size_t stepCount = audioBuffer->size / mFrameSizeAF; + if (stepCount == 0) { + return; + } + + Proxy::Buffer buffer; + buffer.mFrameCount = stepCount; + buffer.mRaw = audioBuffer->raw; AutoMutex lock(mLock); - audio_track_cblk_t* cblk = mCblk; - (void) mProxy->stepUser(audioBuffer->frameCount); - if (audioBuffer->frameCount > 0) { - // restart track if it was disabled by audioflinger due to previous underrun - if (mActive && (cblk->flags & CBLK_DISABLED)) { - android_atomic_and(~CBLK_DISABLED, &cblk->flags); - ALOGW("releaseBuffer() track %p name=%#x disabled, restarting", this, cblk->mName); + mInUnderrun = false; + mProxy->releaseBuffer(&buffer); + + // restart track if it was disabled by audioflinger due to previous underrun + if (mState == STATE_ACTIVE) { + audio_track_cblk_t* cblk = mCblk; + if (android_atomic_and(~CBLK_DISABLED, &cblk->flags) & CBLK_DISABLED) { + ALOGW("releaseBuffer() track %p name=%#x disabled due to previous underrun, restarting", + this, cblk->mName); + // FIXME ignoring status mAudioTrack->start(); } } @@ -1127,68 +1100,46 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) ssize_t AudioTrack::write(const void* buffer, size_t userSize) { - - if (mSharedBuffer != 0 || mIsTimed) { + if (mTransfer != TRANSFER_SYNC || mIsTimed) { return INVALID_OPERATION; } - if (ssize_t(userSize) < 0) { + if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) { // Sanity-check: user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). - ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)", - buffer, userSize, userSize); + ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize); return BAD_VALUE; } - ALOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive); - - if (userSize == 0) { - return 0; - } - - // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed - // while we are accessing the cblk - mLock.lock(); - sp audioTrack = mAudioTrack; - sp iMem = mCblkMemory; - mLock.unlock(); - - // since mLock is unlocked the IAudioTrack and shared memory may be re-created, - // so all cblk references might still refer to old shared memory, but that should be benign - - ssize_t written = 0; - const int8_t *src = (const int8_t *)buffer; + size_t written = 0; Buffer audioBuffer; - size_t frameSz = frameSize(); - do { - audioBuffer.frameCount = userSize/frameSz; + while (userSize >= mFrameSize) { + audioBuffer.frameCount = userSize / mFrameSize; - status_t err = obtainBuffer(&audioBuffer, -1); + status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); if (err < 0) { - // out of buffers, return #bytes written - if (err == status_t(NO_MORE_BUFFERS)) { + if (written > 0) { break; } return ssize_t(err); } size_t toWrite; - if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { // Divide capacity by 2 to take expansion into account - toWrite = audioBuffer.size>>1; - memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) src, toWrite); + toWrite = audioBuffer.size >> 1; + memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite); } else { toWrite = audioBuffer.size; - memcpy(audioBuffer.i8, src, toWrite); + memcpy(audioBuffer.i8, buffer, toWrite); } - src += toWrite; + buffer = ((const char *) buffer) + toWrite; userSize -= toWrite; written += toWrite; releaseBuffer(&audioBuffer); - } while (userSize >= frameSz); + } return written; } @@ -1204,10 +1155,12 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) AutoMutex lock(mLock); status_t result = UNKNOWN_ERROR; +#if 1 // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed // while we are accessing the cblk sp audioTrack = mAudioTrack; sp iMem = mCblkMemory; +#endif // If the track is not invalid already, try to allocate a buffer. alloc // fails indicating that the server is dead, flag the track as invalid so @@ -1223,13 +1176,9 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) // If the track is invalid at this point, attempt to restore it. and try the // allocation one more time. if (cblk->flags & CBLK_INVALID) { - cblk->lock.lock(); - audio_track_cblk_t* temp = cblk; - result = restoreTrack_l(temp, false /*fromStart*/); - cblk = temp; - cblk->lock.unlock(); + result = restoreTrack_l("allocateTimedBuffer"); - if (result == OK) { + if (result == NO_ERROR) { result = mAudioTrack->allocateTimedBuffer(size, buffer); } } @@ -1246,9 +1195,10 @@ status_t TimedAudioTrack::queueTimedBuffer(const sp& buffer, audio_track_cblk_t* cblk = mCblk; // restart track if it was disabled by audioflinger due to previous underrun if (buffer->size() != 0 && status == NO_ERROR && - mActive && (cblk->flags & CBLK_DISABLED)) { + (mState == STATE_ACTIVE) && (cblk->flags & CBLK_DISABLED)) { android_atomic_and(~CBLK_DISABLED, &cblk->flags); ALOGW("queueTimedBuffer() track %p disabled, restarting", this); + // FIXME ignoring status mAudioTrack->start(); } } @@ -1263,12 +1213,8 @@ status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform, // ------------------------------------------------------------------------- -bool AudioTrack::processAudioBuffer(const sp& thread) +nsecs_t AudioTrack::processAudioBuffer(const sp& thread) { - Buffer audioBuffer; - uint32_t frames; - size_t writtenSize; - mLock.lock(); if (mAwaitBoost) { mAwaitBoost = false; @@ -1289,86 +1235,181 @@ bool AudioTrack::processAudioBuffer(const sp& thread) } return true; } - // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed - // while we are accessing the cblk - sp audioTrack = mAudioTrack; - sp iMem = mCblkMemory; - audio_track_cblk_t* cblk = mCblk; - bool active = mActive; - mLock.unlock(); - // since mLock is unlocked the IAudioTrack and shared memory may be re-created, - // so all cblk references might still refer to old shared memory, but that should be benign + // Can only reference mCblk while locked + int32_t flags = android_atomic_and( + ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), &mCblk->flags); - // Manage underrun callback - if (active && (mProxy->framesAvailable() == mFrameCount)) { - ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); - if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) { - mCbf(EVENT_UNDERRUN, mUserData, 0); - if (cblk->server == mFrameCount) { - mCbf(EVENT_BUFFER_END, mUserData, 0); - } - if (mSharedBuffer != 0) { - return false; - } - } + // Check for track invalidation + if (flags & CBLK_INVALID) { + (void) restoreTrack_l("processAudioBuffer"); + mLock.unlock(); + // Run again immediately, but with a new IAudioTrack + return 0; } - // Manage loop end callback - while (mLoopCount > cblk->loopCount) { - int loopCount = -1; - mLoopCount--; - if (mLoopCount >= 0) loopCount = mLoopCount; + bool active = mState == STATE_ACTIVE; - mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount); + // Manage underrun callback, must be done under lock to avoid race with releaseBuffer() + bool newUnderrun = false; + if (flags & CBLK_UNDERRUN) { +#if 0 + // Currently in shared buffer mode, when the server reaches the end of buffer, + // the track stays active in continuous underrun state. It's up to the application + // to pause or stop the track, or set the position to a new offset within buffer. + // This was some experimental code to auto-pause on underrun. Keeping it here + // in "if 0" so we can re-visit this if we add a real sequencer for shared memory content. + if (mTransfer == TRANSFER_SHARED) { + mState = STATE_PAUSED; + active = false; + } +#endif + if (!mInUnderrun) { + mInUnderrun = true; + newUnderrun = true; + } } + // Get current position of server + size_t position = mProxy->getPosition(); + // Manage marker callback - if (!mMarkerReached && (mMarkerPosition > 0)) { - if (cblk->server >= mMarkerPosition) { - mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); - mMarkerReached = true; - } + bool markerReached = false; + size_t markerPosition = mMarkerPosition; + // FIXME fails for wraparound, need 64 bits + if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition)) { + mMarkerReached = markerReached = true; + } + + // Determine number of new position callback(s) that will be needed, while locked + size_t newPosCount = 0; + size_t newPosition = mNewPosition; + size_t updatePeriod = mUpdatePeriod; + // FIXME fails for wraparound, need 64 bits + if (updatePeriod > 0 && position >= newPosition) { + newPosCount = ((position - newPosition) / updatePeriod) + 1; + mNewPosition += updatePeriod * newPosCount; + } + + // Cache other fields that will be needed soon + uint32_t loopPeriod = mLoopPeriod; + uint32_t sampleRate = mSampleRate; + size_t notificationFrames = mNotificationFramesAct; + if (mRefreshRemaining) { + mRefreshRemaining = false; + mRemainingFrames = notificationFrames; + mRetryOnPartialBuffer = false; + } + size_t misalignment = mProxy->getMisalignment(); + int32_t sequence = mSequence; + + // These fields don't need to be cached, because they are assigned only by set(): + // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags + // mFlags is also assigned by createTrack_l(), but not the bit we care about. + + mLock.unlock(); + + // perform callbacks while unlocked + if (newUnderrun) { + mCbf(EVENT_UNDERRUN, mUserData, NULL); + } + // FIXME we will miss loops if loop cycle was signaled several times since last call + // to processAudioBuffer() + if (flags & (CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL)) { + mCbf(EVENT_LOOP_END, mUserData, NULL); + } + if (flags & CBLK_BUFFER_END) { + mCbf(EVENT_BUFFER_END, mUserData, NULL); + } + if (markerReached) { + mCbf(EVENT_MARKER, mUserData, &markerPosition); + } + while (newPosCount > 0) { + size_t temp = newPosition; + mCbf(EVENT_NEW_POS, mUserData, &temp); + newPosition += updatePeriod; + newPosCount--; + } + if (mObservedSequence != sequence) { + mObservedSequence = sequence; + mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL); } - // Manage new position callback - if (mUpdatePeriod > 0) { - while (cblk->server >= mNewPosition) { - mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); - mNewPosition += mUpdatePeriod; - } + // if inactive, then don't run me again until re-started + if (!active) { + return NS_INACTIVE; } - // If Shared buffer is used, no data is requested from client. - if (mSharedBuffer != 0) { - frames = 0; - } else { - frames = mRemainingFrames; + // Compute the estimated time until the next timed event (position, markers, loops) + // FIXME only for non-compressed audio + uint32_t minFrames = ~0; + if (!markerReached && position < markerPosition) { + minFrames = markerPosition - position; + } + if (loopPeriod > 0 && loopPeriod < minFrames) { + minFrames = loopPeriod; + } + if (updatePeriod > 0 && updatePeriod < minFrames) { + minFrames = updatePeriod; } - // See description of waitCount parameter at declaration of obtainBuffer(). - // The logic below prevents us from being stuck below at obtainBuffer() - // not being able to handle timed events (position, markers, loops). - int32_t waitCount = -1; - if (mUpdatePeriod || (!mMarkerReached && mMarkerPosition) || mLoopCount) { - waitCount = 1; + // If > 0, poll periodically to recover from a stuck server. A good value is 2. + static const uint32_t kPoll = 0; + if (kPoll > 0 && mTransfer == TRANSFER_CALLBACK && kPoll * notificationFrames < minFrames) { + minFrames = kPoll * notificationFrames; } - do { + // Convert frame units to time units + nsecs_t ns = NS_WHENEVER; + if (minFrames != (uint32_t) ~0) { + // This "fudge factor" avoids soaking CPU, and compensates for late progress by server + static const nsecs_t kFudgeNs = 10000000LL; // 10 ms + ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs; + } + + // If not supplying data by EVENT_MORE_DATA, then we're done + if (mTransfer != TRANSFER_CALLBACK) { + return ns; + } - audioBuffer.frameCount = frames; + struct timespec timeout; + const struct timespec *requested = &ClientProxy::kForever; + if (ns != NS_WHENEVER) { + timeout.tv_sec = ns / 1000000000LL; + timeout.tv_nsec = ns % 1000000000LL; + ALOGV("timeout %ld.%03d", timeout.tv_sec, (int) timeout.tv_nsec / 1000000); + requested = &timeout; + } + + while (mRemainingFrames > 0) { - status_t err = obtainBuffer(&audioBuffer, waitCount); - if (err < NO_ERROR) { - if (err != TIMED_OUT) { - ALOGE_IF(err != status_t(NO_MORE_BUFFERS), - "Error obtaining an audio buffer, giving up."); - return false; + Buffer audioBuffer; + audioBuffer.frameCount = mRemainingFrames; + size_t nonContig; + status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig); + LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0), + "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount); + requested = &ClientProxy::kNonBlocking; + size_t avail = audioBuffer.frameCount + nonContig; + ALOGV("obtainBuffer(%u) returned %u = %u + %u", + mRemainingFrames, avail, audioBuffer.frameCount, nonContig); + if (err != NO_ERROR) { + if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) { + return 0; } - break; + ALOGE("Error %d obtaining an audio buffer, giving up.", err); + return NS_NEVER; } - if (err == status_t(STOPPED)) { - return false; + + if (mRetryOnPartialBuffer) { + mRetryOnPartialBuffer = false; + if (avail < mRemainingFrames) { + int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate; + if (ns < 0 || myns < ns) { + ns = myns; + } + return ns; + } } // Divide buffer size by 2 to take into account the expansion @@ -1380,66 +1421,76 @@ bool AudioTrack::processAudioBuffer(const sp& thread) size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); - writtenSize = audioBuffer.size; + size_t writtenSize = audioBuffer.size; + size_t writtenFrames = writtenSize / mFrameSize; // Sanity check on returned size - if (ssize_t(writtenSize) <= 0) { + if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) { + ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes", + reqSize, (int) writtenSize); + return NS_NEVER; + } + + if (writtenSize == 0) { // The callback is done filling buffers // Keep this thread going to handle timed events and // still try to get more data in intervals of WAIT_PERIOD_MS // but don't just loop and block the CPU, so wait - usleep(WAIT_PERIOD_MS*1000); - break; - } - - if (writtenSize > reqSize) { - writtenSize = reqSize; + return WAIT_PERIOD_MS * 1000000LL; } if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { // 8 to 16 bit conversion, note that source and destination are the same address memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) audioBuffer.i8, writtenSize); - writtenSize <<= 1; + audioBuffer.size <<= 1; } - audioBuffer.size = writtenSize; - // NOTE: cblk->frameSize is not equal to AudioTrack::frameSize() for - // 8 bit PCM data: in this case, cblk->frameSize is based on a sample size of - // 16 bit. - audioBuffer.frameCount = writtenSize / mFrameSizeAF; - - frames -= audioBuffer.frameCount; + size_t releasedFrames = audioBuffer.size / mFrameSizeAF; + audioBuffer.frameCount = releasedFrames; + mRemainingFrames -= releasedFrames; + if (misalignment >= releasedFrames) { + misalignment -= releasedFrames; + } else { + misalignment = 0; + } releaseBuffer(&audioBuffer); - } - while (frames); - if (frames == 0) { - mRemainingFrames = mNotificationFramesAct; - } else { - mRemainingFrames = frames; + // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer + // if callback doesn't like to accept the full chunk + if (writtenSize < reqSize) { + continue; + } + + // There could be enough non-contiguous frames available to satisfy the remaining request + if (mRemainingFrames <= nonContig) { + continue; + } + +#if 0 + // This heuristic tries to collapse a series of EVENT_MORE_DATA that would total to a + // sum <= notificationFrames. It replaces that series by at most two EVENT_MORE_DATA + // that total to a sum == notificationFrames. + if (0 < misalignment && misalignment <= mRemainingFrames) { + mRemainingFrames = misalignment; + return (mRemainingFrames * 1100000000LL) / sampleRate; + } +#endif + } - return true; + mRemainingFrames = notificationFrames; + mRetryOnPartialBuffer = true; + + // A lot has transpired since ns was calculated, so run again immediately and re-calculate + return 0; } -// must be called with mLock and refCblk.lock held. Callers must also hold strong references on -// the IAudioTrack and IMemory in case they are recreated here. -// If the IAudioTrack is successfully restored, the refCblk pointer is updated -// FIXME Don't depend on caller to hold strong references. -status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart) +status_t AudioTrack::restoreTrack_l(const char *from) { + ALOGW("dead IAudioTrack, creating a new one from %s()", from); + ++mSequence; status_t result; - audio_track_cblk_t* cblk = refCblk; - audio_track_cblk_t* newCblk = cblk; - ALOGW("dead IAudioTrack, creating a new one from %s", - fromStart ? "start()" : "obtainBuffer()"); - - // signal old cblk condition so that other threads waiting for available buffers stop - // waiting now - cblk->cv.broadcast(); - cblk->lock.unlock(); - // refresh the audio configuration cache in this process to make sure we get new // output parameters in getOutput_l() and createTrack_l() AudioSystem::clearAudioConfigCache(); @@ -1447,68 +1498,47 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart // if the new IAudioTrack is created, createTrack_l() will modify the // following member variables: mAudioTrack, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioTrack and IMemory + size_t position = mProxy->getPosition(); + mNewPosition = position + mUpdatePeriod; + size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0; result = createTrack_l(mStreamType, mSampleRate, mFormat, mReqFrameCount, // so that frame count never goes down mFlags, mSharedBuffer, - getOutput_l()); + getOutput_l(), + position /*epoch*/); if (result == NO_ERROR) { - uint32_t user = cblk->user; - uint32_t server = cblk->server; + // continue playback from last known position, but + // don't attempt to restore loop after invalidation; it's difficult and not worthwhile + if (mStaticProxy != NULL) { + mLoopPeriod = 0; + mStaticProxy->setLoop(bufferPosition, mFrameCount, 0); + } + // FIXME How do we simulate the fact that all frames present in the buffer at the time of + // track destruction have been played? This is critical for SoundPool implementation + // This must be broken, and needs to be tested/debugged. +#if 0 // restore write index and set other indexes to reflect empty buffer status - newCblk = mCblk; - newCblk->user = user; - newCblk->server = user; - newCblk->userBase = user; - newCblk->serverBase = user; - // restore loop: this is not guaranteed to succeed if new frame count is not - // compatible with loop length - setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount); - size_t frames = 0; - if (!fromStart) { - newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + if (!strcmp(from, "start")) { // Make sure that a client relying on callback events indicating underrun or // the actual amount of audio frames played (e.g SoundPool) receives them. if (mSharedBuffer == 0) { - if (user > server) { - frames = ((user - server) > mFrameCount) ? - mFrameCount : (user - server); - memset(mBuffers, 0, frames * mFrameSizeAF); - } // restart playback even if buffer is not completely filled. - android_atomic_or(CBLK_FORCEREADY, &newCblk->flags); + android_atomic_or(CBLK_FORCEREADY, &mCblk->flags); } } - if (mSharedBuffer != 0) { - frames = mFrameCount; - } - if (frames > 0) { - // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to - // the client - mProxy->stepUser(frames); - } - if (mActive) { +#endif + if (mState == STATE_ACTIVE) { result = mAudioTrack->start(); - ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result); - } - if (fromStart && result == NO_ERROR) { - mNewPosition = newCblk->server + mUpdatePeriod; } } - ALOGW_IF(result != NO_ERROR, "restoreTrack_l() failed status %d", result); - ALOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x", - result, mActive, newCblk, cblk, newCblk->flags, cblk->flags); - - if (result == NO_ERROR) { - // from now on we switch to the newly created cblk - refCblk = newCblk; + if (result != NO_ERROR) { + ALOGW("restoreTrack_l() failed status %d", result); + mState = STATE_STOPPED; } - newCblk->lock.lock(); - - ALOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result); return result; } @@ -1529,16 +1559,33 @@ status_t AudioTrack::dump(int fd, const Vector& args) const result.append(buffer); snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus); result.append(buffer); - snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); + snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency); result.append(buffer); ::write(fd, result.string(), result.size()); return NO_ERROR; } +uint32_t AudioTrack::getUnderrunFrames() const +{ + AutoMutex lock(mLock); + return mProxy->getUnderrunFrames(); +} + +// ========================================================================= + +void AudioTrack::DeathNotifier::binderDied(const wp& who) +{ + sp audioTrack = mAudioTrack.promote(); + if (audioTrack != 0) { + AutoMutex lock(audioTrack->mLock); + audioTrack->mProxy->binderDied(); + } +} + // ========================================================================= AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver), mPaused(true) + : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false) { } @@ -1556,10 +1603,26 @@ bool AudioTrack::AudioTrackThread::threadLoop() return true; } } - if (!mReceiver.processAudioBuffer(this)) { - pause(); + nsecs_t ns = mReceiver.processAudioBuffer(this); + switch (ns) { + case 0: + return true; + case NS_WHENEVER: + sleep(1); + return true; + case NS_INACTIVE: + pauseConditional(); + return true; + case NS_NEVER: + return false; + default: + LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns); + struct timespec req; + req.tv_sec = ns / 1000000000LL; + req.tv_nsec = ns % 1000000000LL; + nanosleep(&req, NULL /*rem*/); + return true; } - return true; } void AudioTrack::AudioTrackThread::requestExit() @@ -1573,6 +1636,17 @@ void AudioTrack::AudioTrackThread::pause() { AutoMutex _l(mMyLock); mPaused = true; + mResumeLatch = false; +} + +void AudioTrack::AudioTrackThread::pauseConditional() +{ + AutoMutex _l(mMyLock); + if (mResumeLatch) { + mResumeLatch = false; + } else { + mPaused = true; + } } void AudioTrack::AudioTrackThread::resume() @@ -1580,7 +1654,10 @@ void AudioTrack::AudioTrackThread::resume() AutoMutex _l(mMyLock); if (mPaused) { mPaused = false; + mResumeLatch = false; mMyCond.signal(); + } else { + mResumeLatch = true; } } diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 13d47c9..f034164 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -19,178 +19,664 @@ #include #include +extern "C" { +#include "../private/bionic_futex.h" +} namespace android { audio_track_cblk_t::audio_track_cblk_t() - : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), - userBase(0), serverBase(0), frameCount_(0), - loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000), - mSampleRate(0), mSendLevel(0), flags(0) + : server(0), frameCount_(0), mFutex(0), mMinimum(0), + mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), flags(0) { + memset(&u, 0, sizeof(u)); } -uint32_t audio_track_cblk_t::stepUser(size_t stepCount, size_t frameCount, bool isOut) +// --------------------------------------------------------------------------- + +Proxy::Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, + bool isOut, bool clientInServer) + : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize), + mFrameCountP2(roundup(frameCount)), mIsOut(isOut), mClientInServer(clientInServer), + mIsShutdown(false) { - ALOGV("stepuser %08x %08x %d", user, server, stepCount); +} - uint32_t u = user; - u += stepCount; - // Ensure that user is never ahead of server for AudioRecord - if (isOut) { - // If stepServer() has been called once, switch to normal obtainBuffer() timeout period - if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { - bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - } - } else if (u > server) { - ALOGW("stepUser occurred after track reset"); - u = server; +// --------------------------------------------------------------------------- + +ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, + size_t frameSize, bool isOut, bool clientInServer) + : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mEpoch(0) +{ +} + +const struct timespec ClientProxy::kForever = {INT_MAX /*tv_sec*/, 0 /*tv_nsec*/}; +const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/}; + +#define MEASURE_NS 10000000 // attempt to provide accurate timeouts if requested >= MEASURE_NS + +// To facilitate quicker recovery from server failure, this value limits the timeout per each futex +// wait. However it does not protect infinite timeouts. If defined to be zero, there is no limit. +// FIXME May not be compatible with audio tunneling requirements where timeout should be in the +// order of minutes. +#define MAX_SEC 5 + +status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested, + struct timespec *elapsed) +{ + if (buffer == NULL || buffer->mFrameCount == 0) { + ALOGE("%s BAD_VALUE", __func__); + return BAD_VALUE; } + struct timespec total; // total elapsed time spent waiting + total.tv_sec = 0; + total.tv_nsec = 0; + bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting - if (u >= frameCount) { - // common case, user didn't just wrap - if (u - frameCount >= userBase ) { - userBase += frameCount; + status_t status; + enum { + TIMEOUT_ZERO, // requested == NULL || *requested == 0 + TIMEOUT_INFINITE, // *requested == infinity + TIMEOUT_FINITE, // 0 < *requested < infinity + TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE + } timeout; + if (requested == NULL) { + timeout = TIMEOUT_ZERO; + } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) { + timeout = TIMEOUT_ZERO; + } else if (requested->tv_sec == INT_MAX) { + timeout = TIMEOUT_INFINITE; + } else { + timeout = TIMEOUT_FINITE; + if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) { + measure = true; + } + } + struct timespec before; + bool beforeIsValid = false; + audio_track_cblk_t* cblk = mCblk; + bool ignoreInitialPendingInterrupt = true; + // check for shared memory corruption + if (mIsShutdown) { + status = NO_INIT; + goto end; + } + for (;;) { + int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->flags); + // check for track invalidation by server, or server death detection + if (flags & CBLK_INVALID) { + ALOGV("Track invalidated"); + status = DEAD_OBJECT; + goto end; + } + // check for obtainBuffer interrupted by client + if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) { + ALOGV("obtainBuffer() interrupted by client"); + status = -EINTR; + goto end; + } + ignoreInitialPendingInterrupt = false; + // compute number of frames available to write (AudioTrack) or read (AudioRecord) + int32_t front; + int32_t rear; + if (mIsOut) { + // The barrier following the read of mFront is probably redundant. + // We're about to perform a conditional branch based on 'filled', + // which will force the processor to observe the read of mFront + // prior to allowing data writes starting at mRaw. + // However, the processor may support speculative execution, + // and be unable to undo speculative writes into shared memory. + // The barrier will prevent such speculative execution. + front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); + rear = cblk->u.mStreaming.mRear; + } else { + // On the other hand, this barrier is required. + rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); + front = cblk->u.mStreaming.mFront; + } + ssize_t filled = rear - front; + // pipe should not be overfull + if (!(0 <= filled && (size_t) filled <= mFrameCount)) { + ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled); + mIsShutdown = true; + status = NO_INIT; + goto end; + } + // don't allow filling pipe beyond the nominal size + size_t avail = mIsOut ? mFrameCount - filled : filled; + if (avail > 0) { + // 'avail' may be non-contiguous, so return only the first contiguous chunk + size_t part1; + if (mIsOut) { + rear &= mFrameCountP2 - 1; + part1 = mFrameCountP2 - rear; + } else { + front &= mFrameCountP2 - 1; + part1 = mFrameCountP2 - front; + } + if (part1 > avail) { + part1 = avail; + } + if (part1 > buffer->mFrameCount) { + part1 = buffer->mFrameCount; + } + buffer->mFrameCount = part1; + buffer->mRaw = part1 > 0 ? + &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL; + buffer->mNonContig = avail - part1; + // mUnreleased = part1; + status = NO_ERROR; + break; + } + struct timespec remaining; + const struct timespec *ts; + switch (timeout) { + case TIMEOUT_ZERO: + status = WOULD_BLOCK; + goto end; + case TIMEOUT_INFINITE: + ts = NULL; + break; + case TIMEOUT_FINITE: + timeout = TIMEOUT_CONTINUE; + if (MAX_SEC == 0) { + ts = requested; + break; + } + // fall through + case TIMEOUT_CONTINUE: + // FIXME we do not retry if requested < 10ms? needs documentation on this state machine + if (!measure || requested->tv_sec < total.tv_sec || + (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) { + status = TIMED_OUT; + goto end; + } + remaining.tv_sec = requested->tv_sec - total.tv_sec; + if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) { + remaining.tv_nsec += 1000000000; + remaining.tv_sec++; + } + if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) { + remaining.tv_sec = MAX_SEC; + remaining.tv_nsec = 0; + } + ts = &remaining; + break; + default: + LOG_FATAL("%s timeout=%d", timeout); + ts = NULL; + break; + } + int32_t old = android_atomic_dec(&cblk->mFutex); + if (old <= 0) { + int rc; + if (measure && !beforeIsValid) { + clock_gettime(CLOCK_MONOTONIC, &before); + beforeIsValid = true; + } + int ret = __futex_syscall4(&cblk->mFutex, + mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old - 1, ts); + // update total elapsed time spent waiting + if (measure) { + struct timespec after; + clock_gettime(CLOCK_MONOTONIC, &after); + total.tv_sec += after.tv_sec - before.tv_sec; + long deltaNs = after.tv_nsec - before.tv_nsec; + if (deltaNs < 0) { + deltaNs += 1000000000; + total.tv_sec--; + } + if ((total.tv_nsec += deltaNs) >= 1000000000) { + total.tv_nsec -= 1000000000; + total.tv_sec++; + } + before = after; + beforeIsValid = true; + } + switch (ret) { + case 0: // normal wakeup by server, or by binderDied() + case -EWOULDBLOCK: // benign race condition with server + case -EINTR: // wait was interrupted by signal or other spurious wakeup + case -ETIMEDOUT: // time-out expired + break; + default: + ALOGE("%s unexpected error %d", __func__, ret); + status = -ret; + goto end; + } } - } else if (u >= userBase + frameCount) { - // user just wrapped - userBase += frameCount; } - user = u; - - // Clear flow control error condition as new data has been written/read to/from buffer. - if (flags & CBLK_UNDERRUN) { - android_atomic_and(~CBLK_UNDERRUN, &flags); +end: + if (status != NO_ERROR) { + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; + } + if (elapsed != NULL) { + *elapsed = total; } + if (requested == NULL) { + requested = &kNonBlocking; + } + if (measure) { + ALOGV("requested %d.%03d elapsed %d.%03d", requested->tv_sec, requested->tv_nsec / 1000000, + total.tv_sec, total.tv_nsec / 1000000); + } + return status; +} - return u; +void ClientProxy::releaseBuffer(Buffer* buffer) +{ + size_t stepCount = buffer->mFrameCount; + // FIXME + // check mUnreleased + // verify that stepCount <= frameCount returned by the last obtainBuffer() + // verify stepCount not > total frame count of pipe + if (stepCount == 0) { + return; + } + audio_track_cblk_t* cblk = mCblk; + // Both of these barriers are required + if (mIsOut) { + int32_t rear = cblk->u.mStreaming.mRear; + android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear); + } else { + int32_t front = cblk->u.mStreaming.mFront; + android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront); + } } -bool audio_track_cblk_t::stepServer(size_t stepCount, size_t frameCount, bool isOut) +void ClientProxy::binderDied() { - ALOGV("stepserver %08x %08x %d", user, server, stepCount); + audio_track_cblk_t* cblk = mCblk; + if (!(android_atomic_or(CBLK_INVALID, &cblk->flags) & CBLK_INVALID)) { + // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process + (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, + 1); + } +} - if (!tryLock()) { - ALOGW("stepServer() could not lock cblk"); - return false; +void ClientProxy::interrupt() +{ + audio_track_cblk_t* cblk = mCblk; + if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->flags) & CBLK_INTERRUPT)) { + (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, + 1); } +} - uint32_t s = server; - bool flushed = (s == user); +size_t ClientProxy::getMisalignment() +{ + audio_track_cblk_t* cblk = mCblk; + return (mFrameCountP2 - (mIsOut ? cblk->u.mStreaming.mRear : cblk->u.mStreaming.mFront)) & + (mFrameCountP2 - 1); +} - s += stepCount; - if (isOut) { - // Mark that we have read the first buffer so that next time stepUser() is called - // we switch to normal obtainBuffer() timeout period - if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) { - bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1; - } - // It is possible that we receive a flush() - // while the mixer is processing a block: in this case, - // stepServer() is called After the flush() has reset u & s and - // we have s > u - if (flushed) { - ALOGW("stepServer occurred after track reset"); - s = user; +// --------------------------------------------------------------------------- + +void AudioTrackClientProxy::flush() +{ + mCblk->u.mStreaming.mFlush++; +} + +// --------------------------------------------------------------------------- + +StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, + size_t frameCount, size_t frameSize) + : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize), + mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0) +{ +} + +void StaticAudioTrackClientProxy::flush() +{ + LOG_FATAL("static flush"); +} + +void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int loopCount) +{ + StaticAudioTrackState newState; + newState.mLoopStart = loopStart; + newState.mLoopEnd = loopEnd; + newState.mLoopCount = loopCount; + mBufferPosition = loopStart; + (void) mMutator.push(newState); +} + +size_t StaticAudioTrackClientProxy::getBufferPosition() +{ + size_t bufferPosition; + if (mMutator.ack()) { + bufferPosition = mCblk->u.mStatic.mBufferPosition; + if (bufferPosition > mFrameCount) { + bufferPosition = mFrameCount; } + } else { + bufferPosition = mBufferPosition; } + return bufferPosition; +} + +// --------------------------------------------------------------------------- - if (s >= loopEnd) { - ALOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd); - s = loopStart; - if (--loopCount == 0) { - loopEnd = UINT_MAX; - loopStart = UINT_MAX; +ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, + size_t frameSize, bool isOut, bool clientInServer) + : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mUnreleased(0), + mAvailToClient(0), mFlush(0), mDeferWake(false) +{ +} + +status_t ServerProxy::obtainBuffer(Buffer* buffer) +{ + if (mIsShutdown) { + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; + mUnreleased = 0; + return NO_INIT; + } + audio_track_cblk_t* cblk = mCblk; + // compute number of frames available to write (AudioTrack) or read (AudioRecord), + // or use previous cached value from framesReady(), with added barrier if it omits. + int32_t front; + int32_t rear; + // See notes on barriers at ClientProxy::obtainBuffer() + if (mIsOut) { + int32_t flush = cblk->u.mStreaming.mFlush; + rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); + if (flush != mFlush) { + front = rear; + mFlush = flush; + } else { + front = cblk->u.mStreaming.mFront; } + } else { + front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); + rear = cblk->u.mStreaming.mRear; + } + ssize_t filled = rear - front; + // pipe should not already be overfull + if (!(0 <= filled && (size_t) filled <= mFrameCount)) { + ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled); + mIsShutdown = true; + } + if (mIsShutdown) { + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; + mUnreleased = 0; + return NO_INIT; } + // don't allow filling pipe beyond the nominal size + size_t availToServer; + if (mIsOut) { + availToServer = filled; + mAvailToClient = mFrameCount - filled; + } else { + availToServer = mFrameCount - filled; + mAvailToClient = filled; + } + // 'availToServer' may be non-contiguous, so return only the first contiguous chunk + size_t part1; + if (mIsOut) { + front &= mFrameCountP2 - 1; + part1 = mFrameCountP2 - front; + } else { + rear &= mFrameCountP2 - 1; + part1 = mFrameCountP2 - rear; + } + if (part1 > availToServer) { + part1 = availToServer; + } + size_t ask = buffer->mFrameCount; + if (part1 > ask) { + part1 = ask; + } + // is assignment redundant in some cases? + buffer->mFrameCount = part1; + buffer->mRaw = part1 > 0 ? + &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL; + buffer->mNonContig = availToServer - part1; + mUnreleased = part1; + // optimization to avoid waking up the client too early + // FIXME need to test for recording + mDeferWake = part1 < ask && availToServer >= ask; + return part1 > 0 ? NO_ERROR : WOULD_BLOCK; +} - if (s >= frameCount) { - // common case, server didn't just wrap - if (s - frameCount >= serverBase ) { - serverBase += frameCount; - } - } else if (s >= serverBase + frameCount) { - // server just wrapped - serverBase += frameCount; +void ServerProxy::releaseBuffer(Buffer* buffer) +{ + if (mIsShutdown) { + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; + return; + } + size_t stepCount = buffer->mFrameCount; + LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased); + if (stepCount == 0) { + buffer->mRaw = NULL; + buffer->mNonContig = 0; + return; + } + mUnreleased -= stepCount; + audio_track_cblk_t* cblk = mCblk; + if (mIsOut) { + int32_t front = cblk->u.mStreaming.mFront; + android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront); + } else { + int32_t rear = cblk->u.mStreaming.mRear; + android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear); } - server = s; + mCblk->server += stepCount; - if (!(flags & CBLK_INVALID)) { - cv.signal(); + size_t half = mFrameCount / 2; + if (half == 0) { + half = 1; + } + size_t minimum = cblk->mMinimum; + if (minimum == 0) { + minimum = mIsOut ? half : 1; + } else if (minimum > half) { + minimum = half; + } + if (!mDeferWake && mAvailToClient + stepCount >= minimum) { + ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum); + // could client be sleeping, or not need this increment and counter overflows? + int32_t old = android_atomic_inc(&cblk->mFutex); + if (old == -1) { + (void) __futex_syscall3(&cblk->mFutex, + mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); + } } - lock.unlock(); - return true; + + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; } -void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offset) const +// --------------------------------------------------------------------------- + +size_t AudioTrackServerProxy::framesReady() { - return (int8_t *)buffers + (offset - userBase) * frameSize; + LOG_ALWAYS_FATAL_IF(!mIsOut); + + if (mIsShutdown) { + return 0; + } + audio_track_cblk_t* cblk = mCblk; + // the acquire might not be necessary since not doing a subsequent read + int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); + ssize_t filled = rear - cblk->u.mStreaming.mFront; + // pipe should not already be overfull + if (!(0 <= filled && (size_t) filled <= mFrameCount)) { + ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled); + mIsShutdown = true; + return 0; + } + // cache this value for later use by obtainBuffer(), with added barrier + // and racy if called by normal mixer thread + // ignores flush(), so framesReady() may report a larger mFrameCount than obtainBuffer() + return filled; } -uint32_t audio_track_cblk_t::framesAvailable(size_t frameCount, bool isOut) +// --------------------------------------------------------------------------- + +StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, + size_t frameCount, size_t frameSize) + : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize), + mObserver(&cblk->u.mStatic.mSingleStateQueue), mPosition(0), + mEnd(frameCount), mFramesReadyIsCalledByMultipleThreads(false) { - Mutex::Autolock _l(lock); - return framesAvailable_l(frameCount, isOut); + mState.mLoopStart = 0; + mState.mLoopEnd = 0; + mState.mLoopCount = 0; } -uint32_t audio_track_cblk_t::framesAvailable_l(size_t frameCount, bool isOut) +void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads() { - uint32_t u = user; - uint32_t s = server; + mFramesReadyIsCalledByMultipleThreads = true; +} - if (isOut) { - uint32_t limit = (s < loopStart) ? s : loopStart; - return limit + frameCount - u; - } else { - return frameCount + u - s; +size_t StaticAudioTrackServerProxy::framesReady() +{ + // FIXME + // This is racy if called by normal mixer thread, + // as we're reading 2 independent variables without a lock. + // Can't call mObserver.poll(), as we might be called from wrong thread. + // If looping is enabled, should return a higher number (since includes non-contiguous). + size_t position = mPosition; + if (!mFramesReadyIsCalledByMultipleThreads) { + ssize_t positionOrStatus = pollPosition(); + if (positionOrStatus >= 0) { + position = (size_t) positionOrStatus; + } } + size_t end = mEnd; + return position < end ? end - position : 0; } -uint32_t audio_track_cblk_t::framesReady(bool isOut) +ssize_t StaticAudioTrackServerProxy::pollPosition() { - uint32_t u = user; - uint32_t s = server; - - if (isOut) { - if (u < loopEnd) { - return u - s; - } else { - // do not block on mutex shared with client on AudioFlinger side - if (!tryLock()) { - ALOGW("framesReady() could not lock cblk"); - return 0; + size_t position = mPosition; + StaticAudioTrackState state; + if (mObserver.poll(state)) { + bool valid = false; + size_t loopStart = state.mLoopStart; + size_t loopEnd = state.mLoopEnd; + if (state.mLoopCount == 0) { + if (loopStart > mFrameCount) { + loopStart = mFrameCount; } - uint32_t frames = UINT_MAX; - if (loopCount >= 0) { - frames = (loopEnd - loopStart)*loopCount + u - s; + // ignore loopEnd + mPosition = position = loopStart; + mEnd = mFrameCount; + mState.mLoopCount = 0; + valid = true; + } else { + if (loopStart < loopEnd && loopEnd <= mFrameCount && + loopEnd - loopStart >= MIN_LOOP) { + if (!(loopStart <= position && position < loopEnd)) { + mPosition = position = loopStart; + } + mEnd = loopEnd; + mState = state; + valid = true; } - lock.unlock(); - return frames; } - } else { - return s - u; + if (!valid) { + ALOGE("%s client pushed an invalid state, shutting down", __func__); + mIsShutdown = true; + return (ssize_t) NO_INIT; + } + mCblk->u.mStatic.mBufferPosition = position; } + return (ssize_t) position; } -bool audio_track_cblk_t::tryLock() +status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer) { - // the code below simulates lock-with-timeout - // we MUST do this to protect the AudioFlinger server - // as this lock is shared with the client. - status_t err; + if (mIsShutdown) { + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; + mUnreleased = 0; + return NO_INIT; + } + ssize_t positionOrStatus = pollPosition(); + if (positionOrStatus < 0) { + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; + mUnreleased = 0; + return (status_t) positionOrStatus; + } + size_t position = (size_t) positionOrStatus; + size_t avail; + if (position < mEnd) { + avail = mEnd - position; + size_t wanted = buffer->mFrameCount; + if (avail < wanted) { + buffer->mFrameCount = avail; + } else { + avail = wanted; + } + buffer->mRaw = &((char *) mBuffers)[position * mFrameSize]; + } else { + avail = 0; + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + } + buffer->mNonContig = 0; // FIXME should be > 0 for looping + mUnreleased = avail; + return NO_ERROR; +} - err = lock.tryLock(); - if (err == -EBUSY) { // just wait a bit - usleep(1000); - err = lock.tryLock(); +void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) +{ + size_t stepCount = buffer->mFrameCount; + LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased); + if (stepCount == 0) { + buffer->mRaw = NULL; + buffer->mNonContig = 0; + return; } - if (err != NO_ERROR) { - // probably, the client just died. - return false; + mUnreleased -= stepCount; + audio_track_cblk_t* cblk = mCblk; + size_t position = mPosition; + size_t newPosition = position + stepCount; + int32_t setFlags = 0; + if (!(position <= newPosition && newPosition <= mFrameCount)) { + ALOGW("%s newPosition %u outside [%u, %u]", __func__, newPosition, position, mFrameCount); + newPosition = mFrameCount; + } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) { + if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) { + newPosition = mState.mLoopStart; + setFlags = CBLK_LOOP_CYCLE; + } else { + mEnd = mFrameCount; // this is what allows playback to continue after the loop + setFlags = CBLK_LOOP_FINAL; + } } - return true; + if (newPosition == mFrameCount) { + setFlags |= CBLK_BUFFER_END; + } + mPosition = newPosition; + + cblk->server += stepCount; + cblk->u.mStatic.mBufferPosition = newPosition; + if (setFlags != 0) { + (void) android_atomic_or(setFlags, &cblk->flags); + // this would be a good place to wake a futex + } + + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; } +// --------------------------------------------------------------------------- + } // namespace android diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index ebe1ba1..f9ad31d 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -1060,7 +1060,9 @@ bool ToneGenerator::initAudioTrack() { this, // user 0, // notificationFrames 0, // sharedBuffer - mThreadCanCallJava); + mThreadCanCallJava, + 0, // sessionId + AudioTrack::TRANSFER_CALLBACK); if (mpAudioTrack->initCheck() != NO_ERROR) { ALOGE("AudioTrack->initCheck failed"); -- cgit v1.1 From e2ffd5b583da9d30d96710b0e8879e90b2b51d30 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 13 Jun 2013 13:47:02 -0700 Subject: AudioRecord must be used as sp<> only Bug: 9423855 Change-Id: I78ba8228c60dff11fb466156bb632c5dda45cdaf --- media/libstagefright/AudioSource.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'media') diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 3cf4d5c..bdd842f 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -49,8 +49,7 @@ static void AudioRecordCallbackFunction(int event, void *user, void *info) { AudioSource::AudioSource( audio_source_t inputSource, uint32_t sampleRate, uint32_t channelCount) - : mRecord(NULL), - mStarted(false), + : mStarted(false), mSampleRate(sampleRate), mPrevSampleTimeUs(0), mNumFramesReceived(0), @@ -91,9 +90,6 @@ AudioSource::~AudioSource() { if (mStarted) { reset(); } - - delete mRecord; - mRecord = NULL; } status_t AudioSource::initCheck() const { @@ -122,8 +118,7 @@ status_t AudioSource::start(MetaData *params) { if (err == OK) { mStarted = true; } else { - delete mRecord; - mRecord = NULL; + mRecord.clear(); } -- cgit v1.1 From 2309d1a1ff016a31d9aa68272bcb471e64a26cfa Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 14 Jun 2013 11:58:27 -0700 Subject: The software avc decoder would silently drop output frames if not enough room was available in the output queue at the time they were available. No more. Change-Id: I5957290d40ba31bda7944271ec7f2aa0f1f7043c --- .../libstagefright/codecs/on2/h264dec/SoftAVC.cpp | 65 ++++++++++------------ media/libstagefright/codecs/on2/h264dec/SoftAVC.h | 2 +- 2 files changed, 31 insertions(+), 36 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp index 3bd9f47..7ddb13c 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp @@ -109,13 +109,21 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { List &inQueue = getPortQueue(kInputPortIndex); List &outQueue = getPortQueue(kOutputPortIndex); + + if (mHeadersDecoded) { + // Dequeue any already decoded output frames to free up space + // in the output queue. + + drainAllOutputBuffers(false /* eos */); + } + H264SwDecRet ret = H264SWDEC_PIC_RDY; bool portSettingsChanged = false; while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty()) && outQueue.size() == kNumOutputBuffers) { if (mEOSStatus == INPUT_EOS_SEEN) { - drainAllOutputBuffers(); + drainAllOutputBuffers(true /* eos */); return; } @@ -203,15 +211,7 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { mFirstPictureId = -1; } - while (!outQueue.empty() && - mHeadersDecoded && - H264SwDecNextPicture(mHandle, &decodedPicture, 0) - == H264SWDEC_PIC_RDY) { - - int32_t picId = decodedPicture.picId; - uint8_t *data = (uint8_t *) decodedPicture.pOutputPicture; - drainOneOutputBuffer(picId, data); - } + drainAllOutputBuffers(false /* eos */); } } @@ -272,43 +272,38 @@ void SoftAVC::drainOneOutputBuffer(int32_t picId, uint8_t* data) { notifyFillBufferDone(outHeader); } -bool SoftAVC::drainAllOutputBuffers() { +void SoftAVC::drainAllOutputBuffers(bool eos) { List &outQueue = getPortQueue(kOutputPortIndex); H264SwDecPicture decodedPicture; + if (mHeadersDecoded) { + while (!outQueue.empty() + && H264SWDEC_PIC_RDY == H264SwDecNextPicture( + mHandle, &decodedPicture, eos /* flush */)) { + int32_t picId = decodedPicture.picId; + uint8_t *data = (uint8_t *) decodedPicture.pOutputPicture; + drainOneOutputBuffer(picId, data); + } + } + + if (!eos) { + return; + } + while (!outQueue.empty()) { BufferInfo *outInfo = *outQueue.begin(); outQueue.erase(outQueue.begin()); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - if (mHeadersDecoded && - H264SWDEC_PIC_RDY == - H264SwDecNextPicture(mHandle, &decodedPicture, 1 /* flush */)) { - int32_t picId = decodedPicture.picId; - CHECK(mPicToHeaderMap.indexOfKey(picId) >= 0); - - memcpy(outHeader->pBuffer + outHeader->nOffset, - decodedPicture.pOutputPicture, - mPictureSize); - - OMX_BUFFERHEADERTYPE *header = mPicToHeaderMap.valueFor(picId); - outHeader->nTimeStamp = header->nTimeStamp; - outHeader->nFlags = header->nFlags; - outHeader->nFilledLen = mPictureSize; - mPicToHeaderMap.removeItem(picId); - delete header; - } else { - outHeader->nTimeStamp = 0; - outHeader->nFilledLen = 0; - outHeader->nFlags = OMX_BUFFERFLAG_EOS; - mEOSStatus = OUTPUT_FRAMES_FLUSHED; - } + outHeader->nTimeStamp = 0; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); - } - return true; + mEOSStatus = OUTPUT_FRAMES_FLUSHED; + } } void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) { diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h index 0ed7ebe..ee69926 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h @@ -72,7 +72,7 @@ private: bool mSignalledError; status_t initDecoder(); - bool drainAllOutputBuffers(); + void drainAllOutputBuffers(bool eos); void drainOneOutputBuffer(int32_t picId, uint8_t *data); void saveFirstOutputBuffer(int32_t pidId, uint8_t *data); bool handleCropRectEvent(const CropParams* crop); -- cgit v1.1 From 72a43b68da48890273508cb1c9d646b7d75fc101 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 17 Jun 2013 16:14:39 -0700 Subject: Speed up id3v2 unsynchronization Instead of doing many overlapping memmoves, do a single copy pass that skips over the inserted unsynchronization bytes. For some files this reduces parsing time from minutes to milliseconds. b/9463262 Change-Id: I735b7051e77a093d86fb7a3e46209875946225ed --- media/libstagefright/id3/ID3.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 8d3013b..34d671a 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -357,17 +357,22 @@ bool ID3::removeUnsynchronizationV2_4(bool iTunesHack) { } if (flags & 2) { - // Unsynchronization added. + // This file has "unsynchronization", so we have to replace occurrences + // of 0xff 0x00 with just 0xff in order to get the real data. + size_t readOffset = offset + 11; + size_t writeOffset = offset + 11; for (size_t i = 0; i + 1 < dataSize; ++i) { - if (mData[offset + 10 + i] == 0xff - && mData[offset + 11 + i] == 0x00) { - memmove(&mData[offset + 11 + i], &mData[offset + 12 + i], - mSize - offset - 12 - i); + if (mData[readOffset - 1] == 0xff + && mData[readOffset] == 0x00) { + ++readOffset; --mSize; --dataSize; } + mData[writeOffset++] = mData[readOffset++]; } + // move the remaining data following this frame + memmove(&mData[writeOffset], &mData[readOffset], oldSize - readOffset); flags &= ~2; } -- cgit v1.1 From 921832327619f7852b16f73a19504702c5a28a31 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 18 Jun 2013 09:39:15 -0700 Subject: mv libcpustats from frameworks/native to frameworks/av OK to lose history Change-Id: Ieca78edc5dfe479dd7ea48fe7e0f3c164356cee3 --- media/libcpustats/Android.mk | 11 + media/libcpustats/CentralTendencyStatistics.cpp | 81 ++++++++ media/libcpustats/ThreadCpuUsage.cpp | 255 ++++++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 media/libcpustats/Android.mk create mode 100644 media/libcpustats/CentralTendencyStatistics.cpp create mode 100644 media/libcpustats/ThreadCpuUsage.cpp (limited to 'media') diff --git a/media/libcpustats/Android.mk b/media/libcpustats/Android.mk new file mode 100644 index 0000000..b506353 --- /dev/null +++ b/media/libcpustats/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + CentralTendencyStatistics.cpp \ + ThreadCpuUsage.cpp + +LOCAL_MODULE := libcpustats + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libcpustats/CentralTendencyStatistics.cpp b/media/libcpustats/CentralTendencyStatistics.cpp new file mode 100644 index 0000000..42ab62b --- /dev/null +++ b/media/libcpustats/CentralTendencyStatistics.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +void CentralTendencyStatistics::sample(double x) +{ + // update min and max + if (x < mMinimum) + mMinimum = x; + if (x > mMaximum) + mMaximum = x; + // Knuth + if (mN == 0) { + mMean = 0; + } + ++mN; + double delta = x - mMean; + mMean += delta / mN; + mM2 += delta * (x - mMean); +} + +void CentralTendencyStatistics::reset() +{ + mMean = NAN; + mMedian = NAN; + mMinimum = INFINITY; + mMaximum = -INFINITY; + mN = 0; + mM2 = 0; + mVariance = NAN; + mVarianceKnownForN = 0; + mStddev = NAN; + mStddevKnownForN = 0; +} + +double CentralTendencyStatistics::variance() const +{ + double variance; + if (mVarianceKnownForN != mN) { + if (mN > 1) { + // double variance_n = M2/n; + variance = mM2 / (mN - 1); + } else { + variance = NAN; + } + mVariance = variance; + mVarianceKnownForN = mN; + } else { + variance = mVariance; + } + return variance; +} + +double CentralTendencyStatistics::stddev() const +{ + double stddev; + if (mStddevKnownForN != mN) { + stddev = sqrt(variance()); + mStddev = stddev; + mStddevKnownForN = mN; + } else { + stddev = mStddev; + } + return stddev; +} diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp new file mode 100644 index 0000000..637402a --- /dev/null +++ b/media/libcpustats/ThreadCpuUsage.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ThreadCpuUsage" +//#define LOG_NDEBUG 0 + +#include +#include +#include + +#include +#include + +#include + +namespace android { + +bool ThreadCpuUsage::setEnabled(bool isEnabled) +{ + bool wasEnabled = mIsEnabled; + // only do something if there is a change + if (isEnabled != wasEnabled) { + ALOGV("setEnabled(%d)", isEnabled); + int rc; + // enabling + if (isEnabled) { + rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mPreviousTs); + if (rc) { + ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); + isEnabled = false; + } else { + mWasEverEnabled = true; + // record wall clock time at first enable + if (!mMonotonicKnown) { + rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); + if (rc) { + ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); + } else { + mMonotonicKnown = true; + } + } + } + // disabling + } else { + struct timespec ts; + rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + if (rc) { + ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); + } else { + long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + + (ts.tv_nsec - mPreviousTs.tv_nsec); + mAccumulator += delta; +#if 0 + mPreviousTs = ts; +#endif + } + } + mIsEnabled = isEnabled; + } + return wasEnabled; +} + +bool ThreadCpuUsage::sampleAndEnable(double& ns) +{ + bool ret; + bool wasEverEnabled = mWasEverEnabled; + if (enable()) { + // already enabled, so add a new sample relative to previous + return sample(ns); + } else if (wasEverEnabled) { + // was disabled, but add sample for accumulated time while enabled + ns = (double) mAccumulator; + mAccumulator = 0; + ALOGV("sampleAndEnable %.0f", ns); + return true; + } else { + // first time called + ns = 0.0; + ALOGV("sampleAndEnable false"); + return false; + } +} + +bool ThreadCpuUsage::sample(double &ns) +{ + if (mWasEverEnabled) { + if (mIsEnabled) { + struct timespec ts; + int rc; + rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + if (rc) { + ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); + ns = 0.0; + return false; + } else { + long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + + (ts.tv_nsec - mPreviousTs.tv_nsec); + mAccumulator += delta; + mPreviousTs = ts; + } + } else { + mWasEverEnabled = false; + } + ns = (double) mAccumulator; + ALOGV("sample %.0f", ns); + mAccumulator = 0; + return true; + } else { + ALOGW("Can't add sample because measurements have never been enabled"); + ns = 0.0; + return false; + } +} + +long long ThreadCpuUsage::elapsed() const +{ + long long elapsed; + if (mMonotonicKnown) { + struct timespec ts; + int rc; + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rc) { + ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); + elapsed = 0; + } else { + // mMonotonicTs is updated only at first enable and resetStatistics + elapsed = (ts.tv_sec - mMonotonicTs.tv_sec) * 1000000000LL + + (ts.tv_nsec - mMonotonicTs.tv_nsec); + } + } else { + ALOGW("Can't compute elapsed time because measurements have never been enabled"); + elapsed = 0; + } + ALOGV("elapsed %lld", elapsed); + return elapsed; +} + +void ThreadCpuUsage::resetElapsed() +{ + ALOGV("resetElapsed"); + if (mMonotonicKnown) { + int rc; + rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); + if (rc) { + ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); + mMonotonicKnown = false; + } + } +} + +/*static*/ +int ThreadCpuUsage::sScalingFds[ThreadCpuUsage::MAX_CPU]; +pthread_once_t ThreadCpuUsage::sOnceControl = PTHREAD_ONCE_INIT; +int ThreadCpuUsage::sKernelMax; +pthread_mutex_t ThreadCpuUsage::sMutex = PTHREAD_MUTEX_INITIALIZER; + +/*static*/ +void ThreadCpuUsage::init() +{ + // read the number of CPUs + sKernelMax = 1; + int fd = open("/sys/devices/system/cpu/kernel_max", O_RDONLY); + if (fd >= 0) { +#define KERNEL_MAX_SIZE 12 + char kernelMax[KERNEL_MAX_SIZE]; + ssize_t actual = read(fd, kernelMax, sizeof(kernelMax)); + if (actual >= 2 && kernelMax[actual-1] == '\n') { + sKernelMax = atoi(kernelMax); + if (sKernelMax >= MAX_CPU - 1) { + ALOGW("kernel_max %d but MAX_CPU %d", sKernelMax, MAX_CPU); + sKernelMax = MAX_CPU; + } else if (sKernelMax < 0) { + ALOGW("kernel_max invalid %d", sKernelMax); + sKernelMax = 1; + } else { + ++sKernelMax; + ALOGV("number of CPUs %d", sKernelMax); + } + } else { + ALOGW("Can't read number of CPUs"); + } + (void) close(fd); + } else { + ALOGW("Can't open number of CPUs"); + } + int i; + for (i = 0; i < MAX_CPU; ++i) { + sScalingFds[i] = -1; + } +} + +uint32_t ThreadCpuUsage::getCpukHz(int cpuNum) +{ + if (cpuNum < 0 || cpuNum >= MAX_CPU) { + ALOGW("getCpukHz called with invalid CPU %d", cpuNum); + return 0; + } + // double-checked locking idiom is not broken for atomic values such as fd + int fd = sScalingFds[cpuNum]; + if (fd < 0) { + // some kernels can't open a scaling file until hot plug complete + pthread_mutex_lock(&sMutex); + fd = sScalingFds[cpuNum]; + if (fd < 0) { +#define FREQ_SIZE 64 + char freq_path[FREQ_SIZE]; +#define FREQ_DIGIT 27 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10); +#define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq" + strlcpy(freq_path, FREQ_PATH, sizeof(freq_path)); + freq_path[FREQ_DIGIT] = cpuNum + '0'; + fd = open(freq_path, O_RDONLY | O_CLOEXEC); + // keep this fd until process exit or exec + sScalingFds[cpuNum] = fd; + } + pthread_mutex_unlock(&sMutex); + if (fd < 0) { + ALOGW("getCpukHz can't open CPU %d", cpuNum); + return 0; + } + } +#define KHZ_SIZE 12 + char kHz[KHZ_SIZE]; // kHz base 10 + ssize_t actual = pread(fd, kHz, sizeof(kHz), (off_t) 0); + uint32_t ret; + if (actual >= 2 && kHz[actual-1] == '\n') { + ret = atoi(kHz); + } else { + ret = 0; + } + if (ret != mCurrentkHz[cpuNum]) { + if (ret > 0) { + ALOGV("CPU %d frequency %u kHz", cpuNum, ret); + } else { + ALOGW("Can't read CPU %d frequency", cpuNum); + } + mCurrentkHz[cpuNum] = ret; + } + return ret; +} + +} // namespace android -- cgit v1.1 From 054e7347cc60ad4b9dd2e8f456406f122f9f5879 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 2 May 2013 16:37:36 -0700 Subject: stagefright: support for video decoder metadata mode Change-Id: Id360f29236798163f9f3a82135f601083a8a5058 Signed-off-by: Lajos Molnar Bug: 7093648 --- media/libstagefright/ACodec.cpp | 201 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 186 insertions(+), 15 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index bf650b4..1a4f069 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -255,6 +255,8 @@ private: struct ACodec::ExecutingState : public ACodec::BaseState { ExecutingState(ACodec *codec); + void submitRegularOutputBuffers(); + void submitOutputMetaBuffers(); void submitOutputBuffers(); // Submit output buffers to the decoder, submit input buffers to client @@ -364,7 +366,10 @@ ACodec::ACodec() mEncoderDelay(0), mEncoderPadding(0), mChannelMaskPresent(false), - mChannelMask(0) { + mChannelMask(0), + mDequeueCounter(0), + mStoreMetaDataInOutputBuffers(false), + mMetaDataBuffersToSubmit(0) { mUninitializedState = new UninitializedState(this); mLoadedState = new LoadedState(this); mLoadedToIdleState = new LoadedToIdleState(this); @@ -454,7 +459,11 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { status_t err; if (mNativeWindow != NULL && portIndex == kPortIndexOutput) { - err = allocateOutputBuffersFromNativeWindow(); + if (mStoreMetaDataInOutputBuffers) { + err = allocateOutputMetaDataBuffers(); + } else { + err = allocateOutputBuffersFromNativeWindow(); + } } else { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -536,7 +545,9 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { return OK; } -status_t ACodec::allocateOutputBuffersFromNativeWindow() { +status_t ACodec::configureOutputBuffersFromNativeWindow( + OMX_U32 *bufferCount, OMX_U32 *bufferSize, + OMX_U32 *minUndequeuedBuffers) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); def.nPortIndex = kPortIndexOutput; @@ -601,10 +612,10 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { return err; } - int minUndequeuedBufs = 0; + *minUndequeuedBuffers = 0; err = mNativeWindow->query( mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBufs); + (int *)minUndequeuedBuffers); if (err != 0) { ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)", @@ -615,8 +626,8 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { // XXX: Is this the right logic to use? It's not clear to me what the OMX // buffer counts refer to - how do they account for the renderer holding on // to buffers? - if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) { - OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs; + if (def.nBufferCountActual < def.nBufferCountMin + *minUndequeuedBuffers) { + OMX_U32 newBufferCount = def.nBufferCountMin + *minUndequeuedBuffers; def.nBufferCountActual = newBufferCount; err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); @@ -637,12 +648,24 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { return err; } + *bufferCount = def.nBufferCountActual; + *bufferSize = def.nBufferSize; + return err; +} + +status_t ACodec::allocateOutputBuffersFromNativeWindow() { + OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers; + status_t err = configureOutputBuffersFromNativeWindow( + &bufferCount, &bufferSize, &minUndequeuedBuffers); + if (err != 0) + return err; + ALOGV("[%s] Allocating %lu buffers from a native window of size %lu on " "output port", - mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize); + mComponentName.c_str(), bufferCount, bufferSize); // Dequeue buffers and send them to OMX - for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) { + for (OMX_U32 i = 0; i < bufferCount; i++) { ANativeWindowBuffer *buf; err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); if (err != 0) { @@ -653,7 +676,7 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { sp graphicBuffer(new GraphicBuffer(buf, false)); BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; - info.mData = new ABuffer(NULL /* data */, def.nBufferSize /* capacity */); + info.mData = new ABuffer(NULL /* data */, bufferSize /* capacity */); info.mGraphicBuffer = graphicBuffer; mBuffers[kPortIndexOutput].push(info); @@ -682,9 +705,9 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { cancelStart = 0; cancelEnd = mBuffers[kPortIndexOutput].size(); } else { - // Return the last two buffers to the native window. - cancelStart = def.nBufferCountActual - minUndequeuedBufs; - cancelEnd = def.nBufferCountActual; + // Return the required minimum undequeued buffers to the native window. + cancelStart = bufferCount - minUndequeuedBuffers; + cancelEnd = bufferCount; } for (OMX_U32 i = cancelStart; i < cancelEnd; i++) { @@ -695,6 +718,65 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { return err; } +status_t ACodec::allocateOutputMetaDataBuffers() { + OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers; + status_t err = configureOutputBuffersFromNativeWindow( + &bufferCount, &bufferSize, &minUndequeuedBuffers); + if (err != 0) + return err; + + ALOGV("[%s] Allocating %lu meta buffers on output port", + mComponentName.c_str(), bufferCount); + + size_t totalSize = bufferCount * 8; + mDealer[kPortIndexOutput] = new MemoryDealer(totalSize, "ACodec"); + + // Dequeue buffers and send them to OMX + for (OMX_U32 i = 0; i < bufferCount; i++) { + BufferInfo info; + info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; + info.mGraphicBuffer = NULL; + info.mDequeuedAt = mDequeueCounter; + + sp mem = mDealer[kPortIndexOutput]->allocate( + sizeof(struct VideoDecoderOutputMetaData)); + CHECK(mem.get() != NULL); + info.mData = new ABuffer(mem->pointer(), mem->size()); + + // we use useBuffer for metadata regardless of quirks + err = mOMX->useBuffer( + mNode, kPortIndexOutput, mem, &info.mBufferID); + + mBuffers[kPortIndexOutput].push(info); + + ALOGV("[%s] allocated meta buffer with ID %p (pointer = %p)", + mComponentName.c_str(), info.mBufferID, mem->pointer()); + } + + mMetaDataBuffersToSubmit = bufferCount - minUndequeuedBuffers; + return err; +} + +status_t ACodec::submitOutputMetaDataBuffer() { + CHECK(mStoreMetaDataInOutputBuffers); + if (mMetaDataBuffersToSubmit == 0) + return OK; + + BufferInfo *info = dequeueBufferFromNativeWindow(); + if (info == NULL) + return ERROR_IO; + + ALOGV("[%s] submitting output meta buffer ID %p for graphic buffer %p", + mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get()); + + --mMetaDataBuffersToSubmit; + CHECK_EQ(mOMX->fillBuffer(mNode, info->mBufferID), + (status_t)OK); + + info->mStatus = BufferInfo::OWNED_BY_COMPONENT; + return OK; +} + status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US); @@ -714,16 +796,19 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { ANativeWindowBuffer *buf; int fenceFd = -1; + CHECK(mNativeWindow.get() != NULL); if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { ALOGE("dequeueBuffer failed."); return NULL; } + BufferInfo *oldest = NULL; for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); - if (info->mGraphicBuffer->handle == buf->handle) { + if (info->mGraphicBuffer != NULL && + info->mGraphicBuffer->handle == buf->handle) { CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_NATIVE_WINDOW); @@ -731,6 +816,34 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { return info; } + + if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW && + (oldest == NULL || + // avoid potential issues from counter rolling over + mDequeueCounter - info->mDequeuedAt > + mDequeueCounter - oldest->mDequeuedAt)) { + oldest = info; + } + } + + if (oldest) { + CHECK(mStoreMetaDataInOutputBuffers); + + // discard buffer in LRU info and replace with new buffer + oldest->mGraphicBuffer = new GraphicBuffer(buf, false); + oldest->mStatus = BufferInfo::OWNED_BY_US; + + struct VideoDecoderOutputMetaData metaData; + metaData.eType = kMetadataBufferTypeGrallocSource; + metaData.pHandle = oldest->mGraphicBuffer->handle; + memcpy(oldest->mData->base(), &metaData, sizeof(metaData)); + + ALOGV("replaced oldest buffer #%u with age %u (%p stored in %p)", + oldest - &mBuffers[kPortIndexOutput][0], + mDequeueCounter - oldest->mDequeuedAt, + metaData.pHandle, oldest->mData->base()); + + return oldest; } TRESPASS(); @@ -971,6 +1084,24 @@ status_t ACodec::configureCodec( } } + // Always try to enable dynamic output buffers on native surface + sp obj; + int32_t haveNativeWindow = msg->findObject("native-window", &obj) && + obj != NULL; + mStoreMetaDataInOutputBuffers = false; + if (!encoder && video && haveNativeWindow) { + err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexOutput, OMX_TRUE); + if (err != OK) { + // allow failure + ALOGE("[%s] storeMetaDataInBuffers failed w/ err %d", + mComponentName.c_str(), err); + err = OK; + } else { + ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str()); + mStoreMetaDataInOutputBuffers = true; + } + } + if (video) { if (encoder) { err = setupVideoEncoder(mime, msg); @@ -2949,6 +3080,20 @@ void ACodec::BaseState::onInputBufferFilled(const sp &msg) { mCodec->mBufferStats.add(timeUs, stats); #endif + if (mCodec->mStoreMetaDataInOutputBuffers) { + // try to submit an output buffer for each input buffer + PortMode outputMode = getPortMode(kPortIndexOutput); + + ALOGV("MetaDataBuffersToSubmit=%u portMode=%s", + mCodec->mMetaDataBuffersToSubmit, + (outputMode == FREE_BUFFERS ? "FREE" : + outputMode == KEEP_BUFFERS ? "KEEP" : "RESUBMIT")); + if (outputMode == RESUBMIT_BUFFERS) { + CHECK_EQ(mCodec->submitOutputMetaDataBuffer(), + (status_t)OK); + } + } + CHECK_EQ(mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, @@ -3066,6 +3211,7 @@ bool ACodec::BaseState::onOMXFillBufferDone( CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT); + info->mDequeuedAt = ++mCodec->mDequeueCounter; info->mStatus = BufferInfo::OWNED_BY_US; PortMode mode = getPortMode(kPortIndexOutput); @@ -3447,6 +3593,9 @@ void ACodec::LoadedState::stateEntered() { mCodec->mInputEOSResult = OK; + mCodec->mDequeueCounter = 0; + mCodec->mMetaDataBuffersToSubmit = 0; + if (mCodec->mShutdownInProgress) { bool keepComponentAllocated = mCodec->mKeepComponentAllocated; @@ -3764,7 +3913,20 @@ ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode( return RESUBMIT_BUFFERS; } -void ACodec::ExecutingState::submitOutputBuffers() { +void ACodec::ExecutingState::submitOutputMetaBuffers() { + // submit as many buffers as there are input buffers with the codec + // in case we are in port reconfiguring + for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); ++i) { + BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i); + + if (info->mStatus == BufferInfo::OWNED_BY_COMPONENT) { + if (mCodec->submitOutputMetaDataBuffer() != OK) + break; + } + } +} + +void ACodec::ExecutingState::submitRegularOutputBuffers() { for (size_t i = 0; i < mCodec->mBuffers[kPortIndexOutput].size(); ++i) { BufferInfo *info = &mCodec->mBuffers[kPortIndexOutput].editItemAt(i); @@ -3789,6 +3951,14 @@ void ACodec::ExecutingState::submitOutputBuffers() { } } +void ACodec::ExecutingState::submitOutputBuffers() { + if (mCodec->mStoreMetaDataInOutputBuffers) { + submitOutputMetaBuffers(); + } else { + submitRegularOutputBuffers(); + } +} + void ACodec::ExecutingState::resume() { if (mActive) { ALOGV("[%s] We're already active, no need to resume.", @@ -3955,6 +4125,7 @@ bool ACodec::ExecutingState::onOMXEvent( CHECK_EQ(data1, (OMX_U32)kPortIndexOutput); if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) { + mCodec->mMetaDataBuffersToSubmit = 0; CHECK_EQ(mCodec->mOMX->sendCommand( mCodec->mNode, OMX_CommandPortDisable, kPortIndexOutput), -- cgit v1.1 From 94705aff3c9eef58cbb72ec6fe5d2dcfd9481646 Mon Sep 17 00:00:00 2001 From: hkuang Date: Mon, 24 Jun 2013 11:21:17 -0700 Subject: Adds VP9 decoding support for stagefright. Also change the VP8 encoder role name from video_encoder.vpx to video_encoder.vp8 for future VP9 encoder support. Requires the change in frameworks/native and media_codecs.xml corresponding to the device. VP9 decoding test will be added to cts repo later. --- media/libstagefright/ACodec.cpp | 9 +++++--- media/libstagefright/MediaDefs.cpp | 3 ++- media/libstagefright/OMXCodec.cpp | 12 +++++++---- media/libstagefright/codecs/on2/dec/SoftVPX.cpp | 25 +++++++++++++++++----- media/libstagefright/codecs/on2/dec/SoftVPX.h | 7 ++++++ .../codecs/on2/enc/SoftVPXEncoder.cpp | 10 ++++----- .../libstagefright/codecs/on2/enc/SoftVPXEncoder.h | 2 +- .../libstagefright/matroska/MatroskaExtractor.cpp | 4 +++- media/libstagefright/omx/SoftOMXPlugin.cpp | 5 +++-- media/libstagefright/omx/tests/OMXHarness.cpp | 3 ++- 10 files changed, 57 insertions(+), 23 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index bf650b4..2466a6b 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -836,8 +836,10 @@ status_t ACodec::setComponentRole( "video_decoder.mpeg4", "video_encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_H263, "video_decoder.h263", "video_encoder.h263" }, - { MEDIA_MIMETYPE_VIDEO_VPX, - "video_decoder.vpx", "video_encoder.vpx" }, + { MEDIA_MIMETYPE_VIDEO_VP8, + "video_decoder.vp8", "video_encoder.vp8" }, + { MEDIA_MIMETYPE_VIDEO_VP9, + "video_decoder.vp9", "video_encoder.vp9" }, { MEDIA_MIMETYPE_AUDIO_RAW, "audio_decoder.raw", "audio_encoder.raw" }, { MEDIA_MIMETYPE_AUDIO_FLAC, @@ -1501,7 +1503,8 @@ static const struct VideoCodingMapEntry { { MEDIA_MIMETYPE_VIDEO_MPEG4, OMX_VIDEO_CodingMPEG4 }, { MEDIA_MIMETYPE_VIDEO_H263, OMX_VIDEO_CodingH263 }, { MEDIA_MIMETYPE_VIDEO_MPEG2, OMX_VIDEO_CodingMPEG2 }, - { MEDIA_MIMETYPE_VIDEO_VPX, OMX_VIDEO_CodingVPX }, + { MEDIA_MIMETYPE_VIDEO_VP8, OMX_VIDEO_CodingVP8 }, + { MEDIA_MIMETYPE_VIDEO_VP9, OMX_VIDEO_CodingVP9 }, }; static status_t GetVideoCodingTypeFromMime( diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 5d8029c..b5d4e44 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -20,7 +20,8 @@ namespace android { const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg"; -const char *MEDIA_MIMETYPE_VIDEO_VPX = "video/x-vnd.on2.vp8"; +const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; +const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc"; const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp"; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 9d349a1..3de3c28 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1195,8 +1195,10 @@ status_t OMXCodec::setVideoOutputFormat( compressionFormat = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { compressionFormat = OMX_VIDEO_CodingH263; - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) { - compressionFormat = OMX_VIDEO_CodingVPX; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP8, mime)) { + compressionFormat = OMX_VIDEO_CodingVP8; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP9, mime)) { + compressionFormat = OMX_VIDEO_CodingVP9; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { compressionFormat = OMX_VIDEO_CodingMPEG2; } else { @@ -1388,8 +1390,10 @@ void OMXCodec::setComponentRole( "video_decoder.mpeg4", "video_encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_H263, "video_decoder.h263", "video_encoder.h263" }, - { MEDIA_MIMETYPE_VIDEO_VPX, - "video_decoder.vpx", "video_encoder.vpx" }, + { MEDIA_MIMETYPE_VIDEO_VP8, + "video_decoder.vp8", "video_encoder.vp8" }, + { MEDIA_MIMETYPE_VIDEO_VP9, + "video_decoder.vp9", "video_encoder.vp9" }, { MEDIA_MIMETYPE_AUDIO_RAW, "audio_decoder.raw", "audio_encoder.raw" }, { MEDIA_MIMETYPE_AUDIO_FLAC, diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index 43d0263..476e986 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -31,16 +31,20 @@ namespace android { SoftVPX::SoftVPX( const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) : SoftVideoDecoderOMXComponent( - name, "video_decoder.vpx", OMX_VIDEO_CodingVPX, + name, componentRole, codingType, NULL /* profileLevels */, 0 /* numProfileLevels */, 320 /* width */, 240 /* height */, callbacks, appData, component), + mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9), mCtx(NULL) { initPorts(kNumBuffers, 768 * 1024 /* inputBufferSize */, - kNumBuffers, MEDIA_MIMETYPE_VIDEO_VPX); + kNumBuffers, + codingType == OMX_VIDEO_CodingVP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9); CHECK_EQ(initDecoder(), (status_t)OK); } @@ -71,7 +75,9 @@ status_t SoftVPX::initDecoder() { memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); cfg.threads = GetCPUCoreCount(); if ((vpx_err = vpx_codec_dec_init( - (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, &cfg, 0))) { + (vpx_codec_ctx_t *)mCtx, + mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo, + &cfg, 0))) { ALOGE("on2 decoder failed to initialize. (%d)", vpx_err); return UNKNOWN_ERROR; } @@ -194,6 +200,15 @@ void SoftVPX::onQueueFilled(OMX_U32 portIndex) { android::SoftOMXComponent *createSoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { - return new android::SoftVPX(name, callbacks, appData, component); + if (!strcmp(name, "OMX.google.vp8.decoder")) { + return new android::SoftVPX( + name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, + callbacks, appData, component); + } else if (!strcmp(name, "OMX.google.vp9.decoder")) { + return new android::SoftVPX( + name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, + callbacks, appData, component); + } else { + CHECK(!"Unknown component"); + } } - diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h index 626307b..cd5eb28 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.h +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -24,6 +24,8 @@ namespace android { struct SoftVPX : public SoftVideoDecoderOMXComponent { SoftVPX(const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component); @@ -38,6 +40,11 @@ private: kNumBuffers = 4 }; + enum { + MODE_VP8, + MODE_VP9 + } mMode; + void *mCtx; status_t initDecoder(); diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index e25637a..74d6df5 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -165,8 +165,8 @@ void SoftVPXEncoder::initPorts() { outputPort.eDir = OMX_DirOutput; outputPort.nBufferAlignment = kOutputBufferAlignment; outputPort.format.video.cMIMEType = - const_cast(MEDIA_MIMETYPE_VIDEO_VPX); - outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX; + const_cast(MEDIA_MIMETYPE_VIDEO_VP8); + outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVP8; outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused; outputPort.format.video.pNativeWindow = NULL; outputPort.nBufferSize = 256 * 1024; // arbitrary @@ -315,7 +315,7 @@ OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, formatParams->xFramerate = (1000000/mFrameDurationUs) << 16; return OMX_ErrorNone; } else if (formatParams->nPortIndex == kOutputPortIndex) { - formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX; + formatParams->eCompressionFormat = OMX_VIDEO_CodingVP8; formatParams->eColorFormat = OMX_COLOR_FormatUnused; formatParams->xFramerate = 0; return OMX_ErrorNone; @@ -513,7 +513,7 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams( return OMX_ErrorUnsupportedSetting; } } else if (format->nPortIndex == kOutputPortIndex) { - if (format->eCompressionFormat == OMX_VIDEO_CodingVPX) { + if (format->eCompressionFormat == OMX_VIDEO_CodingVP8) { return OMX_ErrorNone; } else { return OMX_ErrorUnsupportedSetting; @@ -529,7 +529,7 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetRoleParams( const char* roleText = (const char*)role->cRole; const size_t roleTextMaxSize = OMX_MAX_STRINGNAME_SIZE - 1; - if (strncmp(roleText, "video_encoder.vpx", roleTextMaxSize)) { + if (strncmp(roleText, "video_encoder.vp8", roleTextMaxSize)) { ALOGE("Unsupported component role"); return OMX_ErrorBadParameter; } diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index 3bc05c0..a0a8ee6 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -175,7 +175,7 @@ class SoftVPXEncoder : public SimpleSoftOMXComponent { const OMX_VIDEO_PARAM_PORTFORMATTYPE* format); // Verifies the component role tried to be set to this OMX component is - // strictly video_encoder.vpx + // strictly video_encoder.vp8 OMX_ERRORTYPE internalSetRoleParams( const OMX_PARAM_COMPONENTROLETYPE* role); diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index b304749..d260d0f 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -870,7 +870,9 @@ void MatroskaExtractor::addTracks() { continue; } } else if (!strcmp("V_VP8", codecID)) { - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX); + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP8); + } else if (!strcmp("V_VP9", codecID)) { + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP9); } else { ALOGW("%s is not supported.", codecID); continue; diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index b3fe98e..d6cde73 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -50,8 +50,9 @@ static const struct { { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" }, { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" }, { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" }, - { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" }, - { "OMX.google.vpx.encoder", "vpxenc", "video_encoder.vpx" }, + { "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" }, + { "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" }, + { "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" }, { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" }, { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" }, { "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" }, diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index 6cca8da..4bee808 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -449,7 +449,8 @@ static const char *GetMimeFromComponentRole(const char *componentRole) { { "video_decoder.avc", "video/avc" }, { "video_decoder.mpeg4", "video/mp4v-es" }, { "video_decoder.h263", "video/3gpp" }, - { "video_decoder.vpx", "video/x-vnd.on2.vp8" }, + { "video_decoder.vp8", "video/x-vnd.on2.vp8" }, + { "video_decoder.vp9", "video/x-vnd.on2.vp9" }, // we appear to use this as a synonym to amrnb. { "audio_decoder.amr", "audio/3gpp" }, -- cgit v1.1 From 0d09a9bec07b3bec78bd473ff0bfcf0a261f3f25 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 24 Jun 2013 12:06:46 -0700 Subject: Use mFutex as an event flag rather than semaphore An event flag can be more fault-tolerant in case of loss of synchronization, as it cannot overflow. It also allows more bits to be used in the future. See http://en.wikipedia.org/wiki/Event_flag Change-Id: I01ca25d951eb263124da54bb4738f0d94ec4a48b --- media/libmedia/AudioTrackShared.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index f034164..4b7f368 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -207,15 +207,15 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques ts = NULL; break; } - int32_t old = android_atomic_dec(&cblk->mFutex); - if (old <= 0) { + int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex); + if (!(old & CBLK_FUTEX_WAKE)) { int rc; if (measure && !beforeIsValid) { clock_gettime(CLOCK_MONOTONIC, &before); beforeIsValid = true; } int ret = __futex_syscall4(&cblk->mFutex, - mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old - 1, ts); + mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts); // update total elapsed time spent waiting if (measure) { struct timespec after; @@ -484,9 +484,8 @@ void ServerProxy::releaseBuffer(Buffer* buffer) } if (!mDeferWake && mAvailToClient + stepCount >= minimum) { ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum); - // could client be sleeping, or not need this increment and counter overflows? - int32_t old = android_atomic_inc(&cblk->mFutex); - if (old == -1) { + int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); + if (!(old & CBLK_FUTEX_WAKE)) { (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); } -- cgit v1.1 From 93bb77da5481ab75c2cd6e3aa681839273c6e43d Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 24 Jun 2013 12:10:45 -0700 Subject: Workaround AudioRecord bug for large buffer sizes Bug: 9556436 Change-Id: I92d1238b623d2cfd648e0a684d0e710fb0bd8b43 --- media/libmedia/AudioTrackShared.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 4b7f368..5f8f292 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -482,7 +482,8 @@ void ServerProxy::releaseBuffer(Buffer* buffer) } else if (minimum > half) { minimum = half; } - if (!mDeferWake && mAvailToClient + stepCount >= minimum) { + // FIXME AudioRecord wakeup needs to be optimized; it currently wakes up client every time + if (!mIsOut || (!mDeferWake && mAvailToClient + stepCount >= minimum)) { ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum); int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); if (!(old & CBLK_FUTEX_WAKE)) { -- cgit v1.1 From ad3af3305f024bcbbd55c894a4995e449498e1ba Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 25 Mar 2013 16:54:37 +0000 Subject: Public API changes for audio offload support. NOTE: this does _not_ include all private member variables added to classes as part of offload support. Only public/protected functions and stubs functions/variables needed to make the changes buildable. - isOffloadSupported() added to audio policy service A stub implementation is required to build, this always returns false - setParameters() added to IAudioTrack A stub implementation is required to build, this always returns INVALID_OPERATION - CBlk flag for stream end - Change AudioSystem::getRenderPosition() to take an audio_output_t so caller can specify which output to query - Add AudioSystem::isOffloadSupported() This is fully implemented down to the AudioFlinger function AudioPolicyServer::isOffloadSupported() which is just a stub that always returns false. - Add EVENT_STREAM_END to AudioTrack interface. STREAM_END is used to signal when the hardware has actually finished playing all the data it was sent. - Add event type enumeration to media player interface AudioSink callbacks so that the same callback can be used to handle multiple types of event. For offloaded tracks we also have to handle STREAM_END and TEAR_DOWN events - Pass audio_offload_info_t to various functions used for opening outputs, tracks and audio players. This passes additional information about the compressed stream down to the HAL when using offload. For publicly-available APIs this is an optional parameter (for some of the internal and low-level APIs around the HAL interface it is mandatory) - Add getParameters() and setParameters() API to AudioTrack Currently dummy implementations. - Change AudioPlayer contructor so that it takes a set of bitflags defining what options are required. This replaces the original bool which only specified whether to use deep buffering. - Changes to StageFright class definition related to handling tearing-down of an offloaded track when we need to switch back to software decode - Define new StageFright utility functions used for offloaded tracks Currently dummy implementations. - AudioFlinger changes to use extended audio_config_t. Fills in audio_offload_info_t member if this info is passed in when opening an output. - libvideoeditor changes required to add the new event type parameter to AudioSink callback functions - libmediaplayerservice changes required to add the new event type parameter to AudioSink callback functions Change-Id: I3ab41138aa1083d81fe83b886a9b1021ec7320f1 Signed-off-by: Richard Fitzgerald Signed-off-by: Eric Laurent --- media/libmedia/AudioSystem.cpp | 23 ++++++++++++---- media/libmedia/AudioTrack.cpp | 31 +++++++++++++++++----- media/libmedia/IAudioFlinger.cpp | 3 ++- media/libmedia/IAudioPolicyService.cpp | 12 +++++++-- media/libmedia/IAudioTrack.cpp | 18 +++++++++++++ media/libmediaplayerservice/MediaPlayerService.cpp | 12 ++++++--- media/libmediaplayerservice/MediaPlayerService.h | 6 +++-- media/libstagefright/AudioPlayer.cpp | 7 ++--- media/libstagefright/Utils.cpp | 19 +++++++++++++ media/libstagefright/include/AwesomePlayer.h | 17 ++++++++++-- media/libstagefright/include/ESDS.h | 6 +++++ 11 files changed, 129 insertions(+), 25 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 693df60..a6dedec 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -361,8 +361,8 @@ status_t AudioSystem::setVoiceVolume(float value) return af->setVoiceVolume(value); } -status_t AudioSystem::getRenderPosition(size_t *halFrames, size_t *dspFrames, - audio_stream_type_t stream) +status_t AudioSystem::getRenderPosition(audio_io_handle_t output, size_t *halFrames, + size_t *dspFrames, audio_stream_type_t stream) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; @@ -371,7 +371,11 @@ status_t AudioSystem::getRenderPosition(size_t *halFrames, size_t *dspFrames, stream = AUDIO_STREAM_MUSIC; } - return af->getRenderPosition(halFrames, dspFrames, getOutput(stream)); + if (output == 0) { + output = getOutput(stream); + } + + return af->getRenderPosition(halFrames, dspFrames, output); } size_t AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) { @@ -585,11 +589,12 @@ audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - audio_output_flags_t flags) + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; - return aps->getOutput(stream, samplingRate, format, channelMask, flags); + return aps->getOutput(stream, samplingRate, format, channelMask, flags, offloadInfo); } status_t AudioSystem::startOutput(audio_io_handle_t output, @@ -771,6 +776,14 @@ void AudioSystem::clearAudioConfigCache() gOutputs.clear(); } +bool AudioSystem::isOffloadSupported(const audio_offload_info_t& info) +{ + ALOGV("isOffloadSupported()"); + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return false; + return aps->isOffloadSupported(info); +} + // --------------------------------------------------------------------------- void AudioSystem::AudioPolicyServiceClient::binderDied(const wp& who) { diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index faca054..2af162c 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -97,7 +97,8 @@ AudioTrack::AudioTrack( void* user, int notificationFrames, int sessionId, - transfer_type transferType) + transfer_type transferType, + const audio_offload_info_t *offloadInfo) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), @@ -105,7 +106,7 @@ AudioTrack::AudioTrack( { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, - 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType); + 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo); } AudioTrack::AudioTrack( @@ -119,7 +120,8 @@ AudioTrack::AudioTrack( void* user, int notificationFrames, int sessionId, - transfer_type transferType) + transfer_type transferType, + const audio_offload_info_t *offloadInfo) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), @@ -127,7 +129,7 @@ AudioTrack::AudioTrack( { mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, - sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType); + sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo); } AudioTrack::~AudioTrack() @@ -164,7 +166,8 @@ status_t AudioTrack::set( const sp& sharedBuffer, bool threadCanCallJava, int sessionId, - transfer_type transferType) + transfer_type transferType, + const audio_offload_info_t *offloadInfo) { switch (transferType) { case TRANSFER_DEFAULT: @@ -284,7 +287,8 @@ status_t AudioTrack::set( audio_io_handle_t output = AudioSystem::getOutput( streamType, sampleRate, format, channelMask, - flags); + flags, + offloadInfo); if (output == 0) { ALOGE("Could not get audio output for stream type %d", streamType); @@ -1543,6 +1547,21 @@ status_t AudioTrack::restoreTrack_l(const char *from) return result; } +status_t AudioTrack::setParameters(const String8& keyValuePairs) +{ + AutoMutex lock(mLock); + if (mAudioTrack != 0) { + return mAudioTrack->setParameters(keyValuePairs); + } else { + return NO_INIT; + } +} + +String8 AudioTrack::getParameters(const String8& keys) +{ + return String8::empty(); +} + status_t AudioTrack::dump(int fd, const Vector& args) const { diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 2f18680..e4df77d 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -361,7 +361,8 @@ public: audio_format_t *pFormat, audio_channel_mask_t *pChannelMask, uint32_t *pLatencyMs, - audio_output_flags_t flags) + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) { Parcel data, reply; audio_devices_t devices = pDevices ? *pDevices : (audio_devices_t)0; diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 386c351..57de58f 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -56,7 +56,8 @@ enum { GET_DEVICES_FOR_STREAM, QUERY_DEFAULT_PRE_PROCESSING, SET_EFFECT_ENABLED, - IS_STREAM_ACTIVE_REMOTELY + IS_STREAM_ACTIVE_REMOTELY, + IS_OFFLOAD_SUPPORTED }; class BpAudioPolicyService : public BpInterface @@ -126,7 +127,8 @@ public: uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, - audio_output_flags_t flags) + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -374,6 +376,12 @@ public: *count = retCount; return status; } + + virtual bool isOffloadSupported(const audio_offload_info_t& info) + { + // stub function + return false; + } }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index e92f8aa..a2b49a3 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -39,6 +39,7 @@ enum { ALLOCATE_TIMED_BUFFER, QUEUE_TIMED_BUFFER, SET_MEDIA_TIME_TRANSFORM, + SET_PARAMETERS }; class BpAudioTrack : public BpInterface @@ -154,6 +155,17 @@ public: } return status; } + + virtual status_t setParameters(const String8& keyValuePairs) { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + data.writeString8(keyValuePairs); + status_t status = remote()->transact(SET_PARAMETERS, data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + } + return status; + } }; IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack"); @@ -223,6 +235,12 @@ status_t BnAudioTrack::onTransact( reply->writeInt32(setMediaTimeTransform(xform, target)); return NO_ERROR; } break; + case SET_PARAMETERS: { + CHECK_INTERFACE(IAudioTrack, data, reply); + String8 keyValuePairs(data.readString8()); + reply->writeInt32(setParameters(keyValuePairs)); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index fa1ff36..53dce65 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1385,7 +1385,8 @@ status_t MediaPlayerService::AudioOutput::open( uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, audio_format_t format, int bufferCount, AudioCallback cb, void *cookie, - audio_output_flags_t flags) + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) { mCallback = cb; mCallbackCookie = cookie; @@ -1661,7 +1662,8 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( } size_t actualSize = (*me->mCallback)( - me, buffer->raw, buffer->size, me->mCallbackCookie); + me, buffer->raw, buffer->size, me->mCallbackCookie, + CB_EVENT_FILL_BUFFER); if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { // We've reached EOS but the audio track is not stopped yet, @@ -1767,7 +1769,8 @@ bool CallbackThread::threadLoop() { } size_t actualSize = - (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie); + (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie, + MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER); if (actualSize > 0) { sink->write(mBuffer, actualSize); @@ -1781,7 +1784,8 @@ bool CallbackThread::threadLoop() { status_t MediaPlayerService::AudioCache::open( uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, audio_format_t format, int bufferCount, - AudioCallback cb, void *cookie, audio_output_flags_t flags) + AudioCallback cb, void *cookie, audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) { ALOGV("open(%u, %d, 0x%x, %d, %d)", sampleRate, channelCount, channelMask, format, bufferCount); if (mHeap->getHeapID() < 0) { diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index e586156..1f8bcc7 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -94,7 +94,8 @@ class MediaPlayerService : public BnMediaPlayerService uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, audio_format_t format, int bufferCount, AudioCallback cb, void *cookie, - audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE); + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL); virtual void start(); virtual ssize_t write(const void* buffer, size_t size); @@ -195,7 +196,8 @@ class MediaPlayerService : public BnMediaPlayerService uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, audio_format_t format, int bufferCount = 1, AudioCallback cb = NULL, void *cookie = NULL, - audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE); + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL); virtual void start(); virtual ssize_t write(const void* buffer, size_t size); diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 92efae8..61d6746 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -34,7 +34,7 @@ namespace android { AudioPlayer::AudioPlayer( const sp &audioSink, - bool allowDeepBuffering, + uint32_t flags, AwesomePlayer *observer) : mInputBuffer(NULL), mSampleRate(0), @@ -52,7 +52,7 @@ AudioPlayer::AudioPlayer( mFirstBufferResult(OK), mFirstBuffer(NULL), mAudioSink(audioSink), - mAllowDeepBuffering(allowDeepBuffering), + mAllowDeepBuffering((flags & ALLOW_DEEP_BUFFERING) != 0), mObserver(observer), mPinnedTimeUs(-1ll) { } @@ -304,7 +304,8 @@ status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) { // static size_t AudioPlayer::AudioSinkCallback( MediaPlayerBase::AudioSink *audioSink, - void *buffer, size_t size, void *cookie) { + void *buffer, size_t size, void *cookie, + MediaPlayerBase::AudioSink::cb_event_t event) { AudioPlayer *me = (AudioPlayer *)cookie; return me->fillBuffer(buffer, size); diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index b0df379..e9789d3 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -471,5 +471,24 @@ AString MakeUserAgent() { return ua; } +status_t sendMetaDataToHal(sp& sink, + const sp& meta) +{ + // stub + return OK; +} + +status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime) +{ + // stub + return BAD_VALUE; +} + +bool canOffloadStream(const sp& meta, bool hasVideo, bool isStreaming) +{ + // stub + return false; +} + } // namespace android diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 2306f31..0d17d65 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -100,7 +101,7 @@ struct AwesomePlayer { void postAudioEOS(int64_t delayUs = 0ll); void postAudioSeekComplete(); - + void postAudioTearDown(); status_t dump(int fd, const Vector &args) const; private: @@ -171,6 +172,7 @@ private: ssize_t mActiveAudioTrackIndex; sp mAudioTrack; + sp mOmxSource; sp mAudioSource; AudioPlayer *mAudioPlayer; int64_t mDurationUs; @@ -211,7 +213,8 @@ private: bool mAudioStatusEventPending; sp mVideoLagEvent; bool mVideoLagEventPending; - + sp mAudioTearDownEvent; + bool mAudioTearDownEventPending; sp mAsyncPrepareEvent; Condition mPreparedCondition; bool mIsAsyncPrepare; @@ -223,6 +226,8 @@ private: void postStreamDoneEvent_l(status_t status); void postCheckAudioStatusEvent(int64_t delayUs); void postVideoLagEvent_l(); + void postAudioTearDownEvent(); + status_t play_l(); MediaBuffer *mVideoBuffer; @@ -257,6 +262,7 @@ private: void setAudioSource(sp source); status_t initAudioDecoder(); + void setVideoSource(sp source); status_t initVideoDecoder(uint32_t flags = 0); @@ -273,6 +279,9 @@ private: void abortPrepare(status_t err); void finishAsyncPrepare_l(); void onVideoLagUpdate(); + void onAudioTearDownEvent(); + + void beginPrepareAsync_l(); bool getCachedDuration_l(int64_t *durationUs, bool *eos); @@ -285,6 +294,7 @@ private: void finishSeekIfNecessary(int64_t videoTimeUs); void ensureCacheIsFetching_l(); + void createAudioPlayer_l(); status_t startAudioPlayer_l(bool sendErrorNotification = true); void shutdownVideoDecoder_l(); @@ -327,6 +337,9 @@ private: Vector mTracks; } mStats; + bool mOffloadAudio; + bool mAudioTearDown; + status_t setVideoScalingMode(int32_t mode); status_t setVideoScalingMode_l(int32_t mode); status_t getTrackInfo(Parcel* reply) const; diff --git a/media/libstagefright/include/ESDS.h b/media/libstagefright/include/ESDS.h index 3a79951..2f40dae 100644 --- a/media/libstagefright/include/ESDS.h +++ b/media/libstagefright/include/ESDS.h @@ -33,6 +33,9 @@ public: status_t getObjectTypeIndication(uint8_t *objectTypeIndication) const; status_t getCodecSpecificInfo(const void **data, size_t *size) const; + status_t getCodecSpecificOffset(size_t *offset, size_t *size) const; + status_t getBitRate(uint32_t *brateMax, uint32_t *brateAvg) const; + status_t getStreamType(uint8_t *streamType) const; private: enum { @@ -49,6 +52,9 @@ private: size_t mDecoderSpecificOffset; size_t mDecoderSpecificLength; uint8_t mObjectTypeIndication; + uint8_t mStreamType; + uint32_t mBitRateMax; + uint32_t mBitRateAvg; status_t skipDescriptorHeader( size_t offset, size_t size, -- cgit v1.1 From 1ab85ec401801ef9a9184650d0f5a1639b45eeb9 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 31 May 2013 09:18:43 -0700 Subject: Include what is needed Remove old includes. Header files only include other header files that they directly need themselves. Change-Id: Ic471386808d9f42ea19ccbd59cb50a5f83a89dd0 --- media/libmedia/AudioRecord.cpp | 1 + media/libmedia/AudioSystem.cpp | 1 + media/libmedia/AudioTrack.cpp | 1 + media/libmedia/JetPlayer.cpp | 2 -- media/libmedia/SoundPool.cpp | 6 ------ media/libmedia/ToneGenerator.cpp | 4 ---- media/libmedia/Visualizer.cpp | 1 + media/libmediaplayerservice/MediaPlayerService.h | 3 --- media/libstagefright/wifi-display/sink/DirectRenderer.h | 2 -- 9 files changed, 4 insertions(+), 17 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 9faa497..8ae0908 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #define WAIT_PERIOD_MS 10 diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index a6dedec..22d6763 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 2af162c..33c4462 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #define WAIT_PERIOD_MS 10 diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index 8fe5bb3..e914b34 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -18,8 +18,6 @@ #define LOG_TAG "JetPlayer-C" #include -#include - #include diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index e1e88ec..7f10e05 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -20,14 +20,8 @@ //#define USE_SHARED_MEM_BUFFER -// XXX needed for timing latency -#include - #include #include - -#include - #include #include "SoundPoolThread.h" diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index f9ad31d..adef3be 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -16,13 +16,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ToneGenerator" -#include -#include #include #include -#include -#include #include #include "media/ToneGenerator.h" diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index 5b4071b..e519f13 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -28,6 +28,7 @@ #include #include +#include namespace android { diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 1f8bcc7..f7076cc 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -20,15 +20,12 @@ #include -#include #include -#include #include #include #include #include -#include #include #include #include diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h index c5a4a83..1e7dc34 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -23,9 +23,7 @@ namespace android { struct ABuffer; -struct AudioTrack; struct IGraphicBufferProducer; -struct MediaCodec; // Renders audio and video data queued by calls to "queueAccessUnit". struct DirectRenderer : public AHandler { -- cgit v1.1 From 85007a9bd3c310f96fed47208dfee566fd00351f Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 13 Nov 2012 15:06:37 -0800 Subject: Fix typo in logs Change-Id: I889e31ea3a45a3d8d34fdfb54ebc3947de51d2be --- media/libmedia/AudioSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index a6dedec..fb19357 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -453,7 +453,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle } break; case OUTPUT_CLOSED: { if (gOutputs.indexOfKey(ioHandle) < 0) { - ALOGW("ioConfigChanged() closing unknow output! %d", ioHandle); + ALOGW("ioConfigChanged() closing unknown output! %d", ioHandle); break; } ALOGV("ioConfigChanged() output %d closed", ioHandle); @@ -464,7 +464,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle case OUTPUT_CONFIG_CHANGED: { int index = gOutputs.indexOfKey(ioHandle); if (index < 0) { - ALOGW("ioConfigChanged() modifying unknow output! %d", ioHandle); + ALOGW("ioConfigChanged() modifying unknown output! %d", ioHandle); break; } if (param2 == NULL) break; -- cgit v1.1 From 7db7df0e8d9d7cee8ba374468cdbfa0108e3337c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 25 Jun 2013 16:13:23 -0700 Subject: AudioTrackShared cleanup Maintain unreleased frame count on client side also (was already there on server side). Assertion failure instead of BAD_VALUE status for incorrect usage of APIs. Clean up error handling code. Change-Id: I23ca2f6f8a7c18645309ee5d64fbc844429bcba8 --- media/libmedia/AudioTrackShared.cpp | 63 +++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 5f8f292..554802d 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -38,7 +38,7 @@ Proxy::Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t bool isOut, bool clientInServer) : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize), mFrameCountP2(roundup(frameCount)), mIsOut(isOut), mClientInServer(clientInServer), - mIsShutdown(false) + mIsShutdown(false), mUnreleased(0) { } @@ -64,10 +64,7 @@ const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/}; status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested, struct timespec *elapsed) { - if (buffer == NULL || buffer->mFrameCount == 0) { - ALOGE("%s BAD_VALUE", __func__); - return BAD_VALUE; - } + LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0); struct timespec total; // total elapsed time spent waiting total.tv_sec = 0; total.tv_nsec = 0; @@ -164,7 +161,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques buffer->mRaw = part1 > 0 ? &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL; buffer->mNonContig = avail - part1; - // mUnreleased = part1; + mUnreleased = part1; status = NO_ERROR; break; } @@ -238,6 +235,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques case -EWOULDBLOCK: // benign race condition with server case -EINTR: // wait was interrupted by signal or other spurious wakeup case -ETIMEDOUT: // time-out expired + // FIXME these error/non-0 status are being dropped break; default: ALOGE("%s unexpected error %d", __func__, ret); @@ -252,6 +250,7 @@ end: buffer->mFrameCount = 0; buffer->mRaw = NULL; buffer->mNonContig = 0; + mUnreleased = 0; } if (elapsed != NULL) { *elapsed = total; @@ -268,14 +267,17 @@ end: void ClientProxy::releaseBuffer(Buffer* buffer) { + LOG_ALWAYS_FATAL_IF(buffer == NULL); size_t stepCount = buffer->mFrameCount; - // FIXME - // check mUnreleased - // verify that stepCount <= frameCount returned by the last obtainBuffer() - // verify stepCount not > total frame count of pipe - if (stepCount == 0) { + if (stepCount == 0 || mIsShutdown) { + // prevent accidental re-use of buffer + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; return; } + LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount)); + mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; // Both of these barriers are required if (mIsOut) { @@ -362,20 +364,18 @@ size_t StaticAudioTrackClientProxy::getBufferPosition() ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut, bool clientInServer) - : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mUnreleased(0), + : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mAvailToClient(0), mFlush(0), mDeferWake(false) { } status_t ServerProxy::obtainBuffer(Buffer* buffer) { + LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0); if (mIsShutdown) { - buffer->mFrameCount = 0; - buffer->mRaw = NULL; - buffer->mNonContig = 0; - mUnreleased = 0; - return NO_INIT; + goto no_init; } + { audio_track_cblk_t* cblk = mCblk; // compute number of frames available to write (AudioTrack) or read (AudioRecord), // or use previous cached value from framesReady(), with added barrier if it omits. @@ -402,11 +402,7 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer) mIsShutdown = true; } if (mIsShutdown) { - buffer->mFrameCount = 0; - buffer->mRaw = NULL; - buffer->mNonContig = 0; - mUnreleased = 0; - return NO_INIT; + goto no_init; } // don't allow filling pipe beyond the nominal size size_t availToServer; @@ -443,23 +439,27 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer) // FIXME need to test for recording mDeferWake = part1 < ask && availToServer >= ask; return part1 > 0 ? NO_ERROR : WOULD_BLOCK; + } +no_init: + buffer->mFrameCount = 0; + buffer->mRaw = NULL; + buffer->mNonContig = 0; + mUnreleased = 0; + return NO_INIT; } void ServerProxy::releaseBuffer(Buffer* buffer) { - if (mIsShutdown) { - buffer->mFrameCount = 0; - buffer->mRaw = NULL; - buffer->mNonContig = 0; - return; - } + LOG_ALWAYS_FATAL_IF(buffer == NULL); size_t stepCount = buffer->mFrameCount; - LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased); - if (stepCount == 0) { + if (stepCount == 0 || mIsShutdown) { + // prevent accidental re-use of buffer + buffer->mFrameCount = 0; buffer->mRaw = NULL; buffer->mNonContig = 0; return; } + LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount)); mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; if (mIsOut) { @@ -637,8 +637,9 @@ status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer) void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) { size_t stepCount = buffer->mFrameCount; - LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased); + LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased)); if (stepCount == 0) { + // prevent accidental re-use of buffer buffer->mRaw = NULL; buffer->mNonContig = 0; return; -- cgit v1.1 From 7c5977f0322204240b3d1874a44c1f3911275ae5 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 2 Jul 2013 14:17:22 -0700 Subject: Explicitly compare raw pointers to NULL Change-Id: Id2c7828a36a6912333465475b21fa87e294c83c7 --- media/libmedia/IAudioFlinger.cpp | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'media') diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index e4df77d..6bb7df6 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -365,11 +365,12 @@ public: const audio_offload_info_t *offloadInfo) { Parcel data, reply; - audio_devices_t devices = pDevices ? *pDevices : (audio_devices_t)0; - uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0; - audio_format_t format = pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT; - audio_channel_mask_t channelMask = pChannelMask ? *pChannelMask : (audio_channel_mask_t)0; - uint32_t latency = pLatencyMs ? *pLatencyMs : 0; + audio_devices_t devices = pDevices != NULL ? *pDevices : (audio_devices_t)0; + uint32_t samplingRate = pSamplingRate != NULL ? *pSamplingRate : 0; + audio_format_t format = pFormat != NULL ? *pFormat : AUDIO_FORMAT_DEFAULT; + audio_channel_mask_t channelMask = pChannelMask != NULL ? + *pChannelMask : (audio_channel_mask_t)0; + uint32_t latency = pLatencyMs != NULL ? *pLatencyMs : 0; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.writeInt32(module); @@ -383,15 +384,15 @@ public: audio_io_handle_t output = (audio_io_handle_t) reply.readInt32(); ALOGV("openOutput() returned output, %d", output); devices = (audio_devices_t)reply.readInt32(); - if (pDevices) *pDevices = devices; + if (pDevices != NULL) *pDevices = devices; samplingRate = reply.readInt32(); - if (pSamplingRate) *pSamplingRate = samplingRate; + if (pSamplingRate != NULL) *pSamplingRate = samplingRate; format = (audio_format_t) reply.readInt32(); - if (pFormat) *pFormat = format; + if (pFormat != NULL) *pFormat = format; channelMask = (audio_channel_mask_t)reply.readInt32(); - if (pChannelMask) *pChannelMask = channelMask; + if (pChannelMask != NULL) *pChannelMask = channelMask; latency = reply.readInt32(); - if (pLatencyMs) *pLatencyMs = latency; + if (pLatencyMs != NULL) *pLatencyMs = latency; return output; } @@ -440,10 +441,11 @@ public: audio_channel_mask_t *pChannelMask) { Parcel data, reply; - audio_devices_t devices = pDevices ? *pDevices : (audio_devices_t)0; - uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0; - audio_format_t format = pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT; - audio_channel_mask_t channelMask = pChannelMask ? *pChannelMask : (audio_channel_mask_t)0; + audio_devices_t devices = pDevices != NULL ? *pDevices : (audio_devices_t)0; + uint32_t samplingRate = pSamplingRate != NULL ? *pSamplingRate : 0; + audio_format_t format = pFormat != NULL ? *pFormat : AUDIO_FORMAT_DEFAULT; + audio_channel_mask_t channelMask = pChannelMask != NULL ? + *pChannelMask : (audio_channel_mask_t)0; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.writeInt32(module); @@ -454,13 +456,13 @@ public: remote()->transact(OPEN_INPUT, data, &reply); audio_io_handle_t input = (audio_io_handle_t) reply.readInt32(); devices = (audio_devices_t)reply.readInt32(); - if (pDevices) *pDevices = devices; + if (pDevices != NULL) *pDevices = devices; samplingRate = reply.readInt32(); - if (pSamplingRate) *pSamplingRate = samplingRate; + if (pSamplingRate != NULL) *pSamplingRate = samplingRate; format = (audio_format_t) reply.readInt32(); - if (pFormat) *pFormat = format; + if (pFormat != NULL) *pFormat = format; channelMask = (audio_channel_mask_t)reply.readInt32(); - if (pChannelMask) *pChannelMask = channelMask; + if (pChannelMask != NULL) *pChannelMask = channelMask; return input; } -- cgit v1.1 From 656e86250cd68f7f362c50a4bc92a865e9deacbe Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Fri, 28 Jun 2013 14:03:03 -0700 Subject: Pass additional arg to acquireBuffer calls. Bug 7900302 Change-Id: I30b9cca783e0a48f77035b745b7d5e20edf10f27 --- media/libstagefright/SurfaceMediaSource.cpp | 2 +- media/libstagefright/omx/GraphicBufferSource.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 71b6569..305e7e0 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -293,7 +293,7 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer, // wait here till the frames come in from the client side while (mStarted) { - status_t err = mBufferQueue->acquireBuffer(&item); + status_t err = mBufferQueue->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // wait for a buffer to be queued mFrameAvailableCondition.wait(mMutex); diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index b3a8463..b3167b5 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -251,7 +251,7 @@ bool GraphicBufferSource::fillCodecBuffer_l() { ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", mNumFramesAvailable); BufferQueue::BufferItem item; - status_t err = mBufferQueue->acquireBuffer(&item); + status_t err = mBufferQueue->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // shouldn't happen ALOGW("fillCodecBuffer_l: frame was not available"); @@ -422,7 +422,7 @@ void GraphicBufferSource::onFrameAvailable() { ALOGW("onFrameAvailable: EOS is set, ignoring frame"); BufferQueue::BufferItem item; - status_t err = mBufferQueue->acquireBuffer(&item); + status_t err = mBufferQueue->acquireBuffer(&item, 0); if (err == OK) { mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); -- cgit v1.1 From f90b123a3a67316284ba4b48a4fb0c5a36158545 Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Wed, 10 Jul 2013 15:09:38 +0900 Subject: Fix typo in AwesomePlayer Change-Id: I32113e382a3033c9a1b038dc06e4ccddc2a97d7f --- media/libstagefright/AwesomePlayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index b505518..a4b0194 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -206,7 +206,7 @@ AwesomePlayer::AwesomePlayer() mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate); mBufferingEventPending = false; mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate); - mVideoEventPending = false; + mVideoLagEventPending = false; mCheckAudioStatusEvent = new AwesomeEvent( this, &AwesomePlayer::onCheckAudioStatus); -- cgit v1.1 From b7f08d386f2bddb8f3c87858f9204754b7fdb857 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 18 Jun 2013 11:46:28 -0700 Subject: Clean up references to AUDIO_FORMAT_PCM_8_24_BIT Change-Id: I08771eb2664b7082561a40937218c7f4414e2cce --- media/libeffects/testlibs/AudioFormatAdapter.h | 1 + media/libeffects/testlibs/EffectEqualizer.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libeffects/testlibs/AudioFormatAdapter.h b/media/libeffects/testlibs/AudioFormatAdapter.h index 41f1810..dea2734 100644 --- a/media/libeffects/testlibs/AudioFormatAdapter.h +++ b/media/libeffects/testlibs/AudioFormatAdapter.h @@ -75,6 +75,7 @@ public: while (numSamples > 0) { uint32_t numSamplesIter = min(numSamples, mMaxSamplesPerCall); uint32_t nSamplesChannels = numSamplesIter * mNumChannels; + // This branch of "if" is untested if (mPcmFormat == AUDIO_FORMAT_PCM_8_24_BIT) { if (mBehavior == EFFECT_BUFFER_ACCESS_WRITE) { mpProcessor->process( diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp index c35453b..8d00206 100644 --- a/media/libeffects/testlibs/EffectEqualizer.cpp +++ b/media/libeffects/testlibs/EffectEqualizer.cpp @@ -234,8 +234,7 @@ int Equalizer_setConfig(EqualizerContext *pContext, effect_config_t *pConfig) (pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_STEREO)); CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); - CHECK_ARG(pConfig->inputCfg.format == AUDIO_FORMAT_PCM_8_24_BIT - || pConfig->inputCfg.format == AUDIO_FORMAT_PCM_16_BIT); + CHECK_ARG(pConfig->inputCfg.format == AUDIO_FORMAT_PCM_16_BIT); int channelCount; if (pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_MONO) { -- cgit v1.1 From b0dfd4613225a3b2a17bdf8d85e89a4b04d65ef3 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 10 Jul 2013 16:52:47 -0700 Subject: Fix type error in AudioTrack::processAudioBuffer It returned a bool instead of nsecs_t Change-Id: If0c096dac411afc0a4142ec1e59c1fdd36d4867c --- media/libmedia/AudioTrack.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 33c4462..00f4640 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1238,7 +1238,8 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) if (tryCounter < 0) { ALOGE("did not receive expected priority boost on time"); } - return true; + // Run again immediately + return 0; } // Can only reference mCblk while locked -- cgit v1.1 From fb1fdc9d6603aa228362e7349451f6455c9849c2 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 10 Jul 2013 17:03:19 -0700 Subject: Add comments Change-Id: Ifbf3a46a4183c8abc0feee1c588953ab10303cc1 --- media/libmedia/AudioSystem.cpp | 2 ++ media/libmedia/AudioTrack.cpp | 7 +++++++ media/libmediaplayerservice/MidiFile.cpp | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 22d6763..7ceffd3 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -537,6 +537,8 @@ const sp& AudioSystem::get_audio_policy_service() return gAudioPolicyService; } +// --------------------------------------------------------------------------- + status_t AudioSystem::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address) diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 33c4462..3f4cbc2 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -585,6 +585,7 @@ void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) status_t AudioTrack::setMarkerPosition(uint32_t marker) { + // The only purpose of setting marker position is to get a callback if (mCbf == NULL) { return INVALID_OPERATION; } @@ -610,6 +611,7 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) { + // The only purpose of setting position update period is to get a callback if (mCbf == NULL) { return INVALID_OPERATION; } @@ -1220,6 +1222,11 @@ status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform, nsecs_t AudioTrack::processAudioBuffer(const sp& thread) { + // Currently the AudioTrack thread is not created if there are no callbacks. + // Would it ever make sense to run the thread, even without callbacks? + // If so, then replace this by checks at each use for mCbf != NULL. + LOG_ALWAYS_FATAL_IF(mCblk == NULL); + mLock.lock(); if (mAwaitBoost) { mAwaitBoost = false; diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 8db5b9b..270b872 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -422,7 +422,7 @@ status_t MidiFile::setLooping(int loop) status_t MidiFile::createOutputTrack() { if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, - CHANNEL_MASK_USE_CHANNEL_ORDER, AUDIO_FORMAT_PCM_16_BIT, 2) != NO_ERROR) { + CHANNEL_MASK_USE_CHANNEL_ORDER, AUDIO_FORMAT_PCM_16_BIT, 2 /*bufferCount*/) != NO_ERROR) { ALOGE("mAudioSink open failed"); return ERROR_OPEN_FAILED; } -- cgit v1.1 From 050501d11d944dcb256d37d3b86bd658d94f6a7f Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 11 Jul 2013 10:35:38 -0700 Subject: Fix AudioTrack::flush() It was only flushing at a surface level, and even then only the first time the server observed the client's flush request. Now it flushes at a deeper level, but there may be even deeper device-specific flushing. Bug: 9770947 Change-Id: I687cc3410ff9e5e5d4a5dcb9e3b129501e53d247 --- media/libmedia/AudioTrackShared.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 5f8f292..6bb4ff7 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -388,6 +388,8 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer) if (flush != mFlush) { front = rear; mFlush = flush; + // effectively obtain then release whatever is in the buffer + android_atomic_release_store(rear, &cblk->u.mStreaming.mFront); } else { front = cblk->u.mStreaming.mFront; } -- cgit v1.1 From 5e1f08b3917ac7900f8a11118afb7e8bf3e61c64 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 16 Jul 2013 22:54:39 -0700 Subject: update to new Consumer APIs Change-Id: I3c5d4be2a2e8783fbf98b3e268fd02658f71dc7d --- media/libstagefright/SurfaceMediaSource.cpp | 7 +++---- media/libstagefright/omx/GraphicBufferSource.cpp | 9 ++++----- 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 305e7e0..befd4cc 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -54,9 +54,8 @@ SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeig ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight); } - mBufferQueue = new BufferQueue(true); + mBufferQueue = new BufferQueue(); mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight); - mBufferQueue->setSynchronousMode(true); mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_TEXTURE); @@ -71,7 +70,7 @@ SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeig listener = static_cast(this); proxy = new BufferQueue::ProxyConsumerListener(listener); - status_t err = mBufferQueue->consumerConnect(proxy); + status_t err = mBufferQueue->consumerConnect(proxy, false); if (err != NO_ERROR) { ALOGE("SurfaceMediaSource: error connecting to BufferQueue: %s (%d)", strerror(-err), err); diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index b3167b5..5f7c26a 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -18,12 +18,12 @@ //#define LOG_NDEBUG 0 #include -#include +#include "GraphicBufferSource.h" #include #include -#include +#include #include namespace android { @@ -51,10 +51,9 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, String8 name("GraphicBufferSource"); - mBufferQueue = new BufferQueue(true); + mBufferQueue = new BufferQueue(); mBufferQueue->setConsumerName(name); mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight); - mBufferQueue->setSynchronousMode(true); mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_TEXTURE); @@ -75,7 +74,7 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, sp proxy; proxy = new BufferQueue::ProxyConsumerListener(listener); - mInitCheck = mBufferQueue->consumerConnect(proxy); + mInitCheck = mBufferQueue->consumerConnect(proxy, false); if (mInitCheck != NO_ERROR) { ALOGE("Error connecting to BufferQueue: %s (%d)", strerror(-mInitCheck), mInitCheck); -- cgit v1.1 From d1eff5718510228503958e8fafa698c9e6a4a230 Mon Sep 17 00:00:00 2001 From: "leozwang@google.com" Date: Sat, 13 Jul 2013 21:52:50 -0700 Subject: Output more detailed error message if loading library fails Bug: 9805979 Change-Id: I77b19d6a65ff9fb72e7428ce79b117628e4c8658 --- media/libmediaplayerservice/Crypto.cpp | 3 ++- media/libmediaplayerservice/SharedLibrary.cpp | 6 ++++++ media/libmediaplayerservice/SharedLibrary.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp index ae4d845..62593b2 100644 --- a/media/libmediaplayerservice/Crypto.cpp +++ b/media/libmediaplayerservice/Crypto.cpp @@ -134,7 +134,6 @@ void Crypto::findFactoryForScheme(const uint8_t uuid[16]) { return; } - ALOGE("Failed to find crypto plugin"); mInitCheck = ERROR_UNSUPPORTED; } @@ -151,6 +150,7 @@ bool Crypto::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) { if (!mLibrary.get()) { mLibrary = new SharedLibrary(path); if (!*mLibrary) { + ALOGE("loadLibraryForScheme failed:%s", mLibrary->lastError()); return false; } @@ -165,6 +165,7 @@ bool Crypto::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) { if (createCryptoFactory == NULL || (mFactory = createCryptoFactory()) == NULL || !mFactory->isCryptoSchemeSupported(uuid)) { + ALOGE("createCryptoFactory failed:%s", mLibrary->lastError()); closeFactory(); return false; } diff --git a/media/libmediaplayerservice/SharedLibrary.cpp b/media/libmediaplayerservice/SharedLibrary.cpp index 178e15d..34db761 100644 --- a/media/libmediaplayerservice/SharedLibrary.cpp +++ b/media/libmediaplayerservice/SharedLibrary.cpp @@ -46,4 +46,10 @@ namespace android { } return dlsym(mLibHandle, symbol); } + + const char *SharedLibrary::lastError() const { + const char *error = dlerror(); + return error ? error : "No errors or unknown error"; + } + }; diff --git a/media/libmediaplayerservice/SharedLibrary.h b/media/libmediaplayerservice/SharedLibrary.h index 5353642..88451a0 100644 --- a/media/libmediaplayerservice/SharedLibrary.h +++ b/media/libmediaplayerservice/SharedLibrary.h @@ -29,6 +29,7 @@ namespace android { bool operator!() const; void *lookup(const char *symbol) const; + const char *lastError() const; private: void *mLibHandle; -- cgit v1.1 From a0a63e13788a77bc502da0c72269d82c4779ac91 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 17 Jul 2013 14:02:31 -0700 Subject: Experimental support for enabling the use of "surface input" mode even with the software VP8 encoder. This relies heavily on the fact that the "Nexus" devices use ARGB32 as the colorspace for the data underlying a surface provided by SurfaceFlinger (mirroring). Generally there are no such guarantees. Change-Id: I1de32f591a3bb935ca76151816b3a02665bec40b --- media/libstagefright/codecs/on2/enc/Android.mk | 5 + .../codecs/on2/enc/SoftVPXEncoder.cpp | 159 ++++++++++++++++++--- .../libstagefright/codecs/on2/enc/SoftVPXEncoder.h | 15 +- 3 files changed, 158 insertions(+), 21 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk index a92d376..4060a0a 100644 --- a/media/libstagefright/codecs/on2/enc/Android.mk +++ b/media/libstagefright/codecs/on2/enc/Android.mk @@ -12,11 +12,16 @@ LOCAL_C_INCLUDES := \ frameworks/av/media/libstagefright/include \ frameworks/native/include/media/openmax \ +ifeq ($(TARGET_DEVICE), manta) + LOCAL_CFLAGS += -DSURFACE_IS_BGR32 +endif + LOCAL_STATIC_LIBRARIES := \ libvpx LOCAL_SHARED_LIBRARIES := \ libstagefright libstagefright_omx libstagefright_foundation libutils liblog \ + libhardware \ LOCAL_MODULE := libstagefright_soft_vpxenc LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index 74d6df5..d8456fe 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -20,6 +20,8 @@ #include +#include +#include #include #include @@ -81,6 +83,52 @@ inline static void ConvertSemiPlanarToPlanar(uint8_t *inyuv, } } +static void ConvertRGB32ToPlanar( + const uint8_t *src, uint8_t *dstY, int32_t width, int32_t height) { + CHECK((width & 1) == 0); + CHECK((height & 1) == 0); + + uint8_t *dstU = dstY + width * height; + uint8_t *dstV = dstU + (width / 2) * (height / 2); + + for (int32_t y = 0; y < height; ++y) { + for (int32_t x = 0; x < width; ++x) { +#ifdef SURFACE_IS_BGR32 + unsigned blue = src[4 * x]; + unsigned green = src[4 * x + 1]; + unsigned red= src[4 * x + 2]; +#else + unsigned red= src[4 * x]; + unsigned green = src[4 * x + 1]; + unsigned blue = src[4 * x + 2]; +#endif + + unsigned luma = + ((red * 66 + green * 129 + blue * 25) >> 8) + 16; + + dstY[x] = luma; + + if ((x & 1) == 0 && (y & 1) == 0) { + unsigned U = + ((-red * 38 - green * 74 + blue * 112) >> 8) + 128; + + unsigned V = + ((red * 112 - green * 94 - blue * 18) >> 8) + 128; + + dstU[x / 2] = U; + dstV[x / 2] = V; + } + } + + if ((y & 1) == 0) { + dstU += width / 2; + dstV += width / 2; + } + + src += 4 * width; + dstY += width; + } +} SoftVPXEncoder::SoftVPXEncoder(const char *name, const OMX_CALLBACKTYPE *callbacks, @@ -99,8 +147,9 @@ SoftVPXEncoder::SoftVPXEncoder(const char *name, mErrorResilience(OMX_FALSE), mColorFormat(OMX_COLOR_FormatYUV420Planar), mLevel(OMX_VIDEO_VP8Level_Version0), - mConversionBuffer(NULL) { - + mConversionBuffer(NULL), + mInputDataIsMeta(false), + mGrallocModule(NULL) { initPorts(); } @@ -247,7 +296,7 @@ status_t SoftVPXEncoder::initEncoder() { return UNKNOWN_ERROR; } - if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || mInputDataIsMeta) { if (mConversionBuffer == NULL) { mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2); if (mConversionBuffer == NULL) { @@ -427,9 +476,17 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, (const OMX_VIDEO_PARAM_BITRATETYPE *)param); case OMX_IndexParamPortDefinition: - return internalSetPortParams( + { + OMX_ERRORTYPE err = internalSetPortParams( (const OMX_PARAM_PORTDEFINITIONTYPE *)param); + if (err != OMX_ErrorNone) { + return err; + } + + return SimpleSoftOMXComponent::internalSetParameter(index, param); + } + case OMX_IndexParamVideoPortFormat: return internalSetFormatParams( (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param); @@ -442,6 +499,21 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, return internalSetProfileLevel( (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param); + case OMX_IndexVendorStartUnused: + { + // storeMetaDataInBuffers + const StoreMetaDataInBuffersParams *storeParam = + (const StoreMetaDataInBuffersParams *)param; + + if (storeParam->nPortIndex != kInputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + mInputDataIsMeta = (storeParam->bStoreMetaData == OMX_TRUE); + + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalSetParameter(index, param); } @@ -507,6 +579,10 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams( format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) { mColorFormat = format->eColorFormat; + + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; + def->format.video.eColorFormat = mColorFormat; + return OMX_ErrorNone; } else { ALOGE("Unsupported color format %i", format->eColorFormat); @@ -552,11 +628,17 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar || port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || port->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) { - mColorFormat = port->format.video.eColorFormat; + mColorFormat = port->format.video.eColorFormat; } else { return OMX_ErrorUnsupportedSetting; } + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; + def->format.video.nFrameWidth = mWidth; + def->format.video.nFrameHeight = mHeight; + def->format.video.xFramerate = port->format.video.xFramerate; + def->format.video.eColorFormat = mColorFormat; + return OMX_ErrorNone; } else if (port->nPortIndex == kOutputPortIndex) { mBitrate = port->format.video.nBitrate; @@ -625,24 +707,56 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { return; } - uint8_t* source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset; + uint8_t *source = + inputBufferHeader->pBuffer + inputBufferHeader->nOffset; + + if (mInputDataIsMeta) { + CHECK_GE(inputBufferHeader->nFilledLen, + 4 + sizeof(buffer_handle_t)); + + uint32_t bufferType = *(uint32_t *)source; + CHECK_EQ(bufferType, kMetadataBufferTypeGrallocSource); + + if (mGrallocModule == NULL) { + CHECK_EQ(0, hw_get_module( + GRALLOC_HARDWARE_MODULE_ID, &mGrallocModule)); + } + + const gralloc_module_t *grmodule = + (const gralloc_module_t *)mGrallocModule; + + buffer_handle_t handle = *(buffer_handle_t *)(source + 4); + + void *bits; + CHECK_EQ(0, + grmodule->lock( + grmodule, handle, + GRALLOC_USAGE_SW_READ_OFTEN + | GRALLOC_USAGE_SW_WRITE_NEVER, + 0, 0, mWidth, mHeight, &bits)); + + ConvertRGB32ToPlanar( + (const uint8_t *)bits, mConversionBuffer, mWidth, mHeight); + + source = mConversionBuffer; + + CHECK_EQ(0, grmodule->unlock(grmodule, handle)); + } else if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + ConvertSemiPlanarToPlanar( + source, mConversionBuffer, mWidth, mHeight); - // NOTE: As much as nothing is known about color format - // when it is denoted as AndroidOpaque, it is at least - // assumed to be planar. - if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { - ConvertSemiPlanarToPlanar(source, mConversionBuffer, mWidth, mHeight); source = mConversionBuffer; } vpx_image_t raw_frame; vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight, kInputBufferAlignment, source); - codec_return = vpx_codec_encode(mCodecContext, - &raw_frame, - inputBufferHeader->nTimeStamp, // in timebase units - mFrameDurationUs, // frame duration in timebase units - 0, // frame flags - VPX_DL_REALTIME); // encoding deadline + codec_return = vpx_codec_encode( + mCodecContext, + &raw_frame, + inputBufferHeader->nTimeStamp, // in timebase units + mFrameDurationUs, // frame duration in timebase units + 0, // frame flags + VPX_DL_REALTIME); // encoding deadline if (codec_return != VPX_CODEC_OK) { ALOGE("vpx encoder failed to encode frame"); notify(OMX_EventError, @@ -676,6 +790,17 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { notifyEmptyBufferDone(inputBufferHeader); } } + +OMX_ERRORTYPE SoftVPXEncoder::getExtensionIndex( + const char *name, OMX_INDEXTYPE *index) { + if (!strcmp(name, "OMX.google.android.index.storeMetaDataInBuffers")) { + *index = OMX_IndexVendorStartUnused; + return OMX_ErrorNone; + } + + return SimpleSoftOMXComponent::getExtensionIndex(name, index); +} + } // namespace android diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index a0a8ee6..d570154 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -23,6 +23,8 @@ #include #include +#include + #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" @@ -57,14 +59,13 @@ namespace android { // - OMX timestamps are in microseconds, therefore // encoder timebase is fixed to 1/1000000 -class SoftVPXEncoder : public SimpleSoftOMXComponent { - public: +struct SoftVPXEncoder : public SimpleSoftOMXComponent { SoftVPXEncoder(const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component); - protected: +protected: virtual ~SoftVPXEncoder(); // Returns current values for requested OMX @@ -83,7 +84,10 @@ class SoftVPXEncoder : public SimpleSoftOMXComponent { // encoding of the frame virtual void onQueueFilled(OMX_U32 portIndex); - private: + virtual OMX_ERRORTYPE getExtensionIndex( + const char *name, OMX_INDEXTYPE *index); + +private: // number of buffers allocated per port static const uint32_t kNumBuffers = 4; @@ -156,6 +160,9 @@ class SoftVPXEncoder : public SimpleSoftOMXComponent { // indeed YUV420SemiPlanar. uint8_t* mConversionBuffer; + bool mInputDataIsMeta; + const hw_module_t *mGrallocModule; + // Initializes input and output OMX ports with sensible // default values. void initPorts(); -- cgit v1.1 From e40cda70eec141fa05cbcca1de420fdb22b98be6 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 17 Jul 2013 13:55:26 -0700 Subject: Support "suspension" of a video encoder in "surface-input" mode. i.e. feed no more input frames to the encoder while suspended. Change-Id: I51391e18c1517548e869f8ddece19f4af37e78f9 --- media/libmedia/IOMX.cpp | 29 ++++++++++++ media/libstagefright/ACodec.cpp | 13 ++++++ media/libstagefright/OMXClient.cpp | 16 +++++++ media/libstagefright/include/OMX.h | 7 +++ media/libstagefright/include/OMXNodeInstance.h | 6 +++ media/libstagefright/omx/GraphicBufferSource.cpp | 48 ++++++++++++++++++-- media/libstagefright/omx/GraphicBufferSource.h | 6 +++ media/libstagefright/omx/OMX.cpp | 9 ++++ media/libstagefright/omx/OMXNodeInstance.cpp | 57 +++++++++++++++++++----- 9 files changed, 177 insertions(+), 14 deletions(-) (limited to 'media') diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index d6cd43a..5bbb2f0 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -51,6 +51,7 @@ enum { GET_EXTENSION_INDEX, OBSERVER_ON_MSG, GET_GRAPHIC_BUFFER_USAGE, + SET_INTERNAL_OPTION, }; class BpOMX : public BpInterface { @@ -439,6 +440,24 @@ public: return err; } + + virtual status_t setInternalOption( + node_id node, + OMX_U32 port_index, + InternalOptionType type, + const void *optionData, + size_t size) { + Parcel data, reply; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeIntPtr((intptr_t)node); + data.writeInt32(port_index); + data.writeInt32(size); + data.write(optionData, size); + data.writeInt32(type); + remote()->transact(SET_INTERNAL_OPTION, data, &reply); + + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); @@ -537,6 +556,7 @@ status_t BnOMX::onTransact( case SET_PARAMETER: case GET_CONFIG: case SET_CONFIG: + case SET_INTERNAL_OPTION: { CHECK_OMX_INTERFACE(IOMX, data, reply); @@ -562,6 +582,15 @@ status_t BnOMX::onTransact( case SET_CONFIG: err = setConfig(node, index, params, size); break; + case SET_INTERNAL_OPTION: + { + InternalOptionType type = + (InternalOptionType)data.readInt32(); + + err = setInternalOption(node, index, type, params, size); + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 6bc7718..8d1020e 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -4106,6 +4106,19 @@ status_t ACodec::setParameters(const sp ¶ms) { } } + int32_t dropInputFrames; + if (params->findInt32("drop-input-frames", &dropInputFrames)) { + bool suspend = dropInputFrames != 0; + + CHECK_EQ((status_t)OK, + mOMX->setInternalOption( + mNode, + kPortIndexInput, + IOMX::INTERNAL_OPTION_SUSPEND, + &suspend, + sizeof(suspend))); + } + return OK; } diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index 1822f07..810d88f 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -113,6 +113,13 @@ struct MuxOMX : public IOMX { const char *parameter_name, OMX_INDEXTYPE *index); + virtual status_t setInternalOption( + node_id node, + OMX_U32 port_index, + InternalOptionType type, + const void *data, + size_t size); + private: mutable Mutex mLock; @@ -331,6 +338,15 @@ status_t MuxOMX::getExtensionIndex( return getOMX(node)->getExtensionIndex(node, parameter_name, index); } +status_t MuxOMX::setInternalOption( + node_id node, + OMX_U32 port_index, + InternalOptionType type, + const void *data, + size_t size) { + return getOMX(node)->setInternalOption(node, port_index, type, data, size); +} + OMXClient::OMXClient() { } diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index 24b8d98..7fed7d4 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -109,6 +109,13 @@ public: const char *parameter_name, OMX_INDEXTYPE *index); + virtual status_t setInternalOption( + node_id node, + OMX_U32 port_index, + InternalOptionType type, + const void *data, + size_t size); + virtual void binderDied(const wp &the_late_who); OMX_ERRORTYPE OnEvent( diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index 67aba6b..f6ae376 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -96,6 +96,12 @@ struct OMXNodeInstance { status_t getExtensionIndex( const char *parameterName, OMX_INDEXTYPE *index); + status_t setInternalOption( + OMX_U32 portIndex, + IOMX::InternalOptionType type, + const void *data, + size_t size); + void onMessage(const omx_message &msg); void onObserverDied(OMXMaster *master); void onGetHandleFailed(); diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index b3167b5..6f3ed0d 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -36,6 +36,7 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, mInitCheck(UNKNOWN_ERROR), mNodeInstance(nodeInstance), mExecuting(false), + mSuspended(false), mNumFramesAvailable(0), mEndOfStream(false), mEndOfStreamSent(false) { @@ -237,9 +238,43 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { return; } +void GraphicBufferSource::suspend(bool suspend) { + Mutex::Autolock autoLock(mMutex); + + if (suspend) { + mSuspended = true; + + while (mNumFramesAvailable > 0) { + BufferQueue::BufferItem item; + status_t err = mBufferQueue->acquireBuffer(&item, 0); + + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // shouldn't happen. + ALOGW("suspend: frame was not available"); + break; + } else if (err != OK) { + ALOGW("suspend: acquireBuffer returned err=%d", err); + break; + } + + --mNumFramesAvailable; + + mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); + } + return; + } + + mSuspended = false; +} + bool GraphicBufferSource::fillCodecBuffer_l() { CHECK(mExecuting && mNumFramesAvailable > 0); + if (mSuspended) { + return false; + } + int cbi = findAvailableCodecBuffer_l(); if (cbi < 0) { // No buffers available, bail. @@ -416,10 +451,15 @@ void GraphicBufferSource::onFrameAvailable() { ALOGV("onFrameAvailable exec=%d avail=%d", mExecuting, mNumFramesAvailable); - if (mEndOfStream) { - // This should only be possible if a new buffer was queued after - // EOS was signaled, i.e. the app is misbehaving. - ALOGW("onFrameAvailable: EOS is set, ignoring frame"); + if (mEndOfStream || mSuspended) { + if (mEndOfStream) { + // This should only be possible if a new buffer was queued after + // EOS was signaled, i.e. the app is misbehaving. + + ALOGW("onFrameAvailable: EOS is set, ignoring frame"); + } else { + ALOGV("onFrameAvailable: suspended, ignoring frame"); + } BufferQueue::BufferItem item; status_t err = mBufferQueue->acquireBuffer(&item, 0); diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 8c6b470..ac73770 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -85,6 +85,10 @@ public: // have a codec buffer ready, we just set the mEndOfStream flag. status_t signalEndOfInputStream(); + // If suspend is true, all incoming buffers (including those currently + // in the BufferQueue) will be discarded until the suspension is lifted. + void suspend(bool suspend); + protected: // BufferQueue::ConsumerListener interface, called when a new frame of // data is available. If we're executing and a codec buffer is @@ -155,6 +159,8 @@ private: // Set by omxExecuting() / omxIdling(). bool mExecuting; + bool mSuspended; + // We consume graphic buffers from this. sp mBufferQueue; diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 3987ead..4b1dbe6 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -396,6 +396,15 @@ status_t OMX::getExtensionIndex( parameter_name, index); } +status_t OMX::setInternalOption( + node_id node, + OMX_U32 port_index, + InternalOptionType type, + const void *data, + size_t size) { + return findInstance(node)->setInternalOption(port_index, type, data, size); +} + OMX_ERRORTYPE OMX::OnEvent( node_id node, OMX_IN OMX_EVENTTYPE eEvent, diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index a9eb94f..61a866f 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -238,6 +238,18 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { status_t OMXNodeInstance::sendCommand( OMX_COMMANDTYPE cmd, OMX_S32 param) { + const sp& bufferSource(getGraphicBufferSource()); + if (bufferSource != NULL + && cmd == OMX_CommandStateSet + && param == OMX_StateLoaded) { + // Initiating transition from Executing -> Loaded + // Buffers are about to be freed. + bufferSource->omxLoaded(); + setGraphicBufferSource(NULL); + + // fall through + } + Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_SendCommand(mHandle, cmd, param, NULL); @@ -769,6 +781,36 @@ status_t OMXNodeInstance::getExtensionIndex( return StatusFromOMXError(err); } +status_t OMXNodeInstance::setInternalOption( + OMX_U32 portIndex, + IOMX::InternalOptionType type, + const void *data, + size_t size) { + switch (type) { + case IOMX::INTERNAL_OPTION_SUSPEND: + { + const sp &bufferSource = + getGraphicBufferSource(); + + if (bufferSource == NULL || portIndex != kPortIndexInput) { + return ERROR_UNSUPPORTED; + } + + if (size != sizeof(bool)) { + return INVALID_OPERATION; + } + + bool suspend = *(bool *)data; + bufferSource->suspend(suspend); + + return OK; + } + + default: + return ERROR_UNSUPPORTED; + } +} + void OMXNodeInstance::onMessage(const omx_message &msg) { if (msg.type == omx_message::FILL_BUFFER_DONE) { OMX_BUFFERHEADERTYPE *buffer = @@ -818,16 +860,11 @@ void OMXNodeInstance::onEvent( OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) { const sp& bufferSource(getGraphicBufferSource()); - if (bufferSource != NULL && event == OMX_EventCmdComplete && - arg1 == OMX_CommandStateSet) { - if (arg2 == OMX_StateExecuting) { - bufferSource->omxExecuting(); - } else if (arg2 == OMX_StateLoaded) { - // Must be shutting down -- won't have a GraphicBufferSource - // on the way up. - bufferSource->omxLoaded(); - setGraphicBufferSource(NULL); - } + if (bufferSource != NULL + && event == OMX_EventCmdComplete + && arg1 == OMX_CommandStateSet + && arg2 == OMX_StateExecuting) { + bufferSource->omxExecuting(); } } -- cgit v1.1 From 87eb285dca94b20dc5f0ff8e60a0d395a4ca3be9 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 18 Jul 2013 11:43:39 -0700 Subject: stagefright: set scaling mode for blank frames in ACodec Analogous to 1d5ac80d0c6d3deabcc9e9b4abc9e3ef536aeb27 (by jgennis), this sets the scaling mode for the ANativeWindow to SCALE_TO_WINDOW prior to pushing the blank frames during decoder tear down. Without this, the window defaults to FREEZE and SF ignores the new frames. Bug: 9516405 Change-Id: I39ef30922d733034bf01100d7ff24ac9c0c33b7d Signed-off-by: Dima Zavin --- media/libstagefright/ACodec.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 6bc7718..9b24d44 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2630,6 +2630,14 @@ status_t ACodec::pushBlankBuffersToNativeWindow() { goto error; } + err = native_window_set_scaling_mode(mNativeWindow.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + if (err != NO_ERROR) { + ALOGE("error pushing blank_frames: set_scaling_mode failed: %s (%d)", + strerror(-err), -err); + goto error; + } + err = native_window_set_usage(mNativeWindow.get(), GRALLOC_USAGE_SW_WRITE_OFTEN); if (err != NO_ERROR) { -- cgit v1.1 From 8060060217ff16cd67c8f6a15c649f44c343acf0 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 18 Jul 2013 14:36:18 -0700 Subject: ANetworkSession and ParsedMessage are now part of stagefright foundation. Also fixes some bugs in ParseMessage and adds "WebSocket" mode to ANetworkSession, something that's opt-in and should not affect existing clients of the API. Change-Id: I86d5748e0d818231d85d3590d86c2b41d4f8b1f1 --- media/libmediaplayerservice/RemoteDisplay.cpp | 13 +- media/libmediaplayerservice/RemoteDisplay.h | 5 +- .../libstagefright/foundation/ANetworkSession.cpp | 1412 ++++++++++++++++++++ media/libstagefright/foundation/Android.mk | 2 + media/libstagefright/foundation/ParsedMessage.cpp | 302 +++++ .../wifi-display/ANetworkSession.cpp | 1255 ----------------- .../libstagefright/wifi-display/ANetworkSession.h | 132 -- media/libstagefright/wifi-display/Android.mk | 2 - .../libstagefright/wifi-display/MediaReceiver.cpp | 2 +- media/libstagefright/wifi-display/MediaSender.cpp | 2 +- .../libstagefright/wifi-display/ParsedMessage.cpp | 284 ---- media/libstagefright/wifi-display/ParsedMessage.h | 60 - media/libstagefright/wifi-display/TimeSyncer.cpp | 3 +- media/libstagefright/wifi-display/nettest.cpp | 2 +- .../wifi-display/rtp/RTPReceiver.cpp | 3 +- .../libstagefright/wifi-display/rtp/RTPSender.cpp | 3 +- media/libstagefright/wifi-display/rtptest.cpp | 2 +- .../wifi-display/sink/WifiDisplaySink.cpp | 2 +- .../wifi-display/sink/WifiDisplaySink.h | 3 +- .../wifi-display/source/MediaPuller.cpp | 3 + .../wifi-display/source/WifiDisplaySource.cpp | 2 +- .../wifi-display/source/WifiDisplaySource.h | 2 +- media/libstagefright/wifi-display/udptest.cpp | 2 +- media/libstagefright/wifi-display/wfd.cpp | 3 +- 24 files changed, 1746 insertions(+), 1755 deletions(-) create mode 100644 media/libstagefright/foundation/ANetworkSession.cpp create mode 100644 media/libstagefright/foundation/ParsedMessage.cpp delete mode 100644 media/libstagefright/wifi-display/ANetworkSession.cpp delete mode 100644 media/libstagefright/wifi-display/ANetworkSession.h delete mode 100644 media/libstagefright/wifi-display/ParsedMessage.cpp delete mode 100644 media/libstagefright/wifi-display/ParsedMessage.h (limited to 'media') diff --git a/media/libmediaplayerservice/RemoteDisplay.cpp b/media/libmediaplayerservice/RemoteDisplay.cpp index 20e6513..eb959b4 100644 --- a/media/libmediaplayerservice/RemoteDisplay.cpp +++ b/media/libmediaplayerservice/RemoteDisplay.cpp @@ -16,19 +16,23 @@ #include "RemoteDisplay.h" -#include "ANetworkSession.h" #include "source/WifiDisplaySource.h" #include +#include +#include +#include namespace android { RemoteDisplay::RemoteDisplay( - const sp &client, const char *iface) + const sp &client, + const char *iface) : mLooper(new ALooper), - mNetSession(new ANetworkSession), - mSource(new WifiDisplaySource(mNetSession, client)) { + mNetSession(new ANetworkSession) { mLooper->setName("wfd_looper"); + + mSource = new WifiDisplaySource(mNetSession, client); mLooper->registerHandler(mSource); mNetSession->start(); @@ -50,6 +54,7 @@ status_t RemoteDisplay::resume() { status_t RemoteDisplay::dispose() { mSource->stop(); + mSource.clear(); mLooper->stop(); mNetSession->stop(); diff --git a/media/libmediaplayerservice/RemoteDisplay.h b/media/libmediaplayerservice/RemoteDisplay.h index bd8b684..82a0116 100644 --- a/media/libmediaplayerservice/RemoteDisplay.h +++ b/media/libmediaplayerservice/RemoteDisplay.h @@ -18,6 +18,7 @@ #define REMOTE_DISPLAY_H_ +#include #include #include #include @@ -31,7 +32,9 @@ struct IRemoteDisplayClient; struct WifiDisplaySource; struct RemoteDisplay : public BnRemoteDisplay { - RemoteDisplay(const sp &client, const char *iface); + RemoteDisplay( + const sp &client, + const char *iface); virtual status_t pause(); virtual status_t resume(); diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp new file mode 100644 index 0000000..e629588 --- /dev/null +++ b/media/libstagefright/foundation/ANetworkSession.cpp @@ -0,0 +1,1412 @@ +/* + * Copyright 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 "NetworkSession" +#include + +#include "ANetworkSession.h" +#include "ParsedMessage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + +static uint16_t U16_AT(const uint8_t *ptr) { + return ptr[0] << 8 | ptr[1]; +} + +static uint32_t U32_AT(const uint8_t *ptr) { + return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; +} + +static uint64_t U64_AT(const uint8_t *ptr) { + return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4); +} + +static const size_t kMaxUDPSize = 1500; +static const int32_t kMaxUDPRetries = 200; + +struct ANetworkSession::NetworkThread : public Thread { + NetworkThread(ANetworkSession *session); + +protected: + virtual ~NetworkThread(); + +private: + ANetworkSession *mSession; + + virtual bool threadLoop(); + + DISALLOW_EVIL_CONSTRUCTORS(NetworkThread); +}; + +struct ANetworkSession::Session : public RefBase { + enum Mode { + MODE_RTSP, + MODE_DATAGRAM, + MODE_WEBSOCKET, + }; + + enum State { + CONNECTING, + CONNECTED, + LISTENING_RTSP, + LISTENING_TCP_DGRAMS, + DATAGRAM, + }; + + Session(int32_t sessionID, + State state, + int s, + const sp ¬ify); + + int32_t sessionID() const; + int socket() const; + sp getNotificationMessage() const; + + bool isRTSPServer() const; + bool isTCPDatagramServer() const; + + bool wantsToRead(); + bool wantsToWrite(); + + status_t readMore(); + status_t writeMore(); + + status_t sendRequest( + const void *data, ssize_t size, bool timeValid, int64_t timeUs); + + void setMode(Mode mode); + + status_t switchToWebSocketMode(); + +protected: + virtual ~Session(); + +private: + enum { + FRAGMENT_FLAG_TIME_VALID = 1, + }; + struct Fragment { + uint32_t mFlags; + int64_t mTimeUs; + sp mBuffer; + }; + + int32_t mSessionID; + State mState; + Mode mMode; + int mSocket; + sp mNotify; + bool mSawReceiveFailure, mSawSendFailure; + int32_t mUDPRetries; + + List mOutFragments; + + AString mInBuffer; + + int64_t mLastStallReportUs; + + void notifyError(bool send, status_t err, const char *detail); + void notify(NotificationReason reason); + + void dumpFragmentStats(const Fragment &frag); + + DISALLOW_EVIL_CONSTRUCTORS(Session); +}; +//////////////////////////////////////////////////////////////////////////////// + +ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session) + : mSession(session) { +} + +ANetworkSession::NetworkThread::~NetworkThread() { +} + +bool ANetworkSession::NetworkThread::threadLoop() { + mSession->threadLoop(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + +ANetworkSession::Session::Session( + int32_t sessionID, + State state, + int s, + const sp ¬ify) + : mSessionID(sessionID), + mState(state), + mMode(MODE_DATAGRAM), + mSocket(s), + mNotify(notify), + mSawReceiveFailure(false), + mSawSendFailure(false), + mUDPRetries(kMaxUDPRetries), + mLastStallReportUs(-1ll) { + if (mState == CONNECTED) { + struct sockaddr_in localAddr; + socklen_t localAddrLen = sizeof(localAddr); + + int res = getsockname( + mSocket, (struct sockaddr *)&localAddr, &localAddrLen); + CHECK_GE(res, 0); + + struct sockaddr_in remoteAddr; + socklen_t remoteAddrLen = sizeof(remoteAddr); + + res = getpeername( + mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen); + CHECK_GE(res, 0); + + in_addr_t addr = ntohl(localAddr.sin_addr.s_addr); + AString localAddrString = StringPrintf( + "%d.%d.%d.%d", + (addr >> 24), + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + addr & 0xff); + + addr = ntohl(remoteAddr.sin_addr.s_addr); + AString remoteAddrString = StringPrintf( + "%d.%d.%d.%d", + (addr >> 24), + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + addr & 0xff); + + sp msg = mNotify->dup(); + msg->setInt32("sessionID", mSessionID); + msg->setInt32("reason", kWhatClientConnected); + msg->setString("server-ip", localAddrString.c_str()); + msg->setInt32("server-port", ntohs(localAddr.sin_port)); + msg->setString("client-ip", remoteAddrString.c_str()); + msg->setInt32("client-port", ntohs(remoteAddr.sin_port)); + msg->post(); + } +} + +ANetworkSession::Session::~Session() { + ALOGV("Session %d gone", mSessionID); + + close(mSocket); + mSocket = -1; +} + +int32_t ANetworkSession::Session::sessionID() const { + return mSessionID; +} + +int ANetworkSession::Session::socket() const { + return mSocket; +} + +void ANetworkSession::Session::setMode(Mode mode) { + mMode = mode; +} + +status_t ANetworkSession::Session::switchToWebSocketMode() { + if (mState != CONNECTED || mMode != MODE_RTSP) { + return INVALID_OPERATION; + } + + mMode = MODE_WEBSOCKET; + + return OK; +} + +sp ANetworkSession::Session::getNotificationMessage() const { + return mNotify; +} + +bool ANetworkSession::Session::isRTSPServer() const { + return mState == LISTENING_RTSP; +} + +bool ANetworkSession::Session::isTCPDatagramServer() const { + return mState == LISTENING_TCP_DGRAMS; +} + +bool ANetworkSession::Session::wantsToRead() { + return !mSawReceiveFailure && mState != CONNECTING; +} + +bool ANetworkSession::Session::wantsToWrite() { + return !mSawSendFailure + && (mState == CONNECTING + || (mState == CONNECTED && !mOutFragments.empty()) + || (mState == DATAGRAM && !mOutFragments.empty())); +} + +status_t ANetworkSession::Session::readMore() { + if (mState == DATAGRAM) { + CHECK_EQ(mMode, MODE_DATAGRAM); + + status_t err; + do { + sp buf = new ABuffer(kMaxUDPSize); + + struct sockaddr_in remoteAddr; + socklen_t remoteAddrLen = sizeof(remoteAddr); + + ssize_t n; + do { + n = recvfrom( + mSocket, buf->data(), buf->capacity(), 0, + (struct sockaddr *)&remoteAddr, &remoteAddrLen); + } while (n < 0 && errno == EINTR); + + err = OK; + if (n < 0) { + err = -errno; + } else if (n == 0) { + err = -ECONNRESET; + } else { + buf->setRange(0, n); + + int64_t nowUs = ALooper::GetNowUs(); + buf->meta()->setInt64("arrivalTimeUs", nowUs); + + sp notify = mNotify->dup(); + notify->setInt32("sessionID", mSessionID); + notify->setInt32("reason", kWhatDatagram); + + uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr); + notify->setString( + "fromAddr", + StringPrintf( + "%u.%u.%u.%u", + ip >> 24, + (ip >> 16) & 0xff, + (ip >> 8) & 0xff, + ip & 0xff).c_str()); + + notify->setInt32("fromPort", ntohs(remoteAddr.sin_port)); + + notify->setBuffer("data", buf); + notify->post(); + } + } while (err == OK); + + if (err == -EAGAIN) { + err = OK; + } + + if (err != OK) { + if (!mUDPRetries) { + notifyError(false /* send */, err, "Recvfrom failed."); + mSawReceiveFailure = true; + } else { + mUDPRetries--; + ALOGE("Recvfrom failed, %d/%d retries left", + mUDPRetries, kMaxUDPRetries); + err = OK; + } + } else { + mUDPRetries = kMaxUDPRetries; + } + + return err; + } + + char tmp[512]; + ssize_t n; + do { + n = recv(mSocket, tmp, sizeof(tmp), 0); + } while (n < 0 && errno == EINTR); + + status_t err = OK; + + if (n > 0) { + mInBuffer.append(tmp, n); + +#if 0 + ALOGI("in:"); + hexdump(tmp, n); +#endif + } else if (n < 0) { + err = -errno; + } else { + err = -ECONNRESET; + } + + if (mMode == MODE_DATAGRAM) { + // TCP stream carrying 16-bit length-prefixed datagrams. + + while (mInBuffer.size() >= 2) { + size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str()); + + if (mInBuffer.size() < packetSize + 2) { + break; + } + + sp packet = new ABuffer(packetSize); + memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize); + + int64_t nowUs = ALooper::GetNowUs(); + packet->meta()->setInt64("arrivalTimeUs", nowUs); + + sp notify = mNotify->dup(); + notify->setInt32("sessionID", mSessionID); + notify->setInt32("reason", kWhatDatagram); + notify->setBuffer("data", packet); + notify->post(); + + mInBuffer.erase(0, packetSize + 2); + } + } else if (mMode == MODE_RTSP) { + for (;;) { + size_t length; + + if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') { + if (mInBuffer.size() < 4) { + break; + } + + length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2); + + if (mInBuffer.size() < 4 + length) { + break; + } + + sp notify = mNotify->dup(); + notify->setInt32("sessionID", mSessionID); + notify->setInt32("reason", kWhatBinaryData); + notify->setInt32("channel", mInBuffer.c_str()[1]); + + sp data = new ABuffer(length); + memcpy(data->data(), mInBuffer.c_str() + 4, length); + + int64_t nowUs = ALooper::GetNowUs(); + data->meta()->setInt64("arrivalTimeUs", nowUs); + + notify->setBuffer("data", data); + notify->post(); + + mInBuffer.erase(0, 4 + length); + continue; + } + + sp msg = + ParsedMessage::Parse( + mInBuffer.c_str(), mInBuffer.size(), err != OK, &length); + + if (msg == NULL) { + break; + } + + sp notify = mNotify->dup(); + notify->setInt32("sessionID", mSessionID); + notify->setInt32("reason", kWhatData); + notify->setObject("data", msg); + notify->post(); + +#if 1 + // XXX The (old) dongle sends the wrong content length header on a + // SET_PARAMETER request that signals a "wfd_idr_request". + // (17 instead of 19). + const char *content = msg->getContent(); + if (content + && !memcmp(content, "wfd_idr_request\r\n", 17) + && length >= 19 + && mInBuffer.c_str()[length] == '\r' + && mInBuffer.c_str()[length + 1] == '\n') { + length += 2; + } +#endif + + mInBuffer.erase(0, length); + + if (err != OK) { + break; + } + } + } else { + CHECK_EQ(mMode, MODE_WEBSOCKET); + + const uint8_t *data = (const uint8_t *)mInBuffer.c_str(); + // hexdump(data, mInBuffer.size()); + + while (mInBuffer.size() >= 2) { + size_t offset = 2; + + unsigned payloadLen = data[1] & 0x7f; + if (payloadLen == 126) { + if (offset + 2 > mInBuffer.size()) { + break; + } + + payloadLen = U16_AT(&data[offset]); + offset += 2; + } else if (payloadLen == 127) { + if (offset + 8 > mInBuffer.size()) { + break; + } + + payloadLen = U64_AT(&data[offset]); + offset += 8; + } + + uint32_t mask = 0; + if (data[1] & 0x80) { + // MASK==1 + if (offset + 4 > mInBuffer.size()) { + break; + } + + mask = U32_AT(&data[offset]); + offset += 4; + } + + if (offset + payloadLen > mInBuffer.size()) { + break; + } + + // We have the full message. + + sp packet = new ABuffer(payloadLen); + memcpy(packet->data(), &data[offset], payloadLen); + + if (mask != 0) { + for (size_t i = 0; i < payloadLen; ++i) { + packet->data()[i] = + data[offset + i] + ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff); + } + } + + sp notify = mNotify->dup(); + notify->setInt32("sessionID", mSessionID); + notify->setInt32("reason", kWhatWebSocketMessage); + notify->setBuffer("data", packet); + notify->setInt32("headerByte", data[0]); + notify->post(); + + mInBuffer.erase(0, offset + payloadLen); + } + } + + if (err != OK) { + notifyError(false /* send */, err, "Recv failed."); + mSawReceiveFailure = true; + } + + return err; +} + +void ANetworkSession::Session::dumpFragmentStats(const Fragment &frag) { +#if 0 + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll; + + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("[%lld]: (%4lld ms) %s\n", + frag.mTimeUs / 1000, + delayMs, + kPattern + kPatternSize - n); +#endif +} + +status_t ANetworkSession::Session::writeMore() { + if (mState == DATAGRAM) { + CHECK(!mOutFragments.empty()); + + status_t err; + do { + const Fragment &frag = *mOutFragments.begin(); + const sp &datagram = frag.mBuffer; + + int n; + do { + n = send(mSocket, datagram->data(), datagram->size(), 0); + } while (n < 0 && errno == EINTR); + + err = OK; + + if (n > 0) { + if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { + dumpFragmentStats(frag); + } + + mOutFragments.erase(mOutFragments.begin()); + } else if (n < 0) { + err = -errno; + } else if (n == 0) { + err = -ECONNRESET; + } + } while (err == OK && !mOutFragments.empty()); + + if (err == -EAGAIN) { + if (!mOutFragments.empty()) { + ALOGI("%d datagrams remain queued.", mOutFragments.size()); + } + err = OK; + } + + if (err != OK) { + if (!mUDPRetries) { + notifyError(true /* send */, err, "Send datagram failed."); + mSawSendFailure = true; + } else { + mUDPRetries--; + ALOGE("Send datagram failed, %d/%d retries left", + mUDPRetries, kMaxUDPRetries); + err = OK; + } + } else { + mUDPRetries = kMaxUDPRetries; + } + + return err; + } + + if (mState == CONNECTING) { + int err; + socklen_t optionLen = sizeof(err); + CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0); + CHECK_EQ(optionLen, (socklen_t)sizeof(err)); + + if (err != 0) { + notifyError(kWhatError, -err, "Connection failed"); + mSawSendFailure = true; + + return -err; + } + + mState = CONNECTED; + notify(kWhatConnected); + + return OK; + } + + CHECK_EQ(mState, CONNECTED); + CHECK(!mOutFragments.empty()); + + ssize_t n; + while (!mOutFragments.empty()) { + const Fragment &frag = *mOutFragments.begin(); + + do { + n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0); + } while (n < 0 && errno == EINTR); + + if (n <= 0) { + break; + } + + frag.mBuffer->setRange( + frag.mBuffer->offset() + n, frag.mBuffer->size() - n); + + if (frag.mBuffer->size() > 0) { + break; + } + + if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { + dumpFragmentStats(frag); + } + + mOutFragments.erase(mOutFragments.begin()); + } + + status_t err = OK; + + if (n < 0) { + err = -errno; + } else if (n == 0) { + err = -ECONNRESET; + } + + if (err != OK) { + notifyError(true /* send */, err, "Send failed."); + mSawSendFailure = true; + } + +#if 0 + int numBytesQueued; + int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); + if (res == 0 && numBytesQueued > 50 * 1024) { + if (numBytesQueued > 409600) { + ALOGW("!!! numBytesQueued = %d", numBytesQueued); + } + + int64_t nowUs = ALooper::GetNowUs(); + + if (mLastStallReportUs < 0ll + || nowUs > mLastStallReportUs + 100000ll) { + sp msg = mNotify->dup(); + msg->setInt32("sessionID", mSessionID); + msg->setInt32("reason", kWhatNetworkStall); + msg->setSize("numBytesQueued", numBytesQueued); + msg->post(); + + mLastStallReportUs = nowUs; + } + } +#endif + + return err; +} + +status_t ANetworkSession::Session::sendRequest( + const void *data, ssize_t size, bool timeValid, int64_t timeUs) { + CHECK(mState == CONNECTED || mState == DATAGRAM); + + if (size < 0) { + size = strlen((const char *)data); + } + + if (size == 0) { + return OK; + } + + sp buffer; + + if (mState == CONNECTED && mMode == MODE_DATAGRAM) { + CHECK_LE(size, 65535); + + buffer = new ABuffer(size + 2); + buffer->data()[0] = size >> 8; + buffer->data()[1] = size & 0xff; + memcpy(buffer->data() + 2, data, size); + } else if (mState == CONNECTED && mMode == MODE_WEBSOCKET) { + static const bool kUseMask = false; // Chromium doesn't like it. + + size_t numHeaderBytes = 2 + (kUseMask ? 4 : 0); + if (size > 65535) { + numHeaderBytes += 8; + } else if (size > 125) { + numHeaderBytes += 2; + } + + buffer = new ABuffer(numHeaderBytes + size); + buffer->data()[0] = 0x81; // FIN==1 | opcode=1 (text) + buffer->data()[1] = kUseMask ? 0x80 : 0x00; + + if (size > 65535) { + buffer->data()[1] |= 127; + buffer->data()[2] = 0x00; + buffer->data()[3] = 0x00; + buffer->data()[4] = 0x00; + buffer->data()[5] = 0x00; + buffer->data()[6] = (size >> 24) & 0xff; + buffer->data()[7] = (size >> 16) & 0xff; + buffer->data()[8] = (size >> 8) & 0xff; + buffer->data()[9] = size & 0xff; + } else if (size > 125) { + buffer->data()[1] |= 126; + buffer->data()[2] = (size >> 8) & 0xff; + buffer->data()[3] = size & 0xff; + } else { + buffer->data()[1] |= size; + } + + if (kUseMask) { + uint32_t mask = rand(); + + buffer->data()[numHeaderBytes - 4] = (mask >> 24) & 0xff; + buffer->data()[numHeaderBytes - 3] = (mask >> 16) & 0xff; + buffer->data()[numHeaderBytes - 2] = (mask >> 8) & 0xff; + buffer->data()[numHeaderBytes - 1] = mask & 0xff; + + for (size_t i = 0; i < (size_t)size; ++i) { + buffer->data()[numHeaderBytes + i] = + ((const uint8_t *)data)[i] + ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff); + } + } else { + memcpy(buffer->data() + numHeaderBytes, data, size); + } + } else { + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + } + + Fragment frag; + + frag.mFlags = 0; + if (timeValid) { + frag.mFlags = FRAGMENT_FLAG_TIME_VALID; + frag.mTimeUs = timeUs; + } + + frag.mBuffer = buffer; + + mOutFragments.push_back(frag); + + return OK; +} + +void ANetworkSession::Session::notifyError( + bool send, status_t err, const char *detail) { + sp msg = mNotify->dup(); + msg->setInt32("sessionID", mSessionID); + msg->setInt32("reason", kWhatError); + msg->setInt32("send", send); + msg->setInt32("err", err); + msg->setString("detail", detail); + msg->post(); +} + +void ANetworkSession::Session::notify(NotificationReason reason) { + sp msg = mNotify->dup(); + msg->setInt32("sessionID", mSessionID); + msg->setInt32("reason", reason); + msg->post(); +} + +//////////////////////////////////////////////////////////////////////////////// + +ANetworkSession::ANetworkSession() + : mNextSessionID(1) { + mPipeFd[0] = mPipeFd[1] = -1; +} + +ANetworkSession::~ANetworkSession() { + stop(); +} + +status_t ANetworkSession::start() { + if (mThread != NULL) { + return INVALID_OPERATION; + } + + int res = pipe(mPipeFd); + if (res != 0) { + mPipeFd[0] = mPipeFd[1] = -1; + return -errno; + } + + mThread = new NetworkThread(this); + + status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO); + + if (err != OK) { + mThread.clear(); + + close(mPipeFd[0]); + close(mPipeFd[1]); + mPipeFd[0] = mPipeFd[1] = -1; + + return err; + } + + return OK; +} + +status_t ANetworkSession::stop() { + if (mThread == NULL) { + return INVALID_OPERATION; + } + + mThread->requestExit(); + interrupt(); + mThread->requestExitAndWait(); + + mThread.clear(); + + close(mPipeFd[0]); + close(mPipeFd[1]); + mPipeFd[0] = mPipeFd[1] = -1; + + return OK; +} + +status_t ANetworkSession::createRTSPClient( + const char *host, unsigned port, const sp ¬ify, + int32_t *sessionID) { + return createClientOrServer( + kModeCreateRTSPClient, + NULL /* addr */, + 0 /* port */, + host, + port, + notify, + sessionID); +} + +status_t ANetworkSession::createRTSPServer( + const struct in_addr &addr, unsigned port, + const sp ¬ify, int32_t *sessionID) { + return createClientOrServer( + kModeCreateRTSPServer, + &addr, + port, + NULL /* remoteHost */, + 0 /* remotePort */, + notify, + sessionID); +} + +status_t ANetworkSession::createUDPSession( + unsigned localPort, const sp ¬ify, int32_t *sessionID) { + return createUDPSession(localPort, NULL, 0, notify, sessionID); +} + +status_t ANetworkSession::createUDPSession( + unsigned localPort, + const char *remoteHost, + unsigned remotePort, + const sp ¬ify, + int32_t *sessionID) { + return createClientOrServer( + kModeCreateUDPSession, + NULL /* addr */, + localPort, + remoteHost, + remotePort, + notify, + sessionID); +} + +status_t ANetworkSession::createTCPDatagramSession( + const struct in_addr &addr, unsigned port, + const sp ¬ify, int32_t *sessionID) { + return createClientOrServer( + kModeCreateTCPDatagramSessionPassive, + &addr, + port, + NULL /* remoteHost */, + 0 /* remotePort */, + notify, + sessionID); +} + +status_t ANetworkSession::createTCPDatagramSession( + unsigned localPort, + const char *remoteHost, + unsigned remotePort, + const sp ¬ify, + int32_t *sessionID) { + return createClientOrServer( + kModeCreateTCPDatagramSessionActive, + NULL /* addr */, + localPort, + remoteHost, + remotePort, + notify, + sessionID); +} + +status_t ANetworkSession::destroySession(int32_t sessionID) { + Mutex::Autolock autoLock(mLock); + + ssize_t index = mSessions.indexOfKey(sessionID); + + if (index < 0) { + return -ENOENT; + } + + mSessions.removeItemsAt(index); + + interrupt(); + + return OK; +} + +// static +status_t ANetworkSession::MakeSocketNonBlocking(int s) { + int flags = fcntl(s, F_GETFL, 0); + if (flags < 0) { + flags = 0; + } + + int res = fcntl(s, F_SETFL, flags | O_NONBLOCK); + if (res < 0) { + return -errno; + } + + return OK; +} + +status_t ANetworkSession::createClientOrServer( + Mode mode, + const struct in_addr *localAddr, + unsigned port, + const char *remoteHost, + unsigned remotePort, + const sp ¬ify, + int32_t *sessionID) { + Mutex::Autolock autoLock(mLock); + + *sessionID = 0; + status_t err = OK; + int s, res; + sp session; + + s = socket( + AF_INET, + (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM, + 0); + + if (s < 0) { + err = -errno; + goto bail; + } + + if (mode == kModeCreateRTSPServer + || mode == kModeCreateTCPDatagramSessionPassive) { + const int yes = 1; + res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + + if (res < 0) { + err = -errno; + goto bail2; + } + } + + if (mode == kModeCreateUDPSession) { + int size = 256 * 1024; + + res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + + if (res < 0) { + err = -errno; + goto bail2; + } + + res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); + + if (res < 0) { + err = -errno; + goto bail2; + } + } else if (mode == kModeCreateTCPDatagramSessionActive) { + int flag = 1; + res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + + if (res < 0) { + err = -errno; + goto bail2; + } + + int tos = 224; // VOICE + res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + + if (res < 0) { + err = -errno; + goto bail2; + } + } + + err = MakeSocketNonBlocking(s); + + if (err != OK) { + goto bail2; + } + + struct sockaddr_in addr; + memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); + addr.sin_family = AF_INET; + + if (mode == kModeCreateRTSPClient + || mode == kModeCreateTCPDatagramSessionActive) { + struct hostent *ent= gethostbyname(remoteHost); + if (ent == NULL) { + err = -h_errno; + goto bail2; + } + + addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; + addr.sin_port = htons(remotePort); + } else if (localAddr != NULL) { + addr.sin_addr = *localAddr; + addr.sin_port = htons(port); + } else { + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + } + + if (mode == kModeCreateRTSPClient + || mode == kModeCreateTCPDatagramSessionActive) { + in_addr_t x = ntohl(addr.sin_addr.s_addr); + ALOGI("connecting socket %d to %d.%d.%d.%d:%d", + s, + (x >> 24), + (x >> 16) & 0xff, + (x >> 8) & 0xff, + x & 0xff, + ntohs(addr.sin_port)); + + res = connect(s, (const struct sockaddr *)&addr, sizeof(addr)); + + CHECK_LT(res, 0); + if (errno == EINPROGRESS) { + res = 0; + } + } else { + res = bind(s, (const struct sockaddr *)&addr, sizeof(addr)); + + if (res == 0) { + if (mode == kModeCreateRTSPServer + || mode == kModeCreateTCPDatagramSessionPassive) { + res = listen(s, 4); + } else { + CHECK_EQ(mode, kModeCreateUDPSession); + + if (remoteHost != NULL) { + struct sockaddr_in remoteAddr; + memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero)); + remoteAddr.sin_family = AF_INET; + remoteAddr.sin_port = htons(remotePort); + + struct hostent *ent= gethostbyname(remoteHost); + if (ent == NULL) { + err = -h_errno; + goto bail2; + } + + remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; + + res = connect( + s, + (const struct sockaddr *)&remoteAddr, + sizeof(remoteAddr)); + } + } + } + } + + if (res < 0) { + err = -errno; + goto bail2; + } + + Session::State state; + switch (mode) { + case kModeCreateRTSPClient: + state = Session::CONNECTING; + break; + + case kModeCreateTCPDatagramSessionActive: + state = Session::CONNECTING; + break; + + case kModeCreateTCPDatagramSessionPassive: + state = Session::LISTENING_TCP_DGRAMS; + break; + + case kModeCreateRTSPServer: + state = Session::LISTENING_RTSP; + break; + + default: + CHECK_EQ(mode, kModeCreateUDPSession); + state = Session::DATAGRAM; + break; + } + + session = new Session( + mNextSessionID++, + state, + s, + notify); + + if (mode == kModeCreateTCPDatagramSessionActive) { + session->setMode(Session::MODE_DATAGRAM); + } else if (mode == kModeCreateRTSPClient) { + session->setMode(Session::MODE_RTSP); + } + + mSessions.add(session->sessionID(), session); + + interrupt(); + + *sessionID = session->sessionID(); + + goto bail; + +bail2: + close(s); + s = -1; + +bail: + return err; +} + +status_t ANetworkSession::connectUDPSession( + int32_t sessionID, const char *remoteHost, unsigned remotePort) { + Mutex::Autolock autoLock(mLock); + + ssize_t index = mSessions.indexOfKey(sessionID); + + if (index < 0) { + return -ENOENT; + } + + const sp session = mSessions.valueAt(index); + int s = session->socket(); + + struct sockaddr_in remoteAddr; + memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero)); + remoteAddr.sin_family = AF_INET; + remoteAddr.sin_port = htons(remotePort); + + status_t err = OK; + struct hostent *ent = gethostbyname(remoteHost); + if (ent == NULL) { + err = -h_errno; + } else { + remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; + + int res = connect( + s, + (const struct sockaddr *)&remoteAddr, + sizeof(remoteAddr)); + + if (res < 0) { + err = -errno; + } + } + + return err; +} + +status_t ANetworkSession::sendRequest( + int32_t sessionID, const void *data, ssize_t size, + bool timeValid, int64_t timeUs) { + Mutex::Autolock autoLock(mLock); + + ssize_t index = mSessions.indexOfKey(sessionID); + + if (index < 0) { + return -ENOENT; + } + + const sp session = mSessions.valueAt(index); + + status_t err = session->sendRequest(data, size, timeValid, timeUs); + + interrupt(); + + return err; +} + +status_t ANetworkSession::switchToWebSocketMode(int32_t sessionID) { + Mutex::Autolock autoLock(mLock); + + ssize_t index = mSessions.indexOfKey(sessionID); + + if (index < 0) { + return -ENOENT; + } + + const sp session = mSessions.valueAt(index); + return session->switchToWebSocketMode(); +} + +void ANetworkSession::interrupt() { + static const char dummy = 0; + + ssize_t n; + do { + n = write(mPipeFd[1], &dummy, 1); + } while (n < 0 && errno == EINTR); + + if (n < 0) { + ALOGW("Error writing to pipe (%s)", strerror(errno)); + } +} + +void ANetworkSession::threadLoop() { + fd_set rs, ws; + FD_ZERO(&rs); + FD_ZERO(&ws); + + FD_SET(mPipeFd[0], &rs); + int maxFd = mPipeFd[0]; + + { + Mutex::Autolock autoLock(mLock); + + for (size_t i = 0; i < mSessions.size(); ++i) { + const sp &session = mSessions.valueAt(i); + + int s = session->socket(); + + if (s < 0) { + continue; + } + + if (session->wantsToRead()) { + FD_SET(s, &rs); + if (s > maxFd) { + maxFd = s; + } + } + + if (session->wantsToWrite()) { + FD_SET(s, &ws); + if (s > maxFd) { + maxFd = s; + } + } + } + } + + int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */); + + if (res == 0) { + return; + } + + if (res < 0) { + if (errno == EINTR) { + return; + } + + ALOGE("select failed w/ error %d (%s)", errno, strerror(errno)); + return; + } + + if (FD_ISSET(mPipeFd[0], &rs)) { + char c; + ssize_t n; + do { + n = read(mPipeFd[0], &c, 1); + } while (n < 0 && errno == EINTR); + + if (n < 0) { + ALOGW("Error reading from pipe (%s)", strerror(errno)); + } + + --res; + } + + { + Mutex::Autolock autoLock(mLock); + + List > sessionsToAdd; + + for (size_t i = mSessions.size(); res > 0 && i-- > 0;) { + const sp &session = mSessions.valueAt(i); + + int s = session->socket(); + + if (s < 0) { + continue; + } + + if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) { + --res; + } + + if (FD_ISSET(s, &rs)) { + if (session->isRTSPServer() || session->isTCPDatagramServer()) { + struct sockaddr_in remoteAddr; + socklen_t remoteAddrLen = sizeof(remoteAddr); + + int clientSocket = accept( + s, (struct sockaddr *)&remoteAddr, &remoteAddrLen); + + if (clientSocket >= 0) { + status_t err = MakeSocketNonBlocking(clientSocket); + + if (err != OK) { + ALOGE("Unable to make client socket non blocking, " + "failed w/ error %d (%s)", + err, strerror(-err)); + + close(clientSocket); + clientSocket = -1; + } else { + in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr); + + ALOGI("incoming connection from %d.%d.%d.%d:%d " + "(socket %d)", + (addr >> 24), + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + addr & 0xff, + ntohs(remoteAddr.sin_port), + clientSocket); + + sp clientSession = + new Session( + mNextSessionID++, + Session::CONNECTED, + clientSocket, + session->getNotificationMessage()); + + clientSession->setMode( + session->isRTSPServer() + ? Session::MODE_RTSP + : Session::MODE_DATAGRAM); + + sessionsToAdd.push_back(clientSession); + } + } else { + ALOGE("accept returned error %d (%s)", + errno, strerror(errno)); + } + } else { + status_t err = session->readMore(); + if (err != OK) { + ALOGE("readMore on socket %d failed w/ error %d (%s)", + s, err, strerror(-err)); + } + } + } + + if (FD_ISSET(s, &ws)) { + status_t err = session->writeMore(); + if (err != OK) { + ALOGE("writeMore on socket %d failed w/ error %d (%s)", + s, err, strerror(-err)); + } + } + } + + while (!sessionsToAdd.empty()) { + sp session = *sessionsToAdd.begin(); + sessionsToAdd.erase(sessionsToAdd.begin()); + + mSessions.add(session->sessionID(), session); + + ALOGI("added clientSession %d", session->sessionID()); + } + } +} + +} // namespace android + diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk index d65e213..ad2dab5 100644 --- a/media/libstagefright/foundation/Android.mk +++ b/media/libstagefright/foundation/Android.mk @@ -10,7 +10,9 @@ LOCAL_SRC_FILES:= \ ALooper.cpp \ ALooperRoster.cpp \ AMessage.cpp \ + ANetworkSession.cpp \ AString.cpp \ + ParsedMessage.cpp \ base64.cpp \ hexdump.cpp diff --git a/media/libstagefright/foundation/ParsedMessage.cpp b/media/libstagefright/foundation/ParsedMessage.cpp new file mode 100644 index 0000000..049c9ad --- /dev/null +++ b/media/libstagefright/foundation/ParsedMessage.cpp @@ -0,0 +1,302 @@ +/* + * Copyright 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 "ParsedMessage.h" + +#include +#include +#include +#include + +namespace android { + +// static +sp ParsedMessage::Parse( + const char *data, size_t size, bool noMoreData, size_t *length) { + sp msg = new ParsedMessage; + ssize_t res = msg->parse(data, size, noMoreData); + + if (res < 0) { + *length = 0; + return NULL; + } + + *length = res; + return msg; +} + +ParsedMessage::ParsedMessage() { +} + +ParsedMessage::~ParsedMessage() { +} + +bool ParsedMessage::findString(const char *name, AString *value) const { + AString key = name; + key.tolower(); + + ssize_t index = mDict.indexOfKey(key); + + if (index < 0) { + value->clear(); + + return false; + } + + *value = mDict.valueAt(index); + return true; +} + +bool ParsedMessage::findInt32(const char *name, int32_t *value) const { + AString stringValue; + + if (!findString(name, &stringValue)) { + return false; + } + + char *end; + *value = strtol(stringValue.c_str(), &end, 10); + + if (end == stringValue.c_str() || *end != '\0') { + *value = 0; + return false; + } + + return true; +} + +const char *ParsedMessage::getContent() const { + return mContent.c_str(); +} + +ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) { + if (size == 0) { + return -1; + } + + ssize_t lastDictIndex = -1; + + size_t offset = 0; + bool headersComplete = false; + while (offset < size) { + size_t lineEndOffset = offset; + while (lineEndOffset + 1 < size + && (data[lineEndOffset] != '\r' + || data[lineEndOffset + 1] != '\n')) { + ++lineEndOffset; + } + + if (lineEndOffset + 1 >= size) { + return -1; + } + + AString line(&data[offset], lineEndOffset - offset); + + if (offset == 0) { + // Special handling for the request/status line. + + mDict.add(AString("_"), line); + offset = lineEndOffset + 2; + + continue; + } + + if (lineEndOffset == offset) { + // An empty line separates headers from body. + headersComplete = true; + offset += 2; + break; + } + + if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') { + // Support for folded header values. + + if (lastDictIndex >= 0) { + // Otherwise it's malformed since the first header line + // cannot continue anything... + + AString &value = mDict.editValueAt(lastDictIndex); + value.append(line); + } + + offset = lineEndOffset + 2; + continue; + } + + ssize_t colonPos = line.find(":"); + if (colonPos >= 0) { + AString key(line, 0, colonPos); + key.trim(); + key.tolower(); + + line.erase(0, colonPos + 1); + + lastDictIndex = mDict.add(key, line); + } + + offset = lineEndOffset + 2; + } + + if (!headersComplete && (!noMoreData || offset == 0)) { + // We either saw the empty line separating headers from body + // or we saw at least the status line and know that no more data + // is going to follow. + return -1; + } + + for (size_t i = 0; i < mDict.size(); ++i) { + mDict.editValueAt(i).trim(); + } + + int32_t contentLength; + if (!findInt32("content-length", &contentLength) || contentLength < 0) { + contentLength = 0; + } + + size_t totalLength = offset + contentLength; + + if (size < totalLength) { + return -1; + } + + mContent.setTo(&data[offset], contentLength); + + return totalLength; +} + +bool ParsedMessage::getRequestField(size_t index, AString *field) const { + AString line; + CHECK(findString("_", &line)); + + size_t prevOffset = 0; + size_t offset = 0; + for (size_t i = 0; i <= index; ++i) { + if (offset >= line.size()) { + return false; + } + + ssize_t spacePos = line.find(" ", offset); + + if (spacePos < 0) { + spacePos = line.size(); + } + + prevOffset = offset; + offset = spacePos + 1; + } + + field->setTo(line, prevOffset, offset - prevOffset - 1); + + return true; +} + +bool ParsedMessage::getStatusCode(int32_t *statusCode) const { + AString statusCodeString; + if (!getRequestField(1, &statusCodeString)) { + *statusCode = 0; + return false; + } + + char *end; + *statusCode = strtol(statusCodeString.c_str(), &end, 10); + + if (*end != '\0' || end == statusCodeString.c_str() + || (*statusCode) < 100 || (*statusCode) > 999) { + *statusCode = 0; + return false; + } + + return true; +} + +AString ParsedMessage::debugString() const { + AString line; + CHECK(findString("_", &line)); + + line.append("\n"); + + for (size_t i = 0; i < mDict.size(); ++i) { + const AString &key = mDict.keyAt(i); + const AString &value = mDict.valueAt(i); + + if (key == AString("_")) { + continue; + } + + line.append(key); + line.append(": "); + line.append(value); + line.append("\n"); + } + + line.append("\n"); + line.append(mContent); + + return line; +} + +// static +bool ParsedMessage::GetAttribute( + const char *s, const char *key, AString *value) { + value->clear(); + + size_t keyLen = strlen(key); + + for (;;) { + while (isspace(*s)) { + ++s; + } + + const char *colonPos = strchr(s, ';'); + + size_t len = + (colonPos == NULL) ? strlen(s) : colonPos - s; + + if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { + value->setTo(&s[keyLen + 1], len - keyLen - 1); + return true; + } + + if (colonPos == NULL) { + return false; + } + + s = colonPos + 1; + } +} + +// static +bool ParsedMessage::GetInt32Attribute( + const char *s, const char *key, int32_t *value) { + AString stringValue; + if (!GetAttribute(s, key, &stringValue)) { + *value = 0; + return false; + } + + char *end; + *value = strtol(stringValue.c_str(), &end, 10); + + if (end == stringValue.c_str() || *end != '\0') { + *value = 0; + return false; + } + + return true; +} + +} // namespace android + diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp deleted file mode 100644 index 938d601..0000000 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ /dev/null @@ -1,1255 +0,0 @@ -/* - * Copyright 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 "NetworkSession" -#include - -#include "ANetworkSession.h" -#include "ParsedMessage.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace android { - -static const size_t kMaxUDPSize = 1500; -static const int32_t kMaxUDPRetries = 200; - -struct ANetworkSession::NetworkThread : public Thread { - NetworkThread(ANetworkSession *session); - -protected: - virtual ~NetworkThread(); - -private: - ANetworkSession *mSession; - - virtual bool threadLoop(); - - DISALLOW_EVIL_CONSTRUCTORS(NetworkThread); -}; - -struct ANetworkSession::Session : public RefBase { - enum State { - CONNECTING, - CONNECTED, - LISTENING_RTSP, - LISTENING_TCP_DGRAMS, - DATAGRAM, - }; - - Session(int32_t sessionID, - State state, - int s, - const sp ¬ify); - - int32_t sessionID() const; - int socket() const; - sp getNotificationMessage() const; - - bool isRTSPServer() const; - bool isTCPDatagramServer() const; - - bool wantsToRead(); - bool wantsToWrite(); - - status_t readMore(); - status_t writeMore(); - - status_t sendRequest( - const void *data, ssize_t size, bool timeValid, int64_t timeUs); - - void setIsRTSPConnection(bool yesno); - -protected: - virtual ~Session(); - -private: - enum { - FRAGMENT_FLAG_TIME_VALID = 1, - }; - struct Fragment { - uint32_t mFlags; - int64_t mTimeUs; - sp mBuffer; - }; - - int32_t mSessionID; - State mState; - bool mIsRTSPConnection; - int mSocket; - sp mNotify; - bool mSawReceiveFailure, mSawSendFailure; - int32_t mUDPRetries; - - List mOutFragments; - - AString mInBuffer; - - int64_t mLastStallReportUs; - - void notifyError(bool send, status_t err, const char *detail); - void notify(NotificationReason reason); - - void dumpFragmentStats(const Fragment &frag); - - DISALLOW_EVIL_CONSTRUCTORS(Session); -}; -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session) - : mSession(session) { -} - -ANetworkSession::NetworkThread::~NetworkThread() { -} - -bool ANetworkSession::NetworkThread::threadLoop() { - mSession->threadLoop(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::Session::Session( - int32_t sessionID, - State state, - int s, - const sp ¬ify) - : mSessionID(sessionID), - mState(state), - mIsRTSPConnection(false), - mSocket(s), - mNotify(notify), - mSawReceiveFailure(false), - mSawSendFailure(false), - mUDPRetries(kMaxUDPRetries), - mLastStallReportUs(-1ll) { - if (mState == CONNECTED) { - struct sockaddr_in localAddr; - socklen_t localAddrLen = sizeof(localAddr); - - int res = getsockname( - mSocket, (struct sockaddr *)&localAddr, &localAddrLen); - CHECK_GE(res, 0); - - struct sockaddr_in remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - res = getpeername( - mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen); - CHECK_GE(res, 0); - - in_addr_t addr = ntohl(localAddr.sin_addr.s_addr); - AString localAddrString = StringPrintf( - "%d.%d.%d.%d", - (addr >> 24), - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff); - - addr = ntohl(remoteAddr.sin_addr.s_addr); - AString remoteAddrString = StringPrintf( - "%d.%d.%d.%d", - (addr >> 24), - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff); - - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", kWhatClientConnected); - msg->setString("server-ip", localAddrString.c_str()); - msg->setInt32("server-port", ntohs(localAddr.sin_port)); - msg->setString("client-ip", remoteAddrString.c_str()); - msg->setInt32("client-port", ntohs(remoteAddr.sin_port)); - msg->post(); - } -} - -ANetworkSession::Session::~Session() { - ALOGV("Session %d gone", mSessionID); - - close(mSocket); - mSocket = -1; -} - -int32_t ANetworkSession::Session::sessionID() const { - return mSessionID; -} - -int ANetworkSession::Session::socket() const { - return mSocket; -} - -void ANetworkSession::Session::setIsRTSPConnection(bool yesno) { - mIsRTSPConnection = yesno; -} - -sp ANetworkSession::Session::getNotificationMessage() const { - return mNotify; -} - -bool ANetworkSession::Session::isRTSPServer() const { - return mState == LISTENING_RTSP; -} - -bool ANetworkSession::Session::isTCPDatagramServer() const { - return mState == LISTENING_TCP_DGRAMS; -} - -bool ANetworkSession::Session::wantsToRead() { - return !mSawReceiveFailure && mState != CONNECTING; -} - -bool ANetworkSession::Session::wantsToWrite() { - return !mSawSendFailure - && (mState == CONNECTING - || (mState == CONNECTED && !mOutFragments.empty()) - || (mState == DATAGRAM && !mOutFragments.empty())); -} - -status_t ANetworkSession::Session::readMore() { - if (mState == DATAGRAM) { - status_t err; - do { - sp buf = new ABuffer(kMaxUDPSize); - - struct sockaddr_in remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - ssize_t n; - do { - n = recvfrom( - mSocket, buf->data(), buf->capacity(), 0, - (struct sockaddr *)&remoteAddr, &remoteAddrLen); - } while (n < 0 && errno == EINTR); - - err = OK; - if (n < 0) { - err = -errno; - } else if (n == 0) { - err = -ECONNRESET; - } else { - buf->setRange(0, n); - - int64_t nowUs = ALooper::GetNowUs(); - buf->meta()->setInt64("arrivalTimeUs", nowUs); - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatDatagram); - - uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr); - notify->setString( - "fromAddr", - StringPrintf( - "%u.%u.%u.%u", - ip >> 24, - (ip >> 16) & 0xff, - (ip >> 8) & 0xff, - ip & 0xff).c_str()); - - notify->setInt32("fromPort", ntohs(remoteAddr.sin_port)); - - notify->setBuffer("data", buf); - notify->post(); - } - } while (err == OK); - - if (err == -EAGAIN) { - err = OK; - } - - if (err != OK) { - if (!mUDPRetries) { - notifyError(false /* send */, err, "Recvfrom failed."); - mSawReceiveFailure = true; - } else { - mUDPRetries--; - ALOGE("Recvfrom failed, %d/%d retries left", - mUDPRetries, kMaxUDPRetries); - err = OK; - } - } else { - mUDPRetries = kMaxUDPRetries; - } - - return err; - } - - char tmp[512]; - ssize_t n; - do { - n = recv(mSocket, tmp, sizeof(tmp), 0); - } while (n < 0 && errno == EINTR); - - status_t err = OK; - - if (n > 0) { - mInBuffer.append(tmp, n); - -#if 0 - ALOGI("in:"); - hexdump(tmp, n); -#endif - } else if (n < 0) { - err = -errno; - } else { - err = -ECONNRESET; - } - - if (!mIsRTSPConnection) { - // TCP stream carrying 16-bit length-prefixed datagrams. - - while (mInBuffer.size() >= 2) { - size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str()); - - if (mInBuffer.size() < packetSize + 2) { - break; - } - - sp packet = new ABuffer(packetSize); - memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize); - - int64_t nowUs = ALooper::GetNowUs(); - packet->meta()->setInt64("arrivalTimeUs", nowUs); - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatDatagram); - notify->setBuffer("data", packet); - notify->post(); - - mInBuffer.erase(0, packetSize + 2); - } - } else { - for (;;) { - size_t length; - - if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') { - if (mInBuffer.size() < 4) { - break; - } - - length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2); - - if (mInBuffer.size() < 4 + length) { - break; - } - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatBinaryData); - notify->setInt32("channel", mInBuffer.c_str()[1]); - - sp data = new ABuffer(length); - memcpy(data->data(), mInBuffer.c_str() + 4, length); - - int64_t nowUs = ALooper::GetNowUs(); - data->meta()->setInt64("arrivalTimeUs", nowUs); - - notify->setBuffer("data", data); - notify->post(); - - mInBuffer.erase(0, 4 + length); - continue; - } - - sp msg = - ParsedMessage::Parse( - mInBuffer.c_str(), mInBuffer.size(), err != OK, &length); - - if (msg == NULL) { - break; - } - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatData); - notify->setObject("data", msg); - notify->post(); - -#if 1 - // XXX The (old) dongle sends the wrong content length header on a - // SET_PARAMETER request that signals a "wfd_idr_request". - // (17 instead of 19). - const char *content = msg->getContent(); - if (content - && !memcmp(content, "wfd_idr_request\r\n", 17) - && length >= 19 - && mInBuffer.c_str()[length] == '\r' - && mInBuffer.c_str()[length + 1] == '\n') { - length += 2; - } -#endif - - mInBuffer.erase(0, length); - - if (err != OK) { - break; - } - } - } - - if (err != OK) { - notifyError(false /* send */, err, "Recv failed."); - mSawReceiveFailure = true; - } - - return err; -} - -void ANetworkSession::Session::dumpFragmentStats(const Fragment &frag) { -#if 0 - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll; - - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - ALOGI("[%lld]: (%4lld ms) %s\n", - frag.mTimeUs / 1000, - delayMs, - kPattern + kPatternSize - n); -#endif -} - -status_t ANetworkSession::Session::writeMore() { - if (mState == DATAGRAM) { - CHECK(!mOutFragments.empty()); - - status_t err; - do { - const Fragment &frag = *mOutFragments.begin(); - const sp &datagram = frag.mBuffer; - - int n; - do { - n = send(mSocket, datagram->data(), datagram->size(), 0); - } while (n < 0 && errno == EINTR); - - err = OK; - - if (n > 0) { - if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { - dumpFragmentStats(frag); - } - - mOutFragments.erase(mOutFragments.begin()); - } else if (n < 0) { - err = -errno; - } else if (n == 0) { - err = -ECONNRESET; - } - } while (err == OK && !mOutFragments.empty()); - - if (err == -EAGAIN) { - if (!mOutFragments.empty()) { - ALOGI("%d datagrams remain queued.", mOutFragments.size()); - } - err = OK; - } - - if (err != OK) { - if (!mUDPRetries) { - notifyError(true /* send */, err, "Send datagram failed."); - mSawSendFailure = true; - } else { - mUDPRetries--; - ALOGE("Send datagram failed, %d/%d retries left", - mUDPRetries, kMaxUDPRetries); - err = OK; - } - } else { - mUDPRetries = kMaxUDPRetries; - } - - return err; - } - - if (mState == CONNECTING) { - int err; - socklen_t optionLen = sizeof(err); - CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0); - CHECK_EQ(optionLen, (socklen_t)sizeof(err)); - - if (err != 0) { - notifyError(kWhatError, -err, "Connection failed"); - mSawSendFailure = true; - - return -err; - } - - mState = CONNECTED; - notify(kWhatConnected); - - return OK; - } - - CHECK_EQ(mState, CONNECTED); - CHECK(!mOutFragments.empty()); - - ssize_t n; - while (!mOutFragments.empty()) { - const Fragment &frag = *mOutFragments.begin(); - - do { - n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0); - } while (n < 0 && errno == EINTR); - - if (n <= 0) { - break; - } - - frag.mBuffer->setRange( - frag.mBuffer->offset() + n, frag.mBuffer->size() - n); - - if (frag.mBuffer->size() > 0) { - break; - } - - if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { - dumpFragmentStats(frag); - } - - mOutFragments.erase(mOutFragments.begin()); - } - - status_t err = OK; - - if (n < 0) { - err = -errno; - } else if (n == 0) { - err = -ECONNRESET; - } - - if (err != OK) { - notifyError(true /* send */, err, "Send failed."); - mSawSendFailure = true; - } - -#if 0 - int numBytesQueued; - int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); - if (res == 0 && numBytesQueued > 50 * 1024) { - if (numBytesQueued > 409600) { - ALOGW("!!! numBytesQueued = %d", numBytesQueued); - } - - int64_t nowUs = ALooper::GetNowUs(); - - if (mLastStallReportUs < 0ll - || nowUs > mLastStallReportUs + 100000ll) { - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", kWhatNetworkStall); - msg->setSize("numBytesQueued", numBytesQueued); - msg->post(); - - mLastStallReportUs = nowUs; - } - } -#endif - - return err; -} - -status_t ANetworkSession::Session::sendRequest( - const void *data, ssize_t size, bool timeValid, int64_t timeUs) { - CHECK(mState == CONNECTED || mState == DATAGRAM); - - if (size < 0) { - size = strlen((const char *)data); - } - - if (size == 0) { - return OK; - } - - sp buffer; - - if (mState == CONNECTED && !mIsRTSPConnection) { - CHECK_LE(size, 65535); - - buffer = new ABuffer(size + 2); - buffer->data()[0] = size >> 8; - buffer->data()[1] = size & 0xff; - memcpy(buffer->data() + 2, data, size); - } else { - buffer = new ABuffer(size); - memcpy(buffer->data(), data, size); - } - - Fragment frag; - - frag.mFlags = 0; - if (timeValid) { - frag.mFlags = FRAGMENT_FLAG_TIME_VALID; - frag.mTimeUs = timeUs; - } - - frag.mBuffer = buffer; - - mOutFragments.push_back(frag); - - return OK; -} - -void ANetworkSession::Session::notifyError( - bool send, status_t err, const char *detail) { - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", kWhatError); - msg->setInt32("send", send); - msg->setInt32("err", err); - msg->setString("detail", detail); - msg->post(); -} - -void ANetworkSession::Session::notify(NotificationReason reason) { - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", reason); - msg->post(); -} - -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::ANetworkSession() - : mNextSessionID(1) { - mPipeFd[0] = mPipeFd[1] = -1; -} - -ANetworkSession::~ANetworkSession() { - stop(); -} - -status_t ANetworkSession::start() { - if (mThread != NULL) { - return INVALID_OPERATION; - } - - int res = pipe(mPipeFd); - if (res != 0) { - mPipeFd[0] = mPipeFd[1] = -1; - return -errno; - } - - mThread = new NetworkThread(this); - - status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO); - - if (err != OK) { - mThread.clear(); - - close(mPipeFd[0]); - close(mPipeFd[1]); - mPipeFd[0] = mPipeFd[1] = -1; - - return err; - } - - return OK; -} - -status_t ANetworkSession::stop() { - if (mThread == NULL) { - return INVALID_OPERATION; - } - - mThread->requestExit(); - interrupt(); - mThread->requestExitAndWait(); - - mThread.clear(); - - close(mPipeFd[0]); - close(mPipeFd[1]); - mPipeFd[0] = mPipeFd[1] = -1; - - return OK; -} - -status_t ANetworkSession::createRTSPClient( - const char *host, unsigned port, const sp ¬ify, - int32_t *sessionID) { - return createClientOrServer( - kModeCreateRTSPClient, - NULL /* addr */, - 0 /* port */, - host, - port, - notify, - sessionID); -} - -status_t ANetworkSession::createRTSPServer( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID) { - return createClientOrServer( - kModeCreateRTSPServer, - &addr, - port, - NULL /* remoteHost */, - 0 /* remotePort */, - notify, - sessionID); -} - -status_t ANetworkSession::createUDPSession( - unsigned localPort, const sp ¬ify, int32_t *sessionID) { - return createUDPSession(localPort, NULL, 0, notify, sessionID); -} - -status_t ANetworkSession::createUDPSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID) { - return createClientOrServer( - kModeCreateUDPSession, - NULL /* addr */, - localPort, - remoteHost, - remotePort, - notify, - sessionID); -} - -status_t ANetworkSession::createTCPDatagramSession( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID) { - return createClientOrServer( - kModeCreateTCPDatagramSessionPassive, - &addr, - port, - NULL /* remoteHost */, - 0 /* remotePort */, - notify, - sessionID); -} - -status_t ANetworkSession::createTCPDatagramSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID) { - return createClientOrServer( - kModeCreateTCPDatagramSessionActive, - NULL /* addr */, - localPort, - remoteHost, - remotePort, - notify, - sessionID); -} - -status_t ANetworkSession::destroySession(int32_t sessionID) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mSessions.indexOfKey(sessionID); - - if (index < 0) { - return -ENOENT; - } - - mSessions.removeItemsAt(index); - - interrupt(); - - return OK; -} - -// static -status_t ANetworkSession::MakeSocketNonBlocking(int s) { - int flags = fcntl(s, F_GETFL, 0); - if (flags < 0) { - flags = 0; - } - - int res = fcntl(s, F_SETFL, flags | O_NONBLOCK); - if (res < 0) { - return -errno; - } - - return OK; -} - -status_t ANetworkSession::createClientOrServer( - Mode mode, - const struct in_addr *localAddr, - unsigned port, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID) { - Mutex::Autolock autoLock(mLock); - - *sessionID = 0; - status_t err = OK; - int s, res; - sp session; - - s = socket( - AF_INET, - (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM, - 0); - - if (s < 0) { - err = -errno; - goto bail; - } - - if (mode == kModeCreateRTSPServer - || mode == kModeCreateTCPDatagramSessionPassive) { - const int yes = 1; - res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - - if (res < 0) { - err = -errno; - goto bail2; - } - } - - if (mode == kModeCreateUDPSession) { - int size = 256 * 1024; - - res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - - if (res < 0) { - err = -errno; - goto bail2; - } - - res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - - if (res < 0) { - err = -errno; - goto bail2; - } - } else if (mode == kModeCreateTCPDatagramSessionActive) { - int flag = 1; - res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); - - if (res < 0) { - err = -errno; - goto bail2; - } - - int tos = 224; // VOICE - res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - - if (res < 0) { - err = -errno; - goto bail2; - } - } - - err = MakeSocketNonBlocking(s); - - if (err != OK) { - goto bail2; - } - - struct sockaddr_in addr; - memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); - addr.sin_family = AF_INET; - - if (mode == kModeCreateRTSPClient - || mode == kModeCreateTCPDatagramSessionActive) { - struct hostent *ent= gethostbyname(remoteHost); - if (ent == NULL) { - err = -h_errno; - goto bail2; - } - - addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - addr.sin_port = htons(remotePort); - } else if (localAddr != NULL) { - addr.sin_addr = *localAddr; - addr.sin_port = htons(port); - } else { - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(port); - } - - if (mode == kModeCreateRTSPClient - || mode == kModeCreateTCPDatagramSessionActive) { - in_addr_t x = ntohl(addr.sin_addr.s_addr); - ALOGI("connecting socket %d to %d.%d.%d.%d:%d", - s, - (x >> 24), - (x >> 16) & 0xff, - (x >> 8) & 0xff, - x & 0xff, - ntohs(addr.sin_port)); - - res = connect(s, (const struct sockaddr *)&addr, sizeof(addr)); - - CHECK_LT(res, 0); - if (errno == EINPROGRESS) { - res = 0; - } - } else { - res = bind(s, (const struct sockaddr *)&addr, sizeof(addr)); - - if (res == 0) { - if (mode == kModeCreateRTSPServer - || mode == kModeCreateTCPDatagramSessionPassive) { - res = listen(s, 4); - } else { - CHECK_EQ(mode, kModeCreateUDPSession); - - if (remoteHost != NULL) { - struct sockaddr_in remoteAddr; - memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero)); - remoteAddr.sin_family = AF_INET; - remoteAddr.sin_port = htons(remotePort); - - struct hostent *ent= gethostbyname(remoteHost); - if (ent == NULL) { - err = -h_errno; - goto bail2; - } - - remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - - res = connect( - s, - (const struct sockaddr *)&remoteAddr, - sizeof(remoteAddr)); - } - } - } - } - - if (res < 0) { - err = -errno; - goto bail2; - } - - Session::State state; - switch (mode) { - case kModeCreateRTSPClient: - state = Session::CONNECTING; - break; - - case kModeCreateTCPDatagramSessionActive: - state = Session::CONNECTING; - break; - - case kModeCreateTCPDatagramSessionPassive: - state = Session::LISTENING_TCP_DGRAMS; - break; - - case kModeCreateRTSPServer: - state = Session::LISTENING_RTSP; - break; - - default: - CHECK_EQ(mode, kModeCreateUDPSession); - state = Session::DATAGRAM; - break; - } - - session = new Session( - mNextSessionID++, - state, - s, - notify); - - if (mode == kModeCreateTCPDatagramSessionActive) { - session->setIsRTSPConnection(false); - } else if (mode == kModeCreateRTSPClient) { - session->setIsRTSPConnection(true); - } - - mSessions.add(session->sessionID(), session); - - interrupt(); - - *sessionID = session->sessionID(); - - goto bail; - -bail2: - close(s); - s = -1; - -bail: - return err; -} - -status_t ANetworkSession::connectUDPSession( - int32_t sessionID, const char *remoteHost, unsigned remotePort) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mSessions.indexOfKey(sessionID); - - if (index < 0) { - return -ENOENT; - } - - const sp session = mSessions.valueAt(index); - int s = session->socket(); - - struct sockaddr_in remoteAddr; - memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero)); - remoteAddr.sin_family = AF_INET; - remoteAddr.sin_port = htons(remotePort); - - status_t err = OK; - struct hostent *ent = gethostbyname(remoteHost); - if (ent == NULL) { - err = -h_errno; - } else { - remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - - int res = connect( - s, - (const struct sockaddr *)&remoteAddr, - sizeof(remoteAddr)); - - if (res < 0) { - err = -errno; - } - } - - return err; -} - -status_t ANetworkSession::sendRequest( - int32_t sessionID, const void *data, ssize_t size, - bool timeValid, int64_t timeUs) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mSessions.indexOfKey(sessionID); - - if (index < 0) { - return -ENOENT; - } - - const sp session = mSessions.valueAt(index); - - status_t err = session->sendRequest(data, size, timeValid, timeUs); - - interrupt(); - - return err; -} - -void ANetworkSession::interrupt() { - static const char dummy = 0; - - ssize_t n; - do { - n = write(mPipeFd[1], &dummy, 1); - } while (n < 0 && errno == EINTR); - - if (n < 0) { - ALOGW("Error writing to pipe (%s)", strerror(errno)); - } -} - -void ANetworkSession::threadLoop() { - fd_set rs, ws; - FD_ZERO(&rs); - FD_ZERO(&ws); - - FD_SET(mPipeFd[0], &rs); - int maxFd = mPipeFd[0]; - - { - Mutex::Autolock autoLock(mLock); - - for (size_t i = 0; i < mSessions.size(); ++i) { - const sp &session = mSessions.valueAt(i); - - int s = session->socket(); - - if (s < 0) { - continue; - } - - if (session->wantsToRead()) { - FD_SET(s, &rs); - if (s > maxFd) { - maxFd = s; - } - } - - if (session->wantsToWrite()) { - FD_SET(s, &ws); - if (s > maxFd) { - maxFd = s; - } - } - } - } - - int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */); - - if (res == 0) { - return; - } - - if (res < 0) { - if (errno == EINTR) { - return; - } - - ALOGE("select failed w/ error %d (%s)", errno, strerror(errno)); - return; - } - - if (FD_ISSET(mPipeFd[0], &rs)) { - char c; - ssize_t n; - do { - n = read(mPipeFd[0], &c, 1); - } while (n < 0 && errno == EINTR); - - if (n < 0) { - ALOGW("Error reading from pipe (%s)", strerror(errno)); - } - - --res; - } - - { - Mutex::Autolock autoLock(mLock); - - List > sessionsToAdd; - - for (size_t i = mSessions.size(); res > 0 && i-- > 0;) { - const sp &session = mSessions.valueAt(i); - - int s = session->socket(); - - if (s < 0) { - continue; - } - - if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) { - --res; - } - - if (FD_ISSET(s, &rs)) { - if (session->isRTSPServer() || session->isTCPDatagramServer()) { - struct sockaddr_in remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - int clientSocket = accept( - s, (struct sockaddr *)&remoteAddr, &remoteAddrLen); - - if (clientSocket >= 0) { - status_t err = MakeSocketNonBlocking(clientSocket); - - if (err != OK) { - ALOGE("Unable to make client socket non blocking, " - "failed w/ error %d (%s)", - err, strerror(-err)); - - close(clientSocket); - clientSocket = -1; - } else { - in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr); - - ALOGI("incoming connection from %d.%d.%d.%d:%d " - "(socket %d)", - (addr >> 24), - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff, - ntohs(remoteAddr.sin_port), - clientSocket); - - sp clientSession = - new Session( - mNextSessionID++, - Session::CONNECTED, - clientSocket, - session->getNotificationMessage()); - - clientSession->setIsRTSPConnection( - session->isRTSPServer()); - - sessionsToAdd.push_back(clientSession); - } - } else { - ALOGE("accept returned error %d (%s)", - errno, strerror(errno)); - } - } else { - status_t err = session->readMore(); - if (err != OK) { - ALOGE("readMore on socket %d failed w/ error %d (%s)", - s, err, strerror(-err)); - } - } - } - - if (FD_ISSET(s, &ws)) { - status_t err = session->writeMore(); - if (err != OK) { - ALOGE("writeMore on socket %d failed w/ error %d (%s)", - s, err, strerror(-err)); - } - } - } - - while (!sessionsToAdd.empty()) { - sp session = *sessionsToAdd.begin(); - sessionsToAdd.erase(sessionsToAdd.begin()); - - mSessions.add(session->sessionID(), session); - - ALOGI("added clientSession %d", session->sessionID()); - } - } -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/ANetworkSession.h b/media/libstagefright/wifi-display/ANetworkSession.h deleted file mode 100644 index 7c62b29..0000000 --- a/media/libstagefright/wifi-display/ANetworkSession.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 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 A_NETWORK_SESSION_H_ - -#define A_NETWORK_SESSION_H_ - -#include -#include -#include -#include - -#include - -namespace android { - -struct AMessage; - -// Helper class to manage a number of live sockets (datagram and stream-based) -// on a single thread. Clients are notified about activity through AMessages. -struct ANetworkSession : public RefBase { - ANetworkSession(); - - status_t start(); - status_t stop(); - - status_t createRTSPClient( - const char *host, unsigned port, const sp ¬ify, - int32_t *sessionID); - - status_t createRTSPServer( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID); - - status_t createUDPSession( - unsigned localPort, const sp ¬ify, int32_t *sessionID); - - status_t createUDPSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID); - - status_t connectUDPSession( - int32_t sessionID, const char *remoteHost, unsigned remotePort); - - // passive - status_t createTCPDatagramSession( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID); - - // active - status_t createTCPDatagramSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID); - - status_t destroySession(int32_t sessionID); - - status_t sendRequest( - int32_t sessionID, const void *data, ssize_t size = -1, - bool timeValid = false, int64_t timeUs = -1ll); - - enum NotificationReason { - kWhatError, - kWhatConnected, - kWhatClientConnected, - kWhatData, - kWhatDatagram, - kWhatBinaryData, - kWhatNetworkStall, - }; - -protected: - virtual ~ANetworkSession(); - -private: - struct NetworkThread; - struct Session; - - Mutex mLock; - sp mThread; - - int32_t mNextSessionID; - - int mPipeFd[2]; - - KeyedVector > mSessions; - - enum Mode { - kModeCreateUDPSession, - kModeCreateTCPDatagramSessionPassive, - kModeCreateTCPDatagramSessionActive, - kModeCreateRTSPServer, - kModeCreateRTSPClient, - }; - status_t createClientOrServer( - Mode mode, - const struct in_addr *addr, - unsigned port, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID); - - void threadLoop(); - void interrupt(); - - static status_t MakeSocketNonBlocking(int s); - - DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession); -}; - -} // namespace android - -#endif // A_NETWORK_SESSION_H_ diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 404b41e..c7d107e 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -3,11 +3,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - ANetworkSession.cpp \ MediaReceiver.cpp \ MediaSender.cpp \ Parameters.cpp \ - ParsedMessage.cpp \ rtp/RTPAssembler.cpp \ rtp/RTPReceiver.cpp \ rtp/RTPSender.cpp \ diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp index 364acb9..5524235 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.cpp +++ b/media/libstagefright/wifi-display/MediaReceiver.cpp @@ -20,13 +20,13 @@ #include "MediaReceiver.h" -#include "ANetworkSession.h" #include "AnotherPacketSource.h" #include "rtp/RTPReceiver.h" #include #include #include +#include #include #include diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index a230cd8..b1cdec0 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -20,7 +20,6 @@ #include "MediaSender.h" -#include "ANetworkSession.h" #include "rtp/RTPSender.h" #include "source/TSPacketizer.h" @@ -31,6 +30,7 @@ #include #include #include +#include #include namespace android { diff --git a/media/libstagefright/wifi-display/ParsedMessage.cpp b/media/libstagefright/wifi-display/ParsedMessage.cpp deleted file mode 100644 index c0e60c3..0000000 --- a/media/libstagefright/wifi-display/ParsedMessage.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 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 "ParsedMessage.h" - -#include -#include -#include - -namespace android { - -// static -sp ParsedMessage::Parse( - const char *data, size_t size, bool noMoreData, size_t *length) { - sp msg = new ParsedMessage; - ssize_t res = msg->parse(data, size, noMoreData); - - if (res < 0) { - *length = 0; - return NULL; - } - - *length = res; - return msg; -} - -ParsedMessage::ParsedMessage() { -} - -ParsedMessage::~ParsedMessage() { -} - -bool ParsedMessage::findString(const char *name, AString *value) const { - AString key = name; - key.tolower(); - - ssize_t index = mDict.indexOfKey(key); - - if (index < 0) { - value->clear(); - - return false; - } - - *value = mDict.valueAt(index); - return true; -} - -bool ParsedMessage::findInt32(const char *name, int32_t *value) const { - AString stringValue; - - if (!findString(name, &stringValue)) { - return false; - } - - char *end; - *value = strtol(stringValue.c_str(), &end, 10); - - if (end == stringValue.c_str() || *end != '\0') { - *value = 0; - return false; - } - - return true; -} - -const char *ParsedMessage::getContent() const { - return mContent.c_str(); -} - -ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) { - if (size == 0) { - return -1; - } - - ssize_t lastDictIndex = -1; - - size_t offset = 0; - while (offset < size) { - size_t lineEndOffset = offset; - while (lineEndOffset + 1 < size - && (data[lineEndOffset] != '\r' - || data[lineEndOffset + 1] != '\n')) { - ++lineEndOffset; - } - - if (lineEndOffset + 1 >= size) { - return -1; - } - - AString line(&data[offset], lineEndOffset - offset); - - if (offset == 0) { - // Special handling for the request/status line. - - mDict.add(AString("_"), line); - offset = lineEndOffset + 2; - - continue; - } - - if (lineEndOffset == offset) { - offset += 2; - break; - } - - if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') { - // Support for folded header values. - - if (lastDictIndex >= 0) { - // Otherwise it's malformed since the first header line - // cannot continue anything... - - AString &value = mDict.editValueAt(lastDictIndex); - value.append(line); - } - - offset = lineEndOffset + 2; - continue; - } - - ssize_t colonPos = line.find(":"); - if (colonPos >= 0) { - AString key(line, 0, colonPos); - key.trim(); - key.tolower(); - - line.erase(0, colonPos + 1); - - lastDictIndex = mDict.add(key, line); - } - - offset = lineEndOffset + 2; - } - - for (size_t i = 0; i < mDict.size(); ++i) { - mDict.editValueAt(i).trim(); - } - - // Found the end of headers. - - int32_t contentLength; - if (!findInt32("content-length", &contentLength) || contentLength < 0) { - contentLength = 0; - } - - size_t totalLength = offset + contentLength; - - if (size < totalLength) { - return -1; - } - - mContent.setTo(&data[offset], contentLength); - - return totalLength; -} - -void ParsedMessage::getRequestField(size_t index, AString *field) const { - AString line; - CHECK(findString("_", &line)); - - size_t prevOffset = 0; - size_t offset = 0; - for (size_t i = 0; i <= index; ++i) { - ssize_t spacePos = line.find(" ", offset); - - if (spacePos < 0) { - spacePos = line.size(); - } - - prevOffset = offset; - offset = spacePos + 1; - } - - field->setTo(line, prevOffset, offset - prevOffset - 1); -} - -bool ParsedMessage::getStatusCode(int32_t *statusCode) const { - AString statusCodeString; - getRequestField(1, &statusCodeString); - - char *end; - *statusCode = strtol(statusCodeString.c_str(), &end, 10); - - if (*end != '\0' || end == statusCodeString.c_str() - || (*statusCode) < 100 || (*statusCode) > 999) { - *statusCode = 0; - return false; - } - - return true; -} - -AString ParsedMessage::debugString() const { - AString line; - CHECK(findString("_", &line)); - - line.append("\n"); - - for (size_t i = 0; i < mDict.size(); ++i) { - const AString &key = mDict.keyAt(i); - const AString &value = mDict.valueAt(i); - - if (key == AString("_")) { - continue; - } - - line.append(key); - line.append(": "); - line.append(value); - line.append("\n"); - } - - line.append("\n"); - line.append(mContent); - - return line; -} - -// static -bool ParsedMessage::GetAttribute( - const char *s, const char *key, AString *value) { - value->clear(); - - size_t keyLen = strlen(key); - - for (;;) { - while (isspace(*s)) { - ++s; - } - - const char *colonPos = strchr(s, ';'); - - size_t len = - (colonPos == NULL) ? strlen(s) : colonPos - s; - - if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { - value->setTo(&s[keyLen + 1], len - keyLen - 1); - return true; - } - - if (colonPos == NULL) { - return false; - } - - s = colonPos + 1; - } -} - -// static -bool ParsedMessage::GetInt32Attribute( - const char *s, const char *key, int32_t *value) { - AString stringValue; - if (!GetAttribute(s, key, &stringValue)) { - *value = 0; - return false; - } - - char *end; - *value = strtol(stringValue.c_str(), &end, 10); - - if (end == stringValue.c_str() || *end != '\0') { - *value = 0; - return false; - } - - return true; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/ParsedMessage.h b/media/libstagefright/wifi-display/ParsedMessage.h deleted file mode 100644 index e9a1859..0000000 --- a/media/libstagefright/wifi-display/ParsedMessage.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 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 -#include -#include -#include - -namespace android { - -// Encapsulates an "HTTP/RTSP style" response, i.e. a status line, -// key/value pairs making up the headers and an optional body/content. -struct ParsedMessage : public RefBase { - static sp Parse( - const char *data, size_t size, bool noMoreData, size_t *length); - - bool findString(const char *name, AString *value) const; - bool findInt32(const char *name, int32_t *value) const; - - const char *getContent() const; - - void getRequestField(size_t index, AString *field) const; - bool getStatusCode(int32_t *statusCode) const; - - AString debugString() const; - - static bool GetAttribute(const char *s, const char *key, AString *value); - - static bool GetInt32Attribute( - const char *s, const char *key, int32_t *value); - - -protected: - virtual ~ParsedMessage(); - -private: - KeyedVector mDict; - AString mContent; - - ParsedMessage(); - - ssize_t parse(const char *data, size_t size, bool noMoreData); - - DISALLOW_EVIL_CONSTRUCTORS(ParsedMessage); -}; - -} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp index cb429bc..0f4d93a 100644 --- a/media/libstagefright/wifi-display/TimeSyncer.cpp +++ b/media/libstagefright/wifi-display/TimeSyncer.cpp @@ -20,13 +20,12 @@ #include "TimeSyncer.h" -#include "ANetworkSession.h" - #include #include #include #include #include +#include #include namespace android { diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp index 0779bf5..73c0d80 100644 --- a/media/libstagefright/wifi-display/nettest.cpp +++ b/media/libstagefright/wifi-display/nettest.cpp @@ -18,7 +18,6 @@ #define LOG_TAG "nettest" #include -#include "ANetworkSession.h" #include "TimeSyncer.h" #include @@ -27,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp index 2d22e79..3b3bd63 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -21,11 +21,10 @@ #include "RTPAssembler.h" #include "RTPReceiver.h" -#include "ANetworkSession.h" - #include #include #include +#include #include #include #include diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 6bbe650..1887b8b 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -20,11 +20,10 @@ #include "RTPSender.h" -#include "ANetworkSession.h" - #include #include #include +#include #include #include #include diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp index 764a38b..b902f29 100644 --- a/media/libstagefright/wifi-display/rtptest.cpp +++ b/media/libstagefright/wifi-display/rtptest.cpp @@ -18,7 +18,6 @@ #define LOG_TAG "rtptest" #include -#include "ANetworkSession.h" #include "rtp/RTPSender.h" #include "rtp/RTPReceiver.h" #include "TimeSyncer.h" @@ -29,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 5db2099..bc88f1e 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -22,13 +22,13 @@ #include "DirectRenderer.h" #include "MediaReceiver.h" -#include "ParsedMessage.h" #include "TimeSyncer.h" #include #include #include #include +#include #include #include diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index adb9d89..dc1fc32 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -18,12 +18,11 @@ #define WIFI_DISPLAY_SINK_H_ -#include "ANetworkSession.h" - #include "VideoFormats.h" #include #include +#include namespace android { diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp index 189bea3..7e8891d 100644 --- a/media/libstagefright/wifi-display/source/MediaPuller.cpp +++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp @@ -93,6 +93,9 @@ void MediaPuller::onMessageReceived(const sp &msg) { err = mSource->start(params.get()); } else { err = mSource->start(); + if (err != OK) { + ALOGE("source failed to start w/ err %d", err); + } } if (err == OK) { diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index b421b35..4b59e62 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -21,7 +21,6 @@ #include "WifiDisplaySource.h" #include "PlaybackSession.h" #include "Parameters.h" -#include "ParsedMessage.h" #include "rtp/RTPSender.h" #include "TimeSyncer.h" @@ -33,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 64186fc..4f11712 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -18,10 +18,10 @@ #define WIFI_DISPLAY_SOURCE_H_ -#include "ANetworkSession.h" #include "VideoFormats.h" #include +#include #include diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp index 111846d..61eb9f9 100644 --- a/media/libstagefright/wifi-display/udptest.cpp +++ b/media/libstagefright/wifi-display/udptest.cpp @@ -18,11 +18,11 @@ #define LOG_TAG "udptest" #include -#include "ANetworkSession.h" #include "TimeSyncer.h" #include #include +#include namespace android { diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 9fee4d0..4607606 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -175,7 +175,8 @@ static void createSource(const AString &addr, int32_t port) { iface.append(StringPrintf(":%d", port).c_str()); sp client = new RemoteDisplayClient; - sp display = service->listenForRemoteDisplay(client, iface); + sp display = + service->listenForRemoteDisplay(client, iface); client->waitUntilDone(); -- cgit v1.1 From 4182c4e2a07e2441fcd5c22eaff0ddfe7f826f61 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 15 Jul 2013 14:45:07 -0700 Subject: Use AudioSystem::setLowRamDevice() to configure memory Bug: 9798886 Change-Id: I9321e3f369f1ed9429ae222e3926ebdeb012b8b0 --- media/libmedia/AudioSystem.cpp | 7 +++++++ media/libmedia/IAudioFlinger.cpp | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 6b9b3be..0d59af0 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -772,6 +772,13 @@ size_t AudioSystem::getPrimaryOutputFrameCount() return af->getPrimaryOutputFrameCount(); } +status_t AudioSystem::setLowRamDevice(bool isLowRamDevice) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setLowRamDevice(isLowRamDevice); +} + void AudioSystem::clearAudioConfigCache() { Mutex::Autolock _l(gLock); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 6bb7df6..2e2c0cc 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -73,6 +73,7 @@ enum { LOAD_HW_MODULE, GET_PRIMARY_OUTPUT_SAMPLING_RATE, GET_PRIMARY_OUTPUT_FRAME_COUNT, + SET_LOW_RAM_DEVICE, }; class BpAudioFlinger : public BpInterface @@ -698,6 +699,15 @@ public: return reply.readInt32(); } + virtual status_t setLowRamDevice(bool isLowRamDevice) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32((int) isLowRamDevice); + remote()->transact(SET_LOW_RAM_DEVICE, data, &reply); + return reply.readInt32(); + } + }; IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger"); @@ -1059,6 +1069,12 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32(getPrimaryOutputFrameCount()); return NO_ERROR; } break; + case SET_LOW_RAM_DEVICE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + bool isLowRamDevice = data.readInt32() != 0; + reply->writeInt32(setLowRamDevice(isLowRamDevice)); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } -- cgit v1.1 From a05822a368dfc8c220b413c3d23dcc1af58b4b5f Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 19 Jul 2013 11:32:07 -0700 Subject: Minor tweaks to DirectRenderer and Converter Converter now supports automatic prepending of SPS/PPS to IDR frames (h264) as well as using the encoder in "surface-input" mode. The new features are all opt-in and should not affect existing clients. Change-Id: I543cf1d31ba068c1a01ab4e6814ac8d817b63faa --- .../wifi-display/sink/DirectRenderer.cpp | 50 ++++++-- .../wifi-display/sink/DirectRenderer.h | 7 ++ .../wifi-display/source/Converter.cpp | 139 +++++++++++++++++---- .../libstagefright/wifi-display/source/Converter.h | 63 ++++++---- .../wifi-display/source/PlaybackSession.cpp | 10 +- 5 files changed, 209 insertions(+), 60 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index 15f9c88..cdb2267 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -29,9 +29,8 @@ #include #include #include +#include #include -#include -#include namespace android { @@ -488,12 +487,38 @@ void DirectRenderer::onMessageReceived(const sp &msg) { break; } + case kWhatQueueAccessUnit: + onQueueAccessUnit(msg); + break; + + case kWhatSetFormat: + onSetFormat(msg); + break; + default: TRESPASS(); } } void DirectRenderer::setFormat(size_t trackIndex, const sp &format) { + sp msg = new AMessage(kWhatSetFormat, id()); + msg->setSize("trackIndex", trackIndex); + msg->setMessage("format", format); + msg->post(); +} + +void DirectRenderer::onSetFormat(const sp &msg) { + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + sp format; + CHECK(msg->findMessage("format", &format)); + + internalSetFormat(trackIndex, format); +} + +void DirectRenderer::internalSetFormat( + size_t trackIndex, const sp &format) { CHECK_LT(trackIndex, 2u); CHECK(mDecoderContext[trackIndex] == NULL); @@ -517,18 +542,21 @@ void DirectRenderer::setFormat(size_t trackIndex, const sp &format) { void DirectRenderer::queueAccessUnit( size_t trackIndex, const sp &accessUnit) { - CHECK_LT(trackIndex, 2u); + sp msg = new AMessage(kWhatQueueAccessUnit, id()); + msg->setSize("trackIndex", trackIndex); + msg->setBuffer("accessUnit", accessUnit); + msg->post(); +} - if (mDecoderContext[trackIndex] == NULL) { - CHECK_EQ(trackIndex, 0u); +void DirectRenderer::onQueueAccessUnit(const sp &msg) { + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); - sp format = new AMessage; - format->setString("mime", "video/avc"); - format->setInt32("width", 640); - format->setInt32("height", 360); + sp accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); - setFormat(trackIndex, format); - } + CHECK_LT(trackIndex, 2u); + CHECK(mDecoderContext[trackIndex] != NULL); mDecoderContext[trackIndex]->queueInputBuffer(accessUnit); } diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h index 1e7dc34..07c2170 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -43,6 +43,8 @@ private: enum { kWhatDecoderNotify, kWhatRenderVideo, + kWhatQueueAccessUnit, + kWhatSetFormat, }; struct OutputInfo { @@ -72,6 +74,11 @@ private: void scheduleVideoRenderIfNecessary(); void onRenderVideo(); + void onSetFormat(const sp &msg); + void onQueueAccessUnit(const sp &msg); + + void internalSetFormat(size_t trackIndex, const sp &format); + DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); }; diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 0214520..6f23854 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -21,6 +21,7 @@ #include "Converter.h" #include "MediaPuller.h" +#include "include/avc_utils.h" #include #include @@ -33,6 +34,8 @@ #include #include +#include + #include namespace android { @@ -40,12 +43,14 @@ namespace android { Converter::Converter( const sp ¬ify, const sp &codecLooper, - const sp &outputFormat) - : mInitCheck(NO_INIT), - mNotify(notify), + const sp &outputFormat, + uint32_t flags) + : mNotify(notify), mCodecLooper(codecLooper), mOutputFormat(outputFormat), + mFlags(flags), mIsVideo(false), + mIsH264(false), mIsPCMAudio(false), mNeedToManuallyPrependSPSPPS(false), mDoMoreWorkPending(false) @@ -55,21 +60,18 @@ Converter::Converter( #endif ,mPrevVideoBitrate(-1) ,mNumFramesToDrop(0) + ,mEncodingSuspended(false) { AString mime; CHECK(mOutputFormat->findString("mime", &mime)); if (!strncasecmp("video/", mime.c_str(), 6)) { mIsVideo = true; + + mIsH264 = !strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC); } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime.c_str())) { mIsPCMAudio = true; } - - mInitCheck = initEncoder(); - - if (mInitCheck != OK) { - releaseEncoder(); - } } static void ReleaseMediaBufferReference(const sp &accessUnit) { @@ -118,8 +120,19 @@ void Converter::shutdownAsync() { (new AMessage(kWhatShutdown, id()))->post(); } -status_t Converter::initCheck() const { - return mInitCheck; +status_t Converter::init() { + status_t err = initEncoder(); + + if (err != OK) { + releaseEncoder(); + } + + return err; +} + +sp Converter::getGraphicBufferProducer() { + CHECK(mFlags & FLAG_USE_SURFACE_INPUT); + return mGraphicBufferProducer; } size_t Converter::getInputBufferCount() const { @@ -244,6 +257,16 @@ status_t Converter::initEncoder() { return err; } + if (mFlags & FLAG_USE_SURFACE_INPUT) { + CHECK(mIsVideo); + + err = mEncoder->createInputSurface(&mGraphicBufferProducer); + + if (err != OK) { + return err; + } + } + err = mEncoder->start(); if (err != OK) { @@ -256,7 +279,17 @@ status_t Converter::initEncoder() { return err; } - return mEncoder->getOutputBuffers(&mEncoderOutputBuffers); + err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers); + + if (err != OK) { + return err; + } + + if (mFlags & FLAG_USE_SURFACE_INPUT) { + scheduleDoMoreWork(); + } + + return OK; } void Converter::notifyError(status_t err) { @@ -312,9 +345,12 @@ void Converter::onMessageReceived(const sp &msg) { sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); - if (mIsVideo && mNumFramesToDrop) { - --mNumFramesToDrop; - ALOGI("dropping frame."); + if (mNumFramesToDrop > 0 || mEncodingSuspended) { + if (mNumFramesToDrop > 0) { + --mNumFramesToDrop; + ALOGI("dropping frame."); + } + ReleaseMediaBufferReference(accessUnit); break; } @@ -396,7 +432,7 @@ void Converter::onMessageReceived(const sp &msg) { } if (mIsVideo) { - ALOGI("requesting IDR frame"); + ALOGV("requesting IDR frame"); mEncoder->requestIDRFrame(); } break; @@ -411,6 +447,10 @@ void Converter::onMessageReceived(const sp &msg) { AString mime; CHECK(mOutputFormat->findString("mime", &mime)); ALOGI("encoder (%s) shut down.", mime.c_str()); + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatShutdownCompleted); + notify->post(); break; } @@ -431,6 +471,21 @@ void Converter::onMessageReceived(const sp &msg) { break; } + case kWhatSuspendEncoding: + { + int32_t suspend; + CHECK(msg->findInt32("suspend", &suspend)); + + mEncodingSuspended = suspend; + + if (mFlags & FLAG_USE_SURFACE_INPUT) { + sp params = new AMessage; + params->setInt32("drop-input-frames",suspend); + mEncoder->setParameters(params); + } + break; + } + default: TRESPASS(); } @@ -616,22 +671,39 @@ status_t Converter::feedEncoderInputBuffers() { return OK; } +sp Converter::prependCSD(const sp &accessUnit) const { + CHECK(mCSD0 != NULL); + + sp dup = new ABuffer(accessUnit->size() + mCSD0->size()); + memcpy(dup->data(), mCSD0->data(), mCSD0->size()); + memcpy(dup->data() + mCSD0->size(), accessUnit->data(), accessUnit->size()); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + dup->meta()->setInt64("timeUs", timeUs); + + return dup; +} + status_t Converter::doMoreWork() { status_t err; - for (;;) { - size_t bufferIndex; - err = mEncoder->dequeueInputBuffer(&bufferIndex); + if (!(mFlags & FLAG_USE_SURFACE_INPUT)) { + for (;;) { + size_t bufferIndex; + err = mEncoder->dequeueInputBuffer(&bufferIndex); - if (err != OK) { - break; + if (err != OK) { + break; + } + + mAvailEncoderInputIndices.push_back(bufferIndex); } - mAvailEncoderInputIndices.push_back(bufferIndex); + feedEncoderInputBuffers(); } - feedEncoderInputBuffers(); - for (;;) { size_t bufferIndex; size_t offset; @@ -705,9 +777,19 @@ status_t Converter::doMoreWork() { if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) { if (!handle) { + if (mIsH264) { + mCSD0 = buffer; + } mOutputFormat->setBuffer("csd-0", buffer); } } else { + if (mNeedToManuallyPrependSPSPPS + && mIsH264 + && (mFlags & FLAG_PREPEND_CSD_IF_NECESSARY) + && IsIDR(buffer)) { + buffer = prependCSD(buffer); + } + sp notify = mNotify->dup(); notify->setInt32("what", kWhatAccessUnit); notify->setBuffer("accessUnit", buffer); @@ -732,9 +814,18 @@ void Converter::requestIDRFrame() { } void Converter::dropAFrame() { + // Unsupported in surface input mode. + CHECK(!(mFlags & FLAG_USE_SURFACE_INPUT)); + (new AMessage(kWhatDropAFrame, id()))->post(); } +void Converter::suspendEncoding(bool suspend) { + sp msg = new AMessage(kWhatSuspendEncoding, id()); + msg->setInt32("suspend", suspend); + msg->post(); +} + int32_t Converter::getVideoBitrate() const { return mPrevVideoBitrate; } diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 76c8b19..5876e07 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -18,13 +18,12 @@ #define CONVERTER_H_ -#include "WifiDisplaySource.h" - #include namespace android { struct ABuffer; +struct IGraphicBufferProducer; struct MediaCodec; #define ENABLE_SILENCE_DETECTION 0 @@ -33,11 +32,25 @@ struct MediaCodec; // media access unit of a different format. // Right now this'll convert raw video into H.264 and raw audio into AAC. struct Converter : public AHandler { + enum { + kWhatAccessUnit, + kWhatEOS, + kWhatError, + kWhatShutdownCompleted, + }; + + enum FlagBits { + FLAG_USE_SURFACE_INPUT = 1, + FLAG_PREPEND_CSD_IF_NECESSARY = 2, + }; Converter(const sp ¬ify, const sp &codecLooper, - const sp &outputFormat); + const sp &outputFormat, + uint32_t flags = 0); - status_t initCheck() const; + status_t init(); + + sp getGraphicBufferProducer(); size_t getInputBufferCount() const; @@ -50,22 +63,7 @@ struct Converter : public AHandler { void requestIDRFrame(); void dropAFrame(); - - enum { - kWhatAccessUnit, - kWhatEOS, - kWhatError, - }; - - enum { - kWhatDoMoreWork, - kWhatRequestIDRFrame, - kWhatShutdown, - kWhatMediaPullerNotify, - kWhatEncoderActivity, - kWhatDropAFrame, - kWhatReleaseOutputBuffer, - }; + void suspendEncoding(bool suspend); void shutdownAsync(); @@ -74,22 +72,40 @@ struct Converter : public AHandler { static int32_t GetInt32Property(const char *propName, int32_t defaultValue); + enum { + // MUST not conflict with private enums below. + kWhatMediaPullerNotify = 'pulN', + }; + protected: virtual ~Converter(); virtual void onMessageReceived(const sp &msg); private: - status_t mInitCheck; + enum { + kWhatDoMoreWork, + kWhatRequestIDRFrame, + kWhatSuspendEncoding, + kWhatShutdown, + kWhatEncoderActivity, + kWhatDropAFrame, + kWhatReleaseOutputBuffer, + }; + sp mNotify; sp mCodecLooper; sp mOutputFormat; + uint32_t mFlags; bool mIsVideo; + bool mIsH264; bool mIsPCMAudio; bool mNeedToManuallyPrependSPSPPS; sp mEncoder; sp mEncoderActivityNotify; + sp mGraphicBufferProducer; + Vector > mEncoderInputBuffers; Vector > mEncoderOutputBuffers; @@ -97,6 +113,8 @@ private: List > mInputBufferQueue; + sp mCSD0; + bool mDoMoreWorkPending; #if ENABLE_SILENCE_DETECTION @@ -109,6 +127,7 @@ private: int32_t mPrevVideoBitrate; int32_t mNumFramesToDrop; + bool mEncodingSuspended; status_t initEncoder(); void releaseEncoder(); @@ -127,6 +146,8 @@ private: static bool IsSilence(const sp &accessUnit); + sp prependCSD(const sp &accessUnit) const; + DISALLOW_EVIL_CONSTRUCTORS(Converter); }; diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index a15fbac..0aa4ee5 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -521,7 +521,7 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( if (mTracks.isEmpty()) { ALOGI("Reached EOS"); } - } else { + } else if (what != Converter::kWhatShutdownCompleted) { CHECK_EQ(what, Converter::kWhatError); status_t err; @@ -957,14 +957,16 @@ status_t WifiDisplaySource::PlaybackSession::addSource( sp converter = new Converter(notify, codecLooper, format); - err = converter->initCheck(); + looper()->registerHandler(converter); + + err = converter->init(); if (err != OK) { ALOGE("%s converter returned err %d", isVideo ? "video" : "audio", err); + + looper()->unregisterHandler(converter->id()); return err; } - looper()->registerHandler(converter); - notify = new AMessage(Converter::kWhatMediaPullerNotify, converter->id()); notify->setSize("trackIndex", trackIndex); -- cgit v1.1 From 336da16a12423c496efba6ca9813d5d42a1f70f6 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 19 Jul 2013 11:00:43 -0700 Subject: Enable support for explicitly requesting an encoded keyframe from the vp8 encoder. Change-Id: I370d5831f7d6037faf361a92521390f19f179cbe --- .../codecs/on2/enc/SoftVPXEncoder.cpp | 33 ++++++++++++++++++++-- .../libstagefright/codecs/on2/enc/SoftVPXEncoder.h | 5 ++++ 2 files changed, 36 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index d8456fe..5f2b5c8 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -149,7 +149,8 @@ SoftVPXEncoder::SoftVPXEncoder(const char *name, mLevel(OMX_VIDEO_VP8Level_Version0), mConversionBuffer(NULL), mInputDataIsMeta(false), - mGrallocModule(NULL) { + mGrallocModule(NULL), + mKeyFrameRequested(false) { initPorts(); } @@ -519,6 +520,27 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, } } +OMX_ERRORTYPE SoftVPXEncoder::setConfig( + OMX_INDEXTYPE index, const OMX_PTR _params) { + switch (index) { + case OMX_IndexConfigVideoIntraVOPRefresh: + { + OMX_CONFIG_INTRAREFRESHVOPTYPE *params = + (OMX_CONFIG_INTRAREFRESHVOPTYPE *)_params; + + if (params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + mKeyFrameRequested = params->IntraRefreshVOP; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::setConfig(index, _params); + } +} + OMX_ERRORTYPE SoftVPXEncoder::internalSetProfileLevel( const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel) { if (profileAndLevel->nPortIndex != kOutputPortIndex) { @@ -750,12 +772,19 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { vpx_image_t raw_frame; vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight, kInputBufferAlignment, source); + + vpx_enc_frame_flags_t flags = 0; + if (mKeyFrameRequested) { + flags |= VPX_EFLAG_FORCE_KF; + mKeyFrameRequested = false; + } + codec_return = vpx_codec_encode( mCodecContext, &raw_frame, inputBufferHeader->nTimeStamp, // in timebase units mFrameDurationUs, // frame duration in timebase units - 0, // frame flags + flags, // frame flags VPX_DL_REALTIME); // encoding deadline if (codec_return != VPX_CODEC_OK) { ALOGE("vpx encoder failed to encode frame"); diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index d570154..4ee5e51 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -78,6 +78,9 @@ protected: virtual OMX_ERRORTYPE internalSetParameter( OMX_INDEXTYPE index, const OMX_PTR param); + virtual OMX_ERRORTYPE setConfig( + OMX_INDEXTYPE index, const OMX_PTR params); + // OMX callback when buffers available // Note that both an input and output buffer // is expected to be available to carry out @@ -163,6 +166,8 @@ private: bool mInputDataIsMeta; const hw_module_t *mGrallocModule; + bool mKeyFrameRequested; + // Initializes input and output OMX ports with sensible // default values. void initPorts(); -- cgit v1.1 From 5908f88a7e45380a9b0d71a3b1ea535d76c420b3 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Mon, 15 Jul 2013 21:17:03 -0700 Subject: Add routing sockets for the requesting user Mediaserver sockets are now routed as if the connection was in the requesting app in per user routing. Change-Id: I60f4649c3c4145a65264b54c1aa2c6c7741efaba --- media/libstagefright/Android.mk | 2 ++ media/libstagefright/HTTPBase.cpp | 12 ++++++++++++ media/libstagefright/include/HTTPBase.h | 3 +++ media/libstagefright/rtsp/ARTSPConnection.cpp | 6 ++++++ media/libstagefright/rtsp/MyHandler.h | 6 ++++++ 5 files changed, 29 insertions(+) (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 9544dbc..90bf324 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -62,6 +62,7 @@ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/include/media/stagefright/timedtext \ $(TOP)/frameworks/native/include/media/hardware \ $(TOP)/frameworks/native/include/media/openmax \ + $(TOP)/frameworks/native/services/connectivitymanager \ $(TOP)/external/flac/include \ $(TOP)/external/tremolo \ $(TOP)/external/openssl/include \ @@ -69,6 +70,7 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcamera_client \ + libconnectivitymanager \ libcutils \ libdl \ libdrmframework \ diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp index d2cc6c2..5fa4b6f 100644 --- a/media/libstagefright/HTTPBase.cpp +++ b/media/libstagefright/HTTPBase.cpp @@ -30,6 +30,8 @@ #include #include +#include + namespace android { HTTPBase::HTTPBase() @@ -164,4 +166,14 @@ void HTTPBase::UnRegisterSocketUserTag(int sockfd) { } } +// static +void HTTPBase::RegisterSocketUserMark(int sockfd, uid_t uid) { + ConnectivityManager::markSocketAsUser(sockfd, uid); +} + +// static +void HTTPBase::UnRegisterSocketUserMark(int sockfd) { + RegisterSocketUserMark(sockfd, geteuid()); +} + } // namespace android diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h index c2dc351..d4b7f9f 100644 --- a/media/libstagefright/include/HTTPBase.h +++ b/media/libstagefright/include/HTTPBase.h @@ -59,6 +59,9 @@ struct HTTPBase : public DataSource { static void RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag); static void UnRegisterSocketUserTag(int sockfd); + static void RegisterSocketUserMark(int sockfd, uid_t uid); + static void UnRegisterSocketUserMark(int sockfd); + protected: void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 3068541..906aef3 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -60,6 +60,7 @@ ARTSPConnection::~ARTSPConnection() { ALOGE("Connection is still open, closing the socket."); if (mUIDValid) { HTTPBase::UnRegisterSocketUserTag(mSocket); + HTTPBase::UnRegisterSocketUserMark(mSocket); } close(mSocket); mSocket = -1; @@ -214,6 +215,7 @@ void ARTSPConnection::onConnect(const sp &msg) { if (mState != DISCONNECTED) { if (mUIDValid) { HTTPBase::UnRegisterSocketUserTag(mSocket); + HTTPBase::UnRegisterSocketUserMark(mSocket); } close(mSocket); mSocket = -1; @@ -266,6 +268,7 @@ void ARTSPConnection::onConnect(const sp &msg) { if (mUIDValid) { HTTPBase::RegisterSocketUserTag(mSocket, mUID, (uint32_t)*(uint32_t*) "RTSP"); + HTTPBase::RegisterSocketUserMark(mSocket, mUID); } MakeSocketBlocking(mSocket, false); @@ -295,6 +298,7 @@ void ARTSPConnection::onConnect(const sp &msg) { if (mUIDValid) { HTTPBase::UnRegisterSocketUserTag(mSocket); + HTTPBase::UnRegisterSocketUserMark(mSocket); } close(mSocket); mSocket = -1; @@ -312,6 +316,7 @@ void ARTSPConnection::onConnect(const sp &msg) { void ARTSPConnection::performDisconnect() { if (mUIDValid) { HTTPBase::UnRegisterSocketUserTag(mSocket); + HTTPBase::UnRegisterSocketUserMark(mSocket); } close(mSocket); mSocket = -1; @@ -385,6 +390,7 @@ void ARTSPConnection::onCompleteConnection(const sp &msg) { mState = DISCONNECTED; if (mUIDValid) { HTTPBase::UnRegisterSocketUserTag(mSocket); + HTTPBase::UnRegisterSocketUserMark(mSocket); } close(mSocket); mSocket = -1; diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index e51d9e3..5e9ace2 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -712,7 +712,9 @@ struct MyHandler : public AHandler { // Clear the tag if (mUIDValid) { HTTPBase::UnRegisterSocketUserTag(track->mRTPSocket); + HTTPBase::UnRegisterSocketUserMark(track->mRTPSocket); HTTPBase::UnRegisterSocketUserTag(track->mRTCPSocket); + HTTPBase::UnRegisterSocketUserMark(track->mRTCPSocket); } close(track->mRTPSocket); @@ -843,7 +845,9 @@ struct MyHandler : public AHandler { // Clear the tag if (mUIDValid) { HTTPBase::UnRegisterSocketUserTag(info->mRTPSocket); + HTTPBase::UnRegisterSocketUserMark(info->mRTPSocket); HTTPBase::UnRegisterSocketUserTag(info->mRTCPSocket); + HTTPBase::UnRegisterSocketUserMark(info->mRTPCSocket); } close(info->mRTPSocket); @@ -1599,6 +1603,8 @@ private: (uint32_t)*(uint32_t*) "RTP_"); HTTPBase::RegisterSocketUserTag(info->mRTCPSocket, mUID, (uint32_t)*(uint32_t*) "RTP_"); + HTTPBase::RegisterSocketUserMark(info->mRTPSocket, mUID); + HTTPBase::RegisterSocketUserMark(info->mRTCPSocket, mUID); } request.append("Transport: RTP/AVP/UDP;unicast;client_port="); -- cgit v1.1 From 59d3f809024ae5b5a7ea35dcfdd056f1c7ca42b2 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Tue, 23 Jul 2013 11:09:19 -0700 Subject: Fix typo in socket name Change-Id: I29171368f1b69333ef7eae53ada2fab94e3e28b9 --- media/libstagefright/rtsp/MyHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 5e9ace2..946f602 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -847,7 +847,7 @@ struct MyHandler : public AHandler { HTTPBase::UnRegisterSocketUserTag(info->mRTPSocket); HTTPBase::UnRegisterSocketUserMark(info->mRTPSocket); HTTPBase::UnRegisterSocketUserTag(info->mRTCPSocket); - HTTPBase::UnRegisterSocketUserMark(info->mRTPCSocket); + HTTPBase::UnRegisterSocketUserMark(info->mRTCPSocket); } close(info->mRTPSocket); -- cgit v1.1 From ba812e3b3ca0a0c9459fe29bbc211c9a73313b8b Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 23 Jul 2013 13:05:29 -0700 Subject: Don't abort on unusual state transition The state transition check was too strict, and we were crashing mediaserver inappropriately. Bug 9819944 Change-Id: I1482ed1cfee37088d4893ee81cf1b2b950d2e930 --- media/libstagefright/omx/GraphicBufferSource.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index bbd71be..d6fd95b 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -130,10 +130,12 @@ void GraphicBufferSource::omxExecuting() { void GraphicBufferSource::omxLoaded(){ Mutex::Autolock autoLock(mMutex); - ALOGV("--> loaded"); - CHECK(mExecuting); + if (!mExecuting) { + // This can happen if something failed very early. + ALOGW("Dropped back down to Loaded without Executing"); + } - ALOGV("Dropped down to loaded, avail=%d eos=%d eosSent=%d", + ALOGV("--> loaded; avail=%d eos=%d eosSent=%d", mNumFramesAvailable, mEndOfStream, mEndOfStreamSent); // Codec is no longer executing. Discard all codec-related state. -- cgit v1.1 From 291bb6d8947c5b0c062f0895d623c529259bfa39 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 16 Jul 2013 17:23:39 -0700 Subject: AudioRecord and HAL input stream must be 16-bit PCM only Currently there are 16-bit PCM assumptions in several places for capture: - resampler API - mRsmpInBuffer and mRsmpOutBuffer - RecordThread::threadLoop upmix, downmix, and resampling - possibly other places Until those assumptions are removed, this CL enforces 16-bit PCM in both client and server at all places where a format is checked. Change-Id: I08b0570bff626ad0d341804825a72c14e61b4233 --- media/libmedia/AudioRecord.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 8ae0908..603c16e 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -190,6 +190,11 @@ status_t AudioRecord::set( ALOGE("Invalid format %d", format); return BAD_VALUE; } + // Temporary restriction: AudioFlinger currently supports 16-bit PCM only + if (format != AUDIO_FORMAT_PCM_16_BIT) { + ALOGE("Format %d is not supported", format); + return BAD_VALUE; + } mFormat = format; if (!audio_is_input_channel(channelMask)) { -- cgit v1.1 From 92cb8f928dc9e237c356c942d10b5c0c1e04b2ae Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 24 Jul 2013 16:35:12 -0700 Subject: Update error message The color format used for surfaces has two different names. The one in the error message is the "native" name, which doesn't mean anything to external developers. Change-Id: Ic0561f4ad12970b0e0a60bd17b4e3997af1a9f0e --- media/libstagefright/omx/OMXNodeInstance.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 61a866f..525e18d 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -596,7 +596,8 @@ status_t OMXNodeInstance::createInputSurface( CHECK(oerr == OMX_ErrorNone); if (def.format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque) { - ALOGE("createInputSurface requires AndroidOpaque color format"); + ALOGE("createInputSurface requires COLOR_FormatSurface " + "(AndroidOpaque) color format"); return INVALID_OPERATION; } -- cgit v1.1 From fad226abd12435dbcd232f7de396f1a097b2bd5f Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 16 Jul 2013 17:19:58 -0700 Subject: Use standard name and type for channel mask Former name 'channels' was ambiguous with respect to channel count. Change-Id: I716f792d95a7e0c787d27514ad6e93dbcef8a415 --- media/libmedia/AudioSystem.cpp | 8 ++++---- media/libmedia/IAudioFlingerClient.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 0d59af0..a571fe4 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -447,9 +447,9 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle OutputDescriptor *outputDesc = new OutputDescriptor(*desc); gOutputs.add(ioHandle, outputDesc); - ALOGV("ioConfigChanged() new output samplingRate %u, format %d channels %#x frameCount %u " + ALOGV("ioConfigChanged() new output samplingRate %u, format %d channel mask %#x frameCount %u " "latency %d", - outputDesc->samplingRate, outputDesc->format, outputDesc->channels, + outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask, outputDesc->frameCount, outputDesc->latency); } break; case OUTPUT_CLOSED: { @@ -471,10 +471,10 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle if (param2 == NULL) break; desc = (const OutputDescriptor *)param2; - ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %d channels %#x " + ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %d channel mask %#x " "frameCount %d latency %d", ioHandle, desc->samplingRate, desc->format, - desc->channels, desc->frameCount, desc->latency); + desc->channelMask, desc->frameCount, desc->latency); OutputDescriptor *outputDesc = gOutputs.valueAt(index); delete outputDesc; outputDesc = new OutputDescriptor(*desc); diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index 2d1e0f8..84a589a 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -54,7 +54,7 @@ public: (const AudioSystem::OutputDescriptor *)param2; data.writeInt32(desc->samplingRate); data.writeInt32(desc->format); - data.writeInt32(desc->channels); + data.writeInt32(desc->channelMask); data.writeInt32(desc->frameCount); data.writeInt32(desc->latency); } @@ -84,7 +84,7 @@ status_t BnAudioFlingerClient::onTransact( } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) { desc.samplingRate = data.readInt32(); desc.format = data.readInt32(); - desc.channels = data.readInt32(); + desc.channelMask = (audio_channel_mask_t) data.readInt32(); desc.frameCount = data.readInt32(); desc.latency = data.readInt32(); param2 = &desc; -- cgit v1.1 From bfb1b832079bbb9426f72f3863199a54aefd02da Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 7 Jan 2013 09:53:42 -0800 Subject: AudioFlinger: offload playback, non-blocking write - Added specialized playback thread class for offload playback, derived from directoutput thread. This thread type handles specific state transitions for offloaded tracks and offloading commands (pause/resume/drain/flush..) to audio HAL. As opposed to other threads, does not go to standby if the track is paused. - Added support for asynchronous write and drain operations at audio HAL. Use a thread to handle async callback events from HAL: this avoids locking playback thread mutex when executing the callback and cause deadlocks when calling audio HAL functions with the playback thread mutex locked. - Better accouting for track activity: call start/stop and release Output methods in audio policy manager when tracks are actually added and removed from the active tracks list. Added a command thread in audio policy service to handle stop/release commands asynchronously and avoid deadlocks with playback thread. - Track terminated status is not a state anymore. This condition is othogonal to state to permitted state transitions while terminated. Change-Id: Id157f4b3277620568d8eace7535d9186602564de --- media/libmedia/AudioTrackShared.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 55bf175..bd43ad2 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -322,6 +322,14 @@ void AudioTrackClientProxy::flush() mCblk->u.mStreaming.mFlush++; } +bool AudioTrackClientProxy::clearStreamEndDone() { + return android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE; +} + +bool AudioTrackClientProxy::getStreamEndDone() const { + return (mCblk->flags & CBLK_STREAM_END_DONE) != 0; +} + // --------------------------------------------------------------------------- StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, @@ -524,6 +532,16 @@ size_t AudioTrackServerProxy::framesReady() return filled; } +bool AudioTrackServerProxy::setStreamEndDone() { + bool old = + (android_atomic_or(CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0; + if (!old) { + (void) __futex_syscall3(&mCblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, + 1); + } + return old; +} + // --------------------------------------------------------------------------- StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, -- cgit v1.1 From b1a270d1e926fb9a01b4265a7675ed0c2c8f4868 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 14 May 2013 12:12:21 +0100 Subject: libmedia: offloaded playback support - start() returns a status so that upper layers can recreate a non offloaded track in case of error. - Added states to handle offloaded tracks specific: - waiting for stream end (drain) notification by audio flinger - allow pause while waiting for stream end notification - getPosition() queries the render position directly from audio HAL. - disable APIs not applicable to offloaded tracks - Modified track restoring behavior for invalidated offloaded tracks: just send the callback and wait for upper layers to create a new track. - Added wait for stream end management in audio track client proxy. Similar to obtainBuffer and should be factored in. Change-Id: I0fc48117946364cb255afd653195498891f622bd Signed-off-by: Eric Laurent --- media/libmedia/AudioTrack.cpp | 225 +++++++++++++++++++++++++++------ media/libmedia/AudioTrackShared.cpp | 133 ++++++++++++++++++- media/libmedia/IAudioFlinger.cpp | 15 ++- media/libmedia/IAudioPolicyService.cpp | 32 ++++- 4 files changed, 353 insertions(+), 52 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 7b6b38d..3653b7f 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -27,7 +27,9 @@ #include #include -#define WAIT_PERIOD_MS 10 +#define WAIT_PERIOD_MS 10 +#define WAIT_STREAM_END_TIMEOUT_SEC 120 + namespace android { // --------------------------------------------------------------------------- @@ -141,6 +143,7 @@ AudioTrack::~AudioTrack() // Otherwise the callback thread will never exit. stop(); if (mAudioTrackThread != 0) { + mProxy->interrupt(); mAudioTrackThread->requestExit(); // see comment in AudioTrack.h mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); @@ -224,6 +227,8 @@ status_t AudioTrack::set( return INVALID_OPERATION; } + mOutput = 0; + // handle default values first. if (streamType == AUDIO_STREAM_DEFAULT) { streamType = AUDIO_STREAM_MUSIC; @@ -259,7 +264,12 @@ status_t AudioTrack::set( } // force direct flag if format is not linear PCM - if (!audio_is_linear_pcm(format)) { + // or offload was requested + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) + || !audio_is_linear_pcm(format)) { + ALOGV( (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) + ? "Offload request, forcing to Direct Output" + : "Not linear PCM, forcing to Direct Output"); flags = (audio_output_flags_t) // FIXME why can't we allow direct AND fast? ((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST); @@ -325,9 +335,14 @@ status_t AudioTrack::set( if (status != NO_ERROR) { if (mAudioTrackThread != 0) { - mAudioTrackThread->requestExit(); + mAudioTrackThread->requestExit(); // see comment in AudioTrack.h + mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } + //Use of direct and offloaded output streams is ref counted by audio policy manager. + // As getOutput was called above and resulted in an output stream to be opened, + // we need to release it. + AudioSystem::releaseOutput(output); return status; } @@ -346,23 +361,29 @@ status_t AudioTrack::set( mSequence = 1; mObservedSequence = mSequence; mInUnderrun = false; + mOutput = output; return NO_ERROR; } // ------------------------------------------------------------------------- -void AudioTrack::start() +status_t AudioTrack::start() { AutoMutex lock(mLock); + if (mState == STATE_ACTIVE) { - return; + return INVALID_OPERATION; } mInUnderrun = true; State previousState = mState; - mState = STATE_ACTIVE; + if (previousState == STATE_PAUSED_STOPPING) { + mState = STATE_STOPPING; + } else { + mState = STATE_ACTIVE; + } if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) { // reset current position as seen by client to 0 mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition()); @@ -372,7 +393,11 @@ void AudioTrack::start() sp t = mAudioTrackThread; if (t != 0) { - t->resume(); + if (previousState == STATE_STOPPING) { + mProxy->interrupt(); + } else { + t->resume(); + } } else { mPreviousPriority = getpriority(PRIO_PROCESS, 0); get_sched_policy(0, &mPreviousSchedulingGroup); @@ -394,14 +419,16 @@ void AudioTrack::start() ALOGE("start() status %d", status); mState = previousState; if (t != 0) { - t->pause(); + if (previousState != STATE_STOPPING) { + t->pause(); + } } else { setpriority(PRIO_PROCESS, 0, mPreviousPriority); set_sched_policy(0, mPreviousSchedulingGroup); } } - // FIXME discarding status + return status; } void AudioTrack::stop() @@ -412,7 +439,12 @@ void AudioTrack::stop() return; } - mState = STATE_STOPPED; + if (isOffloaded()) { + mState = STATE_STOPPING; + } else { + mState = STATE_STOPPED; + } + mProxy->interrupt(); mAudioTrack->stop(); // the playback head position will reset to 0, so if a marker is set, we need @@ -426,9 +458,12 @@ void AudioTrack::stop() flush_l(); } #endif + sp t = mAudioTrackThread; if (t != 0) { - t->pause(); + if (!isOffloaded()) { + t->pause(); + } } else { setpriority(PRIO_PROCESS, 0, mPreviousPriority); set_sched_policy(0, mPreviousSchedulingGroup); @@ -461,8 +496,12 @@ void AudioTrack::flush_l() mMarkerPosition = 0; mMarkerReached = false; mUpdatePeriod = 0; + mRefreshRemaining = true; mState = STATE_FLUSHED; + if (isOffloaded()) { + mProxy->interrupt(); + } mProxy->flush(); mAudioTrack->flush(); } @@ -470,10 +509,13 @@ void AudioTrack::flush_l() void AudioTrack::pause() { AutoMutex lock(mLock); - if (mState != STATE_ACTIVE) { + if (mState == STATE_ACTIVE) { + mState = STATE_PAUSED; + } else if (mState == STATE_STOPPING) { + mState = STATE_PAUSED_STOPPING; + } else { return; } - mState = STATE_PAUSED; mProxy->interrupt(); mAudioTrack->pause(); } @@ -520,7 +562,7 @@ void AudioTrack::getAuxEffectSendLevel(float* level) const status_t AudioTrack::setSampleRate(uint32_t rate) { - if (mIsTimed) { + if (mIsTimed || isOffloaded()) { return INVALID_OPERATION; } @@ -552,7 +594,7 @@ uint32_t AudioTrack::getSampleRate() const status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) { - if (mSharedBuffer == 0 || mIsTimed) { + if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) { return INVALID_OPERATION; } @@ -586,7 +628,7 @@ void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) status_t AudioTrack::setMarkerPosition(uint32_t marker) { // The only purpose of setting marker position is to get a callback - if (mCbf == NULL) { + if (mCbf == NULL || isOffloaded()) { return INVALID_OPERATION; } @@ -599,6 +641,9 @@ status_t AudioTrack::setMarkerPosition(uint32_t marker) status_t AudioTrack::getMarkerPosition(uint32_t *marker) const { + if (isOffloaded()) { + return INVALID_OPERATION; + } if (marker == NULL) { return BAD_VALUE; } @@ -612,19 +657,21 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) { // The only purpose of setting position update period is to get a callback - if (mCbf == NULL) { + if (mCbf == NULL || isOffloaded()) { return INVALID_OPERATION; } AutoMutex lock(mLock); mNewPosition = mProxy->getPosition() + updatePeriod; mUpdatePeriod = updatePeriod; - return NO_ERROR; } status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const { + if (isOffloaded()) { + return INVALID_OPERATION; + } if (updatePeriod == NULL) { return BAD_VALUE; } @@ -637,7 +684,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const status_t AudioTrack::setPosition(uint32_t position) { - if (mSharedBuffer == 0 || mIsTimed) { + if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) { return INVALID_OPERATION; } if (position > mFrameCount) { @@ -670,10 +717,19 @@ status_t AudioTrack::getPosition(uint32_t *position) const } AutoMutex lock(mLock); - // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes - *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 : - mProxy->getPosition(); + if (isOffloaded()) { + uint32_t dspFrames = 0; + if (mOutput != 0) { + uint32_t halFrames; + AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames); + } + *position = dspFrames; + } else { + // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes + *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 : + mProxy->getPosition(); + } return NO_ERROR; } @@ -693,7 +749,7 @@ status_t AudioTrack::getBufferPosition(size_t *position) status_t AudioTrack::reload() { - if (mSharedBuffer == 0 || mIsTimed) { + if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) { return INVALID_OPERATION; } @@ -713,14 +769,18 @@ status_t AudioTrack::reload() audio_io_handle_t AudioTrack::getOutput() { AutoMutex lock(mLock); - return getOutput_l(); + return mOutput; } // must be called with mLock held audio_io_handle_t AudioTrack::getOutput_l() { - return AudioSystem::getOutput(mStreamType, - mSampleRate, mFormat, mChannelMask, mFlags); + if (mOutput) { + return mOutput; + } else { + return AudioSystem::getOutput(mStreamType, + mSampleRate, mFormat, mChannelMask, mFlags); + } } status_t AudioTrack::attachAuxEffect(int effectId) @@ -791,7 +851,9 @@ status_t AudioTrack::createTrack_l( } frameCount = afFrameCount; } - + if (mNotificationFramesAct != frameCount) { + mNotificationFramesAct = frameCount; + } } else if (sharedBuffer != 0) { // Ensure that buffer alignment matches channel count @@ -875,6 +937,10 @@ status_t AudioTrack::createTrack_l( } } + if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + trackFlags |= IAudioFlinger::TRACK_OFFLOAD; + } + sp track = audioFlinger->createTrack(streamType, sampleRate, // AudioFlinger only sees 16-bit PCM @@ -937,6 +1003,17 @@ status_t AudioTrack::createTrack_l( } } } + if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + if (trackFlags & IAudioFlinger::TRACK_OFFLOAD) { + ALOGV("AUDIO_OUTPUT_FLAG_OFFLOAD successful"); + } else { + ALOGW("AUDIO_OUTPUT_FLAG_OFFLOAD denied by server"); + flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); + mFlags = flags; + return NO_INIT; + } + } + mRefreshRemaining = true; // Starting address of buffers in shared memory. If there is a shared buffer, buffers @@ -1040,6 +1117,9 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re if (newSequence == oldSequence) { status = restoreTrack_l("obtainBuffer"); if (status != NO_ERROR) { + buffer.mFrameCount = 0; + buffer.mRaw = NULL; + buffer.mNonContig = 0; break; } } @@ -1050,6 +1130,14 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re proxy = mProxy; iMem = mCblkMemory; + if (mState == STATE_STOPPING) { + status = -EINTR; + buffer.mFrameCount = 0; + buffer.mRaw = NULL; + buffer.mNonContig = 0; + break; + } + // Non-blocking if track is stopped or paused if (mState != STATE_ACTIVE) { requested = &ClientProxy::kNonBlocking; @@ -1255,12 +1343,18 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) // Check for track invalidation if (flags & CBLK_INVALID) { - (void) restoreTrack_l("processAudioBuffer"); - mLock.unlock(); - // Run again immediately, but with a new IAudioTrack - return 0; + // for offloaded tracks restoreTrack_l() will just update the sequence and clear + // AudioSystem cache. We should not exit here but after calling the callback so + // that the upper layers can recreate the track + if (!isOffloaded() || (mSequence == mObservedSequence)) { + status_t status = restoreTrack_l("processAudioBuffer"); + mLock.unlock(); + // Run again immediately, but with a new IAudioTrack + return 0; + } } + bool waitStreamEnd = mState == STATE_STOPPING; bool active = mState == STATE_ACTIVE; // Manage underrun callback, must be done under lock to avoid race with releaseBuffer() @@ -1314,7 +1408,7 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) mRetryOnPartialBuffer = false; } size_t misalignment = mProxy->getMisalignment(); - int32_t sequence = mSequence; + uint32_t sequence = mSequence; // These fields don't need to be cached, because they are assigned only by set(): // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags @@ -1322,6 +1416,38 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) mLock.unlock(); + if (waitStreamEnd) { + AutoMutex lock(mLock); + + sp proxy = mProxy; + sp iMem = mCblkMemory; + + struct timespec timeout; + timeout.tv_sec = WAIT_STREAM_END_TIMEOUT_SEC; + timeout.tv_nsec = 0; + + mLock.unlock(); + status_t status = mProxy->waitStreamEndDone(&timeout); + mLock.lock(); + switch (status) { + case NO_ERROR: + case DEAD_OBJECT: + case TIMED_OUT: + mLock.unlock(); + mCbf(EVENT_STREAM_END, mUserData, NULL); + mLock.lock(); + if (mState == STATE_STOPPING) { + mState = STATE_STOPPED; + if (status != DEAD_OBJECT) { + return NS_INACTIVE; + } + } + return 0; + default: + return 0; + } + } + // perform callbacks while unlocked if (newUnderrun) { mCbf(EVENT_UNDERRUN, mUserData, NULL); @@ -1343,9 +1469,14 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) newPosition += updatePeriod; newPosCount--; } + if (mObservedSequence != sequence) { mObservedSequence = sequence; mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL); + // for offloaded tracks, just wait for the upper layers to recreate the track + if (isOffloaded()) { + return NS_INACTIVE; + } } // if inactive, then don't run me again until re-started @@ -1404,10 +1535,11 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount); requested = &ClientProxy::kNonBlocking; size_t avail = audioBuffer.frameCount + nonContig; - ALOGV("obtainBuffer(%u) returned %u = %u + %u", - mRemainingFrames, avail, audioBuffer.frameCount, nonContig); + ALOGV("obtainBuffer(%u) returned %u = %u + %u err %d", + mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err); if (err != NO_ERROR) { - if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) { + if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR || + (isOffloaded() && (err == DEAD_OBJECT))) { return 0; } ALOGE("Error %d obtaining an audio buffer, giving up.", err); @@ -1500,7 +1632,8 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) status_t AudioTrack::restoreTrack_l(const char *from) { - ALOGW("dead IAudioTrack, creating a new one from %s()", from); + ALOGW("dead IAudioTrack, %s, creating a new one from %s()", + isOffloaded() ? "Offloaded" : "PCM", from); ++mSequence; status_t result; @@ -1508,6 +1641,14 @@ status_t AudioTrack::restoreTrack_l(const char *from) // output parameters in getOutput_l() and createTrack_l() AudioSystem::clearAudioConfigCache(); + if (isOffloaded()) { + return DEAD_OBJECT; + } + + // force new output query from audio policy manager; + mOutput = 0; + audio_io_handle_t output = getOutput_l(); + // if the new IAudioTrack is created, createTrack_l() will modify the // following member variables: mAudioTrack, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioTrack and IMemory @@ -1520,7 +1661,7 @@ status_t AudioTrack::restoreTrack_l(const char *from) mReqFrameCount, // so that frame count never goes down mFlags, mSharedBuffer, - getOutput_l(), + output, position /*epoch*/); if (result == NO_ERROR) { @@ -1549,6 +1690,10 @@ status_t AudioTrack::restoreTrack_l(const char *from) } } if (result != NO_ERROR) { + //Use of direct and offloaded output streams is ref counted by audio policy manager. + // As getOutput was called above and resulted in an output stream to be opened, + // we need to release it. + AudioSystem::releaseOutput(output); ALOGW("restoreTrack_l() failed status %d", result); mState = STATE_STOPPED; } @@ -1568,7 +1713,11 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs) String8 AudioTrack::getParameters(const String8& keys) { - return String8::empty(); + if (mOutput) { + return AudioSystem::getParameters(mOutput, keys); + } else { + return String8::empty(); + } } status_t AudioTrack::dump(int fd, const Vector& args) const diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index bd43ad2..aa45a2f 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -200,7 +200,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques ts = &remaining; break; default: - LOG_FATAL("%s timeout=%d", timeout); + LOG_FATAL("obtainBuffer() timeout=%d", timeout); ts = NULL; break; } @@ -259,8 +259,9 @@ end: requested = &kNonBlocking; } if (measure) { - ALOGV("requested %d.%03d elapsed %d.%03d", requested->tv_sec, requested->tv_nsec / 1000000, - total.tv_sec, total.tv_nsec / 1000000); + ALOGV("requested %ld.%03ld elapsed %ld.%03ld", + requested->tv_sec, requested->tv_nsec / 1000000, + total.tv_sec, total.tv_nsec / 1000000); } return status; } @@ -323,13 +324,120 @@ void AudioTrackClientProxy::flush() } bool AudioTrackClientProxy::clearStreamEndDone() { - return android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE; + return (android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0; } bool AudioTrackClientProxy::getStreamEndDone() const { return (mCblk->flags & CBLK_STREAM_END_DONE) != 0; } +status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested) +{ + struct timespec total; // total elapsed time spent waiting + total.tv_sec = 0; + total.tv_nsec = 0; + audio_track_cblk_t* cblk = mCblk; + status_t status; + enum { + TIMEOUT_ZERO, // requested == NULL || *requested == 0 + TIMEOUT_INFINITE, // *requested == infinity + TIMEOUT_FINITE, // 0 < *requested < infinity + TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE + } timeout; + if (requested == NULL) { + timeout = TIMEOUT_ZERO; + } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) { + timeout = TIMEOUT_ZERO; + } else if (requested->tv_sec == INT_MAX) { + timeout = TIMEOUT_INFINITE; + } else { + timeout = TIMEOUT_FINITE; + } + for (;;) { + int32_t flags = android_atomic_and(~(CBLK_INTERRUPT|CBLK_STREAM_END_DONE), &cblk->flags); + // check for track invalidation by server, or server death detection + if (flags & CBLK_INVALID) { + ALOGV("Track invalidated"); + status = DEAD_OBJECT; + goto end; + } + if (flags & CBLK_STREAM_END_DONE) { + ALOGV("stream end received"); + status = NO_ERROR; + goto end; + } + // check for obtainBuffer interrupted by client + // check for obtainBuffer interrupted by client + if (flags & CBLK_INTERRUPT) { + ALOGV("waitStreamEndDone() interrupted by client"); + status = -EINTR; + goto end; + } + struct timespec remaining; + const struct timespec *ts; + switch (timeout) { + case TIMEOUT_ZERO: + status = WOULD_BLOCK; + goto end; + case TIMEOUT_INFINITE: + ts = NULL; + break; + case TIMEOUT_FINITE: + timeout = TIMEOUT_CONTINUE; + if (MAX_SEC == 0) { + ts = requested; + break; + } + // fall through + case TIMEOUT_CONTINUE: + // FIXME we do not retry if requested < 10ms? needs documentation on this state machine + if (requested->tv_sec < total.tv_sec || + (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) { + status = TIMED_OUT; + goto end; + } + remaining.tv_sec = requested->tv_sec - total.tv_sec; + if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) { + remaining.tv_nsec += 1000000000; + remaining.tv_sec++; + } + if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) { + remaining.tv_sec = MAX_SEC; + remaining.tv_nsec = 0; + } + ts = &remaining; + break; + default: + LOG_FATAL("waitStreamEndDone() timeout=%d", timeout); + ts = NULL; + break; + } + int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex); + if (!(old & CBLK_FUTEX_WAKE)) { + int rc; + int ret = __futex_syscall4(&cblk->mFutex, + mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts); + switch (ret) { + case 0: // normal wakeup by server, or by binderDied() + case -EWOULDBLOCK: // benign race condition with server + case -EINTR: // wait was interrupted by signal or other spurious wakeup + case -ETIMEDOUT: // time-out expired + break; + default: + ALOGE("%s unexpected error %d", __func__, ret); + status = -ret; + goto end; + } + } + } + +end: + if (requested == NULL) { + requested = &kNonBlocking; + } + return status; +} + // --------------------------------------------------------------------------- StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, @@ -393,13 +501,19 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer) if (mIsOut) { int32_t flush = cblk->u.mStreaming.mFlush; rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); + front = cblk->u.mStreaming.mFront; if (flush != mFlush) { - front = rear; mFlush = flush; // effectively obtain then release whatever is in the buffer android_atomic_release_store(rear, &cblk->u.mStreaming.mFront); - } else { - front = cblk->u.mStreaming.mFront; + if (front != rear) { + int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); + if (!(old & CBLK_FUTEX_WAKE)) { + (void) __futex_syscall3(&cblk->mFutex, + mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); + } + } + front = rear; } } else { front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); @@ -517,6 +631,11 @@ size_t AudioTrackServerProxy::framesReady() return 0; } audio_track_cblk_t* cblk = mCblk; + + int32_t flush = cblk->u.mStreaming.mFlush; + if (flush != mFlush) { + return mFrameCount; + } // the acquire might not be necessary since not doing a subsequent read int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); ssize_t filled = rear - cblk->u.mStreaming.mFront; diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 2e2c0cc..c670936 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -372,7 +372,6 @@ public: audio_channel_mask_t channelMask = pChannelMask != NULL ? *pChannelMask : (audio_channel_mask_t)0; uint32_t latency = pLatencyMs != NULL ? *pLatencyMs : 0; - data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.writeInt32(module); data.writeInt32(devices); @@ -381,6 +380,12 @@ public: data.writeInt32(channelMask); data.writeInt32(latency); data.writeInt32((int32_t) flags); + if (offloadInfo == NULL) { + data.writeInt32(0); + } else { + data.writeInt32(1); + data.write(offloadInfo, sizeof(audio_offload_info_t)); + } remote()->transact(OPEN_OUTPUT, data, &reply); audio_io_handle_t output = (audio_io_handle_t) reply.readInt32(); ALOGV("openOutput() returned output, %d", output); @@ -881,13 +886,19 @@ status_t BnAudioFlinger::onTransact( audio_channel_mask_t channelMask = (audio_channel_mask_t)data.readInt32(); uint32_t latency = data.readInt32(); audio_output_flags_t flags = (audio_output_flags_t) data.readInt32(); + bool hasOffloadInfo = data.readInt32() != 0; + audio_offload_info_t offloadInfo; + if (hasOffloadInfo) { + data.read(&offloadInfo, sizeof(audio_offload_info_t)); + } audio_io_handle_t output = openOutput(module, &devices, &samplingRate, &format, &channelMask, &latency, - flags); + flags, + hasOffloadInfo ? &offloadInfo : NULL); ALOGV("OPEN_OUTPUT output, %p", output); reply->writeInt32((int32_t) output); reply->writeInt32(devices); diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 57de58f..4be3c09 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -137,6 +137,12 @@ public: data.writeInt32(static_cast (format)); data.writeInt32(channelMask); data.writeInt32(static_cast (flags)); + if (offloadInfo == NULL) { + data.writeInt32(0); + } else { + data.writeInt32(1); + data.write(offloadInfo, sizeof(audio_offload_info_t)); + } remote()->transact(GET_OUTPUT, data, &reply); return static_cast (reply.readInt32()); } @@ -379,9 +385,11 @@ public: virtual bool isOffloadSupported(const audio_offload_info_t& info) { - // stub function - return false; - } + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(&info, sizeof(audio_offload_info_t)); + remote()->transact(IS_OFFLOAD_SUPPORTED, data, &reply); + return reply.readInt32(); } }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -450,12 +458,17 @@ status_t BnAudioPolicyService::onTransact( audio_channel_mask_t channelMask = data.readInt32(); audio_output_flags_t flags = static_cast (data.readInt32()); - + bool hasOffloadInfo = data.readInt32() != 0; + audio_offload_info_t offloadInfo; + if (hasOffloadInfo) { + data.read(&offloadInfo, sizeof(audio_offload_info_t)); + } audio_io_handle_t output = getOutput(stream, samplingRate, format, channelMask, - flags); + flags, + hasOffloadInfo ? &offloadInfo : NULL); reply->writeInt32(static_cast (output)); return NO_ERROR; } break; @@ -662,6 +675,15 @@ status_t BnAudioPolicyService::onTransact( return status; } + case IS_OFFLOAD_SUPPORTED: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_offload_info_t info; + data.read(&info, sizeof(audio_offload_info_t)); + bool isSupported = isOffloadSupported(info); + reply->writeInt32(isSupported); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } -- cgit v1.1 From d89532e133b881c7e0dac089333ad7642fc510f1 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 14 May 2013 13:18:21 +0100 Subject: libmediaplayerservice: offload playback support Main change is to how recycled tracks are used for gapless playback. If we are playing offloaded tracks that can't be recycled we don't open a new offloaded output until we have closed the previous one. This is because offloaded tracks are a limited resource so we don't want to spuriously create unnecessary instances. If the tracks cannot be recycled this means that the formats are incompatible and so the hardware most likely will also be unable to use the existing output channel for the new track. If we already have the maximum number of hardware offload channels open (which could be only one) then creation of the next output would fail if we attempted it while the previous output was still open. Change-Id: I4f5958074e7ffd2e17108157fee86329506730ea Signed-off-by: Eric Laurent --- media/libmediaplayerservice/MediaPlayerService.cpp | 284 +++++++++++++++------ media/libmediaplayerservice/MediaPlayerService.h | 7 +- 2 files changed, 207 insertions(+), 84 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index afde373..8833bd7 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include @@ -1381,6 +1383,45 @@ status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritt return OK; } +status_t MediaPlayerService::AudioOutput::setParameters(const String8& keyValuePairs) +{ + if (mTrack == 0) return NO_INIT; + return mTrack->setParameters(keyValuePairs); +} + +String8 MediaPlayerService::AudioOutput::getParameters(const String8& keys) +{ + if (mTrack == 0) return String8::empty(); + return mTrack->getParameters(keys); +} + +void MediaPlayerService::AudioOutput::deleteRecycledTrack() +{ + ALOGV("deleteRecycledTrack"); + + if (mRecycledTrack != 0) { + + if (mCallbackData != NULL) { + mCallbackData->setOutput(NULL); + mCallbackData->endTrackSwitch(); + } + + if ((mRecycledTrack->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) { + mRecycledTrack->flush(); + } + // An offloaded track isn't flushed because the STREAM_END is reported + // slightly prematurely to allow time for the gapless track switch + // but this means that if we decide not to recycle the track there + // could be a small amount of residual data still playing. We leave + // AudioFlinger to drain the track. + + mRecycledTrack.clear(); + delete mCallbackData; + mCallbackData = NULL; + close(); + } +} + status_t MediaPlayerService::AudioOutput::open( uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, audio_format_t format, int bufferCount, @@ -1397,20 +1438,34 @@ status_t MediaPlayerService::AudioOutput::open( bufferCount = mMinBufferCount; } - ALOGV("open(%u, %d, 0x%x, %d, %d, %d)", sampleRate, channelCount, channelMask, - format, bufferCount, mSessionId); + ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask, + format, bufferCount, mSessionId, flags); uint32_t afSampleRate; size_t afFrameCount; uint32_t frameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) { - return NO_INIT; - } - if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) { - return NO_INIT; + // offloading is only supported in callback mode for now. + // offloadInfo must be present if offload flag is set + if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) && + ((cb == NULL) || (offloadInfo == NULL))) { + return BAD_VALUE; } - frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate; + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + frameCount = 0; // AudioTrack will get frame count from AudioFlinger + } else { + uint32_t afSampleRate; + size_t afFrameCount; + + if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) { + return NO_INIT; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) { + return NO_INIT; + } + + frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate; + } if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) { channelMask = audio_channel_out_mask_from_count(channelCount); @@ -1420,65 +1475,108 @@ status_t MediaPlayerService::AudioOutput::open( } } - sp t; - CallbackData *newcbd = NULL; - if (mCallback != NULL) { - newcbd = new CallbackData(this); - t = new AudioTrack( - mStreamType, - sampleRate, - format, - channelMask, - frameCount, - flags, - CallbackWrapper, - newcbd, - 0, // notification frames - mSessionId); - } else { - t = new AudioTrack( - mStreamType, - sampleRate, - format, - channelMask, - frameCount, - flags, - NULL, - NULL, - 0, - mSessionId); - } - - if ((t == 0) || (t->initCheck() != NO_ERROR)) { - ALOGE("Unable to create audio track"); - delete newcbd; - return NO_INIT; - } - + // Check whether we can recycle the track + bool reuse = false; + bool bothOffloaded = false; if (mRecycledTrack != 0) { + // check whether we are switching between two offloaded tracks + bothOffloaded = (flags & mRecycledTrack->getFlags() + & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; + // check if the existing track can be reused as-is, or if a new track needs to be created. + reuse = true; - bool reuse = true; if ((mCallbackData == NULL && mCallback != NULL) || (mCallbackData != NULL && mCallback == NULL)) { // recycled track uses callbacks but the caller wants to use writes, or vice versa ALOGV("can't chain callback and write"); reuse = false; } else if ((mRecycledTrack->getSampleRate() != sampleRate) || - (mRecycledTrack->channelCount() != channelCount) || - (mRecycledTrack->frameCount() != t->frameCount())) { - ALOGV("samplerate, channelcount or framecount differ: %d/%d Hz, %d/%d ch, %d/%d frames", + (mRecycledTrack->channelCount() != (uint32_t)channelCount) ) { + ALOGV("samplerate, channelcount differ: %u/%u Hz, %u/%d ch", mRecycledTrack->getSampleRate(), sampleRate, - mRecycledTrack->channelCount(), channelCount, - mRecycledTrack->frameCount(), t->frameCount()); + mRecycledTrack->channelCount(), channelCount); reuse = false; } else if (flags != mFlags) { ALOGV("output flags differ %08x/%08x", flags, mFlags); reuse = false; + } else if (mRecycledTrack->format() != format) { + reuse = false; + } + } else { + ALOGV("no track available to recycle"); + } + + ALOGV_IF(bothOffloaded, "both tracks offloaded"); + + // If we can't recycle and both tracks are offloaded + // we must close the previous output before opening a new one + if (bothOffloaded && !reuse) { + ALOGV("both offloaded and not recycling"); + deleteRecycledTrack(); + } + + sp t; + CallbackData *newcbd = NULL; + + // We don't attempt to create a new track if we are recycling an + // offloaded track. But, if we are recycling a non-offloaded or we + // are switching where one is offloaded and one isn't then we create + // the new track in advance so that we can read additional stream info + + if (!(reuse && bothOffloaded)) { + ALOGV("creating new AudioTrack"); + + if (mCallback != NULL) { + newcbd = new CallbackData(this); + t = new AudioTrack( + mStreamType, + sampleRate, + format, + channelMask, + frameCount, + flags, + CallbackWrapper, + newcbd, + 0, // notification frames + mSessionId, + AudioTrack::TRANSFER_CALLBACK, + offloadInfo); + } else { + t = new AudioTrack( + mStreamType, + sampleRate, + format, + channelMask, + frameCount, + flags, + NULL, + NULL, + 0, + mSessionId); + } + + if ((t == 0) || (t->initCheck() != NO_ERROR)) { + ALOGE("Unable to create audio track"); + delete newcbd; + return NO_INIT; + } + } + + if (reuse) { + CHECK(mRecycledTrack != NULL); + + if (!bothOffloaded) { + if (mRecycledTrack->frameCount() != t->frameCount()) { + ALOGV("framecount differs: %u/%u frames", + mRecycledTrack->frameCount(), t->frameCount()); + reuse = false; + } } + if (reuse) { - ALOGV("chaining to next output"); + ALOGV("chaining to next output and recycling track"); close(); mTrack = mRecycledTrack; mRecycledTrack.clear(); @@ -1488,19 +1586,16 @@ status_t MediaPlayerService::AudioOutput::open( delete newcbd; return OK; } + } - // if we're not going to reuse the track, unblock and flush it - if (mCallbackData != NULL) { - mCallbackData->setOutput(NULL); - mCallbackData->endTrackSwitch(); - } - mRecycledTrack->flush(); - mRecycledTrack.clear(); - delete mCallbackData; - mCallbackData = NULL; - close(); + // we're not going to reuse the track, unblock and flush it + // this was done earlier if both tracks are offloaded + if (!bothOffloaded) { + deleteRecycledTrack(); } + CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL))); + mCallbackData = newcbd; ALOGV("setVolume"); t->setVolume(mLeftVolume, mRightVolume); @@ -1514,15 +1609,19 @@ status_t MediaPlayerService::AudioOutput::open( } mTrack = t; - status_t res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000); - if (res != NO_ERROR) { - return res; + status_t res = NO_ERROR; + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) { + res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000); + if (res == NO_ERROR) { + t->setAuxEffectSendLevel(mSendLevel); + res = t->attachAuxEffect(mAuxEffectId); + } } - t->setAuxEffectSendLevel(mSendLevel); - return t->attachAuxEffect(mAuxEffectId);; + ALOGV("open() DONE status %d", res); + return res; } -void MediaPlayerService::AudioOutput::start() +status_t MediaPlayerService::AudioOutput::start() { ALOGV("start"); if (mCallbackData != NULL) { @@ -1531,8 +1630,9 @@ void MediaPlayerService::AudioOutput::start() if (mTrack != 0) { mTrack->setVolume(mLeftVolume, mRightVolume); mTrack->setAuxEffectSendLevel(mSendLevel); - mTrack->start(); + return mTrack->start(); } + return NO_INIT; } void MediaPlayerService::AudioOutput::setNextOutput(const sp& nextOutput) { @@ -1645,10 +1745,6 @@ status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId) void MediaPlayerService::AudioOutput::CallbackWrapper( int event, void *cookie, void *info) { //ALOGV("callbackwrapper"); - if (event != AudioTrack::EVENT_MORE_DATA) { - return; - } - CallbackData *data = (CallbackData*)cookie; data->lock(); AudioOutput *me = data->getOutput(); @@ -1657,23 +1753,46 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( // no output set, likely because the track was scheduled to be reused // by another player, but the format turned out to be incompatible. data->unlock(); - buffer->size = 0; + if (buffer != NULL) { + buffer->size = 0; + } return; } - size_t actualSize = (*me->mCallback)( - me, buffer->raw, buffer->size, me->mCallbackCookie, - CB_EVENT_FILL_BUFFER); + switch(event) { + case AudioTrack::EVENT_MORE_DATA: { + size_t actualSize = (*me->mCallback)( + me, buffer->raw, buffer->size, me->mCallbackCookie, + CB_EVENT_FILL_BUFFER); + + if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { + // We've reached EOS but the audio track is not stopped yet, + // keep playing silence. + + memset(buffer->raw, 0, buffer->size); + actualSize = buffer->size; + } + + buffer->size = actualSize; + } break; - if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) { - // We've reached EOS but the audio track is not stopped yet, - // keep playing silence. - memset(buffer->raw, 0, buffer->size); - actualSize = buffer->size; + case AudioTrack::EVENT_STREAM_END: + ALOGV("callbackwrapper: deliver EVENT_STREAM_END"); + (*me->mCallback)(me, NULL /* buffer */, 0 /* size */, + me->mCallbackCookie, CB_EVENT_STREAM_END); + break; + + case AudioTrack::EVENT_NEW_IAUDIOTRACK : + ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN"); + (*me->mCallback)(me, NULL /* buffer */, 0 /* size */, + me->mCallbackCookie, CB_EVENT_TEAR_DOWN); + break; + + default: + ALOGE("received unknown event type: %d inside CallbackWrapper !", event); } - buffer->size = actualSize; data->unlock(); } @@ -1803,10 +1922,11 @@ status_t MediaPlayerService::AudioCache::open( return NO_ERROR; } -void MediaPlayerService::AudioCache::start() { +status_t MediaPlayerService::AudioCache::start() { if (mCallbackThread != NULL) { mCallbackThread->run("AudioCache callback"); } + return NO_ERROR; } void MediaPlayerService::AudioCache::stop() { diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index f7076cc..7d27944 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -94,7 +94,7 @@ class MediaPlayerService : public BnMediaPlayerService audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, const audio_offload_info_t *offloadInfo = NULL); - virtual void start(); + virtual status_t start(); virtual ssize_t write(const void* buffer, size_t size); virtual void stop(); virtual void flush(); @@ -112,11 +112,14 @@ class MediaPlayerService : public BnMediaPlayerService void setNextOutput(const sp& nextOutput); void switchToNextOutput(); virtual bool needsTrailingPadding() { return mNextOutput == NULL; } + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); private: static void setMinBufferCount(); static void CallbackWrapper( int event, void *me, void *info); + void deleteRecycledTrack(); sp mTrack; sp mRecycledTrack; @@ -196,7 +199,7 @@ class MediaPlayerService : public BnMediaPlayerService audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, const audio_offload_info_t *offloadInfo = NULL); - virtual void start(); + virtual status_t start(); virtual ssize_t write(const void* buffer, size_t size); virtual void stop(); virtual void flush() {} -- cgit v1.1 From 94ea60f975c3eb7ce6d2a4430538a42a5fc3babd Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 14 May 2013 15:52:03 +0100 Subject: stagefright: offload playback support Offloading of compressed audio decoding to audio DSP is implemented for audio only, non streamed content. when the datasource is AudioPlayer: - Create an offloaded sink when playing a compressed source - Send metadata to audio HAL - Return sink start error to AwesomePlayer so that a new player for PCM audio can be created in case of problem. - Forward stream end and tear down callback events to AwesomePlayer - Stop the sink and wait for stream end callback when EOS is reached. - Pause and restart the sink if needed before flushing when seeking (otherwise flush is a no op). - For current media time, directly query the render position from the sink and offset by the start position (seek to time) AwesomePlayer: - When initializing the audio decoder, check with audio policy manager if offloading is supported. If yes, create the software decoder in case a reconfiguration is needed but connect the audio track directly to the AudioPlayer. - In case of error when starting the AudioPlayer, reconnect the software decoder (OMXSource) and recreate a PCM AudioPlayer. - Handle AudioPlayer tear down event by detroying and recreating the AudioPlayer to allow transitions between situations were offloading is supported or not. - Force tear down of offloaded AudioPlayer when paused for a certain time: This will close the sink and allow the DSP to power down. Utils: - Added helper methods: - send meta data to audio ia sink setParameters - query audio policy manager if offloading is supported for a given audio content Change-Id: I115842ce424f947b966d45e253a74d3fd5df9aae Signed-off-by: Eric Laurent --- media/libstagefright/Android.mk | 1 + media/libstagefright/AudioPlayer.cpp | 348 +++++++++++++++++++++------ media/libstagefright/AwesomePlayer.cpp | 244 +++++++++++++++---- media/libstagefright/Utils.cpp | 123 +++++++++- media/libstagefright/include/AwesomePlayer.h | 2 +- 5 files changed, 603 insertions(+), 115 deletions(-) (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 90bf324..1f68b51 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -100,6 +100,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_mpeg2ts \ libstagefright_id3 \ libFLAC \ + libmedia_helper LOCAL_SRC_FILES += \ chromium_http_stub.cpp diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 61d6746..2418aab 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioPlayer" #include +#include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include "include/AwesomePlayer.h" @@ -47,14 +49,17 @@ AudioPlayer::AudioPlayer( mSeeking(false), mReachedEOS(false), mFinalStatus(OK), + mSeekTimeUs(0), mStarted(false), mIsFirstBuffer(false), mFirstBufferResult(OK), mFirstBuffer(NULL), mAudioSink(audioSink), - mAllowDeepBuffering((flags & ALLOW_DEEP_BUFFERING) != 0), mObserver(observer), - mPinnedTimeUs(-1ll) { + mPinnedTimeUs(-1ll), + mPlaying(false), + mStartPosUs(0), + mCreateFlags(flags) { } AudioPlayer::~AudioPlayer() { @@ -109,7 +114,7 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { const char *mime; bool success = format->findCString(kKeyMIMEType, &mime); CHECK(success); - CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)); + CHECK(useOffload() || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)); success = format->findInt32(kKeySampleRate, &mSampleRate); CHECK(success); @@ -125,16 +130,74 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; } + audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; + + if (useOffload()) { + if (mapMimeToAudioFormat(audioFormat, mime) != OK) { + ALOGE("Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format", mime); + audioFormat = AUDIO_FORMAT_INVALID; + } else { + ALOGV("Mime type \"%s\" mapped to audio_format 0x%x", mime, audioFormat); + } + } + + int avgBitRate = -1; + format->findInt32(kKeyBitRate, &avgBitRate); + if (mAudioSink.get() != NULL) { + uint32_t flags = AUDIO_OUTPUT_FLAG_NONE; + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + + if (allowDeepBuffering()) { + flags |= AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } + if (useOffload()) { + flags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + + int64_t durationUs; + if (format->findInt64(kKeyDuration, &durationUs)) { + offloadInfo.duration_us = durationUs; + } else { + offloadInfo.duration_us = -1; + } + + offloadInfo.sample_rate = mSampleRate; + offloadInfo.channel_mask = channelMask; + offloadInfo.format = audioFormat; + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; + offloadInfo.bit_rate = avgBitRate; + offloadInfo.has_video = ((mCreateFlags & HAS_VIDEO) != 0); + offloadInfo.is_streaming = ((mCreateFlags & IS_STREAMING) != 0); + } + status_t err = mAudioSink->open( - mSampleRate, numChannels, channelMask, AUDIO_FORMAT_PCM_16_BIT, + mSampleRate, numChannels, channelMask, audioFormat, DEFAULT_AUDIOSINK_BUFFERCOUNT, &AudioPlayer::AudioSinkCallback, this, - (mAllowDeepBuffering ? - AUDIO_OUTPUT_FLAG_DEEP_BUFFER : - AUDIO_OUTPUT_FLAG_NONE)); + (audio_output_flags_t)flags, + useOffload() ? &offloadInfo : NULL); + + if (err == OK) { + mLatencyUs = (int64_t)mAudioSink->latency() * 1000; + mFrameSize = mAudioSink->frameSize(); + + if (useOffload()) { + // If the playback is offloaded to h/w we pass the + // HAL some metadata information + // We don't want to do this for PCM because it will be going + // through the AudioFlinger mixer before reaching the hardware + sendMetaDataToHal(mAudioSink, format); + } + + err = mAudioSink->start(); + // do not alter behavior for non offloaded tracks: ignore start status. + if (!useOffload()) { + err = OK; + } + } + if (err != OK) { if (mFirstBuffer != NULL) { mFirstBuffer->release(); @@ -148,10 +211,6 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { return err; } - mLatencyUs = (int64_t)mAudioSink->latency() * 1000; - mFrameSize = mAudioSink->frameSize(); - - mAudioSink->start(); } else { // playing to an AudioTrack, set up mask if necessary audio_channel_mask_t audioMask = channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER ? @@ -186,6 +245,7 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { } mStarted = true; + mPlaying = true; mPinnedTimeUs = -1ll; return OK; @@ -212,27 +272,56 @@ void AudioPlayer::pause(bool playPendingSamples) { mPinnedTimeUs = ALooper::GetNowUs(); } + + mPlaying = false; } -void AudioPlayer::resume() { +status_t AudioPlayer::resume() { CHECK(mStarted); + status_t err; if (mAudioSink.get() != NULL) { - mAudioSink->start(); + err = mAudioSink->start(); } else { - mAudioTrack->start(); + err = mAudioTrack->start(); } + + if (err == OK) { + mPlaying = true; + } + + return err; } void AudioPlayer::reset() { CHECK(mStarted); + ALOGV("reset: mPlaying=%d mReachedEOS=%d useOffload=%d", + mPlaying, mReachedEOS, useOffload() ); + if (mAudioSink.get() != NULL) { mAudioSink->stop(); + // If we're closing and have reached EOS, we don't want to flush + // the track because if it is offloaded there could be a small + // amount of residual data in the hardware buffer which we must + // play to give gapless playback. + // But if we're resetting when paused or before we've reached EOS + // we can't be doing a gapless playback and there could be a large + // amount of data queued in the hardware if the track is offloaded, + // so we must flush to prevent a track switch being delayed playing + // the buffered data that we don't want now + if (!mPlaying || !mReachedEOS) { + mAudioSink->flush(); + } + mAudioSink->close(); } else { mAudioTrack->stop(); + if (!mPlaying || !mReachedEOS) { + mAudioTrack->flush(); + } + mAudioTrack.clear(); } @@ -256,10 +345,16 @@ void AudioPlayer::reset() { // The following hack is necessary to ensure that the OMX // component is completely released by the time we may try // to instantiate it again. - wp tmp = mSource; - mSource.clear(); - while (tmp.promote() != NULL) { - usleep(1000); + // When offloading, the OMX component is not used so this hack + // is not needed + if (!useOffload()) { + wp tmp = mSource; + mSource.clear(); + while (tmp.promote() != NULL) { + usleep(1000); + } + } else { + mSource.clear(); } IPCThreadState::self()->flushCommands(); @@ -271,6 +366,8 @@ void AudioPlayer::reset() { mReachedEOS = false; mFinalStatus = OK; mStarted = false; + mPlaying = false; + mStartPosUs = 0; } // static @@ -291,6 +388,15 @@ bool AudioPlayer::reachedEOS(status_t *finalStatus) { return mReachedEOS; } +void AudioPlayer::notifyAudioEOS() { + ALOGV("AudioPlayer@0x%p notifyAudioEOS", this); + + if (mObserver != NULL) { + mObserver->postAudioEOS(0); + ALOGV("Notified observer of EOS!"); + } +} + status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) { if (mAudioSink.get() != NULL) { return mAudioSink->setPlaybackRatePermille(ratePermille); @@ -308,18 +414,40 @@ size_t AudioPlayer::AudioSinkCallback( MediaPlayerBase::AudioSink::cb_event_t event) { AudioPlayer *me = (AudioPlayer *)cookie; - return me->fillBuffer(buffer, size); -} + switch(event) { + case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER: + return me->fillBuffer(buffer, size); -void AudioPlayer::AudioCallback(int event, void *info) { - if (event != AudioTrack::EVENT_MORE_DATA) { - return; + case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END: + ALOGV("AudioSinkCallback: stream end"); + me->mReachedEOS = true; + me->notifyAudioEOS(); + break; + + case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN: + ALOGV("AudioSinkCallback: Tear down event"); + me->mObserver->postAudioTearDown(); + break; } - AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; - size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size); + return 0; +} + +void AudioPlayer::AudioCallback(int event, void *info) { + switch (event) { + case AudioTrack::EVENT_MORE_DATA: + { + AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; + size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size); + buffer->size = numBytesWritten; + } + break; - buffer->size = numBytesWritten; + case AudioTrack::EVENT_STREAM_END: + mReachedEOS = true; + notifyAudioEOS(); + break; + } } uint32_t AudioPlayer::getNumFramesPendingPlayout() const { @@ -359,6 +487,7 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { size_t size_remaining = size; while (size_remaining > 0) { MediaSource::ReadOptions options; + bool refreshSeekTime = false; { Mutex::Autolock autoLock(mLock); @@ -373,6 +502,7 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { } options.setSeekTo(mSeekTimeUs); + refreshSeekTime = true; if (mInputBuffer != NULL) { mInputBuffer->release(); @@ -405,43 +535,56 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { Mutex::Autolock autoLock(mLock); if (err != OK) { - if (mObserver && !mReachedEOS) { - // We don't want to post EOS right away but only - // after all frames have actually been played out. - - // These are the number of frames submitted to the - // AudioTrack that you haven't heard yet. - uint32_t numFramesPendingPlayout = - getNumFramesPendingPlayout(); - - // These are the number of frames we're going to - // submit to the AudioTrack by returning from this - // callback. - uint32_t numAdditionalFrames = size_done / mFrameSize; - - numFramesPendingPlayout += numAdditionalFrames; - - int64_t timeToCompletionUs = - (1000000ll * numFramesPendingPlayout) / mSampleRate; - - ALOGV("total number of frames played: %lld (%lld us)", - (mNumFramesPlayed + numAdditionalFrames), - 1000000ll * (mNumFramesPlayed + numAdditionalFrames) - / mSampleRate); - - ALOGV("%d frames left to play, %lld us (%.2f secs)", - numFramesPendingPlayout, - timeToCompletionUs, timeToCompletionUs / 1E6); - - postEOS = true; - if (mAudioSink->needsTrailingPadding()) { - postEOSDelayUs = timeToCompletionUs + mLatencyUs; + if (!mReachedEOS) { + if (useOffload()) { + // no more buffers to push - stop() and wait for STREAM_END + // don't set mReachedEOS until stream end received + if (mAudioSink != NULL) { + mAudioSink->stop(); + } else { + mAudioTrack->stop(); + } } else { - postEOSDelayUs = 0; + if (mObserver) { + // We don't want to post EOS right away but only + // after all frames have actually been played out. + + // These are the number of frames submitted to the + // AudioTrack that you haven't heard yet. + uint32_t numFramesPendingPlayout = + getNumFramesPendingPlayout(); + + // These are the number of frames we're going to + // submit to the AudioTrack by returning from this + // callback. + uint32_t numAdditionalFrames = size_done / mFrameSize; + + numFramesPendingPlayout += numAdditionalFrames; + + int64_t timeToCompletionUs = + (1000000ll * numFramesPendingPlayout) / mSampleRate; + + ALOGV("total number of frames played: %lld (%lld us)", + (mNumFramesPlayed + numAdditionalFrames), + 1000000ll * (mNumFramesPlayed + numAdditionalFrames) + / mSampleRate); + + ALOGV("%d frames left to play, %lld us (%.2f secs)", + numFramesPendingPlayout, + timeToCompletionUs, timeToCompletionUs / 1E6); + + postEOS = true; + if (mAudioSink->needsTrailingPadding()) { + postEOSDelayUs = timeToCompletionUs + mLatencyUs; + } else { + postEOSDelayUs = 0; + } + } + + mReachedEOS = true; } } - mReachedEOS = true; mFinalStatus = err; break; } @@ -452,17 +595,34 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { mLatencyUs = (int64_t)mAudioTrack->latency() * 1000; } - CHECK(mInputBuffer->meta_data()->findInt64( + if(mInputBuffer->range_length() != 0) { + CHECK(mInputBuffer->meta_data()->findInt64( kKeyTime, &mPositionTimeMediaUs)); + } + + // need to adjust the mStartPosUs for offload decoding since parser + // might not be able to get the exact seek time requested. + if (refreshSeekTime && useOffload()) { + if (postSeekComplete) { + ALOGV("fillBuffer is going to post SEEK_COMPLETE"); + mObserver->postAudioSeekComplete(); + postSeekComplete = false; + } + + mStartPosUs = mPositionTimeMediaUs; + ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6); + } - mPositionTimeRealUs = - ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) - / mSampleRate; + if (!useOffload()) { + mPositionTimeRealUs = + ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) + / mSampleRate; + ALOGV("buffer->size() = %d, " + "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", + mInputBuffer->range_length(), + mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); + } - ALOGV("buffer->size() = %d, " - "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", - mInputBuffer->range_length(), - mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); } if (mInputBuffer->range_length() == 0) { @@ -488,6 +648,13 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { size_remaining -= copy; } + if (useOffload()) { + // We must ask the hardware what it has played + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + ALOGV("mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", + mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); + } + { Mutex::Autolock autoLock(mLock); mNumFramesPlayed += size_done / mFrameSize; @@ -536,9 +703,36 @@ int64_t AudioPlayer::getRealTimeUsLocked() const { return result + diffUs; } +int64_t AudioPlayer::getOutputPlayPositionUs_l() const +{ + uint32_t playedSamples = 0; + if (mAudioSink != NULL) { + mAudioSink->getPosition(&playedSamples); + } else { + mAudioTrack->getPosition(&playedSamples); + } + + const int64_t playedUs = (static_cast(playedSamples) * 1000000 ) / mSampleRate; + + // HAL position is relative to the first buffer we sent at mStartPosUs + const int64_t renderedDuration = mStartPosUs + playedUs; + ALOGV("getOutputPlayPositionUs_l %lld", renderedDuration); + return renderedDuration; +} + int64_t AudioPlayer::getMediaTimeUs() { Mutex::Autolock autoLock(mLock); + if (useOffload()) { + if (mSeeking) { + return mSeekTimeUs; + } + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + ALOGV("getMediaTimeUs getOutputPlayPositionUs_l() mPositionTimeRealUs %lld", + mPositionTimeRealUs); + return mPositionTimeRealUs; + } + if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) { if (mSeeking) { return mSeekTimeUs; @@ -547,6 +741,11 @@ int64_t AudioPlayer::getMediaTimeUs() { return 0; } + if (useOffload()) { + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + return mPositionTimeRealUs; + } + int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs; if (realTimeOffset < 0) { realTimeOffset = 0; @@ -568,19 +767,34 @@ bool AudioPlayer::getMediaTimeMapping( status_t AudioPlayer::seekTo(int64_t time_us) { Mutex::Autolock autoLock(mLock); + ALOGV("seekTo( %lld )", time_us); + mSeeking = true; mPositionTimeRealUs = mPositionTimeMediaUs = -1; mReachedEOS = false; mSeekTimeUs = time_us; + mStartPosUs = time_us; // Flush resets the number of played frames mNumFramesPlayed = 0; mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); if (mAudioSink != NULL) { + if (mPlaying) { + mAudioSink->pause(); + } mAudioSink->flush(); + if (mPlaying) { + mAudioSink->start(); + } } else { + if (mPlaying) { + mAudioTrack->pause(); + } mAudioTrack->flush(); + if (mPlaying) { + mAudioTrack->start(); + } } return OK; diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index b505518..3e70dd7 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,11 @@ static int64_t kHighWaterMarkUs = 5000000ll; // 5secs static const size_t kLowWaterMarkBytes = 40000; static const size_t kHighWaterMarkBytes = 200000; +// maximum time in paused state when offloading audio decompression. When elapsed, the AudioPlayer +// is destroyed to allow the audio DSP to power down. +static int64_t kOffloadPauseMaxUs = 60000000ll; + + struct AwesomeEvent : public TimedEventQueue::Event { AwesomeEvent( AwesomePlayer *player, @@ -194,7 +200,9 @@ AwesomePlayer::AwesomePlayer() mVideoBuffer(NULL), mDecryptHandle(NULL), mLastVideoTimeUs(-1), - mTextDriver(NULL) { + mTextDriver(NULL), + mOffloadAudio(false), + mAudioTearDown(false) { CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -213,6 +221,10 @@ AwesomePlayer::AwesomePlayer() mAudioStatusEventPending = false; + mAudioTearDownEvent = new AwesomeEvent(this, + &AwesomePlayer::onAudioTearDownEvent); + mAudioTearDownEventPending = false; + reset(); } @@ -232,6 +244,11 @@ void AwesomePlayer::cancelPlayerEvents(bool keepNotifications) { mQueue.cancelEvent(mVideoLagEvent->eventID()); mVideoLagEventPending = false; + if (mOffloadAudio) { + mQueue.cancelEvent(mAudioTearDownEvent->eventID()); + mAudioTearDownEventPending = false; + } + if (!keepNotifications) { mQueue.cancelEvent(mStreamDoneEvent->eventID()); mStreamDoneEventPending = false; @@ -518,7 +535,7 @@ void AwesomePlayer::reset_l() { mVideoTrack.clear(); mExtractor.clear(); - // Shutdown audio first, so that the respone to the reset request + // Shutdown audio first, so that the response to the reset request // appears to happen instantaneously as far as the user is concerned // If we did this later, audio would continue playing while we // shutdown the video-related resources and the player appear to @@ -531,6 +548,7 @@ void AwesomePlayer::reset_l() { mAudioSource->stop(); } mAudioSource.clear(); + mOmxSource.clear(); mTimeSource = NULL; @@ -586,7 +604,7 @@ void AwesomePlayer::reset_l() { } void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { - if (mListener != NULL) { + if ((mListener != NULL) && !mAudioTearDown) { sp listener = mListener.promote(); if (listener != NULL) { @@ -842,6 +860,13 @@ void AwesomePlayer::onStreamDone() { pause_l(true /* at eos */); + // If audio hasn't completed MEDIA_SEEK_COMPLETE yet, + // notify MEDIA_SEEK_COMPLETE to observer immediately for state persistence. + if (mWatchForAudioSeekComplete) { + notifyListener_l(MEDIA_SEEK_COMPLETE); + mWatchForAudioSeekComplete = false; + } + modifyFlags(AT_EOS, SET); } } @@ -883,41 +908,42 @@ status_t AwesomePlayer::play_l() { if (mAudioSource != NULL) { if (mAudioPlayer == NULL) { - if (mAudioSink != NULL) { - bool allowDeepBuffering; - int64_t cachedDurationUs; - bool eos; - if (mVideoSource == NULL - && (mDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US || - (getCachedDuration_l(&cachedDurationUs, &eos) && - cachedDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US))) { - allowDeepBuffering = true; - } else { - allowDeepBuffering = false; - } - - mAudioPlayer = new AudioPlayer(mAudioSink, allowDeepBuffering, this); - mAudioPlayer->setSource(mAudioSource); - - mTimeSource = mAudioPlayer; - - // If there was a seek request before we ever started, - // honor the request now. - // Make sure to do this before starting the audio player - // to avoid a race condition. - seekAudioIfNecessary_l(); - } + createAudioPlayer_l(); } CHECK(!(mFlags & AUDIO_RUNNING)); if (mVideoSource == NULL) { + // We don't want to post an error notification at this point, // the error returned from MediaPlayer::start() will suffice. status_t err = startAudioPlayer_l( false /* sendErrorNotification */); + if ((err != OK) && mOffloadAudio) { + ALOGI("play_l() cannot create offload output, fallback to sw decode"); + delete mAudioPlayer; + mAudioPlayer = NULL; + // if the player was started it will take care of stopping the source when destroyed + if (!(mFlags & AUDIOPLAYER_STARTED)) { + mAudioSource->stop(); + } + modifyFlags((AUDIO_RUNNING | AUDIOPLAYER_STARTED), CLEAR); + mOffloadAudio = false; + mAudioSource = mOmxSource; + if (mAudioSource != NULL) { + err = mAudioSource->start(); + + if (err != OK) { + mAudioSource.clear(); + } else { + createAudioPlayer_l(); + err = startAudioPlayer_l(false); + } + } + } + if (err != OK) { delete mAudioPlayer; mAudioPlayer = NULL; @@ -966,19 +992,58 @@ status_t AwesomePlayer::play_l() { return OK; } +void AwesomePlayer::createAudioPlayer_l() +{ + uint32_t flags = 0; + int64_t cachedDurationUs; + bool eos; + + if (mOffloadAudio) { + flags |= AudioPlayer::USE_OFFLOAD; + } else if (mVideoSource == NULL + && (mDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US || + (getCachedDuration_l(&cachedDurationUs, &eos) && + cachedDurationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US))) { + flags |= AudioPlayer::ALLOW_DEEP_BUFFERING; + } + if (isStreamingHTTP()) { + flags |= AudioPlayer::IS_STREAMING; + } + if (mVideoSource != NULL) { + flags |= AudioPlayer::HAS_VIDEO; + } + + mAudioPlayer = new AudioPlayer(mAudioSink, flags, this); + mAudioPlayer->setSource(mAudioSource); + + mTimeSource = mAudioPlayer; + + // If there was a seek request before we ever started, + // honor the request now. + // Make sure to do this before starting the audio player + // to avoid a race condition. + seekAudioIfNecessary_l(); +} + status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) { CHECK(!(mFlags & AUDIO_RUNNING)); + status_t err = OK; if (mAudioSource == NULL || mAudioPlayer == NULL) { return OK; } + if (mOffloadAudio) { + mQueue.cancelEvent(mAudioTearDownEvent->eventID()); + mAudioTearDownEventPending = false; + } + if (!(mFlags & AUDIOPLAYER_STARTED)) { bool wasSeeking = mAudioPlayer->isSeeking(); // We've already started the MediaSource in order to enable // the prefetcher to read its data. - status_t err = mAudioPlayer->start( + err = mAudioPlayer->start( true /* sourceAlreadyStarted */); if (err != OK) { @@ -998,14 +1063,16 @@ status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) { postAudioSeekComplete(); } } else { - mAudioPlayer->resume(); + err = mAudioPlayer->resume(); } - modifyFlags(AUDIO_RUNNING, SET); + if (err == OK) { + modifyFlags(AUDIO_RUNNING, SET); - mWatchForAudioEOS = true; + mWatchForAudioEOS = true; + } - return OK; + return err; } void AwesomePlayer::notifyVideoSize_l() { @@ -1137,15 +1204,14 @@ status_t AwesomePlayer::pause_l(bool at_eos) { cancelPlayerEvents(true /* keepNotifications */); if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) { - if (at_eos) { - // If we played the audio stream to completion we - // want to make sure that all samples remaining in the audio - // track's queue are played out. - mAudioPlayer->pause(true /* playPendingSamples */); - } else { - mAudioPlayer->pause(); + // If we played the audio stream to completion we + // want to make sure that all samples remaining in the audio + // track's queue are played out. + mAudioPlayer->pause(at_eos /* playPendingSamples */); + // send us a reminder to tear down the AudioPlayer if paused for too long. + if (mOffloadAudio) { + postAudioTearDownEvent(kOffloadPauseMaxUs); } - modifyFlags(AUDIO_RUNNING, CLEAR); } @@ -1290,7 +1356,6 @@ status_t AwesomePlayer::getPosition(int64_t *positionUs) { } else { *positionUs = 0; } - return OK; } @@ -1385,14 +1450,29 @@ status_t AwesomePlayer::initAudioDecoder() { const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); + // Check whether there is a hardware codec for this stream + // This doesn't guarantee that the hardware has a free stream + // but it avoids us attempting to open (and re-open) an offload + // stream to hardware that doesn't have the necessary codec + mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL), isStreamingHTTP()); if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { + ALOGV("createAudioPlayer: bypass OMX (raw)"); mAudioSource = mAudioTrack; } else { - mAudioSource = OMXCodec::Create( + // If offloading we still create a OMX decoder as a fall-back + // but we don't start it + mOmxSource = OMXCodec::Create( mClient.interface(), mAudioTrack->getFormat(), false, // createEncoder mAudioTrack); + + if (mOffloadAudio) { + ALOGV("createAudioPlayer: bypass OMX (offload)"); + mAudioSource = mAudioTrack; + } else { + mAudioSource = mOmxSource; + } } if (mAudioSource != NULL) { @@ -1408,6 +1488,7 @@ status_t AwesomePlayer::initAudioDecoder() { if (err != OK) { mAudioSource.clear(); + mOmxSource.clear(); return err; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_QCELP)) { @@ -1885,6 +1966,15 @@ void AwesomePlayer::postCheckAudioStatusEvent(int64_t delayUs) { mQueue.postEventWithDelay(mCheckAudioStatusEvent, delayUs); } +void AwesomePlayer::postAudioTearDownEvent(int64_t delayUs) { + Mutex::Autolock autoLock(mAudioLock); + if (mAudioTearDownEventPending) { + return; + } + mAudioTearDownEventPending = true; + mQueue.postEventWithDelay(mAudioTearDownEvent, delayUs); +} + void AwesomePlayer::onCheckAudioStatus() { { Mutex::Autolock autoLock(mAudioLock); @@ -2200,7 +2290,10 @@ bool AwesomePlayer::ContinuePreparation(void *cookie) { void AwesomePlayer::onPrepareAsyncEvent() { Mutex::Autolock autoLock(mLock); + beginPrepareAsync_l(); +} +void AwesomePlayer::beginPrepareAsync_l() { if (mFlags & PREPARE_CANCELLED) { ALOGI("prepare was cancelled before doing anything"); abortPrepare(UNKNOWN_ERROR); @@ -2273,6 +2366,10 @@ void AwesomePlayer::postAudioSeekComplete() { postCheckAudioStatusEvent(0); } +void AwesomePlayer::postAudioTearDown() { + postAudioTearDownEvent(0); +} + status_t AwesomePlayer::setParameter(int key, const Parcel &request) { switch (key) { case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS: @@ -2404,6 +2501,7 @@ status_t AwesomePlayer::selectAudioTrack_l( mAudioSource->stop(); } mAudioSource.clear(); + mOmxSource.clear(); mTimeSource = NULL; @@ -2660,4 +2758,66 @@ void AwesomePlayer::modifyFlags(unsigned value, FlagMode mode) { } } +void AwesomePlayer::onAudioTearDownEvent() { + + Mutex::Autolock autoLock(mLock); + if (!mAudioTearDownEventPending) { + return; + } + mAudioTearDownEventPending = false; + + ALOGV("onAudioTearDownEvent"); + + // stream info is cleared by reset_l() so copy what we need + const bool wasPlaying = (mFlags & PLAYING); + KeyedVector uriHeaders(mUriHeaders); + sp fileSource(mFileSource); + + mStatsLock.lock(); + String8 uri(mStats.mURI); + mStatsLock.unlock(); + + // get current position so we can start recreated stream from here + int64_t position = 0; + getPosition(&position); + + // Reset and recreate + reset_l(); + mFlags |= PREPARING; + + status_t err; + + if (fileSource != NULL) { + mFileSource = fileSource; + err = setDataSource_l(fileSource); + } else { + err = setDataSource_l(uri, &uriHeaders); + } + + if ( err != OK ) { + // This will force beingPrepareAsync_l() to notify + // a MEDIA_ERROR to the client and abort the prepare + mFlags |= PREPARE_CANCELLED; + } + + mAudioTearDown = true; + mIsAsyncPrepare = true; + + // Call parepare for the host decoding + beginPrepareAsync_l(); + + if (mPrepareResult == OK) { + if (mExtractorFlags & MediaExtractor::CAN_SEEK) { + seekTo_l(position); + } + + if (wasPlaying) { + modifyFlags(CACHE_UNDERRUN, CLEAR); + play_l(); + } + } + + mAudioTearDown = false; +} + } // namespace android diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index e9789d3..4db8e80 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -26,7 +26,12 @@ #include #include #include +#include +#include +#include +#include #include +#include namespace android { @@ -474,20 +479,128 @@ AString MakeUserAgent() { status_t sendMetaDataToHal(sp& sink, const sp& meta) { - // stub + int32_t sampleRate = 0; + int32_t bitRate = 0; + int32_t channelMask = 0; + int32_t delaySamples = 0; + int32_t paddingSamples = 0; + + AudioParameter param = AudioParameter(); + + if (meta->findInt32(kKeySampleRate, &sampleRate)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate); + } + if (meta->findInt32(kKeyChannelMask, &channelMask)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask); + } + if (meta->findInt32(kKeyBitRate, &bitRate)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate); + } + if (meta->findInt32(kKeyEncoderDelay, &delaySamples)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples); + } + if (meta->findInt32(kKeyEncoderPadding, &paddingSamples)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples); + } + + ALOGV("sendMetaDataToHal: bitRate %d, sampleRate %d, chanMask %d," + "delaySample %d, paddingSample %d", bitRate, sampleRate, + channelMask, delaySamples, paddingSamples); + + sink->setParameters(param.toString()); return OK; } -status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime) +struct mime_conv_t { + const char* mime; + audio_format_t format; +}; + +static const struct mime_conv_t mimeLookup[] = { + { MEDIA_MIMETYPE_AUDIO_MPEG, AUDIO_FORMAT_MP3 }, + { MEDIA_MIMETYPE_AUDIO_RAW, AUDIO_FORMAT_PCM_16_BIT }, + { MEDIA_MIMETYPE_AUDIO_AMR_NB, AUDIO_FORMAT_AMR_NB }, + { MEDIA_MIMETYPE_AUDIO_AMR_WB, AUDIO_FORMAT_AMR_WB }, + { MEDIA_MIMETYPE_AUDIO_AAC, AUDIO_FORMAT_AAC }, + { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS }, + { 0, AUDIO_FORMAT_INVALID } +}; + +status_t mapMimeToAudioFormat( audio_format_t& format, const char* mime ) { - // stub +const struct mime_conv_t* p = &mimeLookup[0]; + while (p->mime != NULL) { + if (0 == strcasecmp(mime, p->mime)) { + format = p->format; + return OK; + } + ++p; + } + return BAD_VALUE; } bool canOffloadStream(const sp& meta, bool hasVideo, bool isStreaming) { - // stub - return false; + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + audio_offload_info_t info = AUDIO_INFO_INITIALIZER; + + info.format = AUDIO_FORMAT_INVALID; + if (mapMimeToAudioFormat(info.format, mime) != OK) { + ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime); + return false; + } else { + ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format); + } + + if (AUDIO_FORMAT_INVALID == info.format) { + // can't offload if we don't know what the source format is + ALOGE("mime type \"%s\" not a known audio format", mime); + return false; + } + + int32_t srate = -1; + if (!meta->findInt32(kKeySampleRate, &srate)) { + ALOGV("track of type '%s' does not publish sample rate", mime); + } + info.sample_rate = srate; + + int32_t cmask = 0; + if (!meta->findInt32(kKeyChannelMask, &cmask)) { + ALOGV("track of type '%s' does not publish channel mask", mime); + + // Try a channel count instead + int32_t channelCount; + if (!meta->findInt32(kKeyChannelCount, &channelCount)) { + ALOGV("track of type '%s' does not publish channel count", mime); + } else { + cmask = audio_channel_out_mask_from_count(channelCount); + } + } + info.channel_mask = cmask; + + int64_t duration = 0; + if (!meta->findInt64(kKeyDuration, &duration)) { + ALOGV("track of type '%s' does not publish duration", mime); + } + info.duration_us = duration; + + int32_t brate = -1; + if (!meta->findInt32(kKeyBitRate, &brate)) { + ALOGV("track of type '%s' does not publish bitrate", mime); + } + info.bit_rate = brate; + + + info.stream_type = AUDIO_STREAM_MUSIC; + info.has_video = hasVideo; + info.is_streaming = isStreaming; + + // Check if offload is possible for given format, stream type, sample rate, + // bit rate, duration, video and streaming + return AudioSystem::isOffloadSupported(info); } } // namespace android diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 0d17d65..d3c74e2 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -226,7 +226,7 @@ private: void postStreamDoneEvent_l(status_t status); void postCheckAudioStatusEvent(int64_t delayUs); void postVideoLagEvent_l(); - void postAudioTearDownEvent(); + void postAudioTearDownEvent(int64_t delayUs); status_t play_l(); -- cgit v1.1 From 3dcd00dddec86a1c5133083ad7ba2265d49c048c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 17 Jul 2013 10:10:23 -0700 Subject: Declare methods in binder opcode order Change-Id: I5f624b7a51ffe1a17a67c056cf984f74e4c56eac --- media/libmedia/IAudioRecord.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'media') diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp index 0d06e98..4a7de65 100644 --- a/media/libmedia/IAudioRecord.cpp +++ b/media/libmedia/IAudioRecord.cpp @@ -42,6 +42,18 @@ public: { } + virtual sp getCblk() const + { + Parcel data, reply; + sp cblk; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_CBLK, data, &reply); + if (status == NO_ERROR) { + cblk = interface_cast(reply.readStrongBinder()); + } + return cblk; + } + virtual status_t start(int /*AudioSystem::sync_event_t*/ event, int triggerSession) { Parcel data, reply; @@ -64,17 +76,6 @@ public: remote()->transact(STOP, data, &reply); } - virtual sp getCblk() const - { - Parcel data, reply; - sp cblk; - data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_CBLK, data, &reply); - if (status == NO_ERROR) { - cblk = interface_cast(reply.readStrongBinder()); - } - return cblk; - } }; IMPLEMENT_META_INTERFACE(AudioRecord, "android.media.IAudioRecord"); -- cgit v1.1 From 04022b34d2b97938b0926ab62e6c283418da3bba Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 29 Jul 2013 11:22:27 -0700 Subject: Fail more gracefully on version mismatch b/9900647 Change-Id: I9ea508a2685ff8adc780edd5ecec30dd1a9b0997 --- media/libstagefright/MPEG4Extractor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 42a9c7a..ad985ee 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1924,13 +1924,13 @@ status_t MPEG4Extractor::parseTrackHeader( mtime = U64_AT(&buffer[12]); id = U32_AT(&buffer[20]); duration = U64_AT(&buffer[28]); - } else { - CHECK_EQ((unsigned)version, 0u); - + } else if (version == 0) { ctime = U32_AT(&buffer[4]); mtime = U32_AT(&buffer[8]); id = U32_AT(&buffer[12]); duration = U32_AT(&buffer[20]); + } else { + return ERROR_UNSUPPORTED; } mLastTrack->meta->setInt32(kKeyTrackID, id); -- cgit v1.1 From eced2daaa6c91a3731eef978ce65c6ec319c5e6a Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 16 Jul 2013 17:17:28 -0700 Subject: Use correct type for OutputDescriptor::format Change-Id: Ide608ef452d57da29b708180d90470361c123d1d --- media/libmedia/IAudioFlingerClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index 84a589a..3c0d4cf 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -83,7 +83,7 @@ status_t BnAudioFlingerClient::onTransact( ALOGV("STREAM_CONFIG_CHANGED stream %d", stream); } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) { desc.samplingRate = data.readInt32(); - desc.format = data.readInt32(); + desc.format = (audio_format_t) data.readInt32(); desc.channelMask = (audio_channel_mask_t) data.readInt32(); desc.frameCount = data.readInt32(); desc.latency = data.readInt32(); -- cgit v1.1 From f20e1d8df84c5fbeeace0052d100982ae39bb7a4 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 12 Jul 2013 09:45:18 -0700 Subject: Rename control block server to mServer and add comments Change-Id: Ieabd91acee92d0e84e66fbd358df5282b856306e --- media/libmedia/AudioTrackShared.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index aa45a2f..e5f7fcd 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -26,7 +26,7 @@ extern "C" { namespace android { audio_track_cblk_t::audio_track_cblk_t() - : server(0), frameCount_(0), mFutex(0), mMinimum(0), + : mServer(0), frameCount_(0), mFutex(0), mMinimum(0), mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), flags(0) { memset(&u, 0, sizeof(u)); @@ -594,7 +594,7 @@ void ServerProxy::releaseBuffer(Buffer* buffer) android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear); } - mCblk->server += stepCount; + mCblk->mServer += stepCount; size_t half = mFrameCount / 2; if (half == 0) { @@ -805,7 +805,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) } mPosition = newPosition; - cblk->server += stepCount; + cblk->mServer += stepCount; cblk->u.mStatic.mBufferPosition = newPosition; if (setFlags != 0) { (void) android_atomic_or(setFlags, &cblk->flags); -- cgit v1.1 From 96f60d8f04432a1ed503b3e24d5736d28c63c9a2 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 12 Jul 2013 10:21:18 -0700 Subject: Rename control block flags to mFlags Change-Id: I7b6d31e24531954ab1ecdf3ed56c19433700bd89 --- media/libmedia/AudioRecord.cpp | 4 ++-- media/libmedia/AudioTrack.cpp | 18 +++++++++--------- media/libmedia/AudioTrackShared.cpp | 18 +++++++++--------- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 603c16e..0e7e17f 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -300,7 +300,7 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition()); mNewPosition = mProxy->getPosition() + mUpdatePeriod; - int32_t flags = android_atomic_acquire_load(&mCblk->flags); + int32_t flags = android_atomic_acquire_load(&mCblk->mFlags); status_t status = NO_ERROR; if (!(flags & CBLK_INVALID)) { @@ -667,7 +667,7 @@ nsecs_t AudioRecord::processAudioBuffer(const sp& thread) mLock.lock(); // Can only reference mCblk while locked - int32_t flags = android_atomic_and(~CBLK_OVERRUN, &mCblk->flags); + int32_t flags = android_atomic_and(~CBLK_OVERRUN, &mCblk->mFlags); // Check for track invalidation if (flags & CBLK_INVALID) { diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 3653b7f..64a59be 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -389,7 +389,7 @@ status_t AudioTrack::start() mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition()); } mNewPosition = mProxy->getPosition() + mUpdatePeriod; - int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->flags); + int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags); sp t = mAudioTrackThread; if (t != 0) { @@ -1182,7 +1182,7 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) // restart track if it was disabled by audioflinger due to previous underrun if (mState == STATE_ACTIVE) { audio_track_cblk_t* cblk = mCblk; - if (android_atomic_and(~CBLK_DISABLED, &cblk->flags) & CBLK_DISABLED) { + if (android_atomic_and(~CBLK_DISABLED, &cblk->mFlags) & CBLK_DISABLED) { ALOGW("releaseBuffer() track %p name=%#x disabled due to previous underrun, restarting", this, cblk->mName); // FIXME ignoring status @@ -1261,16 +1261,16 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp* buffer) // fails indicating that the server is dead, flag the track as invalid so // we can attempt to restore in just a bit. audio_track_cblk_t* cblk = mCblk; - if (!(cblk->flags & CBLK_INVALID)) { + if (!(cblk->mFlags & CBLK_INVALID)) { result = mAudioTrack->allocateTimedBuffer(size, buffer); if (result == DEAD_OBJECT) { - android_atomic_or(CBLK_INVALID, &cblk->flags); + android_atomic_or(CBLK_INVALID, &cblk->mFlags); } } // If the track is invalid at this point, attempt to restore it. and try the // allocation one more time. - if (cblk->flags & CBLK_INVALID) { + if (cblk->mFlags & CBLK_INVALID) { result = restoreTrack_l("allocateTimedBuffer"); if (result == NO_ERROR) { @@ -1290,8 +1290,8 @@ status_t TimedAudioTrack::queueTimedBuffer(const sp& buffer, audio_track_cblk_t* cblk = mCblk; // restart track if it was disabled by audioflinger due to previous underrun if (buffer->size() != 0 && status == NO_ERROR && - (mState == STATE_ACTIVE) && (cblk->flags & CBLK_DISABLED)) { - android_atomic_and(~CBLK_DISABLED, &cblk->flags); + (mState == STATE_ACTIVE) && (cblk->mFlags & CBLK_DISABLED)) { + android_atomic_and(~CBLK_DISABLED, &cblk->mFlags); ALOGW("queueTimedBuffer() track %p disabled, restarting", this); // FIXME ignoring status mAudioTrack->start(); @@ -1339,7 +1339,7 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) // Can only reference mCblk while locked int32_t flags = android_atomic_and( - ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), &mCblk->flags); + ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), &mCblk->mFlags); // Check for track invalidation if (flags & CBLK_INVALID) { @@ -1681,7 +1681,7 @@ status_t AudioTrack::restoreTrack_l(const char *from) // the actual amount of audio frames played (e.g SoundPool) receives them. if (mSharedBuffer == 0) { // restart playback even if buffer is not completely filled. - android_atomic_or(CBLK_FORCEREADY, &mCblk->flags); + android_atomic_or(CBLK_FORCEREADY, &mCblk->mFlags); } } #endif diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index e5f7fcd..5015b8d 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -27,7 +27,7 @@ namespace android { audio_track_cblk_t::audio_track_cblk_t() : mServer(0), frameCount_(0), mFutex(0), mMinimum(0), - mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), flags(0) + mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), mFlags(0) { memset(&u, 0, sizeof(u)); } @@ -99,7 +99,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques goto end; } for (;;) { - int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->flags); + int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->mFlags); // check for track invalidation by server, or server death detection if (flags & CBLK_INVALID) { ALOGV("Track invalidated"); @@ -293,7 +293,7 @@ void ClientProxy::releaseBuffer(Buffer* buffer) void ClientProxy::binderDied() { audio_track_cblk_t* cblk = mCblk; - if (!(android_atomic_or(CBLK_INVALID, &cblk->flags) & CBLK_INVALID)) { + if (!(android_atomic_or(CBLK_INVALID, &cblk->mFlags) & CBLK_INVALID)) { // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); @@ -303,7 +303,7 @@ void ClientProxy::binderDied() void ClientProxy::interrupt() { audio_track_cblk_t* cblk = mCblk; - if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->flags) & CBLK_INTERRUPT)) { + if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->mFlags) & CBLK_INTERRUPT)) { (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); } @@ -324,11 +324,11 @@ void AudioTrackClientProxy::flush() } bool AudioTrackClientProxy::clearStreamEndDone() { - return (android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0; + return (android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->mFlags) & CBLK_STREAM_END_DONE) != 0; } bool AudioTrackClientProxy::getStreamEndDone() const { - return (mCblk->flags & CBLK_STREAM_END_DONE) != 0; + return (mCblk->mFlags & CBLK_STREAM_END_DONE) != 0; } status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested) @@ -354,7 +354,7 @@ status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *request timeout = TIMEOUT_FINITE; } for (;;) { - int32_t flags = android_atomic_and(~(CBLK_INTERRUPT|CBLK_STREAM_END_DONE), &cblk->flags); + int32_t flags = android_atomic_and(~(CBLK_INTERRUPT|CBLK_STREAM_END_DONE), &cblk->mFlags); // check for track invalidation by server, or server death detection if (flags & CBLK_INVALID) { ALOGV("Track invalidated"); @@ -653,7 +653,7 @@ size_t AudioTrackServerProxy::framesReady() bool AudioTrackServerProxy::setStreamEndDone() { bool old = - (android_atomic_or(CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0; + (android_atomic_or(CBLK_STREAM_END_DONE, &mCblk->mFlags) & CBLK_STREAM_END_DONE) != 0; if (!old) { (void) __futex_syscall3(&mCblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1); @@ -808,7 +808,7 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) cblk->mServer += stepCount; cblk->u.mStatic.mBufferPosition = newPosition; if (setFlags != 0) { - (void) android_atomic_or(setFlags, &cblk->flags); + (void) android_atomic_or(setFlags, &cblk->mFlags); // this would be a good place to wake a futex } -- cgit v1.1 From d054c32443a493513ab63529b0c8b1aca290278c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 12 Jul 2013 12:59:20 -0700 Subject: Move control block mName to createTrack() output This is part of a series of CLs to clean up the shared memory control block, by removing any fields that don't have to be there. Change-Id: I6e51003a1293b6800258c31b22cff2eba42162e7 --- media/libmedia/AudioTrack.cpp | 5 +++-- media/libmedia/AudioTrackShared.cpp | 2 +- media/libmedia/IAudioFlinger.cpp | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 64a59be..dd0ec73 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -953,6 +953,7 @@ status_t AudioTrack::createTrack_l( output, tid, &mSessionId, + mName, &status); if (track == 0) { @@ -1183,8 +1184,8 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer) if (mState == STATE_ACTIVE) { audio_track_cblk_t* cblk = mCblk; if (android_atomic_and(~CBLK_DISABLED, &cblk->mFlags) & CBLK_DISABLED) { - ALOGW("releaseBuffer() track %p name=%#x disabled due to previous underrun, restarting", - this, cblk->mName); + ALOGW("releaseBuffer() track %p name=%s disabled due to previous underrun, restarting", + this, mName.string()); // FIXME ignoring status mAudioTrack->start(); } diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 5015b8d..3b7616f 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -27,7 +27,7 @@ namespace android { audio_track_cblk_t::audio_track_cblk_t() : mServer(0), frameCount_(0), mFutex(0), mMinimum(0), - mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), mFlags(0) + mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mFlags(0) { memset(&u, 0, sizeof(u)); } diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index c670936..c6e43e7 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -95,6 +95,7 @@ public: audio_io_handle_t output, pid_t tid, int *sessionId, + String8& name, status_t *status) { Parcel data, reply; @@ -127,6 +128,7 @@ public: if (sessionId != NULL) { *sessionId = lSessionId; } + name = reply.readString8(); lStatus = reply.readInt32(); track = interface_cast(reply.readStrongBinder()); } @@ -735,12 +737,14 @@ status_t BnAudioFlinger::onTransact( audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); + String8 name; status_t status; sp track = createTrack( (audio_stream_type_t) streamType, sampleRate, format, - channelMask, frameCount, &flags, buffer, output, tid, &sessionId, &status); + channelMask, frameCount, &flags, buffer, output, tid, &sessionId, name, &status); reply->writeInt32(flags); reply->writeInt32(sessionId); + reply->writeString8(name); reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); return NO_ERROR; -- cgit v1.1 From 82aaf94a5b18939e4d790bbc752031f3070704a3 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 17 Jul 2013 16:05:07 -0700 Subject: Report underruns for fast tracks also This fixes a regression that was introduced earlier by commit 9f80dd223d83d9bb9077fb6baee056cee4eaf7e5 called "New control block for AudioTrack and AudioRecord". That commit broke underrun reporting for fast tracks. Also remove Track::mUnderrunCount, which counted the number of underrun events, and was only used by dumpsys media.audio_flinger. Now dumpsys media.audio_flinger reports the number of underrun frames, Isolated underrun-related control block accesses via the proxy, so that the server is not directly poking around in the control block. The new proxy APIs are AudioTrackServerProxy::getUnderrunFrames() and AudioTrackServerProxy::tallyUnderrunFrames(). getUnderrunFrames() returns a rolling counter for streaming tracks, or zero for static buffer tracks which never underrun, but do a kind of 'pause' at end of buffer. tallyUnderrunFrames() increments the counter by a specified number of frames. Change-Id: Ib31fd73eb17cbb23888ce3af8ff29f471f5bd5a2 --- media/libmedia/AudioTrackShared.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 3b7616f..e7abb40 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -661,6 +661,14 @@ bool AudioTrackServerProxy::setStreamEndDone() { return old; } +void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount) +{ + mCblk->u.mStreaming.mUnderrunFrames += frameCount; + + // FIXME also wake futex so that underrun is noticed more quickly + (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags); +} + // --------------------------------------------------------------------------- StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, @@ -817,6 +825,17 @@ void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer) buffer->mNonContig = 0; } +void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount) +{ + // Unlike AudioTrackServerProxy::tallyUnderrunFrames() used for streaming tracks, + // we don't have a location to count underrun frames. The underrun frame counter + // only exists in AudioTrackSharedStreaming. Fortunately, underruns are not + // possible for static buffer tracks other than at end of buffer, so this is not a loss. + + // FIXME also wake futex so that underrun is noticed more quickly + (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags); +} + // --------------------------------------------------------------------------- } // namespace android -- cgit v1.1 From 30873bfd08255e2c4e98ff5732ffff2838772617 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 31 Jul 2013 13:04:50 -0700 Subject: Unregister any handlers still registered on now "dead" ALoopers upon the death of an ALooper. Change-Id: I64c0835b8db04486204f3d0fa7173ee53708a116 related-to-bug: 10106648 --- media/libstagefright/foundation/ALooper.cpp | 4 ++++ media/libstagefright/foundation/ALooperRoster.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'media') diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp index 22777a2..ebf9d8d 100644 --- a/media/libstagefright/foundation/ALooper.cpp +++ b/media/libstagefright/foundation/ALooper.cpp @@ -72,6 +72,10 @@ ALooper::ALooper() ALooper::~ALooper() { stop(); + + // Since this looper is "dead" (or as good as dead by now), + // have ALooperRoster unregister any handlers still registered for it. + gLooperRoster.unregisterStaleHandlers(); } void ALooper::setName(const char *name) { diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp index ad10d2b..0c181ff 100644 --- a/media/libstagefright/foundation/ALooperRoster.cpp +++ b/media/libstagefright/foundation/ALooperRoster.cpp @@ -71,6 +71,20 @@ void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) { mHandlers.removeItemsAt(index); } +void ALooperRoster::unregisterStaleHandlers() { + Mutex::Autolock autoLock(mLock); + + for (size_t i = mHandlers.size(); i-- > 0;) { + const HandlerInfo &info = mHandlers.valueAt(i); + + sp looper = info.mLooper.promote(); + if (looper == NULL) { + ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i)); + mHandlers.removeItemsAt(i); + } + } +} + status_t ALooperRoster::postMessage( const sp &msg, int64_t delayUs) { Mutex::Autolock autoLock(mLock); -- cgit v1.1 From 910813bd66eaf0f6a72769c9b3fa9830dd100a19 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 31 Jul 2013 20:36:27 -0700 Subject: fix fallout from binderizing BufferQueues consumer side Change-Id: I626bac6df4fc3d8478046193f06ecc7ea60dd3a8 --- media/libstagefright/SurfaceMediaSource.cpp | 6 ++---- media/libstagefright/omx/GraphicBufferSource.cpp | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index befd4cc..b082c3a 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -65,10 +65,8 @@ SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeig // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. - wp listener; - sp proxy; - listener = static_cast(this); - proxy = new BufferQueue::ProxyConsumerListener(listener); + wp listener = static_cast(this); + sp proxy = new BufferQueue::ProxyConsumerListener(listener); status_t err = mBufferQueue->consumerConnect(proxy, false); if (err != NO_ERROR) { diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index d6fd95b..325ffcf 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -69,11 +69,8 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. - wp listener; - listener = static_cast(this); - - sp proxy; - proxy = new BufferQueue::ProxyConsumerListener(listener); + wp listener = static_cast(this); + sp proxy = new BufferQueue::ProxyConsumerListener(listener); mInitCheck = mBufferQueue->consumerConnect(proxy, false); if (mInitCheck != NO_ERROR) { -- cgit v1.1 From f0f33c4acd231fa95deb9eeef2c46b0129e64463 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 31 Jul 2013 12:24:36 -0700 Subject: AudioRecord has no default sample rate Change-Id: I72feefdd6f3a623fd3669b80d4b264518fdc0929 --- media/libmedia/AudioRecord.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0e7e17f..b499cbb 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -176,7 +176,8 @@ status_t AudioRecord::set( } if (sampleRate == 0) { - sampleRate = DEFAULT_SAMPLE_RATE; + ALOGE("Invalid sample rate %u", sampleRate); + return BAD_VALUE; } mSampleRate = sampleRate; -- cgit v1.1 From 954315a10089fa3684ac94db5be77c6655c08fc0 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 2 Aug 2013 09:02:07 -0700 Subject: Recording of non-linear formats is not supported Such formats are already rejected in AudioRecord::set() Change-Id: I5ba1fd9e4cd659e5226c75aa4f63e52f655e0521 --- media/libmedia/AudioRecord.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0e7e17f..103a5f1 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -60,10 +60,9 @@ status_t AudioRecord::getMinFrameCount( // We double the size of input buffer for ping pong use of record buffer. size <<= 1; - if (audio_is_linear_pcm(format)) { - uint32_t channelCount = popcount(channelMask); - size /= channelCount * audio_bytes_per_sample(format); - } + // Assumes audio_is_linear_pcm(format) + uint32_t channelCount = popcount(channelMask); + size /= channelCount * audio_bytes_per_sample(format); *frameCount = size; return NO_ERROR; @@ -205,11 +204,8 @@ status_t AudioRecord::set( uint32_t channelCount = popcount(channelMask); mChannelCount = channelCount; - if (audio_is_linear_pcm(format)) { - mFrameSize = channelCount * audio_bytes_per_sample(format); - } else { - mFrameSize = sizeof(uint8_t); - } + // Assumes audio_is_linear_pcm(format), else sizeof(uint8_t) + mFrameSize = channelCount * audio_bytes_per_sample(format); if (sessionId == 0 ) { mSessionId = AudioSystem::newAudioSessionId(); -- cgit v1.1 From 28f1351369682801e1bb40a835bdae3c97b73c1c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 31 Jul 2013 12:27:26 -0700 Subject: AudioRecord callback thread waits for priority boost Change-Id: Iae38fa4ac20a45751566169213a08a15deb0a2f6 --- media/libmedia/AudioRecord.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0e7e17f..7be7529 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -665,6 +665,26 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) nsecs_t AudioRecord::processAudioBuffer(const sp& thread) { mLock.lock(); + if (mAwaitBoost) { + mAwaitBoost = false; + mLock.unlock(); + static const int32_t kMaxTries = 5; + int32_t tryCounter = kMaxTries; + uint32_t pollUs = 10000; + do { + int policy = sched_getscheduler(0); + if (policy == SCHED_FIFO || policy == SCHED_RR) { + break; + } + usleep(pollUs); + pollUs <<= 1; + } while (tryCounter-- > 0); + if (tryCounter < 0) { + ALOGE("did not receive expected priority boost on time"); + } + // Run again immediately + return 0; + } // Can only reference mCblk while locked int32_t flags = android_atomic_and(~CBLK_OVERRUN, &mCblk->mFlags); -- cgit v1.1 From 27f7b2a8fe899565487d8a326676a5f7d0a05a37 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 31 Jul 2013 16:10:22 -0700 Subject: AudioRecord::set and constructor now take flags The new optional parameter 'flags' of type audio_input_flags_t will be used for requesting a fast track. Change-Id: Ia7e070cb57c833e608352da354fb30dc26df6918 --- media/libmedia/AudioRecord.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 7be7529..d4495dc 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -87,7 +87,8 @@ AudioRecord::AudioRecord( void* user, int notificationFrames, int sessionId, - transfer_type transferType) + transfer_type transferType, + audio_input_flags_t flags) : mStatus(NO_INIT), mSessionId(0), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), @@ -129,7 +130,8 @@ status_t AudioRecord::set( int notificationFrames, bool threadCanCallJava, int sessionId, - transfer_type transferType) + transfer_type transferType, + audio_input_flags_t flags) { switch (transferType) { case TRANSFER_DEFAULT: @@ -218,6 +220,8 @@ status_t AudioRecord::set( } ALOGV("set(): mSessionId %d", mSessionId); + mFlags = flags; + audio_io_handle_t input = AudioSystem::getInput(inputSource, sampleRate, format, -- cgit v1.1 From eeca32671896739e84050da5992d5f151a1629de Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 31 Jul 2013 16:12:48 -0700 Subject: IAudioFlinger::openRecord track_flags_t flags is in/out This will allow AudioFlinger to tell client it is denying a request. Change-Id: Iff2be3ad6636371bbda9c9899a283c94620c1f06 --- media/libmedia/AudioRecord.cpp | 3 ++- media/libmedia/IAudioFlinger.cpp | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index d4495dc..b5060b1 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -451,12 +451,13 @@ status_t AudioRecord::openRecord_l( pid_t tid = -1; // FIXME see similar logic at AudioTrack for tid + IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; int originalSessionId = mSessionId; sp record = audioFlinger->openRecord(input, sampleRate, format, mChannelMask, frameCount, - IAudioFlinger::TRACK_DEFAULT, + &trackFlags, tid, &mSessionId, &status); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index c6e43e7..be818c6 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -144,7 +144,7 @@ public: audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, - track_flags_t flags, + track_flags_t *flags, pid_t tid, int *sessionId, status_t *status) @@ -157,7 +157,8 @@ public: data.writeInt32(format); data.writeInt32(channelMask); data.writeInt32(frameCount); - data.writeInt32(flags); + track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT; + data.writeInt32(lFlags); data.writeInt32((int32_t) tid); int lSessionId = 0; if (sessionId != NULL) { @@ -168,6 +169,10 @@ public: if (lStatus != NO_ERROR) { ALOGE("openRecord error: %s", strerror(-lStatus)); } else { + lFlags = reply.readInt32(); + if (flags != NULL) { + *flags = lFlags; + } lSessionId = reply.readInt32(); if (sessionId != NULL) { *sessionId = lSessionId; @@ -761,7 +766,8 @@ status_t BnAudioFlinger::onTransact( int sessionId = data.readInt32(); status_t status; sp record = openRecord(input, - sampleRate, format, channelMask, frameCount, flags, tid, &sessionId, &status); + sampleRate, format, channelMask, frameCount, &flags, tid, &sessionId, &status); + reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeInt32(status); reply->writeStrongBinder(record->asBinder()); -- cgit v1.1 From 73493688f4190f790ee15d9ca54831cd64f4e195 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 31 Jul 2013 16:10:53 -0700 Subject: AudioRecord::openRecord_l now take flags The new parameter 'flags' of type audio_input_flags_t will be used for requesting a fast track, but is currently ignored. Change-Id: If68dfda8b2d4eaaca42927d721b4630c47f71f3b --- media/libmedia/AudioRecord.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index b5060b1..dfaac4c 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -253,7 +253,7 @@ status_t AudioRecord::set( } // create the IAudioRecord - status = openRecord_l(sampleRate, format, frameCount, input, 0 /*epoch*/); + status = openRecord_l(sampleRate, format, frameCount, mFlags, input, 0 /*epoch*/); if (status != NO_ERROR) { return status; } @@ -438,6 +438,7 @@ status_t AudioRecord::openRecord_l( uint32_t sampleRate, audio_format_t format, size_t frameCount, + audio_input_flags_t flags, audio_io_handle_t input, size_t epoch) { @@ -911,7 +912,7 @@ status_t AudioRecord::restoreRecord_l(const char *from) // It will also delete the strong references on previous IAudioRecord and IMemory size_t position = mProxy->getPosition(); mNewPosition = position + mUpdatePeriod; - result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l(), position); + result = openRecord_l(mSampleRate, mFormat, mFrameCount, mFlags, getInput_l(), position); if (result == NO_ERROR) { if (mActive) { // callback thread or sync event hasn't changed -- cgit v1.1 From 3151427b6b0adf99929433715bab6f1e505100c1 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 1 Aug 2013 07:24:34 -0700 Subject: AudioRecord::openRecord_l use flags Use the flags to determine input parameters for IAudioFlinger::openRecord. Change-Id: I98d2726503af75c8830ce80ceaf3b94a755b342f --- media/libmedia/AudioRecord.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index dfaac4c..0c798ae 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -449,10 +449,22 @@ status_t AudioRecord::openRecord_l( return NO_INIT; } + IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; pid_t tid = -1; - // FIXME see similar logic at AudioTrack for tid - IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; + // Client can only express a preference for FAST. Server will perform additional tests. + // The only supported use case for FAST is callback transfer mode. + if (flags & AUDIO_INPUT_FLAG_FAST) { + if ((mTransfer != TRANSFER_CALLBACK) || (mAudioRecordThread == 0)) { + ALOGW("AUDIO_INPUT_FLAG_FAST denied by client"); + // once denied, do not request again if IAudioRecord is re-created + mFlags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST); + } else { + trackFlags |= IAudioFlinger::TRACK_FAST; + tid = mAudioRecordThread->getTid(); + } + } + int originalSessionId = mSessionId; sp record = audioFlinger->openRecord(input, sampleRate, format, -- cgit v1.1 From 7cd9cf70e36ad4b8eb12e24f9adbbe6fd69edebd Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 1 Aug 2013 07:22:02 -0700 Subject: AudioRecord notification frames Change-Id: I76ec536d1504eb9a558178b62bf225aace4b40d1 --- media/libmedia/AudioRecord.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0c798ae..6c04b43 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -270,7 +270,8 @@ status_t AudioRecord::set( mActive = false; mCbf = cbf; - mNotificationFrames = notificationFrames; + mNotificationFramesReq = notificationFrames; + mNotificationFramesAct = 0; mRefreshRemaining = true; mUserData = user; // TODO: add audio hardware input latency here @@ -458,7 +459,8 @@ status_t AudioRecord::openRecord_l( if ((mTransfer != TRANSFER_CALLBACK) || (mAudioRecordThread == 0)) { ALOGW("AUDIO_INPUT_FLAG_FAST denied by client"); // once denied, do not request again if IAudioRecord is re-created - mFlags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST); + flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST); + mFlags = flags; } else { trackFlags |= IAudioFlinger::TRACK_FAST; tid = mAudioRecordThread->getTid(); @@ -494,6 +496,27 @@ status_t AudioRecord::openRecord_l( mCblkMemory = iMem; audio_track_cblk_t* cblk = static_cast(iMem->pointer()); mCblk = cblk; + // FIXME missing fast track frameCount logic + mAwaitBoost = false; + mNotificationFramesAct = mNotificationFramesReq; + if (flags & AUDIO_INPUT_FLAG_FAST) { + if (trackFlags & IAudioFlinger::TRACK_FAST) { + ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", frameCount); + mAwaitBoost = true; + // double-buffering is not required for fast tracks, due to tighter scheduling + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) { + mNotificationFramesAct = frameCount; + } + } else { + ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", frameCount); + // once denied, do not request again if IAudioRecord is re-created + flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST); + mFlags = flags; + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { + mNotificationFramesAct = frameCount/2; + } + } + } // starting address of buffers in shared memory void *buffers = (char*)cblk + sizeof(audio_track_cblk_t); @@ -501,7 +524,7 @@ status_t AudioRecord::openRecord_l( // update proxy mProxy = new AudioRecordClientProxy(cblk, buffers, frameCount, mFrameSize); mProxy->setEpoch(epoch); - mProxy->setMinimum(mNotificationFrames); + mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); mAudioRecord->asBinder()->linkToDeath(mDeathNotifier, this); @@ -748,7 +771,7 @@ nsecs_t AudioRecord::processAudioBuffer(const sp& thread) } // Cache other fields that will be needed soon - size_t notificationFrames = mNotificationFrames; + size_t notificationFrames = mNotificationFramesAct; if (mRefreshRemaining) { mRefreshRemaining = false; mRemainingFrames = notificationFrames; -- cgit v1.1 From b13820ffafcb6bcdd33b6272676535afb4dff479 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 5 Aug 2013 12:22:43 -0700 Subject: Reject unprepared MediaPlayers Don't allow a MediaPlayer that has been reset() or release()d to be used as the argument to setNextMediaPlayer. Change-Id: I47da1460ec3742f5b2bd7b79e7998b290032d5a1 --- media/libmedia/mediaplayer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'media') diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 963b04f..056cc0a 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -811,6 +811,13 @@ status_t MediaPlayer::setNextMediaPlayer(const sp& next) { if (mPlayer == NULL) { return NO_INIT; } + + if (next != NULL && !(next->mCurrentState & + (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE))) { + ALOGE("next player is not prepared"); + return INVALID_OPERATION; + } + return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer); } -- cgit v1.1 From deeef54487a34034dc0cfaab20b20d557224c07c Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 2 Aug 2013 01:50:59 -0700 Subject: separte producer and consumer interfaces Bug: 9265647 Change-Id: Iefabc11e4bd2e2e8ffd31160476c450affe6629c --- media/libstagefright/SurfaceMediaSource.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index b082c3a..6b934d4 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -65,7 +65,7 @@ SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeig // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. - wp listener = static_cast(this); + wp listener = static_cast(this); sp proxy = new BufferQueue::ProxyConsumerListener(listener); status_t err = mBufferQueue->consumerConnect(proxy, false); @@ -105,7 +105,7 @@ void SurfaceMediaSource::dump(String8& result, const char* prefix, Mutex::Autolock lock(mMutex); result.append(buffer); - mBufferQueue->dump(result); + mBufferQueue->dump(result, ""); } status_t SurfaceMediaSource::setFrameRate(int32_t fps) -- cgit v1.1 From d0715867861c216e88a4a7523b6da8a3cb128724 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 22 Jul 2013 12:57:43 -0700 Subject: IOMX: add updateGraphicBufferInMeta method for metadata mode This is used to set the handle in the metadata buffer that is valid in the mediaserver process, as well as to keep a reference for the graphic buffers in BufferMeta. Change-Id: I46bb68d8bed894f357eeeb25368360d11b276634 Signed-off-by: Lajos Molnar --- media/libmedia/IOMX.cpp | 33 ++++++++++++++++++++++++++ media/libstagefright/ACodec.cpp | 17 ++++++++----- media/libstagefright/OMXClient.cpp | 11 +++++++++ media/libstagefright/include/OMX.h | 4 ++++ media/libstagefright/include/OMXNodeInstance.h | 4 ++++ media/libstagefright/omx/OMX.cpp | 7 ++++++ media/libstagefright/omx/OMXNodeInstance.cpp | 20 ++++++++++++++++ 7 files changed, 90 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 5bbb2f0..ef99f4f 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -52,6 +52,7 @@ enum { OBSERVER_ON_MSG, GET_GRAPHIC_BUFFER_USAGE, SET_INTERNAL_OPTION, + UPDATE_GRAPHIC_BUFFER_IN_META, }; class BpOMX : public BpInterface { @@ -283,6 +284,21 @@ public: return err; } + virtual status_t updateGraphicBufferInMeta( + node_id node, OMX_U32 port_index, + const sp &graphicBuffer, buffer_id buffer) { + Parcel data, reply; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeIntPtr((intptr_t)node); + data.writeInt32(port_index); + data.write(*graphicBuffer); + data.writeIntPtr((intptr_t)buffer); + remote()->transact(UPDATE_GRAPHIC_BUFFER_IN_META, data, &reply); + + status_t err = reply.readInt32(); + return err; + } + virtual status_t createInputSurface( node_id node, OMX_U32 port_index, sp *bufferProducer) { @@ -691,6 +707,23 @@ status_t BnOMX::onTransact( return NO_ERROR; } + case UPDATE_GRAPHIC_BUFFER_IN_META: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + node_id node = (void*)data.readIntPtr(); + OMX_U32 port_index = data.readInt32(); + sp graphicBuffer = new GraphicBuffer(); + data.read(*graphicBuffer); + buffer_id buffer = (void*)data.readIntPtr(); + + status_t err = updateGraphicBufferInMeta( + node, port_index, graphicBuffer, buffer); + reply->writeInt32(err); + + return NO_ERROR; + } + case CREATE_INPUT_SURFACE: { CHECK_OMX_INTERFACE(IOMX, data, reply); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 00804c5..5aefa58 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -833,15 +833,20 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { oldest->mGraphicBuffer = new GraphicBuffer(buf, false); oldest->mStatus = BufferInfo::OWNED_BY_US; - struct VideoDecoderOutputMetaData metaData; - metaData.eType = kMetadataBufferTypeGrallocSource; - metaData.pHandle = oldest->mGraphicBuffer->handle; - memcpy(oldest->mData->base(), &metaData, sizeof(metaData)); + mOMX->updateGraphicBufferInMeta( + mNode, kPortIndexOutput, oldest->mGraphicBuffer, + oldest->mBufferID); - ALOGV("replaced oldest buffer #%u with age %u (%p stored in %p)", + VideoDecoderOutputMetaData *metaData = + reinterpret_cast( + oldest->mData->base()); + CHECK_EQ(metaData->eType, kMetadataBufferTypeGrallocSource); + + ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)", oldest - &mBuffers[kPortIndexOutput][0], mDequeueCounter - oldest->mDequeuedAt, - metaData.pHandle, oldest->mData->base()); + metaData->pHandle, + oldest->mGraphicBuffer->handle, oldest->mData->base()); return oldest; } diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index 810d88f..9820ef5 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -83,6 +83,10 @@ struct MuxOMX : public IOMX { node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id *buffer); + virtual status_t updateGraphicBufferInMeta( + node_id node, OMX_U32 port_index, + const sp &graphicBuffer, buffer_id buffer); + virtual status_t createInputSurface( node_id node, OMX_U32 port_index, sp *bufferProducer); @@ -287,6 +291,13 @@ status_t MuxOMX::useGraphicBuffer( node, port_index, graphicBuffer, buffer); } +status_t MuxOMX::updateGraphicBufferInMeta( + node_id node, OMX_U32 port_index, + const sp &graphicBuffer, buffer_id buffer) { + return getOMX(node)->updateGraphicBufferInMeta( + node, port_index, graphicBuffer, buffer); +} + status_t MuxOMX::createInputSurface( node_id node, OMX_U32 port_index, sp *bufferProducer) { diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index 7fed7d4..7e53af3 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -79,6 +79,10 @@ public: node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id *buffer); + virtual status_t updateGraphicBufferInMeta( + node_id node, OMX_U32 port_index, + const sp &graphicBuffer, buffer_id buffer); + virtual status_t createInputSurface( node_id node, OMX_U32 port_index, sp *bufferProducer); diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index f6ae376..ae498b4 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -66,6 +66,10 @@ struct OMXNodeInstance { OMX_U32 portIndex, const sp &graphicBuffer, OMX::buffer_id *buffer); + status_t updateGraphicBufferInMeta( + OMX_U32 portIndex, const sp &graphicBuffer, + OMX::buffer_id buffer); + status_t createInputSurface( OMX_U32 portIndex, sp *bufferProducer); diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 4b1dbe6..aaa9f89 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -345,6 +345,13 @@ status_t OMX::useGraphicBuffer( port_index, graphicBuffer, buffer); } +status_t OMX::updateGraphicBufferInMeta( + node_id node, OMX_U32 port_index, + const sp &graphicBuffer, buffer_id buffer) { + return findInstance(node)->updateGraphicBufferInMeta( + port_index, graphicBuffer, buffer); +} + status_t OMX::createInputSurface( node_id node, OMX_U32 port_index, sp *bufferProducer) { diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 525e18d..8d100f1 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -70,6 +70,10 @@ struct BufferMeta { header->nFilledLen); } + void setGraphicBuffer(const sp &graphicBuffer) { + mGraphicBuffer = graphicBuffer; + } + private: sp mGraphicBuffer; sp mMem; @@ -566,6 +570,22 @@ status_t OMXNodeInstance::useGraphicBuffer( return OK; } +status_t OMXNodeInstance::updateGraphicBufferInMeta( + OMX_U32 portIndex, const sp& graphicBuffer, + OMX::buffer_id buffer) { + Mutex::Autolock autoLock(mLock); + + OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)(buffer); + VideoDecoderOutputMetaData *metadata = + (VideoDecoderOutputMetaData *)(header->pBuffer); + BufferMeta *bufferMeta = (BufferMeta *)(header->pAppPrivate); + bufferMeta->setGraphicBuffer(graphicBuffer); + metadata->eType = kMetadataBufferTypeGrallocSource; + metadata->pHandle = graphicBuffer->handle; + + return OK; +} + status_t OMXNodeInstance::createInputSurface( OMX_U32 portIndex, sp *bufferProducer) { Mutex::Autolock autolock(mLock); -- cgit v1.1 From 4a0efb77198c69df711ab369ac482a42dbdfab07 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 8 Aug 2013 15:20:53 -0700 Subject: Fix regression for AudioRecord streaming callback mode The notification period (frequency of client wakeups) was broken in streaming callback mode. Fast tracks were OK, but non-fast tracks with normal latency weren't getting woken up at all. Bug: 10222943 Change-Id: Ife9a2f57fa73c6eb921f1c5ba62de0bfcc20557b --- media/libmedia/AudioRecord.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 2718420..616c3d6 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -245,9 +245,8 @@ status_t AudioRecord::set( return BAD_VALUE; } - if (notificationFrames == 0) { - notificationFrames = frameCount/2; - } + mNotificationFramesReq = notificationFrames; + mNotificationFramesAct = 0; // create the IAudioRecord status = openRecord_l(sampleRate, format, frameCount, mFlags, input, 0 /*epoch*/); @@ -267,8 +266,6 @@ status_t AudioRecord::set( mActive = false; mCbf = cbf; - mNotificationFramesReq = notificationFrames; - mNotificationFramesAct = 0; mRefreshRemaining = true; mUserData = user; // TODO: add audio hardware input latency here @@ -464,6 +461,15 @@ status_t AudioRecord::openRecord_l( } } + mNotificationFramesAct = mNotificationFramesReq; + + if (!(flags & AUDIO_INPUT_FLAG_FAST)) { + // Make sure that application is notified with sufficient margin before overrun + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { + mNotificationFramesAct = frameCount/2; + } + } + int originalSessionId = mSessionId; sp record = audioFlinger->openRecord(input, sampleRate, format, @@ -495,7 +501,6 @@ status_t AudioRecord::openRecord_l( mCblk = cblk; // FIXME missing fast track frameCount logic mAwaitBoost = false; - mNotificationFramesAct = mNotificationFramesReq; if (flags & AUDIO_INPUT_FLAG_FAST) { if (trackFlags & IAudioFlinger::TRACK_FAST) { ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", frameCount); -- cgit v1.1 From ac0bbe16f3eba46b3d8057b66c2aff9101fc6f7d Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 9 Aug 2013 18:32:30 -0700 Subject: Fix potential leak of audio input handle. The audio input handle is ultimately owned by the audio recorder object but it could be dropped on the floor if an error occurred before that object was fully initialized. Rearranged some of the argument validation and merged getInput_l with openRecord_l to simplify the code and prevent such a leak from occurring. Bug: 10265163 Change-Id: I124dce344b1d11c2dd66ca5e2c9aec0c52c230e2 --- media/libmedia/AudioRecord.cpp | 103 +++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 61 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 616c3d6..e934a3e 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -175,6 +175,7 @@ status_t AudioRecord::set( if (inputSource == AUDIO_SOURCE_DEFAULT) { inputSource = AUDIO_SOURCE_MIC; } + mInputSource = inputSource; if (sampleRate == 0) { ALOGE("Invalid sample rate %u", sampleRate); @@ -210,28 +211,10 @@ status_t AudioRecord::set( // Assumes audio_is_linear_pcm(format), else sizeof(uint8_t) mFrameSize = channelCount * audio_bytes_per_sample(format); - if (sessionId == 0 ) { - mSessionId = AudioSystem::newAudioSessionId(); - } else { - mSessionId = sessionId; - } - ALOGV("set(): mSessionId %d", mSessionId); - - mFlags = flags; - - audio_io_handle_t input = AudioSystem::getInput(inputSource, - sampleRate, - format, - channelMask, - mSessionId); - if (input == 0) { - ALOGE("Could not get audio input for record source %d", inputSource); - return BAD_VALUE; - } - // validate framecount size_t minFrameCount = 0; - status_t status = getMinFrameCount(&minFrameCount, sampleRate, format, channelMask); + status_t status = AudioRecord::getMinFrameCount(&minFrameCount, + sampleRate, format, channelMask); if (status != NO_ERROR) { ALOGE("getMinFrameCount() failed; status %d", status); return status; @@ -244,13 +227,23 @@ status_t AudioRecord::set( ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount); return BAD_VALUE; } + mFrameCount = frameCount; mNotificationFramesReq = notificationFrames; mNotificationFramesAct = 0; + if (sessionId == 0 ) { + mSessionId = AudioSystem::newAudioSessionId(); + } else { + mSessionId = sessionId; + } + ALOGV("set(): mSessionId %d", mSessionId); + + mFlags = flags; + // create the IAudioRecord - status = openRecord_l(sampleRate, format, frameCount, mFlags, input, 0 /*epoch*/); - if (status != NO_ERROR) { + status = openRecord_l(0 /*epoch*/); + if (status) { return status; } @@ -274,8 +267,6 @@ status_t AudioRecord::set( mMarkerReached = false; mNewPosition = 0; mUpdatePeriod = 0; - mInputSource = inputSource; - mInput = input; AudioSystem::acquireAudioSessionId(mSessionId); mSequence = 1; mObservedSequence = mSequence; @@ -429,13 +420,7 @@ unsigned int AudioRecord::getInputFramesLost() const // ------------------------------------------------------------------------- // must be called with mLock held -status_t AudioRecord::openRecord_l( - uint32_t sampleRate, - audio_format_t format, - size_t frameCount, - audio_input_flags_t flags, - audio_io_handle_t input, - size_t epoch) +status_t AudioRecord::openRecord_l(size_t epoch) { status_t status; const sp& audioFlinger = AudioSystem::get_audio_flinger(); @@ -449,12 +434,11 @@ status_t AudioRecord::openRecord_l( // Client can only express a preference for FAST. Server will perform additional tests. // The only supported use case for FAST is callback transfer mode. - if (flags & AUDIO_INPUT_FLAG_FAST) { + if (mFlags & AUDIO_INPUT_FLAG_FAST) { if ((mTransfer != TRANSFER_CALLBACK) || (mAudioRecordThread == 0)) { ALOGW("AUDIO_INPUT_FLAG_FAST denied by client"); // once denied, do not request again if IAudioRecord is re-created - flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST); - mFlags = flags; + mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST); } else { trackFlags |= IAudioFlinger::TRACK_FAST; tid = mAudioRecordThread->getTid(); @@ -463,18 +447,25 @@ status_t AudioRecord::openRecord_l( mNotificationFramesAct = mNotificationFramesReq; - if (!(flags & AUDIO_INPUT_FLAG_FAST)) { + if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) { // Make sure that application is notified with sufficient margin before overrun - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { - mNotificationFramesAct = frameCount/2; + if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount/2) { + mNotificationFramesAct = mFrameCount/2; } } + audio_io_handle_t input = AudioSystem::getInput(mInputSource, mSampleRate, mFormat, + mChannelMask, mSessionId); + if (input == 0) { + ALOGE("Could not get audio input for record source %d", mInputSource); + return BAD_VALUE; + } + int originalSessionId = mSessionId; sp record = audioFlinger->openRecord(input, - sampleRate, format, + mSampleRate, mFormat, mChannelMask, - frameCount, + mFrameCount, &trackFlags, tid, &mSessionId, @@ -484,6 +475,7 @@ status_t AudioRecord::openRecord_l( if (record == 0) { ALOGE("AudioFlinger could not create record track, status: %d", status); + AudioSystem::releaseInput(input); return status; } sp iMem = record->getCblk(); @@ -495,27 +487,27 @@ status_t AudioRecord::openRecord_l( mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this); mDeathNotifier.clear(); } + mInput = input; mAudioRecord = record; mCblkMemory = iMem; audio_track_cblk_t* cblk = static_cast(iMem->pointer()); mCblk = cblk; // FIXME missing fast track frameCount logic mAwaitBoost = false; - if (flags & AUDIO_INPUT_FLAG_FAST) { + if (mFlags & AUDIO_INPUT_FLAG_FAST) { if (trackFlags & IAudioFlinger::TRACK_FAST) { - ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", frameCount); + ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", mFrameCount); mAwaitBoost = true; // double-buffering is not required for fast tracks, due to tighter scheduling - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) { - mNotificationFramesAct = frameCount; + if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount) { + mNotificationFramesAct = mFrameCount; } } else { - ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", frameCount); + ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", mFrameCount); // once denied, do not request again if IAudioRecord is re-created - flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_FAST); - mFlags = flags; - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { - mNotificationFramesAct = frameCount/2; + mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST); + if (mNotificationFramesAct == 0 || mNotificationFramesAct > mFrameCount/2) { + mNotificationFramesAct = mFrameCount/2; } } } @@ -524,7 +516,7 @@ status_t AudioRecord::openRecord_l( void *buffers = (char*)cblk + sizeof(audio_track_cblk_t); // update proxy - mProxy = new AudioRecordClientProxy(cblk, buffers, frameCount, mFrameSize); + mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize); mProxy->setEpoch(epoch); mProxy->setMinimum(mNotificationFramesAct); @@ -651,17 +643,6 @@ audio_io_handle_t AudioRecord::getInput() const return mInput; } -// must be called with mLock held -audio_io_handle_t AudioRecord::getInput_l() -{ - mInput = AudioSystem::getInput(mInputSource, - mSampleRate, - mFormat, - mChannelMask, - mSessionId); - return mInput; -} - // ------------------------------------------------------------------------- ssize_t AudioRecord::read(void* buffer, size_t userSize) @@ -949,7 +930,7 @@ status_t AudioRecord::restoreRecord_l(const char *from) // It will also delete the strong references on previous IAudioRecord and IMemory size_t position = mProxy->getPosition(); mNewPosition = position + mUpdatePeriod; - result = openRecord_l(mSampleRate, mFormat, mFrameCount, mFlags, getInput_l(), position); + result = openRecord_l(position); if (result == NO_ERROR) { if (mActive) { // callback thread or sync event hasn't changed -- cgit v1.1 From b8689b31813f55cbc1bf8e9ca0d46a9ee113e38a Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 12 Aug 2013 10:08:23 -0700 Subject: Support for "request-sync" parameter in ACodec Also returns errors to caller instead of asserting... Change-Id: Id3018655a2b3da4289167fba16af907350a511ae --- media/libstagefright/ACodec.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 5aefa58..36549d1 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -4123,13 +4123,28 @@ status_t ACodec::setParameters(const sp ¶ms) { if (params->findInt32("drop-input-frames", &dropInputFrames)) { bool suspend = dropInputFrames != 0; - CHECK_EQ((status_t)OK, - mOMX->setInternalOption( + status_t err = + mOMX->setInternalOption( mNode, kPortIndexInput, IOMX::INTERNAL_OPTION_SUSPEND, &suspend, - sizeof(suspend))); + sizeof(suspend)); + + if (err != OK) { + ALOGE("Failed to set parameter 'drop-input-frames' (err %d)", err); + return err; + } + } + + int32_t dummy; + if (params->findInt32("request-sync", &dummy)) { + status_t err = requestIDRFrame(); + + if (err != OK) { + ALOGE("Requesting a sync frame failed w/ err %d", err); + return err; + } } return OK; -- cgit v1.1 From a61285dcf1da8a2cf40c499ee3a7b9fc4d74ac58 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 31 Jul 2013 13:50:42 -0700 Subject: Optionally repeat the previously submitted frame to the encoder if no new frame has been delivered by surface flinger within the timeout interval. Change-Id: I282f1b726dfe5646b178d7858d6f5d4f5a264fde --- media/libstagefright/ACodec.cpp | 27 +++- media/libstagefright/omx/GraphicBufferSource.cpp | 167 ++++++++++++++++++++++- media/libstagefright/omx/GraphicBufferSource.h | 38 ++++++ media/libstagefright/omx/OMXNodeInstance.cpp | 21 ++- 4 files changed, 244 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 5aefa58..cd67359 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -369,7 +369,8 @@ ACodec::ACodec() mChannelMask(0), mDequeueCounter(0), mStoreMetaDataInOutputBuffers(false), - mMetaDataBuffersToSubmit(0) { + mMetaDataBuffersToSubmit(0), + mRepeatFrameDelayUs(-1ll) { mUninitializedState = new UninitializedState(this); mLoadedState = new LoadedState(this); mLoadedToIdleState = new LoadedToIdleState(this); @@ -1089,6 +1090,12 @@ status_t ACodec::configureCodec( } else { mUseMetadataOnEncoderOutput = enable; } + + if (!msg->findInt64( + "repeat-previous-frame-after", + &mRepeatFrameDelayUs)) { + mRepeatFrameDelayUs = -1ll; + } } // Always try to enable dynamic output buffers on native surface @@ -3611,6 +3618,7 @@ void ACodec::LoadedState::stateEntered() { mCodec->mDequeueCounter = 0; mCodec->mMetaDataBuffersToSubmit = 0; + mCodec->mRepeatFrameDelayUs = -1ll; if (mCodec->mShutdownInProgress) { bool keepComponentAllocated = mCodec->mKeepComponentAllocated; @@ -3742,6 +3750,23 @@ void ACodec::LoadedState::onCreateInputSurface( err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput, &bufferProducer); + + if (err == OK && mCodec->mRepeatFrameDelayUs > 0ll) { + err = mCodec->mOMX->setInternalOption( + mCodec->mNode, + kPortIndexInput, + IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY, + &mCodec->mRepeatFrameDelayUs, + sizeof(mCodec->mRepeatFrameDelayUs)); + + if (err != OK) { + ALOGE("[%s] Unable to configure option to repeat previous " + "frames (err %d)", + mCodec->mComponentName.c_str(), + err); + } + } + if (err == OK) { notify->setObject("input-surface", new BufferProducerWrapper(bufferProducer)); diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index 325ffcf..cf43e94 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -39,7 +40,13 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, mSuspended(false), mNumFramesAvailable(0), mEndOfStream(false), - mEndOfStreamSent(false) { + mEndOfStreamSent(false), + mRepeatAfterUs(-1ll), + mRepeatLastFrameGeneration(0), + mLatestSubmittedBufferId(-1), + mLatestSubmittedBufferFrameNum(0), + mLatestSubmittedBufferUseCount(0), + mRepeatBufferDeferred(false) { ALOGV("GraphicBufferSource w=%u h=%u c=%u", bufferWidth, bufferHeight, bufferCount); @@ -123,6 +130,22 @@ void GraphicBufferSource::omxExecuting() { if (mEndOfStream && mNumFramesAvailable == 0) { submitEndOfInputStream_l(); } + + if (mRepeatAfterUs > 0ll && mLooper == NULL) { + mReflector = new AHandlerReflector(this); + + mLooper = new ALooper; + mLooper->registerHandler(mReflector); + mLooper->start(); + + if (mLatestSubmittedBufferId >= 0) { + sp msg = + new AMessage(kWhatRepeatLastFrame, mReflector->id()); + + msg->setInt32("generation", ++mRepeatLastFrameGeneration); + msg->post(mRepeatAfterUs); + } + } } void GraphicBufferSource::omxLoaded(){ @@ -132,6 +155,14 @@ void GraphicBufferSource::omxLoaded(){ ALOGW("Dropped back down to Loaded without Executing"); } + if (mLooper != NULL) { + mLooper->unregisterHandler(mReflector->id()); + mReflector.clear(); + + mLooper->stop(); + mLooper.clear(); + } + ALOGV("--> loaded; avail=%d eos=%d eosSent=%d", mNumFramesAvailable, mEndOfStream, mEndOfStreamSent); @@ -211,8 +242,12 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { ALOGV("cbi %d matches bq slot %d, handle=%p", cbi, id, mBufferSlot[id]->handle); - mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + if (id == mLatestSubmittedBufferId) { + CHECK_GT(mLatestSubmittedBufferUseCount--, 0); + } else { + mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + } } else { ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", cbi); @@ -232,7 +267,16 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // send that. ALOGV("buffer freed, EOS pending"); submitEndOfInputStream_l(); + } else if (mRepeatBufferDeferred) { + bool success = repeatLatestSubmittedBuffer_l(); + if (success) { + ALOGV("deferred repeatLatestSubmittedBuffer_l SUCCESS"); + } else { + ALOGV("deferred repeatLatestSubmittedBuffer_l FAILURE"); + } + mRepeatBufferDeferred = false; } + return; } @@ -264,6 +308,16 @@ void GraphicBufferSource::suspend(bool suspend) { } mSuspended = false; + + if (mExecuting && mNumFramesAvailable == 0 && mRepeatBufferDeferred) { + if (repeatLatestSubmittedBuffer_l()) { + ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l SUCCESS"); + + mRepeatBufferDeferred = false; + } else { + ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l FAILURE"); + } + } } bool GraphicBufferSource::fillCodecBuffer_l() { @@ -318,11 +372,68 @@ bool GraphicBufferSource::fillCodecBuffer_l() { EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); } else { ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); + setLatestSubmittedBuffer_l(item); + } + + return true; +} + +bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() { + CHECK(mExecuting && mNumFramesAvailable == 0); + + if (mLatestSubmittedBufferId < 0 || mSuspended) { + return false; + } + + int cbi = findAvailableCodecBuffer_l(); + if (cbi < 0) { + // No buffers available, bail. + ALOGV("repeatLatestSubmittedBuffer_l: no codec buffers."); + return false; + } + + BufferQueue::BufferItem item; + item.mBuf = mLatestSubmittedBufferId; + item.mFrameNumber = mLatestSubmittedBufferFrameNum; + + status_t err = submitBuffer_l(item, cbi); + + if (err != OK) { + return false; } + ++mLatestSubmittedBufferUseCount; + return true; } +void GraphicBufferSource::setLatestSubmittedBuffer_l( + const BufferQueue::BufferItem &item) { + ALOGV("setLatestSubmittedBuffer_l"); + + if (mLatestSubmittedBufferId >= 0) { + if (mLatestSubmittedBufferUseCount == 0) { + mBufferQueue->releaseBuffer( + mLatestSubmittedBufferId, + mLatestSubmittedBufferFrameNum, + EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, + Fence::NO_FENCE); + } + } + + mLatestSubmittedBufferId = item.mBuf; + mLatestSubmittedBufferFrameNum = item.mFrameNumber; + mLatestSubmittedBufferUseCount = 1; + mRepeatBufferDeferred = false; + + if (mReflector != NULL) { + sp msg = new AMessage(kWhatRepeatLastFrame, mReflector->id()); + msg->setInt32("generation", ++mRepeatLastFrameGeneration); + msg->post(mRepeatAfterUs); + } +} + status_t GraphicBufferSource::signalEndOfInputStream() { Mutex::Autolock autoLock(mMutex); ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d", @@ -470,6 +581,9 @@ void GraphicBufferSource::onFrameAvailable() { mNumFramesAvailable++; + mRepeatBufferDeferred = false; + ++mRepeatLastFrameGeneration; + if (mExecuting) { fillCodecBuffer_l(); } @@ -495,4 +609,51 @@ void GraphicBufferSource::onBuffersReleased() { } } +status_t GraphicBufferSource::setRepeatPreviousFrameDelayUs( + int64_t repeatAfterUs) { + Mutex::Autolock autoLock(mMutex); + + if (mExecuting || repeatAfterUs <= 0ll) { + return INVALID_OPERATION; + } + + mRepeatAfterUs = repeatAfterUs; + + return OK; +} + +void GraphicBufferSource::onMessageReceived(const sp &msg) { + switch (msg->what()) { + case kWhatRepeatLastFrame: + { + Mutex::Autolock autoLock(mMutex); + + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mRepeatLastFrameGeneration) { + // stale + break; + } + + if (!mExecuting || mNumFramesAvailable > 0) { + break; + } + + bool success = repeatLatestSubmittedBuffer_l(); + + if (success) { + ALOGV("repeatLatestSubmittedBuffer_l SUCCESS"); + } else { + ALOGV("repeatLatestSubmittedBuffer_l FAILURE"); + mRepeatBufferDeferred = true; + } + break; + } + + default: + TRESPASS(); + } +} + } // namespace android diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index ac73770..244a843 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -25,6 +25,8 @@ #include #include "../include/OMXNodeInstance.h" #include +#include +#include namespace android { @@ -89,6 +91,15 @@ public: // in the BufferQueue) will be discarded until the suspension is lifted. void suspend(bool suspend); + // Specifies the interval after which we requeue the buffer previously + // queued to the encoder. This is useful in the case of surface flinger + // providing the input surface if the resulting encoded stream is to + // be displayed "live". If we were not to push through the extra frame + // the decoder on the remote end would be unable to decode the latest frame. + // This API must be called before transitioning the encoder to "executing" + // state and once this behaviour is specified it cannot be reset. + status_t setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs); + protected: // BufferQueue::ConsumerListener interface, called when a new frame of // data is available. If we're executing and a codec buffer is @@ -147,6 +158,9 @@ private: // doing anything if we don't have a codec buffer available. void submitEndOfInputStream_l(); + void setLatestSubmittedBuffer_l(const BufferQueue::BufferItem &item); + bool repeatLatestSubmittedBuffer_l(); + // Lock, covers all member variables. mutable Mutex mMutex; @@ -181,6 +195,30 @@ private: // Tracks codec buffers. Vector mCodecBuffers; + //// + friend class AHandlerReflector; + + enum { + kWhatRepeatLastFrame, + }; + + int64_t mRepeatAfterUs; + + sp mLooper; + sp > mReflector; + + int32_t mRepeatLastFrameGeneration; + + int mLatestSubmittedBufferId; + uint64_t mLatestSubmittedBufferFrameNum; + int32_t mLatestSubmittedBufferUseCount; + + // The previously submitted buffer should've been repeated but + // no codec buffer was available at the time. + bool mRepeatBufferDeferred; + + void onMessageReceived(const sp &msg); + DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource); }; diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 8d100f1..ef683a0 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -809,6 +809,7 @@ status_t OMXNodeInstance::setInternalOption( size_t size) { switch (type) { case IOMX::INTERNAL_OPTION_SUSPEND: + case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY: { const sp &bufferSource = getGraphicBufferSource(); @@ -817,12 +818,22 @@ status_t OMXNodeInstance::setInternalOption( return ERROR_UNSUPPORTED; } - if (size != sizeof(bool)) { - return INVALID_OPERATION; - } + if (type == IOMX::INTERNAL_OPTION_SUSPEND) { + if (size != sizeof(bool)) { + return INVALID_OPERATION; + } + + bool suspend = *(bool *)data; + bufferSource->suspend(suspend); + } else { + if (size != sizeof(int64_t)) { + return INVALID_OPERATION; + } + + int64_t delayUs = *(int64_t *)data; - bool suspend = *(bool *)data; - bufferSource->suspend(suspend); + return bufferSource->setRepeatPreviousFrameDelayUs(delayUs); + } return OK; } -- cgit v1.1 From 2ee14000a38683220fb250d9e7300e1d71ccdaa0 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 6 Aug 2013 15:02:22 -0700 Subject: Check adaptive playback support via CodecCapabilities Added flags field to CodecCapabilities, so that applications can query whether codecs support various features. For now added one video-decoder feature: kFlagSupportsAdaptivePlayback Media playback applications can query it to see if a codec supports seamless resolution changes, such as by supporting dynamic output buffers. Signed-off-by: Lajos Molnar Change-Id: I09da46e8ab6b8645fa2749e33128e49eda2e865b Related-to-bug: 7093648 --- media/libstagefright/MediaCodecList.cpp | 5 ++++- media/libstagefright/OMXCodec.cpp | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index d24337f..6248e90 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -509,7 +509,8 @@ status_t MediaCodecList::getSupportedTypes( status_t MediaCodecList::getCodecCapabilities( size_t index, const char *type, Vector *profileLevels, - Vector *colorFormats) const { + Vector *colorFormats, + uint32_t *flags) const { profileLevels->clear(); colorFormats->clear(); @@ -547,6 +548,8 @@ status_t MediaCodecList::getCodecCapabilities( colorFormats->push(caps.mColorFormats.itemAt(i)); } + *flags = caps.mFlags; + return OK; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 3de3c28..7b37365 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -4567,7 +4567,7 @@ status_t QueryCodec( CodecCapabilities *caps) { if (strncmp(componentName, "OMX.", 4)) { // Not an OpenMax component but a software codec. - + caps->mFlags = 0; caps->mComponentName = componentName; return OK; } @@ -4582,8 +4582,15 @@ status_t QueryCodec( OMXCodec::setComponentRole(omx, node, isEncoder, mime); + caps->mFlags = 0; caps->mComponentName = componentName; + if (!isEncoder && !strncmp(mime, "video/", 6) && + omx->storeMetaDataInBuffers( + node, 1 /* port index */, OMX_TRUE) == OK) { + caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback; + } + OMX_VIDEO_PARAM_PROFILELEVELTYPE param; InitOMXParams(¶m); -- cgit v1.1 From 0167414e261f88a96b5e4bf6cb592e6ca11e5a95 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 14 Aug 2013 10:28:14 -0700 Subject: You can now opt-in to having the video decoder push blank buffers to the surface on the transition from executing->idle by specifying the key "push-blank-buffers-on-shutdown" with a value of 1 in the call to configure. Change-Id: I1155fccf89f18e717728c985e590651597595515 --- media/libstagefright/ACodec.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 7b0bce0..2e55c4f 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1114,6 +1114,12 @@ status_t ACodec::configureCodec( ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str()); mStoreMetaDataInOutputBuffers = true; } + + int32_t push; + if (msg->findInt32("push-blank-buffers-on-shutdown", &push) + && push != 0) { + mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown; + } } if (video) { @@ -3584,6 +3590,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { if (componentName.endsWith(".secure")) { mCodec->mFlags |= kFlagIsSecure; + mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown; } mCodec->mQuirks = quirks; @@ -4413,7 +4420,8 @@ void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() { CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexInput), (status_t)OK); CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexOutput), (status_t)OK); - if (mCodec->mFlags & kFlagIsSecure && mCodec->mNativeWindow != NULL) { + if ((mCodec->mFlags & kFlagPushBlankBuffersToNativeWindowOnShutdown) + && mCodec->mNativeWindow != NULL) { // We push enough 1x1 blank buffers to ensure that one of // them has made it to the display. This allows the OMX // component teardown to zero out any protected buffers -- cgit v1.1 From 88c3c4acb13a93209b3572fecd585099defec184 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 14 Aug 2013 17:08:40 -0700 Subject: fix build. optional tests broke Change-Id: Ifb38fb2a7bd9c3d6305726f8e6d661be05cdcf7e --- media/libstagefright/tests/SurfaceMediaSource_test.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'media') diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp index a5459fe..49ffcd6 100644 --- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp +++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include #include -- cgit v1.1 From cbaffcffee6418d678806e63097c19fe26d48fe0 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 14 Aug 2013 18:30:38 -0700 Subject: Add MEDIA_STARTED/PAUSED/STOPPED events to media players This is needed for the MediaTimeProvider java interface, so it does not continually poll for current media time. Note: NuPlayer and AwesomePlayer do not correctly handle stop (pause instead), so for those we will signal PAUSED. Signed-off-by: Lajos Molnar Change-Id: I3c61e1bda475f131323f475c18a42e3ec66c9ae1 Bug: 10326117 --- media/libmediaplayerservice/MidiFile.cpp | 6 +++++ media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 3 +++ .../nuplayer/NuPlayerDriver.cpp | 5 ++++ .../nuplayer/NuPlayerRenderer.cpp | 30 ++++++++++++++++++++++ .../nuplayer/NuPlayerRenderer.h | 6 +++++ media/libstagefright/AwesomePlayer.cpp | 22 ++++++++++++++++ media/libstagefright/include/AwesomePlayer.h | 3 +++ 7 files changed, 75 insertions(+) (limited to 'media') diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 270b872..0a6aa90 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -220,6 +220,9 @@ status_t MidiFile::start() } mRender = true; + if (mState == EAS_STATE_PLAY) { + sendEvent(MEDIA_STARTED); + } // wake up render thread ALOGV(" wakeup render thread"); @@ -242,6 +245,7 @@ status_t MidiFile::stop() } } mPaused = false; + sendEvent(MEDIA_STOPPED); return NO_ERROR; } @@ -279,6 +283,7 @@ status_t MidiFile::pause() return ERROR_EAS_FAILURE; } mPaused = true; + sendEvent(MEDIA_PAUSED); return NO_ERROR; } @@ -382,6 +387,7 @@ status_t MidiFile::reset() status_t MidiFile::reset_nosync() { ALOGV("MidiFile::reset_nosync"); + sendEvent(MEDIA_STOPPED); // close file if (mEasHandle) { EAS_CloseFile(mEasData, mEasHandle); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 7e81035..b411f34 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -731,6 +731,9 @@ void NuPlayer::onMessageReceived(const sp &msg) { ALOGV("renderer %s flush completed.", audio ? "audio" : "video"); } else if (what == Renderer::kWhatVideoRenderingStart) { notifyListener(MEDIA_INFO, MEDIA_INFO_RENDERING_START, 0); + } else if (what == Renderer::kWhatMediaRenderingStart) { + ALOGV("media rendering started"); + notifyListener(MEDIA_STARTED, 0, 0); } break; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 68b9623..cf0373c 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -255,6 +255,7 @@ status_t NuPlayerDriver::pause() { return OK; case STATE_RUNNING: + notifyListener(MEDIA_PAUSED); mPlayer->pause(); break; @@ -287,6 +288,8 @@ status_t NuPlayerDriver::seekTo(int msec) { case STATE_PAUSED: { mAtEOS = false; + // seeks can take a while, so we essentially paused + notifyListener(MEDIA_PAUSED); mPlayer->seekToAsync(seekTimeUs); break; } @@ -345,6 +348,8 @@ status_t NuPlayerDriver::reset() { break; } + notifyListener(MEDIA_STOPPED); + mState = STATE_RESET_IN_PROGRESS; mPlayer->resetAsync(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index b543d9d..3b2784b 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -50,6 +50,8 @@ NuPlayer::Renderer::Renderer( mSyncQueues(false), mPaused(false), mVideoRenderingStarted(false), + mVideoRenderingStartGeneration(0), + mAudioRenderingStartGeneration(0), mLastPositionUpdateUs(-1ll), mVideoLateByUs(0ll) { } @@ -220,6 +222,23 @@ void NuPlayer::Renderer::signalAudioSinkChanged() { (new AMessage(kWhatAudioSinkChanged, id()))->post(); } +void NuPlayer::Renderer::prepareForMediaRenderingStart() { + mAudioRenderingStartGeneration = mAudioQueueGeneration; + mVideoRenderingStartGeneration = mVideoQueueGeneration; +} + +void NuPlayer::Renderer::notifyIfMediaRenderingStarted() { + if (mVideoRenderingStartGeneration == mVideoQueueGeneration && + mAudioRenderingStartGeneration == mAudioQueueGeneration) { + mVideoRenderingStartGeneration = -1; + mAudioRenderingStartGeneration = -1; + + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatMediaRenderingStart); + notify->post(); + } +} + bool NuPlayer::Renderer::onDrainAudioQueue() { uint32_t numFramesPlayed; if (mAudioSink->getPosition(&numFramesPlayed) != OK) { @@ -299,6 +318,8 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { numBytesAvailableToWrite -= copy; size_t copiedFrames = copy / mAudioSink->frameSize(); mNumFramesWritten += copiedFrames; + + notifyIfMediaRenderingStarted(); } notifyPosition(); @@ -405,6 +426,8 @@ void NuPlayer::Renderer::onDrainVideoQueue() { notifyVideoRenderingStart(); } + notifyIfMediaRenderingStarted(); + notifyPosition(); } @@ -552,6 +575,7 @@ void NuPlayer::Renderer::onFlush(const sp &msg) { // is flushed. syncQueuesDone(); + ALOGV("flushing %s", audio ? "audio" : "video"); if (audio) { flushQueue(&mAudioQueue); @@ -560,6 +584,8 @@ void NuPlayer::Renderer::onFlush(const sp &msg) { mDrainAudioQueuePending = false; ++mAudioQueueGeneration; + + prepareForMediaRenderingStart(); } else { flushQueue(&mVideoQueue); @@ -568,6 +594,8 @@ void NuPlayer::Renderer::onFlush(const sp &msg) { mDrainVideoQueuePending = false; ++mVideoQueueGeneration; + + prepareForMediaRenderingStart(); } notifyFlushComplete(audio); @@ -658,6 +686,8 @@ void NuPlayer::Renderer::onPause() { mDrainVideoQueuePending = false; ++mVideoQueueGeneration; + prepareForMediaRenderingStart(); + if (mHasAudio) { mAudioSink->pause(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index c9796e2..94a05ea 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -53,6 +53,7 @@ struct NuPlayer::Renderer : public AHandler { kWhatFlushComplete = 'fluC', kWhatPosition = 'posi', kWhatVideoRenderingStart = 'vdrd', + kWhatMediaRenderingStart = 'mdrd', }; protected: @@ -106,6 +107,8 @@ private: bool mPaused; bool mVideoRenderingStarted; + int32_t mVideoRenderingStartGeneration; + int32_t mAudioRenderingStartGeneration; int64_t mLastPositionUpdateUs; int64_t mVideoLateByUs; @@ -116,6 +119,9 @@ private: void onDrainVideoQueue(); void postDrainVideoQueue(); + void prepareForMediaRenderingStart(); + void notifyIfMediaRenderingStarted(); + void onQueueBuffer(const sp &msg); void onQueueEOS(const sp &msg); void onFlush(const sp &msg); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 79f2c91..52e178e 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -191,6 +191,8 @@ AwesomePlayer::AwesomePlayer() mTimeSource(NULL), mVideoRenderingStarted(false), mVideoRendererIsPreview(false), + mMediaRenderingStartGeneration(0), + mStartGeneration(0), mAudioPlayer(NULL), mDisplayWidth(0), mDisplayHeight(0), @@ -491,6 +493,8 @@ void AwesomePlayer::reset_l() { mDisplayWidth = 0; mDisplayHeight = 0; + notifyListener_l(MEDIA_STOPPED); + if (mDecryptHandle != NULL) { mDrmManagerClient->setPlaybackStatus(mDecryptHandle, Playback::STOP, 0); @@ -1025,6 +1029,13 @@ void AwesomePlayer::createAudioPlayer_l() seekAudioIfNecessary_l(); } +void AwesomePlayer::notifyIfMediaStarted_l() { + if (mMediaRenderingStartGeneration == mStartGeneration) { + mMediaRenderingStartGeneration = -1; + notifyListener_l(MEDIA_STARTED); + } +} + status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) { CHECK(!(mFlags & AUDIO_RUNNING)); status_t err = OK; @@ -1061,6 +1072,8 @@ status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) { // We will have finished the seek while starting the audio player. postAudioSeekComplete(); + } else { + notifyIfMediaStarted_l(); } } else { err = mAudioPlayer->resume(); @@ -1201,6 +1214,9 @@ status_t AwesomePlayer::pause_l(bool at_eos) { return OK; } + notifyListener_l(MEDIA_PAUSED); + mMediaRenderingStartGeneration = ++mStartGeneration; + cancelPlayerEvents(true /* keepNotifications */); if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) { @@ -1389,6 +1405,9 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) { mSeekTimeUs = timeUs; modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR); + notifyListener_l(MEDIA_PAUSED); + mMediaRenderingStartGeneration = ++mStartGeneration; + seekAudioIfNecessary_l(); if (mFlags & TEXTPLAYER_INITIALIZED) { @@ -1903,6 +1922,7 @@ void AwesomePlayer::onVideoEvent() { notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START); } + notifyIfMediaStarted_l(); } mVideoBuffer->release(); @@ -1998,6 +2018,8 @@ void AwesomePlayer::onCheckAudioStatus() { } mSeeking = NO_SEEK; + + notifyIfMediaStarted_l(); } status_t finalStatus; diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index d3c74e2..b001cf4 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -169,6 +169,8 @@ private: sp mVideoRenderer; bool mVideoRenderingStarted; bool mVideoRendererIsPreview; + int32_t mMediaRenderingStartGeneration; + int32_t mStartGeneration; ssize_t mActiveAudioTrackIndex; sp mAudioTrack; @@ -294,6 +296,7 @@ private: void finishSeekIfNecessary(int64_t videoTimeUs); void ensureCacheIsFetching_l(); + void notifyIfMediaStarted_l(); void createAudioPlayer_l(); status_t startAudioPlayer_l(bool sendErrorNotification = true); -- cgit v1.1 From 7fb865653293e665f48b31e791ca124e98c7d257 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 15 Aug 2013 18:06:37 -0700 Subject: Remove call to enable/disable remote submix from test program. The submix will be controlled automatically from now on based on whether there is an active audio recorder. Bug: 10265163 Change-Id: Iea8164182daa037066f60974b54597d20db4903b --- media/libstagefright/wifi-display/wfd.cpp | 26 -------------------------- 1 file changed, 26 deletions(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 4607606..04cb319 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -138,28 +138,6 @@ void RemoteDisplayClient::waitUntilDone() { } } -static status_t enableAudioSubmix(bool enable) { - status_t err = AudioSystem::setDeviceConnectionState( - AUDIO_DEVICE_IN_REMOTE_SUBMIX, - enable - ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE - : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - NULL /* device_address */); - - if (err != OK) { - return err; - } - - err = AudioSystem::setDeviceConnectionState( - AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - enable - ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE - : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - NULL /* device_address */); - - return err; -} - static void createSource(const AString &addr, int32_t port) { sp sm = defaultServiceManager(); sp binder = sm->getService(String16("media.player")); @@ -168,8 +146,6 @@ static void createSource(const AString &addr, int32_t port) { CHECK(service.get() != NULL); - enableAudioSubmix(true /* enable */); - String8 iface; iface.append(addr.c_str()); iface.append(StringPrintf(":%d", port).c_str()); @@ -182,8 +158,6 @@ static void createSource(const AString &addr, int32_t port) { display->dispose(); display.clear(); - - enableAudioSubmix(false /* enable */); } static void createFileSource( -- cgit v1.1 From dcb89b3b505522efde173c105a851c412f947178 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 6 Aug 2013 09:44:47 -0700 Subject: MediaPlayer: add listener for raw track data Bug: 10326117 Change-Id: I2c0bdf8adc67b11f8dc633423bee66897548f181 --- media/libmedia/mediaplayer.cpp | 3 + .../nuplayer/HTTPLiveSource.cpp | 58 ++++++++++++- .../nuplayer/HTTPLiveSource.h | 4 + media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 87 +++++++++++++++++++- media/libmediaplayerservice/nuplayer/NuPlayer.h | 6 +- .../nuplayer/NuPlayerDriver.cpp | 22 ++++- .../nuplayer/NuPlayerDriver.h | 2 +- .../nuplayer/NuPlayerSource.h | 9 ++ media/libstagefright/httplive/Android.mk | 1 + media/libstagefright/httplive/LiveSession.cpp | 59 +++++++++++--- media/libstagefright/httplive/LiveSession.h | 9 +- media/libstagefright/httplive/M3UParser.cpp | 95 +++++++++++++++++++++- media/libstagefright/httplive/M3UParser.h | 4 + media/libstagefright/httplive/PlaylistFetcher.cpp | 15 +++- media/libstagefright/httplive/PlaylistFetcher.h | 3 +- 15 files changed, 352 insertions(+), 25 deletions(-) (limited to 'media') diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 056cc0a..4323d0c 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -756,6 +756,9 @@ void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) case MEDIA_TIMED_TEXT: ALOGV("Received timed text message"); break; + case MEDIA_SUBTITLE_DATA: + ALOGV("Received subtitle data message"); + break; default: ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); break; diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index c8901ce..d8b35d7 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -43,7 +43,8 @@ NuPlayer::HTTPLiveSource::HTTPLiveSource( mUID(uid), mFlags(0), mFinalResult(OK), - mOffset(0) { + mOffset(0), + mFetchSubtitleDataGeneration(0) { if (headers) { mExtraHeaders = *headers; @@ -120,6 +121,28 @@ status_t NuPlayer::HTTPLiveSource::getDuration(int64_t *durationUs) { return mLiveSession->getDuration(durationUs); } +status_t NuPlayer::HTTPLiveSource::getTrackInfo(Parcel *reply) const { + return mLiveSession->getTrackInfo(reply); +} + +status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select) { + status_t err = mLiveSession->selectTrack(trackIndex, select); + + if (err == OK) { + mFetchSubtitleDataGeneration++; + if (select) { + sp msg = new AMessage(kWhatFetchSubtitleData, id()); + msg->setInt32("generation", mFetchSubtitleDataGeneration); + msg->post(); + } + } + + // LiveSession::selectTrack returns BAD_VALUE when selecting the currently + // selected track, or unselecting a non-selected track. In this case it's an + // no-op so we return OK. + return (err == OK || err == BAD_VALUE) ? OK : err; +} + status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) { return mLiveSession->seekTo(seekTimeUs); } @@ -132,6 +155,39 @@ void NuPlayer::HTTPLiveSource::onMessageReceived(const sp &msg) { break; } + case kWhatFetchSubtitleData: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mFetchSubtitleDataGeneration) { + // stale + break; + } + + sp buffer; + if (mLiveSession->dequeueAccessUnit( + LiveSession::STREAMTYPE_SUBTITLES, &buffer) == OK) { + sp notify = dupNotify(); + notify->setInt32("what", kWhatSubtitleData); + notify->setBuffer("buffer", buffer); + notify->post(); + + int64_t timeUs, baseUs, durationUs, delayUs; + CHECK(buffer->meta()->findInt64("baseUs", &baseUs)); + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + CHECK(buffer->meta()->findInt64("durationUs", &durationUs)); + delayUs = baseUs + timeUs - ALooper::GetNowUs(); + + msg->post(delayUs > 0ll ? delayUs : 0ll); + } else { + // try again in 1 second + msg->post(1000000ll); + } + + break; + } + default: Source::onMessageReceived(msg); break; diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index aa9434b..bcc3f8b 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -41,6 +41,8 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { virtual status_t feedMoreTSData(); virtual status_t getDuration(int64_t *durationUs); + virtual status_t getTrackInfo(Parcel *reply) const; + virtual status_t selectTrack(size_t trackIndex, bool select); virtual status_t seekTo(int64_t seekTimeUs); protected: @@ -56,6 +58,7 @@ private: enum { kWhatSessionNotify, + kWhatFetchSubtitleData, }; AString mURL; @@ -67,6 +70,7 @@ private: off64_t mOffset; sp mLiveLooper; sp mLiveSession; + int32_t mFetchSubtitleDataGeneration; void onSessionNotify(const sp &msg); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index b411f34..e1735fa 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -340,6 +340,46 @@ void NuPlayer::onMessageReceived(const sp &msg) { break; } + case kWhatGetTrackInfo: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + status_t err = INVALID_OPERATION; + if (mSource != NULL) { + Parcel* reply; + CHECK(msg->findPointer("reply", (void**)&reply)); + err = mSource->getTrackInfo(reply); + } + + sp response = new AMessage; + response->setInt32("err", err); + + response->postReply(replyID); + break; + } + + case kWhatSelectTrack: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + status_t err = INVALID_OPERATION; + if (mSource != NULL) { + size_t trackIndex; + int32_t select; + CHECK(msg->findSize("trackIndex", &trackIndex)); + CHECK(msg->findInt32("select", &select)); + err = mSource->selectTrack(trackIndex, select); + } + + sp response = new AMessage; + response->setInt32("err", err); + + response->postReply(replyID); + break; + } + case kWhatPollDuration: { int32_t generation; @@ -1045,7 +1085,7 @@ void NuPlayer::renderBuffer(bool audio, const sp &msg) { mRenderer->queueBuffer(audio, buffer, reply); } -void NuPlayer::notifyListener(int msg, int ext1, int ext2) { +void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) { if (mDriver == NULL) { return; } @@ -1056,7 +1096,7 @@ void NuPlayer::notifyListener(int msg, int ext1, int ext2) { return; } - driver->notifyListener(msg, ext1, ext2); + driver->notifyListener(msg, ext1, ext2, in); } void NuPlayer::flushDecoder(bool audio, bool needShutdown) { @@ -1132,6 +1172,26 @@ status_t NuPlayer::setVideoScalingMode(int32_t mode) { return OK; } +status_t NuPlayer::getTrackInfo(Parcel* reply) const { + sp msg = new AMessage(kWhatGetTrackInfo, id()); + msg->setPointer("reply", reply); + + sp response; + status_t err = msg->postAndAwaitResponse(&response); + return err; +} + +status_t NuPlayer::selectTrack(size_t trackIndex, bool select) { + sp msg = new AMessage(kWhatSelectTrack, id()); + msg->setSize("trackIndex", trackIndex); + msg->setInt32("select", select); + + sp response; + status_t err = msg->postAndAwaitResponse(&response); + + return err; +} + void NuPlayer::schedulePollDuration() { sp msg = new AMessage(kWhatPollDuration, id()); msg->setInt32("generation", mPollDurationGeneration); @@ -1371,6 +1431,29 @@ void NuPlayer::onSourceNotify(const sp &msg) { break; } + case Source::kWhatSubtitleData: + { + sp buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + int32_t trackIndex; + int64_t timeUs, durationUs; + CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex)); + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + CHECK(buffer->meta()->findInt64("durationUs", &durationUs)); + + Parcel in; + in.writeInt32(trackIndex); + in.writeInt64(timeUs); + in.writeInt64(durationUs); + in.writeInt32(buffer->size()); + in.writeInt32(buffer->size()); + in.write(buffer->data(), buffer->size()); + + notifyListener(MEDIA_SUBTITLE_DATA, 0, 0, &in); + break; + } + case Source::kWhatQueueDecoderShutdown: { int32_t audio, video; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 8b6c8c1..13350f3 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -60,6 +60,8 @@ struct NuPlayer : public AHandler { void seekToAsync(int64_t seekTimeUs); status_t setVideoScalingMode(int32_t mode); + status_t getTrackInfo(Parcel* reply) const; + status_t selectTrack(size_t trackIndex, bool select); protected: virtual ~NuPlayer(); @@ -101,6 +103,8 @@ private: kWhatResume = 'rsme', kWhatPollDuration = 'polD', kWhatSourceNotify = 'srcN', + kWhatGetTrackInfo = 'gTrI', + kWhatSelectTrack = 'selT', }; wp mDriver; @@ -157,7 +161,7 @@ private: status_t feedDecoderInputData(bool audio, const sp &msg); void renderBuffer(bool audio, const sp &msg); - void notifyListener(int msg, int ext1, int ext2); + void notifyListener(int msg, int ext1, int ext2, const Parcel *in = NULL); void finishFlushIfPossible(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index cf0373c..47834fd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -392,6 +392,23 @@ status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { return mPlayer->setVideoScalingMode(mode); } + case INVOKE_ID_GET_TRACK_INFO: + { + return mPlayer->getTrackInfo(reply); + } + + case INVOKE_ID_SELECT_TRACK: + { + int trackIndex = request.readInt32(); + return mPlayer->selectTrack(trackIndex, true /* select */); + } + + case INVOKE_ID_UNSELECT_TRACK: + { + int trackIndex = request.readInt32(); + return mPlayer->selectTrack(trackIndex, false /* select */); + } + default: { return INVALID_OPERATION; @@ -495,12 +512,13 @@ status_t NuPlayerDriver::dump(int fd, const Vector &args) const { return OK; } -void NuPlayerDriver::notifyListener(int msg, int ext1, int ext2) { +void NuPlayerDriver::notifyListener( + int msg, int ext1, int ext2, const Parcel *in) { if (msg == MEDIA_PLAYBACK_COMPLETE || msg == MEDIA_ERROR) { mAtEOS = true; } - sendEvent(msg, ext1, ext2); + sendEvent(msg, ext1, ext2, in); } void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 5df0cfb..99f72a6 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -69,7 +69,7 @@ struct NuPlayerDriver : public MediaPlayerInterface { void notifyPosition(int64_t positionUs); void notifySeekComplete(); void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped); - void notifyListener(int msg, int ext1 = 0, int ext2 = 0); + void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL); void notifyFlagsChanged(uint32_t flags); protected: diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 81ffd21..e50533a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -42,6 +42,7 @@ struct NuPlayer::Source : public AHandler { kWhatVideoSizeChanged, kWhatBufferingStart, kWhatBufferingEnd, + kWhatSubtitleData, kWhatQueueDecoderShutdown, }; @@ -71,6 +72,14 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } + virtual status_t getTrackInfo(Parcel* reply) const { + return INVALID_OPERATION; + } + + virtual status_t selectTrack(size_t trackIndex, bool select) { + return INVALID_OPERATION; + } + virtual status_t seekTo(int64_t seekTimeUs) { return INVALID_OPERATION; } diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk index 85bd492..f3529f9 100644 --- a/media/libstagefright/httplive/Android.mk +++ b/media/libstagefright/httplive/Android.mk @@ -14,6 +14,7 @@ LOCAL_C_INCLUDES:= \ $(TOP)/external/openssl/include LOCAL_SHARED_LIBRARIES := \ + libbinder \ libcrypto \ libcutils \ libmedia \ diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index e91c60b..bd12ddc 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -59,6 +59,7 @@ LiveSession::LiveSession( mStreamMask(0), mCheckBandwidthGeneration(0), mLastDequeuedTimeUs(0ll), + mRealTimeBaseUs(0ll), mReconfigurationInProgress(false), mDisconnectReplyID(0) { if (mUIDValid) { @@ -122,11 +123,18 @@ status_t LiveSession::dequeueAccessUnit( type, extra == NULL ? "NULL" : extra->debugString().c_str()); } else if (err == OK) { - int64_t timeUs; - CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); - ALOGV("[%s] read buffer at time %lld us", streamStr, timeUs); - - mLastDequeuedTimeUs = timeUs; + if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) { + int64_t timeUs; + CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); + ALOGV("[%s] read buffer at time %lld us", streamStr, timeUs); + + mLastDequeuedTimeUs = timeUs; + mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; + } else if (stream == STREAMTYPE_SUBTITLES) { + (*accessUnit)->meta()->setInt32( + "trackIndex", mPlaylist->getSelectedIndex()); + (*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs); + } } else { ALOGI("[%s] encountered error %d", streamStr, err); } @@ -325,6 +333,12 @@ void LiveSession::onMessageReceived(const sp &msg) { break; } + case kWhatChangeConfiguration: + { + onChangeConfiguration(msg); + break; + } + case kWhatChangeConfiguration2: { onChangeConfiguration2(msg); @@ -438,7 +452,8 @@ void LiveSession::onConnect(const sp &msg) { mBandwidthItems.push(item); } - changeConfiguration(0ll /* timeUs */, initialBandwidthIndex); + changeConfiguration( + 0ll /* timeUs */, initialBandwidthIndex, true /* pickTrack */); } void LiveSession::finishDisconnect() { @@ -783,16 +798,31 @@ bool LiveSession::hasDynamicDuration() const { return false; } -void LiveSession::changeConfiguration(int64_t timeUs, size_t bandwidthIndex) { +status_t LiveSession::getTrackInfo(Parcel *reply) const { + return mPlaylist->getTrackInfo(reply); +} + +status_t LiveSession::selectTrack(size_t index, bool select) { + status_t err = mPlaylist->selectTrack(index, select); + if (err == OK) { + (new AMessage(kWhatChangeConfiguration, id()))->post(); + } + return err; +} + +void LiveSession::changeConfiguration( + int64_t timeUs, size_t bandwidthIndex, bool pickTrack) { CHECK(!mReconfigurationInProgress); mReconfigurationInProgress = true; mPrevBandwidthIndex = bandwidthIndex; - ALOGV("changeConfiguration => timeUs:%lld us, bwIndex:%d", - timeUs, bandwidthIndex); + ALOGV("changeConfiguration => timeUs:%lld us, bwIndex:%d, pickTrack:%d", + timeUs, bandwidthIndex, pickTrack); - mPlaylist->pickRandomMediaItems(); + if (pickTrack) { + mPlaylist->pickRandomMediaItems(); + } CHECK_LT(bandwidthIndex, mBandwidthItems.size()); const BandwidthItem &item = mBandwidthItems.itemAt(bandwidthIndex); @@ -862,6 +892,14 @@ void LiveSession::changeConfiguration(int64_t timeUs, size_t bandwidthIndex) { } } +void LiveSession::onChangeConfiguration(const sp &msg) { + if (!mReconfigurationInProgress) { + changeConfiguration(-1ll /* timeUs */, getBandwidthIndex()); + } else { + msg->post(1000000ll); // retry in 1 sec + } +} + void LiveSession::onChangeConfiguration2(const sp &msg) { mContinuation.clear(); @@ -948,6 +986,7 @@ void LiveSession::onChangeConfiguration3(const sp &msg) { if (timeUs < 0ll) { timeUs = mLastDequeuedTimeUs; } + mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; mStreamMask = streamMask; mAudioURI = audioURI; diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index b134725..99b480a8 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -31,6 +31,7 @@ struct HTTPBase; struct LiveDataSource; struct M3UParser; struct PlaylistFetcher; +struct Parcel; struct LiveSession : public AHandler { enum Flags { @@ -60,6 +61,8 @@ struct LiveSession : public AHandler { status_t seekTo(int64_t timeUs); status_t getDuration(int64_t *durationUs) const; + status_t getTrackInfo(Parcel *reply) const; + status_t selectTrack(size_t index, bool select); bool isSeekable() const; bool hasDynamicDuration() const; @@ -85,6 +88,7 @@ private: kWhatSeek = 'seek', kWhatFetcherNotify = 'notf', kWhatCheckBandwidth = 'bndw', + kWhatChangeConfiguration = 'chC0', kWhatChangeConfiguration2 = 'chC2', kWhatChangeConfiguration3 = 'chC3', kWhatFinishDisconnect2 = 'fin2', @@ -130,6 +134,7 @@ private: sp mContinuation; int64_t mLastDequeuedTimeUs; + int64_t mRealTimeBaseUs; bool mReconfigurationInProgress; uint32_t mDisconnectReplyID; @@ -151,7 +156,9 @@ private: static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); - void changeConfiguration(int64_t timeUs, size_t bandwidthIndex); + void changeConfiguration( + int64_t timeUs, size_t bandwidthIndex, bool pickTrack = false); + void onChangeConfiguration(const sp &msg); void onChangeConfiguration2(const sp &msg); void onChangeConfiguration3(const sp &msg); diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index be66252..bc6d629 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -19,11 +19,12 @@ #include #include "M3UParser.h" - +#include #include #include #include #include +#include namespace android { @@ -55,6 +56,9 @@ struct M3UParser::MediaGroup : public RefBase { bool getActiveURI(AString *uri) const; void pickRandomMediaItems(); + status_t selectTrack(size_t index, bool select); + void getTrackInfo(Parcel* reply) const; + size_t countTracks() const; protected: virtual ~MediaGroup(); @@ -150,6 +154,59 @@ void M3UParser::MediaGroup::pickRandomMediaItems() { #endif } +status_t M3UParser::MediaGroup::selectTrack(size_t index, bool select) { + if (mType != TYPE_SUBS) { + ALOGE("only select subtitile tracks for now!"); + return INVALID_OPERATION; + } + + if (select) { + if (index >= mMediaItems.size()) { + ALOGE("track %d does not exist", index); + return INVALID_OPERATION; + } + if (mSelectedIndex == index) { + ALOGE("track %d already selected", index); + return BAD_VALUE; + } + ALOGV("selected track %d", index); + mSelectedIndex = index; + } else { + if (mSelectedIndex != index) { + ALOGE("track %d is not selected", index); + return BAD_VALUE; + } + ALOGV("unselected track %d", index); + mSelectedIndex = -1; + } + + return OK; +} + +void M3UParser::MediaGroup::getTrackInfo(Parcel* reply) const { + for (size_t i = 0; i < mMediaItems.size(); ++i) { + reply->writeInt32(2); // 2 fields + + if (mType == TYPE_AUDIO) { + reply->writeInt32(MEDIA_TRACK_TYPE_AUDIO); + } else if (mType == TYPE_VIDEO) { + reply->writeInt32(MEDIA_TRACK_TYPE_VIDEO); + } else if (mType == TYPE_SUBS) { + reply->writeInt32(MEDIA_TRACK_TYPE_SUBTITLE); + } else { + reply->writeInt32(MEDIA_TRACK_TYPE_UNKNOWN); + } + + const Media &item = mMediaItems.itemAt(i); + const char *lang = item.mLanguage.empty() ? "und" : item.mLanguage.c_str(); + reply->writeString16(String16(lang)); + } +} + +size_t M3UParser::MediaGroup::countTracks() const { + return mMediaItems.size(); +} + bool M3UParser::MediaGroup::getActiveURI(AString *uri) const { for (size_t i = 0; i < mMediaItems.size(); ++i) { if (mSelectedIndex >= 0 && i == (size_t)mSelectedIndex) { @@ -172,7 +229,8 @@ M3UParser::M3UParser( mIsExtM3U(false), mIsVariantPlaylist(false), mIsComplete(false), - mIsEvent(false) { + mIsEvent(false), + mSelectedIndex(-1) { mInitCheck = parse(data, size); } @@ -237,6 +295,39 @@ void M3UParser::pickRandomMediaItems() { } } +status_t M3UParser::selectTrack(size_t index, bool select) { + for (size_t i = 0, ii = index; i < mMediaGroups.size(); ++i) { + sp group = mMediaGroups.valueAt(i); + size_t tracks = group->countTracks(); + if (ii < tracks) { + status_t err = group->selectTrack(ii, select); + if (err == OK) { + mSelectedIndex = select ? index : -1; + } + return err; + } + ii -= tracks; + } + return INVALID_OPERATION; +} + +status_t M3UParser::getTrackInfo(Parcel* reply) const { + size_t trackCount = 0; + for (size_t i = 0; i < mMediaGroups.size(); ++i) { + trackCount += mMediaGroups.valueAt(i)->countTracks(); + } + reply->writeInt32(trackCount); + + for (size_t i = 0; i < mMediaGroups.size(); ++i) { + mMediaGroups.valueAt(i)->getTrackInfo(reply); + } + return OK; +} + +ssize_t M3UParser::getSelectedIndex() const { + return mSelectedIndex; +} + bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const { if (!mIsVariantPlaylist) { *uri = mBaseURI; diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h index abea286..5248004 100644 --- a/media/libstagefright/httplive/M3UParser.h +++ b/media/libstagefright/httplive/M3UParser.h @@ -41,6 +41,9 @@ struct M3UParser : public RefBase { bool itemAt(size_t index, AString *uri, sp *meta = NULL); void pickRandomMediaItems(); + status_t selectTrack(size_t index, bool select); + status_t getTrackInfo(Parcel* reply) const; + ssize_t getSelectedIndex() const; bool getAudioURI(size_t index, AString *uri) const; bool getVideoURI(size_t index, AString *uri) const; @@ -67,6 +70,7 @@ private: sp mMeta; Vector mItems; + ssize_t mSelectedIndex; // Media groups keyed by group ID. KeyedVector > mMediaGroups; diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 8ae70b7..973b779 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -462,7 +462,11 @@ void PlaylistFetcher::onMonitorQueue() { sp packetSource = mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES); - downloadMore = packetSource->hasBufferAvailable(&finalResult); + int64_t bufferedDurationUs = + packetSource->getBufferedDurationUs(&finalResult); + + downloadMore = (bufferedDurationUs < kMinBufferedDurationUs); + finalResult = OK; } else { bool first = true; int64_t minBufferedDurationUs = 0ll; @@ -659,7 +663,7 @@ void PlaylistFetcher::onDownloadNext() { } } - err = extractAndQueueAccessUnits(buffer); + err = extractAndQueueAccessUnits(buffer, itemMeta); if (err != OK) { notifyError(err); @@ -706,7 +710,7 @@ int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const { } status_t PlaylistFetcher::extractAndQueueAccessUnits( - const sp &buffer) { + const sp &buffer, const sp &itemMeta) { if (buffer->size() > 0 && buffer->data()[0] == 0x47) { // Let's assume this is an MPEG2 transport stream. @@ -802,7 +806,10 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( const sp packetSource = mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES); - buffer->meta()->setInt64("timeUs", 0ll); + int64_t durationUs; + CHECK(itemMeta->findInt64("durationUs", &durationUs)); + buffer->meta()->setInt64("timeUs", getSegmentStartTimeUs(mSeqNumber)); + buffer->meta()->setInt64("durationUs", durationUs); packetSource->queueAccessUnit(buffer); return OK; diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h index 5a2b901..1648e02 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.h +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -135,7 +135,8 @@ private: void onMonitorQueue(); void onDownloadNext(); - status_t extractAndQueueAccessUnits(const sp &buffer); + status_t extractAndQueueAccessUnits( + const sp &buffer, const sp &itemMeta); void notifyError(status_t err); -- cgit v1.1 From 46291616486979986cba3ab83e894728ef53063f Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 18 Jul 2013 14:38:44 -0700 Subject: AudioSystem: new audioflinger restart detection Add a specific method to AudioSystem for AudioService to poll for AudioFlinger service restart instead of relying on current callback mechanism which is flaky. Bug: 9693068. Change-Id: Ie88bc9d25033503bc5cd2fa9d8c754d0f8045b8f --- media/libmedia/AudioSystem.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index a571fe4..8033c2c 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -76,6 +76,14 @@ const sp& AudioSystem::get_audio_flinger() return gAudioFlinger; } +/* static */ status_t AudioSystem::checkAudioFlinger() +{ + if (defaultServiceManager()->checkService(String16("media.audio_flinger")) != 0) { + return NO_ERROR; + } + return DEAD_OBJECT; +} + status_t AudioSystem::muteMicrophone(bool state) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; -- cgit v1.1 From 87ecf19404586672008e98babc225e094292ceb5 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 6 Jun 2013 12:42:59 -0700 Subject: wifi-display: pass session info to wifi display settings Bug: 9371882 Change-Id: I9e4b8c2154b03ce8ff3e14c465a5224bb6e8db9a --- media/libmedia/IRemoteDisplayClient.cpp | 6 ++++-- media/libstagefright/wifi-display/source/WifiDisplaySource.cpp | 6 ++++-- media/libstagefright/wifi-display/wfd.cpp | 10 ++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'media') diff --git a/media/libmedia/IRemoteDisplayClient.cpp b/media/libmedia/IRemoteDisplayClient.cpp index 5c494b3..7190879 100644 --- a/media/libmedia/IRemoteDisplayClient.cpp +++ b/media/libmedia/IRemoteDisplayClient.cpp @@ -38,7 +38,7 @@ public: } void onDisplayConnected(const sp& bufferProducer, - uint32_t width, uint32_t height, uint32_t flags) + uint32_t width, uint32_t height, uint32_t flags, uint32_t session) { Parcel data, reply; data.writeInterfaceToken(IRemoteDisplayClient::getInterfaceDescriptor()); @@ -46,6 +46,7 @@ public: data.writeInt32(width); data.writeInt32(height); data.writeInt32(flags); + data.writeInt32(session); remote()->transact(ON_DISPLAY_CONNECTED, data, &reply, IBinder::FLAG_ONEWAY); } @@ -80,7 +81,8 @@ status_t BnRemoteDisplayClient::onTransact( uint32_t width = data.readInt32(); uint32_t height = data.readInt32(); uint32_t flags = data.readInt32(); - onDisplayConnected(surfaceTexture, width, height, flags); + uint32_t session = data.readInt32(); + onDisplayConnected(surfaceTexture, width, height, flags, session); return NO_ERROR; } case ON_DISPLAY_DISCONNECTED: { diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 4b59e62..d72349d 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -416,7 +416,8 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { 0, // height, mUsingHDCP ? IRemoteDisplayClient::kDisplayFlagSecure - : 0); + : 0, + 0); } else { size_t width, height; @@ -435,7 +436,8 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { height, mUsingHDCP ? IRemoteDisplayClient::kDisplayFlagSecure - : 0); + : 0, + playbackSessionID); } } diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 04cb319..52e4e26 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -55,7 +55,8 @@ struct RemoteDisplayClient : public BnRemoteDisplayClient { const sp &bufferProducer, uint32_t width, uint32_t height, - uint32_t flags); + uint32_t flags, + uint32_t session); virtual void onDisplayDisconnected(); virtual void onDisplayError(int32_t error); @@ -91,9 +92,10 @@ void RemoteDisplayClient::onDisplayConnected( const sp &bufferProducer, uint32_t width, uint32_t height, - uint32_t flags) { - ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x", - width, height, flags); + uint32_t flags, + uint32_t session) { + ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x, session = %d", + width, height, flags, session); if (bufferProducer != NULL) { mSurfaceTexture = bufferProducer; -- cgit v1.1 From 4b820b0e1fa069714b123fc35784541d0f94d267 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Wed, 21 Aug 2013 14:39:05 -0700 Subject: Camera1: Rename setPreviewTexture to ...Target for clarity Bug: 10312644 Change-Id: I19976188f0359bfd177209fb40145defdae9c740 --- media/libstagefright/CameraSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 5a26b06..3017fe7 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -536,7 +536,7 @@ status_t CameraSource::initWithCameraAccess( 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->setPreviewTexture(mSurface)); + CHECK_EQ((status_t)OK, mCamera->setPreviewTarget(mSurface)); } // By default, do not store metadata in video buffers -- cgit v1.1 From 9cf69e0fc110f17c28e988ed0f9bf91abfaf710d Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Wed, 21 Aug 2013 11:59:23 -0700 Subject: Add ability to test supported content types to MediaDrm bug: 10244066 Change-Id: I317f05b146db962c271893f6208890a5a6c396f1 --- media/libmedia/IDrm.cpp | 7 +++++-- media/libmediaplayerservice/Drm.cpp | 13 ++++++++----- media/libmediaplayerservice/Drm.h | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'media') diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp index 902aeb2..f7a9a75 100644 --- a/media/libmedia/IDrm.cpp +++ b/media/libmedia/IDrm.cpp @@ -68,10 +68,11 @@ struct BpDrm : public BpInterface { return reply.readInt32(); } - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) { + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.write(uuid, 16); + data.writeString8(mimeType); remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); return reply.readInt32() != 0; @@ -438,7 +439,9 @@ status_t BnDrm::onTransact( CHECK_INTERFACE(IDrm, data, reply); uint8_t uuid[16]; data.read(uuid, sizeof(uuid)); - reply->writeInt32(isCryptoSchemeSupported(uuid)); + String8 mimeType = data.readString8(); + reply->writeInt32(isCryptoSchemeSupported(uuid, mimeType)); + return OK; } diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp index f00f488..4b527d0 100644 --- a/media/libmediaplayerservice/Drm.cpp +++ b/media/libmediaplayerservice/Drm.cpp @@ -211,15 +211,18 @@ bool Drm::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) { return true; } -bool Drm::isCryptoSchemeSupported(const uint8_t uuid[16]) { +bool Drm::isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) { + Mutex::Autolock autoLock(mLock); - if (mFactory && mFactory->isCryptoSchemeSupported(uuid)) { - return true; + if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) { + findFactoryForScheme(uuid); + if (mInitCheck != OK) { + return false; + } } - findFactoryForScheme(uuid); - return (mInitCheck == OK); + return mFactory->isContentTypeSupported(mimeType); } status_t Drm::createPlugin(const uint8_t uuid[16]) { diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h index 3f460f1..119fd50 100644 --- a/media/libmediaplayerservice/Drm.h +++ b/media/libmediaplayerservice/Drm.h @@ -37,7 +37,7 @@ struct Drm : public BnDrm, virtual status_t initCheck() const; - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType); virtual status_t createPlugin(const uint8_t uuid[16]); -- cgit v1.1 From ec9a032c2e104ab1e3b5bf73e69dab1408ced0ad Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 28 Aug 2013 10:23:01 -0700 Subject: AudioTrack: fix unwanted underrun when restarting When restarting an AudioTrack from stopped state, it is necessary to force refresh of mRemainingFrames by processAudioBuffer() as the last write before stop() could be partial. No doing so will lead into unnecessary sleep before filling the non contiguous part of the buffer returned by obtainBuffer() when processAudioBuffer() is executed for the first time after start(). Change-Id: Id703f8dc092a6f07c905eee194054b4a978f979d --- media/libmedia/AudioTrack.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index dd0ec73..214e789 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -387,6 +387,9 @@ status_t AudioTrack::start() if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) { // reset current position as seen by client to 0 mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition()); + // force refresh of remaining frames by processAudioBuffer() as last + // write before stop could be partial. + mRefreshRemaining = true; } mNewPosition = mProxy->getPosition() + mUpdatePeriod; int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags); -- cgit v1.1 From ee7e77d55d510725a314d8ed36dc730c21af6173 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Wed, 28 Aug 2013 16:40:41 -0700 Subject: fix MediaDrm.isCryptoSchemeSupported(uuid) 1. Don't expect plugins to support an empty mimeType in isContentTypeSupported 2. Move the cts test mock drm plugin to the cts tree so it is always used b/10528466 Change-Id: I6023f6165b1e9d294986f7e5cd0896e056e376f1 --- media/libmediaplayerservice/Drm.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp index 4b527d0..eebcb79 100644 --- a/media/libmediaplayerservice/Drm.cpp +++ b/media/libmediaplayerservice/Drm.cpp @@ -222,7 +222,11 @@ bool Drm::isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeTyp } } - return mFactory->isContentTypeSupported(mimeType); + if (mimeType != "") { + return mFactory->isContentTypeSupported(mimeType); + } + + return true; } status_t Drm::createPlugin(const uint8_t uuid[16]) { -- cgit v1.1 From 22f03209ceed3bcdf8c6558fcf02dc7699dde259 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 28 Aug 2013 16:48:35 -0700 Subject: Set PREPARING flag after setDataSource_l() Otherwise the reset_l() inside setDataSource_l() will get stuck waiting for the PREPARING to be cleared. Bug: 10426788 Change-Id: I7ccdf7abcea71cf150544c7cd3f4781e3a946b97 --- media/libstagefright/AwesomePlayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 52e178e..5fbee7e 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -2805,7 +2805,6 @@ void AwesomePlayer::onAudioTearDownEvent() { // Reset and recreate reset_l(); - mFlags |= PREPARING; status_t err; @@ -2816,6 +2815,7 @@ void AwesomePlayer::onAudioTearDownEvent() { err = setDataSource_l(uri, &uriHeaders); } + mFlags |= PREPARING; if ( err != OK ) { // This will force beingPrepareAsync_l() to notify // a MEDIA_ERROR to the client and abort the prepare -- cgit v1.1 From ce70374bf105f8a6160bf5dd70affea012b2a464 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 19 Jul 2013 16:33:58 -0700 Subject: New AudioTrack C++ API for audio timestamps This new API is intended to replace latency(), especially for A/V sync. The application will receive periodic timestamp notifications. The period is unspecified, but will likely be more frequent after a pause or stop, set position, underrun, display on/off change, route change, or when audio framework notices drift. It will be up to the higher level application (e.g. Stagefright) to reconstruct a clock that updates more frequently. The current latency() method doesn't indicate when latency changes due to screen on/off state, route changes, etc. Includes squahsed change-Id: I2082f8752040be0c234b1a6f1be2e269abf2ce7c Dummy implementation of AudioTrack:getTimestamp() Rename AudioTrack::Timestamp to AudioTimestamp. Renaming and pulling up to a higher level allows more modules to use it. Change-Id: Ibf7f6a207c3f8d8697f25ede2cd5200697fadb86 (cherry picked from commit dd69eb893867634fd169c03204a6ad7c74b351e7) --- media/libmedia/AudioTrack.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index dd0ec73..d90e733 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1712,6 +1712,11 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs) } } +status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) +{ + return INVALID_OPERATION; +} + String8 AudioTrack::getParameters(const String8& keys) { if (mOutput) { -- cgit v1.1 From 53cec22821072719ee02c856e9ac2dda2496c570 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 29 Aug 2013 09:01:02 -0700 Subject: Add IAudioTrack::getTimestamp() with dummy implementation in AudioFlinger::TrackHandle, and implement AudioTrack::getTimestamp() using IAudioTrack. Also document invariant that mAudioTrack and control block are always non-0 after successful initialization. Change-Id: I9861d1454cff7decf795d5d5898ac7999a9f3b7e --- media/libmedia/AudioTrack.cpp | 17 +++++++---------- media/libmedia/IAudioTrack.cpp | 30 +++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 11 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index d90e733..78ae37e 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -148,10 +148,8 @@ AudioTrack::~AudioTrack() mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } - if (mAudioTrack != 0) { - mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this); - mAudioTrack.clear(); - } + mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this); + mAudioTrack.clear(); IPCThreadState::self()->flushCommands(); AudioSystem::releaseAudioSessionId(mSessionId); } @@ -222,6 +220,7 @@ status_t AudioTrack::set( AutoMutex lock(mLock); + // invariant that mAudioTrack != 0 is true only after set() returns successfully if (mAudioTrack != 0) { ALOGE("Track already in use"); return INVALID_OPERATION; @@ -965,6 +964,7 @@ status_t AudioTrack::createTrack_l( ALOGE("Could not get control block"); return NO_INIT; } + // invariant that mAudioTrack != 0 is true only after set() returns successfully if (mAudioTrack != 0) { mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this); mDeathNotifier.clear(); @@ -1705,16 +1705,13 @@ status_t AudioTrack::restoreTrack_l(const char *from) status_t AudioTrack::setParameters(const String8& keyValuePairs) { AutoMutex lock(mLock); - if (mAudioTrack != 0) { - return mAudioTrack->setParameters(keyValuePairs); - } else { - return NO_INIT; - } + return mAudioTrack->setParameters(keyValuePairs); } status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) { - return INVALID_OPERATION; + AutoMutex lock(mLock); + return mAudioTrack->getTimestamp(timestamp); } String8 AudioTrack::getParameters(const String8& keys) diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index a2b49a3..f0d75ba 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -39,7 +39,8 @@ enum { ALLOCATE_TIMED_BUFFER, QUEUE_TIMED_BUFFER, SET_MEDIA_TIME_TRANSFORM, - SET_PARAMETERS + SET_PARAMETERS, + GET_TIMESTAMP, }; class BpAudioTrack : public BpInterface @@ -166,6 +167,21 @@ public: } return status; } + + virtual status_t getTimestamp(AudioTimestamp& timestamp) { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_TIMESTAMP, data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + if (status == NO_ERROR) { + timestamp.mPosition = reply.readInt32(); + timestamp.mTime.tv_sec = reply.readInt32(); + timestamp.mTime.tv_nsec = reply.readInt32(); + } + } + return status; + } }; IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack"); @@ -241,6 +257,18 @@ status_t BnAudioTrack::onTransact( reply->writeInt32(setParameters(keyValuePairs)); return NO_ERROR; } break; + case GET_TIMESTAMP: { + CHECK_INTERFACE(IAudioTrack, data, reply); + AudioTimestamp timestamp; + status_t status = getTimestamp(timestamp); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(timestamp.mPosition); + reply->writeInt32(timestamp.mTime.tv_sec); + reply->writeInt32(timestamp.mTime.tv_nsec); + } + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } -- cgit v1.1 From 50d9a8f7de5f79fa8a36489a53846d6653997e38 Mon Sep 17 00:00:00 2001 From: Haynes Mathew George Date: Mon, 5 Aug 2013 11:00:37 -0700 Subject: AudioPlayer: timestamp fixes for compress offload Use realtime queried from AudioTrack as the only time for compress offload playback. Change-Id: I314447637715c4864690c94173260165369146cb --- media/libstagefright/AudioPlayer.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'media') diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 2418aab..e38e261 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -680,6 +680,14 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { int64_t AudioPlayer::getRealTimeUs() { Mutex::Autolock autoLock(mLock); + if (useOffload()) { + if (mSeeking) { + return mSeekTimeUs; + } + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + return mPositionTimeRealUs; + } + return getRealTimeUsLocked(); } @@ -741,11 +749,6 @@ int64_t AudioPlayer::getMediaTimeUs() { return 0; } - if (useOffload()) { - mPositionTimeRealUs = getOutputPlayPositionUs_l(); - return mPositionTimeRealUs; - } - int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs; if (realTimeOffset < 0) { realTimeOffset = 0; @@ -758,8 +761,14 @@ bool AudioPlayer::getMediaTimeMapping( int64_t *realtime_us, int64_t *mediatime_us) { Mutex::Autolock autoLock(mLock); - *realtime_us = mPositionTimeRealUs; - *mediatime_us = mPositionTimeMediaUs; + if (useOffload()) { + mPositionTimeRealUs = getOutputPlayPositionUs_l(); + *realtime_us = mPositionTimeRealUs; + *mediatime_us = mPositionTimeRealUs; + } else { + *realtime_us = mPositionTimeRealUs; + *mediatime_us = mPositionTimeMediaUs; + } return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1; } -- cgit v1.1 From 42a6f422c09ca6a960673e0e805ddf71a9b51bef Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 29 Aug 2013 14:35:05 -0700 Subject: AudioTrack: fix write retries for compressed audio When the amount of frames that can be written to the buffer is less than requested, AudioTrack::processAudioBuffer() estimates the time needed to free the missing amount of frames and asks the callback thread to sleep. This behavior is not possible for compressed audio and should not be enabled for offloaded tracks. Change-Id: I5b657283cfba06254c9ac0ea9b447467cce7eb61 --- media/libmedia/AudioTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 214e789..7aa9e35 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1550,7 +1550,7 @@ nsecs_t AudioTrack::processAudioBuffer(const sp& thread) return NS_NEVER; } - if (mRetryOnPartialBuffer) { + if (mRetryOnPartialBuffer && !isOffloaded()) { mRetryOnPartialBuffer = false; if (avail < mRemainingFrames) { int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate; -- cgit v1.1 From 1b02586f0f41d82c80619cfc0d16cd3feb5eaec7 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Thu, 29 Aug 2013 15:51:31 -0700 Subject: AAC encoder: handle missing object types The audio object type for parametric stereo and lowdelay was not passed to the FDK AAC encoder. Bug 9428126 Change-Id: Ic32822afff8b1da6a2d80c1b65d514f24059fb29 --- media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'media') diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp index 5749733..ff2b503 100644 --- a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp +++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp @@ -292,6 +292,10 @@ static AUDIO_OBJECT_TYPE getAOTFromProfile(OMX_U32 profile) { return AOT_AAC_LC; } else if (profile == OMX_AUDIO_AACObjectHE) { return AOT_SBR; + } else if (profile == OMX_AUDIO_AACObjectHE_PS) { + return AOT_PS; + } else if (profile == OMX_AUDIO_AACObjectLD) { + return AOT_ER_AAC_LD; } else if (profile == OMX_AUDIO_AACObjectELD) { return AOT_ER_AAC_ELD; } else { -- cgit v1.1 From 9da36a6c8df70a5c8179ac78fab33cfbb5078cb2 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 29 Aug 2013 09:31:26 -0700 Subject: Fix decoder EOS handling Conceptually it should be the same whether EOS is signalled on the last buffer holding data, or an empty buffer that follows. Make it so that this actually behaves the same for mp3, AAC and Vorbis. b/8747869 Change-Id: Idece8ef45689a3ffaf70fb45d19862d7b93b2f92 --- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 217 ++++++++++----------- media/libstagefright/codecs/aacdec/SoftAAC2.h | 2 + media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 114 ++++++----- media/libstagefright/codecs/mp3dec/SoftMP3.h | 2 + .../codecs/vorbis/dec/SoftVorbis.cpp | 73 +++---- .../libstagefright/codecs/vorbis/dec/SoftVorbis.h | 2 + 6 files changed, 208 insertions(+), 202 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index 1b20cbb..c9b5d26 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -58,6 +58,8 @@ SoftAAC2::SoftAAC2( mIsADTS(false), mInputBufferCount(0), mSignalledError(false), + mSawInputEos(false), + mSignalledOutputEos(false), mAnchorTimeUs(0), mNumSamplesOutput(0), mOutputPortSettingsChange(NONE) { @@ -350,115 +352,83 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { return; } - while (!inQueue.empty() && !outQueue.empty()) { - BufferInfo *inInfo = *inQueue.begin(); - OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFlags = 0; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inQueue.erase(inQueue.begin()); - inInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inHeader); - - if (mDecoderHasData) { - // flush out the decoder's delayed data by calling DecodeFrame - // one more time, with the AACDEC_FLUSH flag set - INT_PCM *outBuffer = - reinterpret_cast( - outHeader->pBuffer + outHeader->nOffset); - - AAC_DECODER_ERROR decoderErr = - aacDecoder_DecodeFrame(mAACDecoder, - outBuffer, - outHeader->nAllocLen, - AACDEC_FLUSH); - mDecoderHasData = false; - - if (decoderErr != AAC_DEC_OK) { - mSignalledError = true; - - notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, - NULL); - - return; - } - - outHeader->nFilledLen = - mStreamInfo->frameSize - * sizeof(int16_t) - * mStreamInfo->numChannels; - } else { - // we never submitted any data to the decoder, so there's nothing to flush out - outHeader->nFilledLen = 0; + if (inHeader) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; } - outHeader->nFlags = OMX_BUFFERFLAG_EOS; - - outQueue.erase(outQueue.begin()); - outInfo->mOwnedByUs = false; - notifyFillBufferDone(outHeader); - return; - } - - if (inHeader->nOffset == 0) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumSamplesOutput = 0; - } + if (inHeader->nOffset == 0 && inHeader->nFilledLen) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } - size_t adtsHeaderSize = 0; - if (mIsADTS) { - // skip 30 bits, aac_frame_length follows. - // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + if (mIsADTS) { + size_t adtsHeaderSize = 0; + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? - const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; - bool signalError = false; - if (inHeader->nFilledLen < 7) { - ALOGE("Audio data too short to contain even the ADTS header. " - "Got %ld bytes.", inHeader->nFilledLen); - hexdump(adtsHeader, inHeader->nFilledLen); - signalError = true; - } else { - bool protectionAbsent = (adtsHeader[1] & 1); - - unsigned aac_frame_length = - ((adtsHeader[3] & 3) << 11) - | (adtsHeader[4] << 3) - | (adtsHeader[5] >> 5); - - if (inHeader->nFilledLen < aac_frame_length) { - ALOGE("Not enough audio data for the complete frame. " - "Got %ld bytes, frame size according to the ADTS " - "header is %u bytes.", - inHeader->nFilledLen, aac_frame_length); + bool signalError = false; + if (inHeader->nFilledLen < 7) { + ALOGE("Audio data too short to contain even the ADTS header. " + "Got %ld bytes.", inHeader->nFilledLen); hexdump(adtsHeader, inHeader->nFilledLen); signalError = true; } else { - adtsHeaderSize = (protectionAbsent ? 7 : 9); - - inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; - inBufferLength[0] = aac_frame_length - adtsHeaderSize; - - inHeader->nOffset += adtsHeaderSize; - inHeader->nFilledLen -= adtsHeaderSize; + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + if (inHeader->nFilledLen < aac_frame_length) { + ALOGE("Not enough audio data for the complete frame. " + "Got %ld bytes, frame size according to the ADTS " + "header is %u bytes.", + inHeader->nFilledLen, aac_frame_length); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + adtsHeaderSize = (protectionAbsent ? 7 : 9); + + inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; + inBufferLength[0] = aac_frame_length - adtsHeaderSize; + + inHeader->nOffset += adtsHeaderSize; + inHeader->nFilledLen -= adtsHeaderSize; + } } - } - if (signalError) { - mSignalledError = true; + if (signalError) { + mSignalledError = true; - notify(OMX_EventError, - OMX_ErrorStreamCorrupt, - ERROR_MALFORMED, - NULL); + notify(OMX_EventError, + OMX_ErrorStreamCorrupt, + ERROR_MALFORMED, + NULL); - return; + return; + } + } else { + inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; + inBufferLength[0] = inHeader->nFilledLen; } } else { - inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; - inBufferLength[0] = inHeader->nFilledLen; + inBufferLength[0] = 0; } // Fill and decode @@ -471,50 +441,66 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { int prevNumChannels = mStreamInfo->numChannels; AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS; - while (bytesValid[0] > 0 && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { + while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { + mDecoderHasData |= (bytesValid[0] > 0); aacDecoder_Fill(mAACDecoder, inBuffer, inBufferLength, bytesValid); - mDecoderHasData = true; decoderErr = aacDecoder_DecodeFrame(mAACDecoder, outBuffer, outHeader->nAllocLen, 0 /* flags */); - if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { - ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); + if (mSawInputEos && bytesValid[0] <= 0) { + if (mDecoderHasData) { + // flush out the decoder's delayed data by calling DecodeFrame + // one more time, with the AACDEC_FLUSH flag set + decoderErr = aacDecoder_DecodeFrame(mAACDecoder, + outBuffer, + outHeader->nAllocLen, + AACDEC_FLUSH); + mDecoderHasData = false; + } + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + break; + } else { + ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); + } } } size_t numOutBytes = mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; - if (decoderErr == AAC_DEC_OK) { - UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; - inHeader->nFilledLen -= inBufferUsedLength; - inHeader->nOffset += inBufferUsedLength; - } else { - ALOGW("AAC decoder returned error %d, substituting silence", - decoderErr); + if (inHeader) { + if (decoderErr == AAC_DEC_OK) { + UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; + inHeader->nFilledLen -= inBufferUsedLength; + inHeader->nOffset += inBufferUsedLength; + } else { + ALOGW("AAC decoder returned error %d, substituting silence", + decoderErr); - memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); + memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); - // Discard input buffer. - inHeader->nFilledLen = 0; + // Discard input buffer. + inHeader->nFilledLen = 0; - aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); + aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - // fall through - } + // fall through + } - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } } /* @@ -555,7 +541,6 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { // we've previously decoded valid data, in the latter case // (decode failed) we'll output a silent frame. outHeader->nFilledLen = numOutBytes; - outHeader->nFlags = 0; outHeader->nTimeStamp = mAnchorTimeUs @@ -606,6 +591,8 @@ void SoftAAC2::onReset() { mStreamInfo->sampleRate = 0; mSignalledError = false; + mSawInputEos = false; + mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h index 2d960ab..a7ea1e2 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -55,6 +55,8 @@ private: bool mDecoderHasData; size_t mInputBufferCount; bool mSignalledError; + bool mSawInputEos; + bool mSignalledOutputEos; int64_t mAnchorTimeUs; int64_t mNumSamplesOutput; diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index 7c382fb..877e3cb 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -49,6 +49,8 @@ SoftMP3::SoftMP3( mNumChannels(2), mSamplingRate(44100), mSignalledError(false), + mSawInputEos(false), + mSignalledOutputEos(false), mOutputPortSettingsChange(NONE) { initPorts(); initDecoder(); @@ -194,48 +196,36 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { List &inQueue = getPortQueue(0); List &outQueue = getPortQueue(1); - while (!inQueue.empty() && !outQueue.empty()) { - BufferInfo *inInfo = *inQueue.begin(); - OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFlags = 0; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inQueue.erase(inQueue.begin()); - inInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inHeader); - - if (!mIsFirst) { - // 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); + if (inHeader) { + if (inHeader->nOffset == 0 && inHeader->nFilledLen) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } - memset(outHeader->pBuffer, 0, outHeader->nFilledLen); - } else { - // Since we never discarded frames from the start, we won't have - // to add any padding at the end either. - outHeader->nFilledLen = 0; + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; } - outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mConfig->pInputBuffer = + inHeader->pBuffer + inHeader->nOffset; - outQueue.erase(outQueue.begin()); - outInfo->mOwnedByUs = false; - notifyFillBufferDone(outHeader); - return; - } - - if (inHeader->nOffset == 0) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumFramesOutput = 0; + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + } else { + mConfig->pInputBuffer = NULL; + mConfig->inputBufferCurrentLength = 0; } - - mConfig->pInputBuffer = - inHeader->pBuffer + inHeader->nOffset; - - mConfig->inputBufferCurrentLength = inHeader->nFilledLen; mConfig->inputBufferMaxLength = 0; mConfig->inputBufferUsedLength = 0; @@ -262,13 +252,28 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); } - // This is recoverable, just ignore the current frame and - // play silence instead. - memset(outHeader->pBuffer, - 0, - mConfig->outputFrameSize * sizeof(int16_t)); - - mConfig->inputBufferUsedLength = inHeader->nFilledLen; + if (decoderErr == NO_ENOUGH_MAIN_DATA_ERROR && mSawInputEos) { + if (!mIsFirst) { + // pad the end of the stream with 529 samples, since that many samples + // were trimmed off the beginning when decoding started + outHeader->nOffset = 0; + outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); + + memset(outHeader->pBuffer, 0, outHeader->nFilledLen); + } + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + } else { + // This is recoverable, just ignore the current frame and + // play silence instead. + memset(outHeader->pBuffer, + 0, + mConfig->outputFrameSize * sizeof(int16_t)); + + if (inHeader) { + mConfig->inputBufferUsedLength = inHeader->nFilledLen; + } + } } else if (mConfig->samplingRate != mSamplingRate || mConfig->num_channels != mNumChannels) { mSamplingRate = mConfig->samplingRate; @@ -289,7 +294,7 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset; - } else { + } else if (!mSignalledOutputEos) { outHeader->nOffset = 0; outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); } @@ -298,23 +303,24 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate; - outHeader->nFlags = 0; - - CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); + if (inHeader) { + CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); - inHeader->nOffset += mConfig->inputBufferUsedLength; - inHeader->nFilledLen -= mConfig->inputBufferUsedLength; + inHeader->nOffset += mConfig->inputBufferUsedLength; + inHeader->nFilledLen -= mConfig->inputBufferUsedLength; - mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } } + mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; + outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; @@ -362,6 +368,8 @@ void SoftMP3::onReset() { pvmp3_InitDecoder(mConfig, mDecoderBuf); mIsFirst = true; mSignalledError = false; + mSawInputEos = false; + mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h index 4af91ea..f9e7b53 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.h +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -61,6 +61,8 @@ private: bool mIsFirst; bool mSignalledError; + bool mSawInputEos; + bool mSignalledOutputEos; enum { NONE, diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index 51bb958..a377b23 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -54,6 +54,8 @@ SoftVorbis::SoftVorbis( mAnchorTimeUs(0), mNumFramesOutput(0), mNumFramesLeftOnPage(-1), + mSawInputEos(false), + mSignalledOutputEos(false), mOutputPortSettingsChange(NONE) { initPorts(); CHECK_EQ(initDecoder(), (status_t)OK); @@ -290,48 +292,47 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { return; } - while (!inQueue.empty() && !outQueue.empty()) { - BufferInfo *inInfo = *inQueue.begin(); - OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - inQueue.erase(inQueue.begin()); - inInfo->mOwnedByUs = false; - notifyEmptyBufferDone(inHeader); + int32_t numPageSamples = 0; - outHeader->nFilledLen = 0; - outHeader->nFlags = OMX_BUFFERFLAG_EOS; + if (inHeader) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; + } - outQueue.erase(outQueue.begin()); - outInfo->mOwnedByUs = false; - notifyFillBufferDone(outHeader); - return; - } + if (inHeader->nFilledLen || !mSawInputEos) { + CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); + memcpy(&numPageSamples, + inHeader->pBuffer + + inHeader->nOffset + inHeader->nFilledLen - 4, + sizeof(numPageSamples)); - int32_t numPageSamples; - CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); - memcpy(&numPageSamples, - inHeader->pBuffer - + inHeader->nOffset + inHeader->nFilledLen - 4, - sizeof(numPageSamples)); + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } - if (numPageSamples >= 0) { - mNumFramesLeftOnPage = numPageSamples; + inHeader->nFilledLen -= sizeof(numPageSamples);; + } } - if (inHeader->nOffset == 0) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumFramesOutput = 0; + if (numPageSamples >= 0) { + mNumFramesLeftOnPage = numPageSamples; } - inHeader->nFilledLen -= sizeof(numPageSamples);; - ogg_buffer buf; - buf.data = inHeader->pBuffer + inHeader->nOffset; - buf.size = inHeader->nFilledLen; + buf.data = inHeader ? inHeader->pBuffer + inHeader->nOffset : NULL; + buf.size = inHeader ? inHeader->nFilledLen : 0; buf.refcount = 1; buf.ptr.owner = NULL; @@ -384,11 +385,13 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { mNumFramesOutput += numFrames; - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + if (inHeader) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); @@ -425,6 +428,8 @@ void SoftVorbis::onReset() { mVi = NULL; } + mSawInputEos = false; + mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h index cb628a0..1d00816 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h @@ -59,6 +59,8 @@ private: int64_t mAnchorTimeUs; int64_t mNumFramesOutput; int32_t mNumFramesLeftOnPage; + bool mSawInputEos; + bool mSignalledOutputEos; enum { NONE, -- cgit v1.1 From fa51e09b30e884fed20b141783a7447599a6563e Mon Sep 17 00:00:00 2001 From: Rachad Date: Fri, 30 Aug 2013 15:52:00 -0700 Subject: Fixed timestamp handling in ESQueue Access Unit parser. This fixes bug b/10294801 Change-Id: Ie96d36e2ff6fdee0c949a85da3602ab04b34bf6e --- media/libstagefright/mpeg2ts/ESQueue.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'media') diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 9f3b19c..8f9c9c8 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -504,15 +504,11 @@ int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) { if (first) { timeUs = info->mTimestampUs; + first = false; } if (info->mLength > size) { info->mLength -= size; - - if (first) { - info->mTimestampUs = -1; - } - size = 0; } else { size -= info->mLength; @@ -521,7 +517,6 @@ int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) { info = NULL; } - first = false; } if (timeUs == 0ll) { -- cgit v1.1 From b6209a3d4c29bbb88de5a77546f4d545883cc484 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 30 Aug 2013 19:22:29 -0700 Subject: Revert "Fix decoder EOS handling" This reverts commit 9da36a6c8df70a5c8179ac78fab33cfbb5078cb2. Bug: 10571297. Change-Id: I76f20fe34872ea54fce626077462fb86c8c3f02e --- media/libstagefright/codecs/aacdec/SoftAAC2.cpp | 217 +++++++++++---------- media/libstagefright/codecs/aacdec/SoftAAC2.h | 2 - media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 114 +++++------ media/libstagefright/codecs/mp3dec/SoftMP3.h | 2 - .../codecs/vorbis/dec/SoftVorbis.cpp | 73 ++++--- .../libstagefright/codecs/vorbis/dec/SoftVorbis.h | 2 - 6 files changed, 202 insertions(+), 208 deletions(-) (limited to 'media') diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index c9b5d26..1b20cbb 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -58,8 +58,6 @@ SoftAAC2::SoftAAC2( mIsADTS(false), mInputBufferCount(0), mSignalledError(false), - mSawInputEos(false), - mSignalledOutputEos(false), mAnchorTimeUs(0), mNumSamplesOutput(0), mOutputPortSettingsChange(NONE) { @@ -352,83 +350,115 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { return; } - while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { - BufferInfo *inInfo = NULL; - OMX_BUFFERHEADERTYPE *inHeader = NULL; - if (!inQueue.empty()) { - inInfo = *inQueue.begin(); - inHeader = inInfo->mHeader; - } + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - outHeader->nFlags = 0; - if (inHeader) { - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - mSawInputEos = true; - } + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + if (mDecoderHasData) { + // flush out the decoder's delayed data by calling DecodeFrame + // one more time, with the AACDEC_FLUSH flag set + INT_PCM *outBuffer = + reinterpret_cast( + outHeader->pBuffer + outHeader->nOffset); + + AAC_DECODER_ERROR decoderErr = + aacDecoder_DecodeFrame(mAACDecoder, + outBuffer, + outHeader->nAllocLen, + AACDEC_FLUSH); + mDecoderHasData = false; + + if (decoderErr != AAC_DEC_OK) { + mSignalledError = true; + + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, + NULL); - if (inHeader->nOffset == 0 && inHeader->nFilledLen) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumSamplesOutput = 0; + return; + } + + outHeader->nFilledLen = + mStreamInfo->frameSize + * sizeof(int16_t) + * mStreamInfo->numChannels; + } else { + // we never submitted any data to the decoder, so there's nothing to flush out + outHeader->nFilledLen = 0; } - if (mIsADTS) { - size_t adtsHeaderSize = 0; - // skip 30 bits, aac_frame_length follows. - // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + outHeader->nFlags = OMX_BUFFERFLAG_EOS; - const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } - bool signalError = false; - if (inHeader->nFilledLen < 7) { - ALOGE("Audio data too short to contain even the ADTS header. " - "Got %ld bytes.", inHeader->nFilledLen); + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } + + size_t adtsHeaderSize = 0; + if (mIsADTS) { + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + + const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + + bool signalError = false; + if (inHeader->nFilledLen < 7) { + ALOGE("Audio data too short to contain even the ADTS header. " + "Got %ld bytes.", inHeader->nFilledLen); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + if (inHeader->nFilledLen < aac_frame_length) { + ALOGE("Not enough audio data for the complete frame. " + "Got %ld bytes, frame size according to the ADTS " + "header is %u bytes.", + inHeader->nFilledLen, aac_frame_length); hexdump(adtsHeader, inHeader->nFilledLen); signalError = true; } else { - bool protectionAbsent = (adtsHeader[1] & 1); - - unsigned aac_frame_length = - ((adtsHeader[3] & 3) << 11) - | (adtsHeader[4] << 3) - | (adtsHeader[5] >> 5); - - if (inHeader->nFilledLen < aac_frame_length) { - ALOGE("Not enough audio data for the complete frame. " - "Got %ld bytes, frame size according to the ADTS " - "header is %u bytes.", - inHeader->nFilledLen, aac_frame_length); - hexdump(adtsHeader, inHeader->nFilledLen); - signalError = true; - } else { - adtsHeaderSize = (protectionAbsent ? 7 : 9); - - inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; - inBufferLength[0] = aac_frame_length - adtsHeaderSize; - - inHeader->nOffset += adtsHeaderSize; - inHeader->nFilledLen -= adtsHeaderSize; - } + adtsHeaderSize = (protectionAbsent ? 7 : 9); + + inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; + inBufferLength[0] = aac_frame_length - adtsHeaderSize; + + inHeader->nOffset += adtsHeaderSize; + inHeader->nFilledLen -= adtsHeaderSize; } + } - if (signalError) { - mSignalledError = true; + if (signalError) { + mSignalledError = true; - notify(OMX_EventError, - OMX_ErrorStreamCorrupt, - ERROR_MALFORMED, - NULL); + notify(OMX_EventError, + OMX_ErrorStreamCorrupt, + ERROR_MALFORMED, + NULL); - return; - } - } else { - inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; - inBufferLength[0] = inHeader->nFilledLen; + return; } } else { - inBufferLength[0] = 0; + inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; + inBufferLength[0] = inHeader->nFilledLen; } // Fill and decode @@ -441,66 +471,50 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { int prevNumChannels = mStreamInfo->numChannels; AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS; - while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { - mDecoderHasData |= (bytesValid[0] > 0); + while (bytesValid[0] > 0 && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { aacDecoder_Fill(mAACDecoder, inBuffer, inBufferLength, bytesValid); + mDecoderHasData = true; decoderErr = aacDecoder_DecodeFrame(mAACDecoder, outBuffer, outHeader->nAllocLen, 0 /* flags */); + if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { - if (mSawInputEos && bytesValid[0] <= 0) { - if (mDecoderHasData) { - // flush out the decoder's delayed data by calling DecodeFrame - // one more time, with the AACDEC_FLUSH flag set - decoderErr = aacDecoder_DecodeFrame(mAACDecoder, - outBuffer, - outHeader->nAllocLen, - AACDEC_FLUSH); - mDecoderHasData = false; - } - outHeader->nFlags = OMX_BUFFERFLAG_EOS; - mSignalledOutputEos = true; - break; - } else { - ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); - } + ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); } } size_t numOutBytes = mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; - if (inHeader) { - if (decoderErr == AAC_DEC_OK) { - UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; - inHeader->nFilledLen -= inBufferUsedLength; - inHeader->nOffset += inBufferUsedLength; - } else { - ALOGW("AAC decoder returned error %d, substituting silence", - decoderErr); + if (decoderErr == AAC_DEC_OK) { + UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; + inHeader->nFilledLen -= inBufferUsedLength; + inHeader->nOffset += inBufferUsedLength; + } else { + ALOGW("AAC decoder returned error %d, substituting silence", + decoderErr); - memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); + memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); - // Discard input buffer. - inHeader->nFilledLen = 0; + // Discard input buffer. + inHeader->nFilledLen = 0; - aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); + aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - // fall through - } + // fall through + } - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; - } + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; } /* @@ -541,6 +555,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { // we've previously decoded valid data, in the latter case // (decode failed) we'll output a silent frame. outHeader->nFilledLen = numOutBytes; + outHeader->nFlags = 0; outHeader->nTimeStamp = mAnchorTimeUs @@ -591,8 +606,6 @@ void SoftAAC2::onReset() { mStreamInfo->sampleRate = 0; mSignalledError = false; - mSawInputEos = false; - mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h index a7ea1e2..2d960ab 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -55,8 +55,6 @@ private: bool mDecoderHasData; size_t mInputBufferCount; bool mSignalledError; - bool mSawInputEos; - bool mSignalledOutputEos; int64_t mAnchorTimeUs; int64_t mNumSamplesOutput; diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index 877e3cb..7c382fb 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -49,8 +49,6 @@ SoftMP3::SoftMP3( mNumChannels(2), mSamplingRate(44100), mSignalledError(false), - mSawInputEos(false), - mSignalledOutputEos(false), mOutputPortSettingsChange(NONE) { initPorts(); initDecoder(); @@ -196,36 +194,48 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { List &inQueue = getPortQueue(0); List &outQueue = getPortQueue(1); - while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { - BufferInfo *inInfo = NULL; - OMX_BUFFERHEADERTYPE *inHeader = NULL; - if (!inQueue.empty()) { - inInfo = *inQueue.begin(); - inHeader = inInfo->mHeader; - } + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - outHeader->nFlags = 0; - if (inHeader) { - if (inHeader->nOffset == 0 && inHeader->nFilledLen) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumFramesOutput = 0; - } + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - mSawInputEos = true; + if (!mIsFirst) { + // 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); + } else { + // Since we never discarded frames from the start, we won't have + // to add any padding at the end either. + outHeader->nFilledLen = 0; } - mConfig->pInputBuffer = - inHeader->pBuffer + inHeader->nOffset; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; - mConfig->inputBufferCurrentLength = inHeader->nFilledLen; - } else { - mConfig->pInputBuffer = NULL; - mConfig->inputBufferCurrentLength = 0; + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; } + + mConfig->pInputBuffer = + inHeader->pBuffer + inHeader->nOffset; + + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; mConfig->inputBufferMaxLength = 0; mConfig->inputBufferUsedLength = 0; @@ -252,28 +262,13 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); } - if (decoderErr == NO_ENOUGH_MAIN_DATA_ERROR && mSawInputEos) { - if (!mIsFirst) { - // pad the end of the stream with 529 samples, since that many samples - // were trimmed off the beginning when decoding started - outHeader->nOffset = 0; - outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); - - memset(outHeader->pBuffer, 0, outHeader->nFilledLen); - } - outHeader->nFlags = OMX_BUFFERFLAG_EOS; - mSignalledOutputEos = true; - } else { - // This is recoverable, just ignore the current frame and - // play silence instead. - memset(outHeader->pBuffer, - 0, - mConfig->outputFrameSize * sizeof(int16_t)); - - if (inHeader) { - mConfig->inputBufferUsedLength = inHeader->nFilledLen; - } - } + // This is recoverable, just ignore the current frame and + // play silence instead. + memset(outHeader->pBuffer, + 0, + mConfig->outputFrameSize * sizeof(int16_t)); + + mConfig->inputBufferUsedLength = inHeader->nFilledLen; } else if (mConfig->samplingRate != mSamplingRate || mConfig->num_channels != mNumChannels) { mSamplingRate = mConfig->samplingRate; @@ -294,7 +289,7 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset; - } else if (!mSignalledOutputEos) { + } else { outHeader->nOffset = 0; outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); } @@ -303,24 +298,23 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate; - if (inHeader) { - CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); - - inHeader->nOffset += mConfig->inputBufferUsedLength; - inHeader->nFilledLen -= mConfig->inputBufferUsedLength; + outHeader->nFlags = 0; + CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; - } - } + inHeader->nOffset += mConfig->inputBufferUsedLength; + inHeader->nFilledLen -= mConfig->inputBufferUsedLength; mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; @@ -368,8 +362,6 @@ void SoftMP3::onReset() { pvmp3_InitDecoder(mConfig, mDecoderBuf); mIsFirst = true; mSignalledError = false; - mSawInputEos = false; - mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h index f9e7b53..4af91ea 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.h +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -61,8 +61,6 @@ private: bool mIsFirst; bool mSignalledError; - bool mSawInputEos; - bool mSignalledOutputEos; enum { NONE, diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index a377b23..51bb958 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -54,8 +54,6 @@ SoftVorbis::SoftVorbis( mAnchorTimeUs(0), mNumFramesOutput(0), mNumFramesLeftOnPage(-1), - mSawInputEos(false), - mSignalledOutputEos(false), mOutputPortSettingsChange(NONE) { initPorts(); CHECK_EQ(initDecoder(), (status_t)OK); @@ -292,47 +290,48 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { return; } - while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { - BufferInfo *inInfo = NULL; - OMX_BUFFERHEADERTYPE *inHeader = NULL; - if (!inQueue.empty()) { - inInfo = *inQueue.begin(); - inHeader = inInfo->mHeader; - } + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - int32_t numPageSamples = 0; - - if (inHeader) { - if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - mSawInputEos = true; - } - - if (inHeader->nFilledLen || !mSawInputEos) { - CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); - memcpy(&numPageSamples, - inHeader->pBuffer - + inHeader->nOffset + inHeader->nFilledLen - 4, - sizeof(numPageSamples)); + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); - if (inHeader->nOffset == 0) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumFramesOutput = 0; - } + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; - inHeader->nFilledLen -= sizeof(numPageSamples);; - } + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; } + int32_t numPageSamples; + CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); + memcpy(&numPageSamples, + inHeader->pBuffer + + inHeader->nOffset + inHeader->nFilledLen - 4, + sizeof(numPageSamples)); + if (numPageSamples >= 0) { mNumFramesLeftOnPage = numPageSamples; } + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } + + inHeader->nFilledLen -= sizeof(numPageSamples);; + ogg_buffer buf; - buf.data = inHeader ? inHeader->pBuffer + inHeader->nOffset : NULL; - buf.size = inHeader ? inHeader->nFilledLen : 0; + buf.data = inHeader->pBuffer + inHeader->nOffset; + buf.size = inHeader->nFilledLen; buf.refcount = 1; buf.ptr.owner = NULL; @@ -385,13 +384,11 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { mNumFramesOutput += numFrames; - if (inHeader) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; - } + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); @@ -428,8 +425,6 @@ void SoftVorbis::onReset() { mVi = NULL; } - mSawInputEos = false; - mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h index 1d00816..cb628a0 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h @@ -59,8 +59,6 @@ private: int64_t mAnchorTimeUs; int64_t mNumFramesOutput; int32_t mNumFramesLeftOnPage; - bool mSawInputEos; - bool mSignalledOutputEos; enum { NONE, -- cgit v1.1 From 767094dd98b01baf21de2ad09c27b3c98776cf73 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 23 Aug 2013 13:51:43 -0700 Subject: Add NBAIO_Sink::getTimestamp() with a real implementation in AudioStreamOutSink for dummy implementation initially in MonoPipe. Use in AudioFlinger::PlaybackThread::threadLoop_write() to keep the input to the timestamp latch up-to-date. Change-Id: I10ef277991b63bb43d55d6f3df75116ef32246cd --- media/libnbaio/AudioStreamOutSink.cpp | 12 ++++++++++++ media/libnbaio/MonoPipe.cpp | 5 +++++ 2 files changed, 17 insertions(+) (limited to 'media') diff --git a/media/libnbaio/AudioStreamOutSink.cpp b/media/libnbaio/AudioStreamOutSink.cpp index 6f525e5..b2de8a2 100644 --- a/media/libnbaio/AudioStreamOutSink.cpp +++ b/media/libnbaio/AudioStreamOutSink.cpp @@ -79,4 +79,16 @@ status_t AudioStreamOutSink::getNextWriteTimestamp(int64_t *timestamp) { return mStream->get_next_write_timestamp(mStream, timestamp); } +status_t AudioStreamOutSink::getTimestamp(AudioTimestamp& timestamp) +{ + // FIXME position64 won't be needed after AudioTimestamp.mPosition is changed to uint64_t + uint64_t position64; + int ok = mStream->get_presentation_position(mStream, &position64, ×tamp.mTime); + if (ok != 0) { + return INVALID_OPERATION; + } + timestamp.mPosition = position64; + return OK; +} + } // namespace android diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp index e8d3d9b..a74b49e 100644 --- a/media/libnbaio/MonoPipe.cpp +++ b/media/libnbaio/MonoPipe.cpp @@ -310,4 +310,9 @@ bool MonoPipe::isShutdown() return mIsShutdown; } +status_t MonoPipe::getTimestamp(AudioTimestamp& timestamp) +{ + return INVALID_OPERATION; +} + } // namespace android -- cgit v1.1 From a07a1c2c91dc7ee6ded319262499f20cd01edcf7 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 23 Aug 2013 10:54:35 -0700 Subject: Instantiate SingleStateQueue as typedef AudioTimestampSingleStateQueue and use it in MonoPipe. Change-Id: Idaebb362bd9d0a81a7ed83792ab9369dc37c0e74 --- media/libmedia/Android.mk | 1 + media/libmedia/SingleStateQueueInstantiations.cpp | 2 ++ media/libnbaio/Android.mk | 5 ++++- media/libnbaio/MonoPipe.cpp | 5 ++++- 4 files changed, 11 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 96755bb..56e7787 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -62,6 +62,7 @@ LOCAL_SRC_FILES += ../libnbaio/roundup.c LOCAL_CFLAGS += -DANDROID_SMP=$(if $(findstring true,$(TARGET_CPU_SMP)),1,0) LOCAL_SRC_FILES += SingleStateQueue.cpp LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"' +# Consider a separate a library for SingleStateQueueInstantiations. LOCAL_SHARED_LIBRARIES := \ libui liblog libcutils libutils libbinder libsonivox libicuuc libexpat \ diff --git a/media/libmedia/SingleStateQueueInstantiations.cpp b/media/libmedia/SingleStateQueueInstantiations.cpp index 2afebe9..0265c8c 100644 --- a/media/libmedia/SingleStateQueueInstantiations.cpp +++ b/media/libmedia/SingleStateQueueInstantiations.cpp @@ -16,11 +16,13 @@ #include #include +#include // FIXME hack for gcc namespace android { template class SingleStateQueue; // typedef StaticAudioTrackSingleStateQueue +template class SingleStateQueue; // typedef AudioTimestampSingleStateQueue } diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk index 5d00d15..69c75b8 100644 --- a/media/libnbaio/Android.mk +++ b/media/libnbaio/Android.mk @@ -31,6 +31,9 @@ LOCAL_SHARED_LIBRARIES := \ libcommon_time_client \ libcutils \ libutils \ - liblog + liblog \ + libmedia +# This dependency on libmedia is for SingleStateQueueInstantiations. +# Consider a separate a library for SingleStateQueueInstantiations. include $(BUILD_SHARED_LIBRARY) diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp index a74b49e..b55be83 100644 --- a/media/libnbaio/MonoPipe.cpp +++ b/media/libnbaio/MonoPipe.cpp @@ -42,7 +42,10 @@ MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : // mWriteTs mSetpoint((reqFrames * 11) / 16), mWriteCanBlock(writeCanBlock), - mIsShutdown(false) + mIsShutdown(false), + // mTimestampShared + mTimestampMutator(&mTimestampShared), + mTimestampObserver(&mTimestampShared) { CCHelper tmpHelper; status_t res; -- cgit v1.1 From 894d6be4f9b4721c77a01919ecf03b27cec90cc9 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 26 Aug 2013 10:29:28 -0700 Subject: Add NBAIO_Source::onTimestamp() with dummy default implementation, and implement in MonoPipeReader. onTimestamp is meant to be called by the corresponding sink when it has a new timestamp available. Change-Id: I8a90d24d1061e4a592ce5bd8ee1c9fce6bdd8a84 --- media/libnbaio/MonoPipeReader.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'media') diff --git a/media/libnbaio/MonoPipeReader.cpp b/media/libnbaio/MonoPipeReader.cpp index 394f6ac..851341a 100644 --- a/media/libnbaio/MonoPipeReader.cpp +++ b/media/libnbaio/MonoPipeReader.cpp @@ -86,4 +86,9 @@ ssize_t MonoPipeReader::read(void *buffer, size_t count, int64_t readPTS) return red; } +void MonoPipeReader::onTimestamp(const AudioTimestamp& timestamp) +{ + mPipe->mTimestampMutator.push(timestamp); +} + } // namespace android -- cgit v1.1 From 6466c9e6e6278c740aed77f695f679be9f5db478 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 23 Aug 2013 10:54:07 -0700 Subject: Add ExtendedAudioBufferProvider::framesReleased and onTimestamp and implement them in SourceAudioBufferProvider using the associated NBAIO_Source, and in Track using the associated AudioTrackServerProxy. Change-Id: I60dc4adba63fc1dc452ff16caf347e4a7c8242c2 --- media/libnbaio/SourceAudioBufferProvider.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libnbaio/SourceAudioBufferProvider.cpp b/media/libnbaio/SourceAudioBufferProvider.cpp index d11a86c..062fa0f 100644 --- a/media/libnbaio/SourceAudioBufferProvider.cpp +++ b/media/libnbaio/SourceAudioBufferProvider.cpp @@ -25,7 +25,7 @@ namespace android { SourceAudioBufferProvider::SourceAudioBufferProvider(const sp& source) : mSource(source), // mFrameBitShiftFormat below - mAllocated(NULL), mSize(0), mOffset(0), mRemaining(0), mGetCount(0) + mAllocated(NULL), mSize(0), mOffset(0), mRemaining(0), mGetCount(0), mFramesReleased(0) { ALOG_ASSERT(source != 0); @@ -90,6 +90,7 @@ void SourceAudioBufferProvider::releaseBuffer(Buffer *buffer) (mOffset + mRemaining <= mSize)); mOffset += buffer->frameCount; mRemaining -= buffer->frameCount; + mFramesReleased += buffer->frameCount; buffer->raw = NULL; buffer->frameCount = 0; mGetCount = 0; @@ -101,4 +102,14 @@ size_t SourceAudioBufferProvider::framesReady() const return avail < 0 ? 0 : (size_t) avail; } +size_t SourceAudioBufferProvider::framesReleased() const +{ + return mFramesReleased; +} + +void SourceAudioBufferProvider::onTimestamp(const AudioTimestamp& timestamp) +{ + mSource->onTimestamp(timestamp); +} + } // namespace android -- cgit v1.1 From 4d0815d694e5a2edb3ce48427de50f55d0f84c0b Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 29 Aug 2013 14:40:55 -0700 Subject: Implement MonoPipe::getTimestamp using SingleStateQueue observer Change-Id: I7b1928b087f1e676c7b291df6cefa7707301662c --- media/libnbaio/MonoPipe.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'media') diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp index b55be83..de0ad28 100644 --- a/media/libnbaio/MonoPipe.cpp +++ b/media/libnbaio/MonoPipe.cpp @@ -315,6 +315,9 @@ bool MonoPipe::isShutdown() status_t MonoPipe::getTimestamp(AudioTimestamp& timestamp) { + if (mTimestampObserver.poll(timestamp)) { + return OK; + } return INVALID_OPERATION; } -- cgit v1.1 From fe346c707f59d763ded93bc3d27b51f0c0408258 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 30 Aug 2013 13:28:22 -0700 Subject: Fix miscellanous AudioTrack::getTimestamp() bugs Check that get_presentation_position is non-NULL before calling. AudioTrack::getTimestamp not implemented for fast tracks. Fix typo in Track::getTimestamp(). Fix bugs in AudioTrack::getTimestamp after stop: - getTimestamp while stopped is not allowed. - stop, start, getTimestamp now returns the correct value. Change-Id: Ie8d9dc1f28d8927634e04175a68b147ffc2ea8eb --- media/libmedia/AudioTrack.cpp | 13 ++++++++++++- media/libnbaio/AudioStreamOutSink.cpp | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 176197c..744faee 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1714,7 +1714,18 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs) status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) { AutoMutex lock(mLock); - return mAudioTrack->getTimestamp(timestamp); + // FIXME not implemented for fast tracks; should use proxy and SSQ + if (mFlags & AUDIO_OUTPUT_FLAG_FAST) { + return INVALID_OPERATION; + } + if (mState != STATE_ACTIVE && mState != STATE_PAUSED) { + return INVALID_OPERATION; + } + status_t status = mAudioTrack->getTimestamp(timestamp); + if (status == NO_ERROR) { + timestamp.mPosition += mProxy->getEpoch(); + } + return status; } String8 AudioTrack::getParameters(const String8& keys) diff --git a/media/libnbaio/AudioStreamOutSink.cpp b/media/libnbaio/AudioStreamOutSink.cpp index b2de8a2..e4341d7 100644 --- a/media/libnbaio/AudioStreamOutSink.cpp +++ b/media/libnbaio/AudioStreamOutSink.cpp @@ -81,6 +81,9 @@ status_t AudioStreamOutSink::getNextWriteTimestamp(int64_t *timestamp) { status_t AudioStreamOutSink::getTimestamp(AudioTimestamp& timestamp) { + if (mStream->get_presentation_position == NULL) { + return INVALID_OPERATION; + } // FIXME position64 won't be needed after AudioTimestamp.mPosition is changed to uint64_t uint64_t position64; int ok = mStream->get_presentation_position(mStream, &position64, ×tamp.mTime); -- cgit v1.1 From 491211b87dd38357d37ece687cf8795bff8996a5 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 3 Sep 2013 15:29:33 -0700 Subject: Work around decoder slowness b/10528409 Change-Id: Ifcaf0488d63e87676b1e9382437943138deb76a6 --- media/libstagefright/MediaCodec.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'media') diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index f412dc8..66a0b4e 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -106,6 +106,8 @@ status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) { needDedicatedLooper = true; } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) { needDedicatedLooper = true; + } else if (!nameIsType && !strncmp(name, "OMX.qcom.video.decoder.avc.secure", 33)) { + needDedicatedLooper = true; } if (needDedicatedLooper) { -- cgit v1.1 From ec3acca4a75fc4adc076b56751124f507b419622 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 3 Sep 2013 14:35:37 -0700 Subject: wifi-display: do not use HDCP's encryptNative method if its unsupported Bug: 10609422 Change-Id: I005f1d04a4191b1503b5f3e895a98b8d6560c402 --- media/libmedia/IHDCP.cpp | 16 ++++++++++++++++ media/libmediaplayerservice/HDCP.cpp | 14 ++++++++++++++ media/libmediaplayerservice/HDCP.h | 1 + .../wifi-display/source/PlaybackSession.cpp | 3 ++- 4 files changed, 33 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp index a46ff91..1cf987a 100644 --- a/media/libmedia/IHDCP.cpp +++ b/media/libmedia/IHDCP.cpp @@ -30,6 +30,7 @@ enum { HDCP_SET_OBSERVER, HDCP_INIT_ASYNC, HDCP_SHUTDOWN_ASYNC, + HDCP_GET_CAPS, HDCP_ENCRYPT, HDCP_ENCRYPT_NATIVE, HDCP_DECRYPT, @@ -85,6 +86,13 @@ struct BpHDCP : public BpInterface { return reply.readInt32(); } + virtual uint32_t getCaps() { + Parcel data, reply; + data.writeInterfaceToken(IHDCP::getInterfaceDescriptor()); + remote()->transact(HDCP_GET_CAPS, data, &reply); + return reply.readInt32(); + } + virtual status_t encrypt( const void *inData, size_t size, uint32_t streamCTR, uint64_t *outInputCTR, void *outData) { @@ -222,6 +230,14 @@ status_t BnHDCP::onTransact( return OK; } + case HDCP_GET_CAPS: + { + CHECK_INTERFACE(IHDCP, data, reply); + + reply->writeInt32(getCaps()); + return OK; + } + case HDCP_ENCRYPT: { size_t size = data.readInt32(); diff --git a/media/libmediaplayerservice/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp index 8a3188c..c2ac1a3 100644 --- a/media/libmediaplayerservice/HDCP.cpp +++ b/media/libmediaplayerservice/HDCP.cpp @@ -100,6 +100,20 @@ status_t HDCP::shutdownAsync() { return mHDCPModule->shutdownAsync(); } +uint32_t HDCP::getCaps() { + Mutex::Autolock autoLock(mLock); + + if (mHDCPModule == NULL) { + return NO_INIT; + } + + // TO-DO: + // Only support HDCP_CAPS_ENCRYPT (byte-array to byte-array) for now. + // use mHDCPModule->getCaps() when the HDCP libraries get updated. + //return mHDCPModule->getCaps(); + return HDCPModule::HDCP_CAPS_ENCRYPT; +} + status_t HDCP::encrypt( const void *inData, size_t size, uint32_t streamCTR, uint64_t *outInputCTR, void *outData) { diff --git a/media/libmediaplayerservice/HDCP.h b/media/libmediaplayerservice/HDCP.h index c60c2e0..26ddc86 100644 --- a/media/libmediaplayerservice/HDCP.h +++ b/media/libmediaplayerservice/HDCP.h @@ -30,6 +30,7 @@ struct HDCP : public BnHDCP { virtual status_t setObserver(const sp &observer); virtual status_t initAsync(const char *host, unsigned port); virtual status_t shutdownAsync(); + virtual uint32_t getCaps(); virtual status_t encrypt( const void *inData, size_t size, uint32_t streamCTR, diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 0aa4ee5..286ea13 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -939,7 +939,8 @@ status_t WifiDisplaySource::PlaybackSession::addSource( if (isVideo) { format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC); format->setInt32("store-metadata-in-buffers", true); - format->setInt32("store-metadata-in-buffers-output", (mHDCP != NULL)); + format->setInt32("store-metadata-in-buffers-output", (mHDCP != NULL) + && (mHDCP->getCaps() & HDCPModule::HDCP_CAPS_ENCRYPT_NATIVE)); format->setInt32( "color-format", OMX_COLOR_FormatAndroidOpaque); format->setInt32("profile-idc", profileIdc); -- cgit v1.1 From 6f9439efd2a6004b588605f6a9d4af20c98e8e80 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 4 Sep 2013 15:00:07 -0700 Subject: Better workaround for slow decoders. This is more in the spirit of the original code. Now it checks whether a codec instantiated by name is a video codec, and enables the extra looper if so. b/10528409 Change-Id: Ia253c04c1283d4ecf66f213ef4bf523279ad7cca --- media/libstagefright/MediaCodec.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 66a0b4e..e0686be 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -104,10 +105,24 @@ status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) { bool needDedicatedLooper = false; if (nameIsType && !strncasecmp(name, "video/", 6)) { needDedicatedLooper = true; - } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) { - needDedicatedLooper = true; - } else if (!nameIsType && !strncmp(name, "OMX.qcom.video.decoder.avc.secure", 33)) { - needDedicatedLooper = true; + } else { + AString tmp = name; + if (tmp.endsWith(".secure")) { + tmp.erase(tmp.size() - 7, 7); + } + const MediaCodecList *mcl = MediaCodecList::getInstance(); + ssize_t codecIdx = mcl->findCodecByName(tmp.c_str()); + if (codecIdx >= 0) { + Vector types; + if (mcl->getSupportedTypes(codecIdx, &types) == OK) { + for (int i = 0; i < types.size(); i++) { + if (types[i].startsWith("video/")) { + needDedicatedLooper = true; + break; + } + } + } + } } if (needDedicatedLooper) { -- cgit v1.1 From 284c17e73bbff51cb5b1adcee98386d47733757a Mon Sep 17 00:00:00 2001 From: jpadmana Date: Tue, 4 Jun 2013 16:08:29 +0530 Subject: Effects Factory changes for effects offload audio_effects.conf - commented changes to illustrate the addition of Proxy and sub effects to the conf file Added an effectFactoryApi - EffectGetSubEffects for querying the sub effect descriptors from the factory. This api is used by the Proxy to get the sub effects Added functions and data structures in factory code for loading the sub effects gSubEffectList - has the Proxies and their corresponding sub effects - addSubEffect() - reads a sub effect node and adds to the gSubEffectList - findSubEffect() - searches through the gSubEffectList to find a SubEffect Bug: 8174034. Change-Id: I25b0c62b2ad523a52337128b51469e628209ea3e Signed-off-by: jpadmana --- media/libeffects/data/audio_effects.conf | 39 ++++++ media/libeffects/factory/EffectsFactory.c | 218 +++++++++++++++++++++++++++++- media/libeffects/factory/EffectsFactory.h | 19 +++ 3 files changed, 274 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index 93f27cb..aa48e4e 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -6,6 +6,23 @@ # } # } libraries { +# This is a proxy library that will be an abstraction for +# the HW and SW effects + + #proxy { + #path /system/lib/soundfx/libProxy.so + #} + +# This is the SW implementation library of the effect + #libSW { + #path /system/lib/soundfx/libswwrapper.so + #} + +# This is the HW implementation library for the effect + #libHW { + #path /system/lib/soundfx/libhwwrapper.so + #} + bundle { path /system/lib/soundfx/libbundlewrapper.so } @@ -43,6 +60,28 @@ libraries { # } effects { + +# additions for the proxy implementation +# Proxy implementation + #effectname { + #library proxy + #uuid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + + # SW implemetation of the effect. Added as a node under the proxy to + # indicate this as a sub effect. + #libsw { + #library libSW + #uuid yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy + #} End of SW effect + + # HW implementation of the effect. Added as a node under the proxy to + # indicate this as a sub effect. + #libhw { + #library libHW + #uuid zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz + #}End of HW effect + #} End of effect proxy + bassboost { library bundle uuid 8631f300-72e2-11df-b57e-0002a5d5c51b diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c index f158929..f8d6041 100644 --- a/media/libeffects/factory/EffectsFactory.c +++ b/media/libeffects/factory/EffectsFactory.c @@ -28,6 +28,9 @@ static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries +// list of effect_descriptor and list of sub effects : all currently loaded +// It does not contain effects without sub effects. +static list_sub_elem_t *gSubEffectList; static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList static uint32_t gNumEffects; // total number number of effects static list_elem_t *gCurLib; // current library in enumeration process @@ -50,6 +53,8 @@ static int loadLibraries(cnode *root); static int loadLibrary(cnode *root, const char *name); static int loadEffects(cnode *root); static int loadEffect(cnode *node); +// To get and add the effect pointed by the passed node to the gSubEffectList +static int addSubEffect(cnode *root); static lib_entry_t *getLibrary(const char *path); static void resetEffectEnumeration(); static uint32_t updateNumEffects(); @@ -57,6 +62,10 @@ static int findEffect(const effect_uuid_t *type, const effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc); +// To search a subeffect in the gSubEffectList +int findSubEffect(const effect_uuid_t *uuid, + lib_entry_t **lib, + effect_descriptor_t **desc); static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len); static int stringToUuid(const char *str, effect_uuid_t *uuid); static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen); @@ -287,7 +296,12 @@ int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, eff ret = findEffect(NULL, uuid, &l, &d); if (ret < 0){ - goto exit; + // Sub effects are not associated with the library->effects, + // so, findEffect will fail. Search for the effect in gSubEffectList. + ret = findSubEffect(uuid, &l, &d); + if (ret < 0 ) { + goto exit; + } } // create effect in library @@ -354,21 +368,27 @@ int EffectRelease(effect_handle_t handle) } if (e1 == NULL) { ret = -ENOENT; + pthread_mutex_unlock(&gLibLock); goto exit; } // release effect in library if (fx->lib == NULL) { ALOGW("EffectRelease() fx %p library already unloaded", handle); + pthread_mutex_unlock(&gLibLock); } else { pthread_mutex_lock(&fx->lib->lock); + // Releasing the gLibLock here as the list access is over as the + // effect is removed from the list. + // If the gLibLock is not released, we will have a deadlock situation + // since we call the sub effect release inside the EffectRelease of Proxy + pthread_mutex_unlock(&gLibLock); fx->lib->desc->release_effect(fx->subItfe); pthread_mutex_unlock(&fx->lib->lock); } free(fx); exit: - pthread_mutex_unlock(&gLibLock); return ret; } @@ -380,6 +400,49 @@ int EffectIsNullUuid(const effect_uuid_t *uuid) return 1; } +// Function to get the sub effect descriptors of the effect whose uuid +// is pointed by the first argument. It searches the gSubEffectList for the +// matching uuid and then copies the corresponding sub effect descriptors +// to the inout param +int EffectGetSubEffects(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptors, size_t size) +{ + ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X" + "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, + uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2], + uuid->node[3],uuid->node[4],uuid->node[5]); + + // Check if the size of the desc buffer is large enough for 2 subeffects + if ((uuid == NULL) || (pDescriptors == NULL) || + (size < 2*sizeof(effect_descriptor_t))) { + ALOGW("NULL pointer or insufficient memory. Cannot query subeffects"); + return -EINVAL; + } + int ret = init(); + if (ret < 0) + return ret; + list_sub_elem_t *e = gSubEffectList; + sub_effect_entry_t *subeffect; + effect_descriptor_t *d; + int count = 0; + while (e != NULL) { + d = (effect_descriptor_t*)e->object; + if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) { + ALOGV("EffectGetSubEffects: effect found in the list"); + list_elem_t *subefx = e->sub_elem; + while (subefx != NULL) { + subeffect = (sub_effect_entry_t*)subefx->object; + d = (effect_descriptor_t*)(subeffect->object); + pDescriptors[count++] = *d; + subefx = subefx->next; + } + ALOGV("EffectGetSubEffects end - copied the sub effect descriptors"); + return count; + } + e = e->next; + } + return -ENOENT; +} ///////////////////////////////////////////////// // Local functions ///////////////////////////////////////////////// @@ -503,6 +566,65 @@ error: return -EINVAL; } +// This will find the library and UUID tags of the sub effect pointed by the +// node, gets the effect descriptor and lib_entry_t and adds the subeffect - +// sub_entry_t to the gSubEffectList +int addSubEffect(cnode *root) +{ + ALOGV("addSubEffect"); + cnode *node; + effect_uuid_t uuid; + effect_descriptor_t *d; + lib_entry_t *l; + list_elem_t *e; + node = config_find(root, LIBRARY_TAG); + if (node == NULL) { + return -EINVAL; + } + l = getLibrary(node->value); + if (l == NULL) { + ALOGW("addSubEffect() could not get library %s", node->value); + return -EINVAL; + } + node = config_find(root, UUID_TAG); + if (node == NULL) { + return -EINVAL; + } + if (stringToUuid(node->value, &uuid) != 0) { + ALOGW("addSubEffect() invalid uuid %s", node->value); + return -EINVAL; + } + d = malloc(sizeof(effect_descriptor_t)); + if (l->desc->get_descriptor(&uuid, d) != 0) { + char s[40]; + uuidToString(&uuid, s, 40); + ALOGW("Error querying effect %s on lib %s", s, l->name); + free(d); + return -EINVAL; + } +#if (LOG_NDEBUG==0) + char s[256]; + dumpEffectDescriptor(d, s, 256); + ALOGV("addSubEffect() read descriptor %p:%s",d, s); +#endif + if (EFFECT_API_VERSION_MAJOR(d->apiVersion) != + EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) { + ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name); + free(d); + return -EINVAL; + } + sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t)); + sub_effect->object = d; + // lib_entry_t is stored since the sub effects are not linked to the library + sub_effect->lib = l; + e = malloc(sizeof(list_elem_t)); + e->object = sub_effect; + e->next = gSubEffectList->sub_elem; + gSubEffectList->sub_elem = e; + ALOGV("addSubEffect end"); + return 0; +} + int loadEffects(cnode *root) { cnode *node; @@ -571,9 +693,101 @@ int loadEffect(cnode *root) e->next = l->effects; l->effects = e; + // After the UUID node in the config_tree, if node->next is valid, + // that would be sub effect node. + // Find the sub effects and add them to the gSubEffectList + node = node->next; + int count = 2; + bool hwSubefx = false, swSubefx = false; + list_sub_elem_t *sube = NULL; + if (node != NULL) { + ALOGV("Adding the effect to gEffectSubList as there are sub effects"); + sube = malloc(sizeof(list_sub_elem_t)); + sube->object = d; + sube->sub_elem = NULL; + sube->next = gSubEffectList; + gSubEffectList = sube; + } + while (node != NULL && count) { + if (addSubEffect(node)) { + ALOGW("loadEffect() could not add subEffect %s", node->value); + // Change the gSubEffectList to point to older list; + gSubEffectList = sube->next; + free(sube->sub_elem);// Free an already added sub effect + sube->sub_elem = NULL; + free(sube); + return -ENOENT; + } + sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object; + effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object); + // Since we return a dummy descriptor for the proxy during + // get_descriptor call,we replace it with the correspoding + // sw effect descriptor, but with Proxy UUID + // check for Sw desc + if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) == + EFFECT_FLAG_HW_ACC_TUNNEL)) { + swSubefx = true; + *d = *subEffectDesc; + d->uuid = uuid; + ALOGV("loadEffect() Changed the Proxy desc"); + } else + hwSubefx = true; + count--; + node = node->next; + } + // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc + if (hwSubefx && swSubefx) { + d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; + } return 0; } +// Searches the sub effect matching to the specified uuid +// in the gSubEffectList. It gets the lib_entry_t for +// the matched sub_effect . Used in EffectCreate of sub effects +int findSubEffect(const effect_uuid_t *uuid, + lib_entry_t **lib, + effect_descriptor_t **desc) +{ + list_sub_elem_t *e = gSubEffectList; + list_elem_t *subefx; + sub_effect_entry_t *effect; + lib_entry_t *l = NULL; + effect_descriptor_t *d = NULL; + int found = 0; + int ret = 0; + + if (uuid == NULL) + return -EINVAL; + + while (e != NULL && !found) { + subefx = (list_elem_t*)(e->sub_elem); + while (subefx != NULL) { + effect = (sub_effect_entry_t*)subefx->object; + l = (lib_entry_t *)effect->lib; + d = (effect_descriptor_t *)effect->object; + if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) { + ALOGV("uuid matched"); + found = 1; + break; + } + subefx = subefx->next; + } + e = e->next; + } + if (!found) { + ALOGV("findSubEffect() effect not found"); + ret = -ENOENT; + } else { + ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name); + *lib = l; + if (desc != NULL) { + *desc = d; + } + } + return ret; +} + lib_entry_t *getLibrary(const char *name) { list_elem_t *e; diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h index c1d4319..147ff18 100644 --- a/media/libeffects/factory/EffectsFactory.h +++ b/media/libeffects/factory/EffectsFactory.h @@ -32,6 +32,15 @@ typedef struct list_elem_s { struct list_elem_s *next; } list_elem_t; +// Structure used for storing effects with their sub effects. +// Used in creating gSubEffectList. Here, +// object holds the effect desc and the list sub_elem holds the sub effects +typedef struct list_sub_elem_s { + void *object; + list_elem_t *sub_elem; + struct list_sub_elem_s *next; +} list_sub_elem_t; + typedef struct lib_entry_s { audio_effect_library_t *desc; char *name; @@ -47,6 +56,16 @@ typedef struct effect_entry_s { lib_entry_t *lib; } effect_entry_t; +// Structure used to store the lib entry +// and the descriptor of the sub effects. +// The library entry is to be stored in case of +// sub effects as the sub effects are not linked +// to the library list - gLibraryList. +typedef struct sub_effect_entry_s { + lib_entry_t *lib; + void *object; +} sub_effect_entry_t; + #if __cplusplus } // extern "C" #endif -- cgit v1.1 From 60c60df7db278d2fa5c90b0fa14f99a61d50272b Mon Sep 17 00:00:00 2001 From: jpadmana Date: Tue, 4 Jun 2013 16:03:29 +0530 Subject: Effect Offload Proxy for effects offload Effect Proxy abstracts the sub effects to the upper layers. It has the following functionalities: - creation and release of sub effects - routing the effect commands and process to the appropriate sub effect Bug: 8174034. Change-Id: I22d8136636048e7fe8f8807cbc6e348ffa200a22 Signed-off-by: jpadmana --- media/libeffects/data/audio_effects.conf | 2 +- media/libeffects/proxy/Android.mk | 34 ++++ media/libeffects/proxy/EffectProxy.cpp | 298 +++++++++++++++++++++++++++++++ media/libeffects/proxy/EffectProxy.h | 75 ++++++++ 4 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 media/libeffects/proxy/Android.mk create mode 100644 media/libeffects/proxy/EffectProxy.cpp create mode 100644 media/libeffects/proxy/EffectProxy.h (limited to 'media') diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index aa48e4e..69a3c53 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -10,7 +10,7 @@ libraries { # the HW and SW effects #proxy { - #path /system/lib/soundfx/libProxy.so + #path /system/lib/soundfx/libeffectproxy.so #} # This is the SW implementation library of the effect diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk new file mode 100644 index 0000000..01b3be1 --- /dev/null +++ b/media/libeffects/proxy/Android.mk @@ -0,0 +1,34 @@ +# Copyright 2013 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE:= libeffectproxy +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_TAGS := optional + + +LOCAL_SRC_FILES := \ + EffectProxy.cpp + +LOCAL_CFLAGS+= -fvisibility=hidden + +LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libdl libeffects + +LOCAL_C_INCLUDES := \ + system/media/audio_effects/include \ + bionic/libc/include + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp new file mode 100644 index 0000000..77c6e89 --- /dev/null +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "EffectProxy" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +// This is a dummy proxy descriptor just to return to Factory during the initial +// GetDescriptor call. Later in the factory, it is replaced with the +// SW sub effect descriptor +const effect_descriptor_t gProxyDescriptor = { + EFFECT_UUID_INITIALIZER, // type + EFFECT_UUID_INITIALIZER, // uuid + EFFECT_CONTROL_API_VERSION, //version of effect control API + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST | + EFFECT_FLAG_VOLUME_CTRL), // effect capability flags + 0, // CPU load + 1, // Data memory + "Proxy", //effect name + "AOSP", //implementor name +}; + + +static const effect_descriptor_t *const gDescriptors[] = +{ + &gProxyDescriptor, +}; + +int EffectProxyCreate(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle) { + + effect_descriptor_t* desc; + EffectContext* pContext; + if (pHandle == NULL || uuid == NULL) { + ALOGE("EffectProxyCreate() called with NULL pointer"); + return -EINVAL; + } + ALOGV("EffectProxyCreate start.."); + pContext = new EffectContext; + pContext->sessionId = sessionId; + pContext->ioId = ioId; + pContext->uuid = *uuid; + pContext->common_itfe = &gEffectInterface; + // The sub effects will be created in effect_command when the first command + // for the effect is received + pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL; + + // Get the HW and SW sub effect descriptors from the effects factory + desc = new effect_descriptor_t[SUB_FX_COUNT]; + pContext->desc = new effect_descriptor_t[SUB_FX_COUNT]; + int retValue = EffectGetSubEffects(uuid, desc, + sizeof(effect_descriptor_t) * SUB_FX_COUNT); + // EffectGetSubEffects returns the number of sub-effects copied. + if (retValue != SUB_FX_COUNT) { + ALOGE("EffectCreate() could not get the sub effects"); + delete desc; + delete pContext->desc; + return -EINVAL; + } + // Check which is the HW descriptor and copy the descriptors + // to the Context desc array + // Also check if there is only one HW and one SW descriptor. + // HW descriptor alone has the HW_TUNNEL flag. + if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && + !(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { + pContext->desc[SUB_FX_OFFLOAD] = desc[0]; + pContext->desc[SUB_FX_HOST] = desc[1]; + } + else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && + !(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { + pContext->desc[SUB_FX_HOST] = desc[0]; + pContext->desc[SUB_FX_OFFLOAD] = desc[1]; + } + delete desc; +#if (LOG_NDEBUG == 0) + effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid; + ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X" + "%02X%02X\n",uuid_print.timeLow, uuid_print.timeMid, + uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0], + uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], + uuid_print.node[4], uuid_print.node[5]); + ALOGV("EffectCreate() UUID of OFFLOAD: %08X-%04X-%04X-%04X-%02X%02X%02X%02X" + "%02X%02X\n", uuid_print.timeLow, uuid_print.timeMid, + uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0], + uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], + uuid_print.node[4], uuid_print.node[5]); +#endif + *pHandle = (effect_handle_t)pContext; + ALOGV("EffectCreate end"); + return 0; +} //end EffectProxyCreate + +int EffectProxyRelease(effect_handle_t handle) { + EffectContext * pContext = (EffectContext *)handle; + if (pContext == NULL) { + ALOGV("ERROR : EffectRelease called with NULL pointer"); + return -EINVAL; + } + ALOGV("EffectRelease"); + delete pContext->desc; + if (pContext->eHandle[SUB_FX_HOST]) + EffectRelease(pContext->eHandle[SUB_FX_HOST]); + if (pContext->eHandle[SUB_FX_OFFLOAD]) + EffectRelease(pContext->eHandle[SUB_FX_OFFLOAD]); + delete pContext; + pContext = NULL; + return 0; +} /*end EffectProxyRelease */ + +int EffectProxyGetDescriptor(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor) { + const effect_descriptor_t *desc = NULL; + + if (pDescriptor == NULL || uuid == NULL) { + ALOGV("EffectGetDescriptor() called with NULL pointer"); + return -EINVAL; + } + desc = &gProxyDescriptor; + *pDescriptor = *desc; + return 0; +} /* end EffectProxyGetDescriptor */ + +/* Effect Control Interface Implementation: Process */ +int Effect_process(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer) { + + EffectContext *pContext = (EffectContext *) self; + int ret = 0; + if (pContext != NULL) { + int index = pContext->index; + // if the index refers to HW , do not do anything. Just return. + if (index == SUB_FX_HOST) { + ALOGV("Calling CoreProcess"); + ret = (*pContext->eHandle[index])->process(pContext->eHandle[index], + inBuffer, outBuffer); + } + } + return ret; +} /* end Effect_process */ + +/* Effect Control Interface Implementation: Command */ +int Effect_command(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData) { + + EffectContext *pContext = (EffectContext *) self; + int status; + if (pContext == NULL) { + ALOGV("Effect_command() Proxy context is NULL"); + return -EINVAL; + } + if (pContext->eHandle[SUB_FX_HOST] == NULL) { + ALOGV("Effect_command() Calling HOST EffectCreate"); + status = EffectCreate(&pContext->desc[SUB_FX_HOST].uuid, + pContext->sessionId, pContext->ioId, + &(pContext->eHandle[SUB_FX_HOST])); + if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) { + ALOGV("Effect_command() Error creating SW sub effect"); + return status; + } + } + if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) { + ALOGV("Effect_command() Calling OFFLOAD EffectCreate"); + status = EffectCreate(&pContext->desc[SUB_FX_OFFLOAD].uuid, + pContext->sessionId, pContext->ioId, + &(pContext->eHandle[SUB_FX_OFFLOAD])); + if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) { + ALOGV("Effect_command() Error creating HW effect"); + // Do not return error here as SW effect is created + // Return error if the CMD_OFFLOAD sends the index as OFFLOAD + } + pContext->index = SUB_FX_HOST; + } + // EFFECT_CMD_OFFLOAD used to (1) send whether the thread is offload or not + // (2) Send the ioHandle of the effectThread when the effect + // is moved from one type of thread to another. + // pCmdData points to a memory holding effect_offload_param_t structure + if (cmdCode == EFFECT_CMD_OFFLOAD) { + ALOGV("Effect_command() cmdCode = EFFECT_CMD_OFFLOAD"); + if (cmdSize == 0 || pCmdData == NULL) { + ALOGV("effectsOffload: Effect_command: CMD_OFFLOAD has no data"); + *(int*)pReplyData = FAILED_TRANSACTION; + return FAILED_TRANSACTION; + } + effect_offload_param_t* offloadParam = (effect_offload_param_t*)pCmdData; + // Assign the effect context index based on isOffload field of the structure + pContext->index = offloadParam->isOffload ? SUB_FX_OFFLOAD : SUB_FX_HOST; + // if the index is HW and the HW effect is unavailable, return error + // and reset the index to SW + if (pContext->eHandle[pContext->index] == NULL) { + ALOGV("Effect_command()CMD_OFFLOAD sub effect unavailable"); + *(int*)pReplyData = FAILED_TRANSACTION; + return FAILED_TRANSACTION; + } + pContext->ioId = offloadParam->ioHandle; + ALOGV("Effect_command()CMD_OFFLOAD index:%d io %d", pContext->index, pContext->ioId); + // Update the DSP wrapper with the new ioHandle. + // Pass the OFFLOAD command to the wrapper. + // The DSP wrapper needs to handle this CMD + if (pContext->eHandle[SUB_FX_OFFLOAD]) + status = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( + pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + return status; + } + + int index = pContext->index; + if (index != SUB_FX_HOST && index != SUB_FX_OFFLOAD) { + ALOGV("Effect_command: effect index is neither offload nor host"); + return -EINVAL; + } + ALOGV("Effect_command: pContext->eHandle[%d]: %p", + index, pContext->eHandle[index]); + if (pContext->eHandle[SUB_FX_HOST]) + (*pContext->eHandle[SUB_FX_HOST])->command( + pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + if (pContext->eHandle[SUB_FX_OFFLOAD]) { + // In case of SET CMD, when the offload stream is unavailable, + // we will store the effect param values in the DSP effect wrapper. + // When the offload effects get enabled, we send these values to the + // DSP during Effect_config. + // So,we send the params to DSP wrapper also + (*pContext->eHandle[SUB_FX_OFFLOAD])->command( + pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + } + return 0; +} /* end Effect_command */ + + +/* Effect Control Interface Implementation: get_descriptor */ +int Effect_getDescriptor(effect_handle_t self, + effect_descriptor_t *pDescriptor) { + + EffectContext * pContext = (EffectContext *) self; + const effect_descriptor_t *desc; + + ALOGV("Effect_getDescriptor"); + if (pContext == NULL || pDescriptor == NULL) { + ALOGV("Effect_getDescriptor() invalid param"); + return -EINVAL; + } + if (pContext->desc == NULL) { + ALOGV("Effect_getDescriptor() could not get descriptor"); + return -EINVAL; + } + desc = &pContext->desc[SUB_FX_HOST]; + *pDescriptor = *desc; + pDescriptor->uuid = pContext->uuid; // Replace the uuid with the Proxy UUID + // Also set/clear the EFFECT_FLAG_OFFLOAD_SUPPORTED flag based on the sub effects availability + if (pContext->eHandle[SUB_FX_OFFLOAD] != NULL) + pDescriptor->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; + else + pDescriptor->flags &= ~EFFECT_FLAG_OFFLOAD_SUPPORTED; + return 0; +} /* end Effect_getDescriptor */ + +} // namespace android + +__attribute__ ((visibility ("default"))) +audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { + tag : AUDIO_EFFECT_LIBRARY_TAG, + version : EFFECT_LIBRARY_API_VERSION, + name : "Effect Proxy", + implementor : "AOSP", + create_effect : android::EffectProxyCreate, + release_effect : android::EffectProxyRelease, + get_descriptor : android::EffectProxyGetDescriptor, +}; diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h new file mode 100644 index 0000000..8992f93 --- /dev/null +++ b/media/libeffects/proxy/EffectProxy.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +namespace android { +enum { + SUB_FX_HOST, // Index of HOST in the descriptor and handle arrays + // of the Proxy context + SUB_FX_OFFLOAD, // Index of OFFLOAD in the descriptor and handle arrays + // of the Proxy context + SUB_FX_COUNT // The number of sub effects for a Proxy(1 HW, 1 SW) +}; +#if __cplusplus +extern "C" { +#endif + +int EffectProxyCreate(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle); +int EffectProxyRelease(effect_handle_t handle); +int EffectProxyGetDescriptor(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor); +/* Effect Control Interface Implementation: Process */ +int Effect_process(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer); + +/* Effect Control Interface Implementation: Command */ +int Effect_command(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData); +int Effect_getDescriptor(effect_handle_t self, + effect_descriptor_t *pDescriptor); + +const struct effect_interface_s gEffectInterface = { + Effect_process, + Effect_command, + Effect_getDescriptor, + NULL, +}; + +struct EffectContext { + const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy + effect_descriptor_t* desc; // Points to the sub effect descriptors + effect_handle_t eHandle[SUB_FX_COUNT]; // The effect handles of the sub effects + int index; // The index that is currently active - HOST or OFFLOAD + int32_t sessionId; // The sessiond in which the effect is created. + // Stored in context to pass on to sub effect creation + int32_t ioId; // The ioId in which the effect is created. + // Stored in context to pass on to sub effect creation + effect_uuid_t uuid; // UUID of the Proxy +}; + +#if __cplusplus +} // extern "C" +#endif +} //namespace android -- cgit v1.1 From 7f9551f75eedb3e4e1fe8feaaba48d8080635fc4 Mon Sep 17 00:00:00 2001 From: Zhijun He Date: Mon, 9 Sep 2013 15:48:58 -0700 Subject: MediaMuxer: Hook up setLocation method This method is needed when mediamuxer is used for camera video recording. Bug: 10594784 Change-Id: I9bd006a07e5e2ac7019849e3f4f7cf7b8356d669 --- media/libstagefright/MediaMuxer.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'media') diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index 94ce5de..d87e910 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -103,6 +103,16 @@ status_t MediaMuxer::setOrientationHint(int degrees) { return OK; } +status_t MediaMuxer::setLocation(int latitude, int longitude) { + Mutex::Autolock autoLock(mMuxerLock); + if (mState != INITIALIZED) { + ALOGE("setLocation() must be called before start()."); + return INVALID_OPERATION; + } + ALOGV("Setting location: latitude = %d, longitude = %d", latitude, longitude); + return mWriter->setGeoData(latitude, longitude); +} + status_t MediaMuxer::start() { Mutex::Autolock autoLock(mMuxerLock); if (mState == INITIALIZED) { -- cgit v1.1 From 8973c0439984f85870dffa7a100580271933c964 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 11 Sep 2013 14:35:16 -0700 Subject: Fix SoundPool.play() looping This is done by configuring SoundPool for shared memory and fast track. Previously SoundPool used a streaming track, and looping in streaming mode relied on the ability to loop the most recently enqueued data. That 'feature' was lost in the new implementation of streaming, so we're now switching from streaming mode to shared memory mode. Shared memory mode had always been desired, but was blocked by bug 2801375 which is fixed now. Bug: 10171337 Change-Id: I2a938e3ffafa2a74d5210b4198b50db20ad5da0e --- media/libmedia/SoundPool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 7f10e05..0985164 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "SoundPool" #include -//#define USE_SHARED_MEM_BUFFER +#define USE_SHARED_MEM_BUFFER #include #include @@ -602,7 +602,7 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV // do not create a new audio track if current track is compatible with sample parameters #ifdef USE_SHARED_MEM_BUFFER newTrack = new AudioTrack(streamType, sampleRate, sample->format(), - channels, sample->getIMemory(), AUDIO_OUTPUT_FLAG_NONE, callback, userData); + channels, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData); #else newTrack = new AudioTrack(streamType, sampleRate, sample->format(), channels, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData, -- cgit v1.1 From a911f51c21430ac92f1d796b2338878fd98382e9 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 6 Sep 2013 15:34:50 -0700 Subject: Pass subtitle track properties to getTrackInfo Bug: 10326117 Change-Id: I15fcc49ad02e26d7cc92e82ee670bafca62a09a7 --- media/libstagefright/httplive/M3UParser.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'media') diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index bc6d629..243888c 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -200,6 +200,13 @@ void M3UParser::MediaGroup::getTrackInfo(Parcel* reply) const { const Media &item = mMediaItems.itemAt(i); const char *lang = item.mLanguage.empty() ? "und" : item.mLanguage.c_str(); reply->writeString16(String16(lang)); + + if (mType == TYPE_SUBS) { + // TO-DO: pass in a MediaFormat instead + reply->writeInt32(!!(item.mFlags & MediaGroup::FLAG_AUTOSELECT)); + reply->writeInt32(!!(item.mFlags & MediaGroup::FLAG_DEFAULT)); + reply->writeInt32(!!(item.mFlags & MediaGroup::FLAG_FORCED)); + } } } -- cgit v1.1 From 4b7069dac546ad21cf62ca6132d50ea41857d08e Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 11 Sep 2013 12:52:43 -0700 Subject: Add FLAG_CAN_SEEK_BACKWARD and FLAG_CAN_SEEK_FORWARD see flags Also update seek flag in NuPlayerDriver, otherwise MediaPlayer will get wrong flags. Bug: 10676387 Change-Id: Ice30f27a9a04e37b4718d26228a407fea8d9e4fc --- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 5 +++++ media/libmediaplayerservice/nuplayer/RTSPSource.cpp | 9 ++++----- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index e1735fa..750287f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -1396,6 +1396,11 @@ void NuPlayer::onSourceNotify(const sp &msg) { uint32_t flags; CHECK(msg->findInt32("flags", (int32_t *)&flags)); + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifyFlagsChanged(flags); + } + if ((mSourceFlags & Source::FLAG_DYNAMIC_DURATION) && (!(flags & Source::FLAG_DYNAMIC_DURATION))) { cancelPollDuration(); diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index 3385a19..18cf6d1 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -358,11 +358,10 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp &msg) { uint32_t flags = 0; if (mHandler->isSeekable()) { - flags = FLAG_CAN_PAUSE | FLAG_CAN_SEEK; - - // Seeking 10secs forward or backward is a very expensive - // operation for rtsp, so let's not enable that. - // The user can always use the seek bar. + flags = FLAG_CAN_PAUSE + | FLAG_CAN_SEEK + | FLAG_CAN_SEEK_BACKWARD + | FLAG_CAN_SEEK_FORWARD; } notifyFlagsChanged(flags); -- cgit v1.1 From c38fcfba95f711e5738e4c72bd5499317a2f30d9 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 12 Sep 2013 09:12:54 -0700 Subject: ACodec: fix flush/resume for decoder-output-meta-data mode When in decoder-output-meta-data mode, ACodec does not hold onto buffers, but they are either with the native window, or with the component/client. However, for flushing we did not release the discarded buffers back to native window (this makes sense because they will be resubmitted shortly.) This logic can be handled by the normal resubmission. Change-Id: Ic472b386422251515ef12f426e187f208f14decc Signed-off-by: Lajos Molnar Bug: 10621959 Bug: 10192533 --- media/libstagefright/ACodec.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 2e55c4f..5c3abd0 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2366,6 +2366,10 @@ void ACodec::waitUntilAllPossibleNativeWindowBuffersAreReturnedToUs() { while (countBuffersOwnedByNativeWindow() > (size_t)minUndequeuedBufs && dequeueBufferFromNativeWindow() != NULL) { + // these buffers will be submitted as regular buffers; account for this + if (mStoreMetaDataInOutputBuffers && mMetaDataBuffersToSubmit > 0) { + --mMetaDataBuffersToSubmit; + } } } @@ -4000,10 +4004,9 @@ void ACodec::ExecutingState::submitRegularOutputBuffers() { } void ACodec::ExecutingState::submitOutputBuffers() { + submitRegularOutputBuffers(); if (mCodec->mStoreMetaDataInOutputBuffers) { submitOutputMetaBuffers(); - } else { - submitRegularOutputBuffers(); } } -- cgit v1.1 From ee08f7e36eeba80e005f9bdaebce635860a8f005 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 16 Sep 2013 13:30:01 -0700 Subject: Fix hang Specify that the surface is controlled by the app, to avoid a hang. b/10531761 Change-Id: Idccc2c73aa3d368d8e7fbdc071ce36e2382efea4 --- media/libmediaplayerservice/MediaPlayerService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 8833bd7..0dabd37 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -744,7 +744,7 @@ status_t MediaPlayerService::Client::setVideoSurfaceTexture( sp anw; if (bufferProducer != NULL) { - anw = new Surface(bufferProducer); + anw = new Surface(bufferProducer, true /* controlledByApp */); status_t err = native_window_api_connect(anw.get(), NATIVE_WINDOW_API_MEDIA); -- cgit v1.1 From ce8828a016b082f730152af2204b8ea3610dc1ec Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 16 Sep 2013 18:07:38 -0700 Subject: Fix underruns when fast track denied due to SRC OpenSL ES requests a fast track. If sample rate conversion is needed, the request is denied by server, and a larger client buffer is used to handle the higher latency of a normal track. However the client notification period was calculated based on buffer being divided into 2 sub-buffers. That resulted in the notification period being too long. The server pulls chunks that are smaller than half the total buffer. So now the client uses 3 sub-buffers when there is SRC. Also removed the 'defer wake' optimization because it was incorrect. This optimization attempted to reduce the number of wakeups of client, when server releaseBuffer knows that another releaseBuffer will be following. But there is no way for the first releaseBuffer to predict how soon the second releaseBuffer will occur. In some cases it was a long time, and the client underran. So now the client is woken up immediately if the total number of available frames to client is >= the minimum number the client wants to see (the notification period). Also fix bug where minimum frame count was not being used in the calculation of notification period. Bug: 10342804 Change-Id: I3c246f4e7bc3684a344f2cf08268dc082e338e2a --- media/libmedia/AudioTrack.cpp | 67 +++++++++++++++++++------------------ media/libmedia/AudioTrackShared.cpp | 7 ++-- 2 files changed, 37 insertions(+), 37 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 744faee..15249a4 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -815,12 +815,29 @@ status_t AudioTrack::createTrack_l( return NO_INIT; } + // Not all of these values are needed under all conditions, but it is easier to get them all + uint32_t afLatency; - if ((status = AudioSystem::getLatency(output, streamType, &afLatency)) != NO_ERROR) { + status = AudioSystem::getLatency(output, streamType, &afLatency); + if (status != NO_ERROR) { ALOGE("getLatency(%d) failed status %d", output, status); return NO_INIT; } + size_t afFrameCount; + status = AudioSystem::getFrameCount(output, streamType, &afFrameCount); + if (status != NO_ERROR) { + ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, status); + return NO_INIT; + } + + uint32_t afSampleRate; + status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate); + if (status != NO_ERROR) { + ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, streamType, status); + return NO_INIT; + } + // Client decides whether the track is TIMED (see below), but can only express a preference // for FAST. Server will perform additional tests. if ((flags & AUDIO_OUTPUT_FLAG_FAST) && !( @@ -836,6 +853,14 @@ status_t AudioTrack::createTrack_l( } ALOGV("createTrack_l() output %d afLatency %d", output, afLatency); + // The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where + // n = 1 fast track; nBuffering is ignored + // n = 2 normal track, no sample rate conversion + // n = 3 normal track, with sample rate conversion + // (pessimistic; some non-1:1 conversion ratios don't actually need triple-buffering) + // n > 3 very high latency or very small notification interval; nBuffering is ignored + const uint32_t nBuffering = (sampleRate == afSampleRate) ? 2 : 3; + mNotificationFramesAct = mNotificationFramesReq; if (!audio_is_linear_pcm(format)) { @@ -844,13 +869,6 @@ status_t AudioTrack::createTrack_l( // Same comment as below about ignoring frameCount parameter for set() frameCount = sharedBuffer->size(); } else if (frameCount == 0) { - size_t afFrameCount; - status = AudioSystem::getFrameCount(output, streamType, &afFrameCount); - if (status != NO_ERROR) { - ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, - status); - return NO_INIT; - } frameCount = afFrameCount; } if (mNotificationFramesAct != frameCount) { @@ -880,26 +898,13 @@ status_t AudioTrack::createTrack_l( } else if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) { // FIXME move these calculations and associated checks to server - uint32_t afSampleRate; - status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate); - if (status != NO_ERROR) { - ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, streamType, - status); - return NO_INIT; - } - size_t afFrameCount; - status = AudioSystem::getFrameCount(output, streamType, &afFrameCount); - if (status != NO_ERROR) { - ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, status); - return NO_INIT; - } // Ensure that buffer depth covers at least audio hardware latency uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); ALOGV("afFrameCount=%d, minBufCount=%d, afSampleRate=%u, afLatency=%d", afFrameCount, minBufCount, afSampleRate, afLatency); - if (minBufCount <= 2) { - minBufCount = sampleRate == afSampleRate ? 2 : 3; + if (minBufCount <= nBuffering) { + minBufCount = nBuffering; } size_t minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; @@ -909,18 +914,16 @@ status_t AudioTrack::createTrack_l( if (frameCount == 0) { frameCount = minFrameCount; - } - // Make sure that application is notified with sufficient margin - // before underrun - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { - mNotificationFramesAct = frameCount/2; - } - if (frameCount < minFrameCount) { + } else if (frameCount < minFrameCount) { // 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; } + // Make sure that application is notified with sufficient margin before underrun + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { + mNotificationFramesAct = frameCount/nBuffering; + } } else { // For fast tracks, the frame count calculations and checks are done by server @@ -1001,8 +1004,8 @@ status_t AudioTrack::createTrack_l( flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST); mFlags = flags; if (sharedBuffer == 0) { - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) { - mNotificationFramesAct = frameCount/2; + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { + mNotificationFramesAct = frameCount/nBuffering; } } } diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index e7abb40..4fd92b2 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -481,7 +481,7 @@ size_t StaticAudioTrackClientProxy::getBufferPosition() ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut, bool clientInServer) : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), - mAvailToClient(0), mFlush(0), mDeferWake(false) + mAvailToClient(0), mFlush(0) { } @@ -559,9 +559,6 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer) &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL; buffer->mNonContig = availToServer - part1; mUnreleased = part1; - // optimization to avoid waking up the client too early - // FIXME need to test for recording - mDeferWake = part1 < ask && availToServer >= ask; return part1 > 0 ? NO_ERROR : WOULD_BLOCK; } no_init: @@ -607,7 +604,7 @@ void ServerProxy::releaseBuffer(Buffer* buffer) minimum = half; } // FIXME AudioRecord wakeup needs to be optimized; it currently wakes up client every time - if (!mIsOut || (!mDeferWake && mAvailToClient + stepCount >= minimum)) { + if (!mIsOut || (mAvailToClient + stepCount >= minimum)) { ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum); int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); if (!(old & CBLK_FUTEX_WAKE)) { -- cgit v1.1 From 6cc3a9948b51193dfdcb0c3527d7f3d1ca38aa3c Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Tue, 10 Sep 2013 09:15:18 -0700 Subject: LoudnessEnhancer audio effect implementation Implementation based on DRC effect, controlled by a target gain. The target gain is used to amplify the signal at the input of the DRC, and to compute the knee of the DRC. Bug 8413913 Change-Id: I386d64793a9fa3f7218e053d6f0a99f6836c02bd --- media/libeffects/data/audio_effects.conf | 7 + media/libeffects/loudness/Android.mk | 27 ++ .../libeffects/loudness/EffectLoudnessEnhancer.cpp | 474 +++++++++++++++++++++ media/libeffects/loudness/MODULE_LICENSE_APACHE2 | 0 media/libeffects/loudness/NOTICE | 190 +++++++++ .../libeffects/loudness/common/core/basic_types.h | 114 +++++ .../libeffects/loudness/common/core/byte_swapper.h | 151 +++++++ media/libeffects/loudness/common/core/math.h | 89 ++++ media/libeffects/loudness/common/core/os.h | 29 ++ media/libeffects/loudness/common/core/types.h | 31 ++ media/libeffects/loudness/dsp/core/basic-inl.h | 48 +++ media/libeffects/loudness/dsp/core/basic.h | 48 +++ .../dsp/core/dynamic_range_compression-inl.h | 45 ++ .../dsp/core/dynamic_range_compression.cpp | 106 +++++ .../loudness/dsp/core/dynamic_range_compression.h | 116 +++++ media/libeffects/loudness/dsp/core/interpolation.h | 24 ++ .../loudness/dsp/core/interpolator_base-inl.h | 180 ++++++++ .../loudness/dsp/core/interpolator_base.h | 112 +++++ .../loudness/dsp/core/interpolator_linear.h | 81 ++++ 19 files changed, 1872 insertions(+) create mode 100644 media/libeffects/loudness/Android.mk create mode 100644 media/libeffects/loudness/EffectLoudnessEnhancer.cpp create mode 100644 media/libeffects/loudness/MODULE_LICENSE_APACHE2 create mode 100644 media/libeffects/loudness/NOTICE create mode 100644 media/libeffects/loudness/common/core/basic_types.h create mode 100644 media/libeffects/loudness/common/core/byte_swapper.h create mode 100644 media/libeffects/loudness/common/core/math.h create mode 100644 media/libeffects/loudness/common/core/os.h create mode 100644 media/libeffects/loudness/common/core/types.h create mode 100644 media/libeffects/loudness/dsp/core/basic-inl.h create mode 100644 media/libeffects/loudness/dsp/core/basic.h create mode 100644 media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h create mode 100644 media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp create mode 100644 media/libeffects/loudness/dsp/core/dynamic_range_compression.h create mode 100644 media/libeffects/loudness/dsp/core/interpolation.h create mode 100644 media/libeffects/loudness/dsp/core/interpolator_base-inl.h create mode 100644 media/libeffects/loudness/dsp/core/interpolator_base.h create mode 100644 media/libeffects/loudness/dsp/core/interpolator_linear.h (limited to 'media') diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index 69a3c53..c3c4b67 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -35,6 +35,9 @@ libraries { downmix { path /system/lib/soundfx/libdownmix.so } + loudness_enhancer { + path /system/lib/soundfx/libldnhncr.so + } } # Default pre-processing library. Add to audio_effect.conf "libraries" section if @@ -122,6 +125,10 @@ effects { library downmix uuid 93f04452-e4fe-41cc-91f9-e475b6d1d69f } + loudness_enhancer { + library loudness_enhancer + uuid fa415329-2034-4bea-b5dc-5b381c8d1e2c + } } # Default pre-processing effects. Add to audio_effect.conf "effects" section if diff --git a/media/libeffects/loudness/Android.mk b/media/libeffects/loudness/Android.mk new file mode 100644 index 0000000..dcb7b27 --- /dev/null +++ b/media/libeffects/loudness/Android.mk @@ -0,0 +1,27 @@ +LOCAL_PATH:= $(call my-dir) + +# LoudnessEnhancer library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EffectLoudnessEnhancer.cpp \ + dsp/core/dynamic_range_compression.cpp + +LOCAL_CFLAGS+= -O2 -fvisibility=hidden + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + liblog \ + libstlport + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE:= libldnhncr + +LOCAL_C_INCLUDES := \ + $(call include-path-for, audio-effects) \ + bionic \ + bionic/libstdc++/include \ + external/stlport/stlport + + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp new file mode 100644 index 0000000..dfc25db --- /dev/null +++ b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "EffectLE" +//#define LOG_NDEBUG 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include "dsp/core/dynamic_range_compression.h" + +extern "C" { + +// effect_handle_t interface implementation for LE effect +extern const struct effect_interface_s gLEInterface; + +// AOSP Loudness Enhancer UUID: fa415329-2034-4bea-b5dc-5b381c8d1e2c +const effect_descriptor_t gLEDescriptor = { + {0xfe3199be, 0xaed0, 0x413f, 0x87bb, {0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}, // type + {0xfa415329, 0x2034, 0x4bea, 0xb5dc, {0x5b, 0x38, 0x1c, 0x8d, 0x1e, 0x2c}}, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST), + 0, // TODO + 1, + "Loudness Enhancer", + "The Android Open Source Project", +}; + +enum le_state_e { + LOUDNESS_ENHANCER_STATE_UNINITIALIZED, + LOUDNESS_ENHANCER_STATE_INITIALIZED, + LOUDNESS_ENHANCER_STATE_ACTIVE, +}; + +struct LoudnessEnhancerContext { + const struct effect_interface_s *mItfe; + effect_config_t mConfig; + uint8_t mState; + int32_t mTargetGainmB;// target gain in mB + // in this implementation, there is no coupling between the compression on the left and right + // channels + le_fx::AdaptiveDynamicRangeCompression* mCompressorL; + le_fx::AdaptiveDynamicRangeCompression* mCompressorR; +}; + +// +//--- Local functions (not directly used by effect interface) +// + +void LE_reset(LoudnessEnhancerContext *pContext) +{ + ALOGV(" > LE_reset(%p)", pContext); + + if ((pContext->mCompressorL != NULL) && (pContext->mCompressorR != NULL)) { + float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification + ALOGV("LE_reset(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp); + pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); + pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); + } else { + ALOGE("LE_reset(%p): null compressors, can't apply target gain", pContext); + } +} + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + +//---------------------------------------------------------------------------- +// LE_setConfig() +//---------------------------------------------------------------------------- +// Purpose: Set input and output audio configuration. +// +// Inputs: +// pContext: effect engine context +// pConfig: pointer to effect_config_t structure holding input and output +// configuration parameters +// +// Outputs: +// +//---------------------------------------------------------------------------- + +int LE_setConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig) +{ + ALOGV("LE_setConfig(%p)", pContext); + + if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL; + if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL; + if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL; + if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL; + if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && + pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; + if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL; + + pContext->mConfig = *pConfig; + + LE_reset(pContext); + + return 0; +} + + +//---------------------------------------------------------------------------- +// LE_getConfig() +//---------------------------------------------------------------------------- +// Purpose: Get input and output audio configuration. +// +// Inputs: +// pContext: effect engine context +// pConfig: pointer to effect_config_t structure holding input and output +// configuration parameters +// +// Outputs: +// +//---------------------------------------------------------------------------- + +void LE_getConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig) +{ + *pConfig = pContext->mConfig; +} + + +//---------------------------------------------------------------------------- +// LE_init() +//---------------------------------------------------------------------------- +// Purpose: Initialize engine with default configuration. +// +// Inputs: +// pContext: effect engine context +// +// Outputs: +// +//---------------------------------------------------------------------------- + +int LE_init(LoudnessEnhancerContext *pContext) +{ + ALOGV("LE_init(%p)", pContext); + + pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pContext->mConfig.inputCfg.samplingRate = 44100; + pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; + pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; + pContext->mConfig.inputCfg.bufferProvider.cookie = NULL; + pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; + pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pContext->mConfig.outputCfg.samplingRate = 44100; + pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; + pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; + pContext->mConfig.outputCfg.bufferProvider.cookie = NULL; + pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; + + pContext->mTargetGainmB = LOUDNESS_ENHANCER_DEFAULT_TARGET_GAIN_MB; + float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification + ALOGV("LE_init(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp); + + if (pContext->mCompressorL == NULL) { + pContext->mCompressorL = new le_fx::AdaptiveDynamicRangeCompression(); + pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); + } + if (pContext->mCompressorR == NULL) { + pContext->mCompressorR = new le_fx::AdaptiveDynamicRangeCompression(); + pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); + } + + LE_setConfig(pContext, &pContext->mConfig); + + return 0; +} + +// +//--- Effect Library Interface Implementation +// + +int LELib_Create(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle) { + ALOGV("LELib_Create()"); + int ret; + int i; + + if (pHandle == NULL || uuid == NULL) { + return -EINVAL; + } + + if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { + return -EINVAL; + } + + LoudnessEnhancerContext *pContext = new LoudnessEnhancerContext; + + pContext->mItfe = &gLEInterface; + pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED; + + pContext->mCompressorL = NULL; + pContext->mCompressorR = NULL; + ret = LE_init(pContext); + if (ret < 0) { + ALOGW("LELib_Create() init failed"); + delete pContext; + return ret; + } + + *pHandle = (effect_handle_t)pContext; + + pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED; + + ALOGV(" LELib_Create context is %p", pContext); + + return 0; + +} + +int LELib_Release(effect_handle_t handle) { + LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)handle; + + ALOGV("LELib_Release %p", handle); + if (pContext == NULL) { + return -EINVAL; + } + pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED; + if (pContext->mCompressorL != NULL) { + delete pContext->mCompressorL; + pContext->mCompressorL = NULL; + } + if (pContext->mCompressorR != NULL) { + delete pContext->mCompressorR; + pContext->mCompressorR = NULL; + } + delete pContext; + + return 0; +} + +int LELib_GetDescriptor(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor) { + + if (pDescriptor == NULL || uuid == NULL){ + ALOGV("LELib_GetDescriptor() called with NULL pointer"); + return -EINVAL; + } + + if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) == 0) { + *pDescriptor = gLEDescriptor; + return 0; + } + + return -EINVAL; +} /* end LELib_GetDescriptor */ + +// +//--- Effect Control Interface Implementation +// +int LE_process( + effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) +{ + LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self; + + if (pContext == NULL) { + return -EINVAL; + } + + if (inBuffer == NULL || inBuffer->raw == NULL || + outBuffer == NULL || outBuffer->raw == NULL || + inBuffer->frameCount != outBuffer->frameCount || + inBuffer->frameCount == 0) { + return -EINVAL; + } + + //ALOGV("LE about to process %d samples", inBuffer->frameCount); + uint16_t inIdx; + float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f); + for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) { + inBuffer->s16[2*inIdx] = pContext->mCompressorL->Compress( + inputAmp * (float)inBuffer->s16[2*inIdx]); + inBuffer->s16[2*inIdx +1] = pContext->mCompressorR->Compress( + inputAmp * (float)inBuffer->s16[2*inIdx +1]); + } + + if (inBuffer->raw != outBuffer->raw) { + if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { + for (size_t i = 0; i < outBuffer->frameCount*2; i++) { + outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); + } + } else { + memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); + } + } + if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) { + return -ENODATA; + } + return 0; +} + +int LE_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, + void *pCmdData, uint32_t *replySize, void *pReplyData) { + + LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self; + int retsize; + + if (pContext == NULL || pContext->mState == LOUDNESS_ENHANCER_STATE_UNINITIALIZED) { + return -EINVAL; + } + +// ALOGV("LE_command command %d cmdSize %d",cmdCode, cmdSize); + switch (cmdCode) { + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = LE_init(pContext); + break; + case EFFECT_CMD_SET_CONFIG: + if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) + || pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = LE_setConfig(pContext, + (effect_config_t *) pCmdData); + break; + case EFFECT_CMD_GET_CONFIG: + if (pReplyData == NULL || + *replySize != sizeof(effect_config_t)) { + return -EINVAL; + } + LE_getConfig(pContext, (effect_config_t *)pReplyData); + break; + case EFFECT_CMD_RESET: + LE_reset(pContext); + break; + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pContext->mState != LOUDNESS_ENHANCER_STATE_INITIALIZED) { + return -ENOSYS; + } + pContext->mState = LOUDNESS_ENHANCER_STATE_ACTIVE; + ALOGV("EFFECT_CMD_ENABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) { + return -ENOSYS; + } + pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED; + ALOGV("EFFECT_CMD_DISABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_GET_PARAM: { + if (pCmdData == NULL || + cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || + pReplyData == NULL || + *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { + return -EINVAL; + } + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); + effect_param_t *p = (effect_param_t *)pReplyData; + p->status = 0; + *replySize = sizeof(effect_param_t) + sizeof(uint32_t); + if (p->psize != sizeof(uint32_t)) { + p->status = -EINVAL; + break; + } + switch (*(uint32_t *)p->data) { + case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB: + ALOGV("get target gain(mB) = %d", pContext->mTargetGainmB); + *((int32_t *)p->data + 1) = pContext->mTargetGainmB; + p->vsize = sizeof(int32_t); + *replySize += sizeof(int32_t); + break; + default: + p->status = -EINVAL; + } + } break; + case EFFECT_CMD_SET_PARAM: { + if (pCmdData == NULL || + cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || + pReplyData == NULL || *replySize != sizeof(int32_t)) { + return -EINVAL; + } + *(int32_t *)pReplyData = 0; + effect_param_t *p = (effect_param_t *)pCmdData; + if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) { + *(int32_t *)pReplyData = -EINVAL; + break; + } + switch (*(uint32_t *)p->data) { + case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB: + pContext->mTargetGainmB = *((int32_t *)p->data + 1); + ALOGV("set target gain(mB) = %d", pContext->mTargetGainmB); + LE_reset(pContext); // apply parameter update + break; + default: + *(int32_t *)pReplyData = -EINVAL; + } + } break; + case EFFECT_CMD_SET_DEVICE: + case EFFECT_CMD_SET_VOLUME: + case EFFECT_CMD_SET_AUDIO_MODE: + break; + + default: + ALOGW("LE_command invalid command %d",cmdCode); + return -EINVAL; + } + + return 0; +} + +/* Effect Control Interface Implementation: get_descriptor */ +int LE_getDescriptor(effect_handle_t self, + effect_descriptor_t *pDescriptor) +{ + LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *) self; + + if (pContext == NULL || pDescriptor == NULL) { + ALOGV("LE_getDescriptor() invalid param"); + return -EINVAL; + } + + *pDescriptor = gLEDescriptor; + + return 0; +} /* end LE_getDescriptor */ + +// effect_handle_t interface implementation for DRC effect +const struct effect_interface_s gLEInterface = { + LE_process, + LE_command, + LE_getDescriptor, + NULL, +}; + +// This is the only symbol that needs to be exported +__attribute__ ((visibility ("default"))) +audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { + tag : AUDIO_EFFECT_LIBRARY_TAG, + version : EFFECT_LIBRARY_API_VERSION, + name : "Loudness Enhancer Library", + implementor : "The Android Open Source Project", + create_effect : LELib_Create, + release_effect : LELib_Release, + get_descriptor : LELib_GetDescriptor, +}; + +}; // extern "C" + diff --git a/media/libeffects/loudness/MODULE_LICENSE_APACHE2 b/media/libeffects/loudness/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libeffects/loudness/NOTICE b/media/libeffects/loudness/NOTICE new file mode 100644 index 0000000..ad6ed94 --- /dev/null +++ b/media/libeffects/loudness/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2013, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/media/libeffects/loudness/common/core/basic_types.h b/media/libeffects/loudness/common/core/basic_types.h new file mode 100644 index 0000000..593e914 --- /dev/null +++ b/media/libeffects/loudness/common/core/basic_types.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_COMMON_CORE_BASIC_TYPES_H_ +#define LE_FX_ENGINE_COMMON_CORE_BASIC_TYPES_H_ + +#include +#include +#include +using ::std::string; +using ::std::basic_string; +#include +using ::std::vector; + +#include "common/core/os.h" + +// ----------------------------------------------------------------------------- +// Definitions of common basic types: +// ----------------------------------------------------------------------------- + +#if !defined(G_COMPILE) && !defined(BASE_INTEGRAL_TYPES_H_) + +namespace le_fx { + +typedef signed char schar; +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef long long int64; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +} // namespace le_fx + +#endif + +namespace le_fx { + +struct FloatArray { + int length; + float *data; + + FloatArray(void) { + data = NULL; + length = 0; + } +}; + +struct Int16Array { + int length; + int16 *data; + + Int16Array(void) { + data = NULL; + length = 0; + } +}; + +struct Int32Array { + int length; + int32 *data; + + Int32Array(void) { + data = NULL; + length = 0; + } +}; + +struct Int8Array { + int length; + uint8 *data; + + Int8Array(void) { + data = NULL; + length = 0; + } +}; + +// +// Simple wrapper for waveform data: +// +class WaveData : public vector { + public: + WaveData(); + ~WaveData(); + + void Set(int number_samples, int sampling_rate, int16 *data); + int sample_rate(void) const; + void set_sample_rate(int sample_rate); + bool Equals(const WaveData &wave_data, int threshold = 0) const; + + private: + int sample_rate_; +}; + +} // namespace le_fx + +#endif // LE_FX_ENGINE_COMMON_CORE_BASIC_TYPES_H_ diff --git a/media/libeffects/loudness/common/core/byte_swapper.h b/media/libeffects/loudness/common/core/byte_swapper.h new file mode 100644 index 0000000..8f0caf3 --- /dev/null +++ b/media/libeffects/loudness/common/core/byte_swapper.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_COMMON_CORE_BYTE_SWAPPER_H_ +#define LE_FX_ENGINE_COMMON_CORE_BYTE_SWAPPER_H_ + +#include +#include + +#include "common/core/basic_types.h" +#include "common/core/os.h" + +namespace le_fx { + +namespace arch { + +inline bool IsLittleEndian(void) { + int16 word = 1; + char *cp = reinterpret_cast(&word); + return cp[0] != 0; +} + +inline bool IsBigEndian(void) { + return !IsLittleEndian(); +} + +template +struct ByteSwapper { + static T Swap(const T &val) { + T new_val = val; + char *first = &new_val, *last = first + kValSize - 1, x; + for (; first < last; ++first, --last) { + x = *last; + *last = *first; + *first = x; + } + return new_val; + } +}; + +template +struct ByteSwapper { + static T Swap(const T &val) { + return val; + } +}; + +template +struct ByteSwapper { + static T Swap(const T &val) { + T new_val; + const char *o = (const char *)&val; + char *p = reinterpret_cast(&new_val); + p[0] = o[1]; + p[1] = o[0]; + return new_val; + } +}; + +template +struct ByteSwapper { + static T Swap(const T &val) { + T new_val; + const char *o = (const char *)&val; + char *p = reinterpret_cast(&new_val); + p[0] = o[3]; + p[1] = o[2]; + p[2] = o[1]; + p[3] = o[0]; + return new_val; + } +}; + +template +struct ByteSwapper { + static T Swap(const T &val) { + T new_val = val; + const char *o = (const char *)&val; + char *p = reinterpret_cast(&new_val); + p[0] = o[7]; + p[1] = o[6]; + p[2] = o[5]; + p[3] = o[4]; + p[4] = o[3]; + p[5] = o[2]; + p[6] = o[1]; + p[7] = o[0]; + return new_val; + } +}; + +template +T SwapBytes(const T &val, bool force_swap) { + if (force_swap) { +#if !defined(LE_FX__NEED_BYTESWAP) + return ByteSwapper::Swap(val); +#else + return val; +#endif // !LE_FX_NEED_BYTESWAP + } else { +#if !defined(LE_FX_NEED_BYTESWAP) + return val; +#else + return ByteSwapper::Swap(val); +#endif // !LE_FX_NEED_BYTESWAP + } +} + +template +const T *SwapBytes(const T *vals, unsigned int num_items, bool force_swap) { + if (force_swap) { +#if !defined(LE_FX_NEED_BYTESWAP) + T *writeable_vals = const_cast(vals); + for (unsigned int i = 0; i < num_items; i++) { + writeable_vals[i] = ByteSwapper::Swap(vals[i]); + } + return writeable_vals; +#else + return vals; +#endif // !LE_FX_NEED_BYTESWAP + } else { +#if !defined(LE_FX_NEED_BYTESWAP) + return vals; +#else + T *writeable_vals = const_cast(vals); + for (unsigned int i = 0; i < num_items; i++) { + writeable_vals[i] = ByteSwapper::Swap(vals[i]); + } + return writeable_vals; +#endif // !LE_FX_NEED_BYTESWAP + } +} + +} // namespace arch + +} // namespace le_fx + +#endif // LE_FX_ENGINE_COMMON_CORE_BYTE_SWAPPER_H_ diff --git a/media/libeffects/loudness/common/core/math.h b/media/libeffects/loudness/common/core/math.h new file mode 100644 index 0000000..3f302cc --- /dev/null +++ b/media/libeffects/loudness/common/core/math.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_COMMON_CORE_MATH_H_ +#define LE_FX_ENGINE_COMMON_CORE_MATH_H_ + +#include +#include +using ::std::min; +using ::std::max; +using ::std::fill; +using ::std::fill_n;using ::std::lower_bound; +#include +#include +//using ::std::fpclassify; + +#include "common/core/os.h" +#include "common/core/types.h" + +namespace le_fx { +namespace math { + +// A fast approximation to log2(.) +inline float fast_log2(float val) { + int* const exp_ptr = reinterpret_cast (&val); + int x = *exp_ptr; + const int log_2 = ((x >> 23) & 255) - 128; + x &= ~(255 << 23); + x += 127 << 23; + *exp_ptr = x; + val = ((-1.0f / 3) * val + 2) * val - 2.0f / 3; + return static_cast(val + log_2); +} + +// A fast approximation to log(.) +inline float fast_log(float val) { + return fast_log2(val) * + 0.693147180559945286226763982995180413126945495605468750f; +} + +// An approximation of the exp(.) function using a 5-th order Taylor expansion. +// It's pretty accurate between +-0.1 and accurate to 10e-3 between +-1 +template +inline T ExpApproximationViaTaylorExpansionOrder5(T x) { + const T x2 = x * x; + const T x3 = x2 * x; + const T x4 = x2 * x2; + const T x5 = x3 * x2; + return 1.0f + x + 0.5f * x2 + + 0.16666666666666665741480812812369549646973609924316406250f * x3 + + 0.0416666666666666643537020320309238741174340248107910156250f * x4 + + 0.008333333333333333217685101601546193705871701240539550781250f * x5; +} + +} // namespace math +} // namespace le_fx + +// Math functions missing in Android NDK: +#if defined(LE_FX_OS_ANDROID) + +namespace std { + +// +// Round to the nearest integer: We need this implementation +// since std::round is missing on android. +// +template +inline T round(const T &x) { + return static_cast(std::floor(static_cast(x) + 0.5)); +} + +} // namespace std + +#endif // LE_FX_OS_ANDROID + +#endif // LE_FX_ENGINE_COMMON_CORE_MATH_H_ diff --git a/media/libeffects/loudness/common/core/os.h b/media/libeffects/loudness/common/core/os.h new file mode 100644 index 0000000..4a8ce82 --- /dev/null +++ b/media/libeffects/loudness/common/core/os.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_COMMON_CORE_OS_H_ +#define LE_FX_ENGINE_COMMON_CORE_OS_H_ + +// ----------------------------------------------------------------------------- +// OS Identification: +// ----------------------------------------------------------------------------- + +#define LE_FX_OS_UNIX +#if defined(__ANDROID__) +# define LE_FX_OS_ANDROID +#endif // Android + +#endif // LE_FX_ENGINE_COMMON_CORE_OS_H_ diff --git a/media/libeffects/loudness/common/core/types.h b/media/libeffects/loudness/common/core/types.h new file mode 100644 index 0000000..d1b6c6a --- /dev/null +++ b/media/libeffects/loudness/common/core/types.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_COMMON_CORE_TYPES_H_ +#define LE_FX_ENGINE_COMMON_CORE_TYPES_H_ + +#include "common/core/os.h" + +#include "common/core/basic_types.h" + +#ifndef LE_FX_DISALLOW_COPY_AND_ASSIGN +#define LE_FX_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif // LE_FX_DISALLOW_COPY_AND_ASSIGN + + +#endif // LE_FX_ENGINE_COMMON_CORE_TYPES_H_ diff --git a/media/libeffects/loudness/dsp/core/basic-inl.h b/media/libeffects/loudness/dsp/core/basic-inl.h new file mode 100644 index 0000000..3f77147 --- /dev/null +++ b/media/libeffects/loudness/dsp/core/basic-inl.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_BASIC_INL_H_ +#define LE_FX_ENGINE_DSP_CORE_BASIC_INL_H_ + +#include + +namespace le_fx { + +namespace sigmod { + +template +int SearchIndex(const T x_data[], + T x, + int start_index, + int end_index) { + int start = start_index; + int end = end_index; + while (end > start + 1) { + int i = (end + start) / 2; + if (x_data[i] > x) { + end = i; + } else { + start = i; + } + } + return start; +} + +} // namespace sigmod + +} // namespace le_fx + +#endif // LE_FX_ENGINE_DSP_CORE_BASIC_INL_H_ diff --git a/media/libeffects/loudness/dsp/core/basic.h b/media/libeffects/loudness/dsp/core/basic.h new file mode 100644 index 0000000..27e0a8d --- /dev/null +++ b/media/libeffects/loudness/dsp/core/basic.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_BASIC_H_ +#define LE_FX_ENGINE_DSP_CORE_BASIC_H_ + +#include +#include "common/core/math.h" +#include "common/core/types.h" + +namespace le_fx { + +namespace sigmod { + +// Searchs for the interval that contains using a divide-and-conquer +// algorithm. +// X[]: a vector of sorted values (X[i+1] > X[i]) +// x: a value +// StartIndex: the minimum searched index +// EndIndex: the maximum searched index +// returns: the index that satisfies: X[i] <= x <= X[i+1] && +// StartIndex <= i <= (EndIndex-1) +template +int SearchIndex(const T x_data[], + T x, + int start_index, + int end_index); + +} // namespace sigmod + +} // namespace le_fx + +#include "dsp/core/basic-inl.h" + +#endif // LE_FX_ENGINE_DSP_CORE_BASIC_H_ diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h new file mode 100644 index 0000000..fed8c2a --- /dev/null +++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_DYNAMIC_RANGE_COMPRESSION_INL_H_ +#define LE_FX_ENGINE_DSP_CORE_DYNAMIC_RANGE_COMPRESSION_INL_H_ + +//#define LOG_NDEBUG 0 +#include + + +namespace le_fx { + + +inline void AdaptiveDynamicRangeCompression::set_knee_threshold(float decibel) { + // Converts to 1og-base + knee_threshold_in_decibel_ = decibel; + knee_threshold_ = 0.1151292546497023061569109358970308676362037658691406250f * + decibel + 10.39717719035538401328722102334722876548767089843750f; +} + + +inline void AdaptiveDynamicRangeCompression::set_knee_threshold_via_target_gain( + float target_gain) { + const float decibel = target_gain_to_knee_threshold_.Interpolate( + target_gain); + ALOGE("set_knee_threshold_via_target_gain: decibel =%.3f", decibel); + set_knee_threshold(decibel); +} + +} // namespace le_fx + + +#endif // LE_FX_ENGINE_DSP_CORE_DYNAMIC_RANGE_COMPRESSION_INL_H_ diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp new file mode 100644 index 0000000..2bbd043 --- /dev/null +++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "common/core/math.h" +#include "common/core/types.h" +#include "dsp/core/basic.h" +#include "dsp/core/interpolation.h" +#include "dsp/core/dynamic_range_compression.h" + +//#define LOG_NDEBUG 0 +#include + + +namespace le_fx { + +// Definitions for static const class members declared in +// dynamic_range_compression.h. +const float AdaptiveDynamicRangeCompression::kMinAbsValue = 0.000001f; +const float AdaptiveDynamicRangeCompression::kMinLogAbsValue = + 0.032766999999999997517097227728299912996590137481689453125f; +const float AdaptiveDynamicRangeCompression::kFixedPointLimit = 32767.0f; +const float AdaptiveDynamicRangeCompression::kInverseFixedPointLimit = + 1.0f / AdaptiveDynamicRangeCompression::kFixedPointLimit; +const float AdaptiveDynamicRangeCompression::kDefaultKneeThresholdInDecibel = + -8.0f; +const float AdaptiveDynamicRangeCompression::kCompressionRatio = 7.0f; +const float AdaptiveDynamicRangeCompression::kTauAttack = 0.001f; +const float AdaptiveDynamicRangeCompression::kTauRelease = 0.015f; + +AdaptiveDynamicRangeCompression::AdaptiveDynamicRangeCompression() { + static const float kTargetGain[] = { + 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + static const float kKneeThreshold[] = { + -8.0f, -8.0f, -8.5f, -9.0f, -10.0f }; + target_gain_to_knee_threshold_.Initialize( + &kTargetGain[0], &kKneeThreshold[0], + sizeof(kTargetGain) / sizeof(kTargetGain[0])); +} + +bool AdaptiveDynamicRangeCompression::Initialize( + float target_gain, float sampling_rate) { + set_knee_threshold_via_target_gain(target_gain); + sampling_rate_ = sampling_rate; + state_ = 0.0f; + compressor_gain_ = 1.0f; + if (kTauAttack > 0.0f) { + const float taufs = kTauAttack * sampling_rate_; + alpha_attack_ = std::exp(-1.0f / taufs); + } else { + alpha_attack_ = 0.0f; + } + if (kTauRelease > 0.0f) { + const float taufs = kTauRelease * sampling_rate_; + alpha_release_ = std::exp(-1.0f / taufs); + } else { + alpha_release_ = 0.0f; + } + // Feed-forward topology + slope_ = 1.0f / kCompressionRatio - 1.0f; + return true; +} + +float AdaptiveDynamicRangeCompression::Compress(float x) { + const float max_abs_x = std::max(std::fabs(x), kMinLogAbsValue); + const float max_abs_x_dB = math::fast_log(max_abs_x); + // Subtract Threshold from log-encoded input to get the amount of overshoot + const float overshoot = max_abs_x_dB - knee_threshold_; + // Hard half-wave rectifier + const float rect = std::max(overshoot, 0.0f); + // Multiply rectified overshoot with slope + const float cv = rect * slope_; + const float prev_state = state_; + if (cv <= state_) { + state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv; + } else { + state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv; + } + compressor_gain_ *= + math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state); + x *= compressor_gain_; + if (x > kFixedPointLimit) { + return kFixedPointLimit; + } + if (x < -kFixedPointLimit) { + return -kFixedPointLimit; + } + return x; +} + +} // namespace le_fx + diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h new file mode 100644 index 0000000..4c015df --- /dev/null +++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_DYNAMIC_RANGE_COMPRESSION_H_ +#define LE_FX_ENGINE_DSP_CORE_DYNAMIC_RANGE_COMPRESSION_H_ + +#include "common/core/types.h" +#include "common/core/math.h" +#include "dsp/core/basic.h" +#include "dsp/core/interpolation.h" + +//#define LOG_NDEBUG 0 +#include + + +namespace le_fx { + +// An adaptive dynamic range compression algorithm. The gain adaptation is made +// at the logarithmic domain and it is based on a Branching-Smooth compensated +// digital peak detector with different time constants for attack and release. +class AdaptiveDynamicRangeCompression { + public: + AdaptiveDynamicRangeCompression(); + + // Initializes the compressor using prior information. It assumes that the + // input signal is speech from high-quality recordings that is scaled and then + // fed to the compressor. The compressor is tuned according to the target gain + // that is expected to be applied. + // + // Target gain receives values between 0.0 and 10.0. The knee threshold is + // reduced as the target gain increases in order to fit the increased range of + // values. + // + // Values between 1.0 and 2.0 will only mildly affect your signal. Higher + // values will reduce the dynamic range of the signal to the benefit of + // increased loudness. + // + // If nothing is known regarding the input, a `target_gain` of 1.0f is a + // relatively safe choice for many signals. + bool Initialize(float target_gain, float sampling_rate); + + // A fast version of the algorithm that uses approximate computations for the + // log(.) and exp(.). + float Compress(float x); + + // This version is slower than Compress(.) but faster than CompressSlow(.) + float CompressNormalSpeed(float x); + + // A slow version of the algorithm that is easier for further developement, + // tuning and debugging + float CompressSlow(float x); + + // Sets knee threshold (in decibel). + void set_knee_threshold(float decibel); + + // Sets knee threshold via the target gain using an experimentally derived + // relationship. + void set_knee_threshold_via_target_gain(float target_gain); + + private: + // The minimum accepted absolute input value and it's natural logarithm. This + // is to prevent numerical issues when the input is close to zero + static const float kMinAbsValue; + static const float kMinLogAbsValue; + // Fixed-point arithmetic limits + static const float kFixedPointLimit; + static const float kInverseFixedPointLimit; + // The default knee threshold in decibel. The knee threshold defines when the + // compressor is actually starting to compress the value of the input samples + static const float kDefaultKneeThresholdInDecibel; + // The compression ratio is the reciprocal of the slope of the line segment + // above the threshold (in the log-domain). The ratio controls the + // effectiveness of the compression. + static const float kCompressionRatio; + // The attack time of the envelope detector + static const float kTauAttack; + // The release time of the envelope detector + static const float kTauRelease; + + float sampling_rate_; + // the internal state of the envelope detector + float state_; + // the latest gain factor that was applied to the input signal + float compressor_gain_; + // attack constant for exponential dumping + float alpha_attack_; + // release constant for exponential dumping + float alpha_release_; + float slope_; + // The knee threshold + float knee_threshold_; + float knee_threshold_in_decibel_; + // This interpolator provides the function that relates target gain to knee + // threshold. + sigmod::InterpolatorLinear target_gain_to_knee_threshold_; + + LE_FX_DISALLOW_COPY_AND_ASSIGN(AdaptiveDynamicRangeCompression); +}; + +} // namespace le_fx + +#include "dsp/core/dynamic_range_compression-inl.h" + +#endif // LE_FX_ENGINE_DSP_CORE_DYNAMIC_RANGE_COMPRESSION_H_ diff --git a/media/libeffects/loudness/dsp/core/interpolation.h b/media/libeffects/loudness/dsp/core/interpolation.h new file mode 100644 index 0000000..23c287c --- /dev/null +++ b/media/libeffects/loudness/dsp/core/interpolation.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_INTERPOLATION_H_ +#define LE_FX_ENGINE_DSP_CORE_INTERPOLATION_H_ + +#include "common/core/math.h" +#include "dsp/core/interpolator_base.h" +#include "dsp/core/interpolator_linear.h" + +#endif // LE_FX_ENGINE_DSP_CORE_INTERPOLATION_H_ + diff --git a/media/libeffects/loudness/dsp/core/interpolator_base-inl.h b/media/libeffects/loudness/dsp/core/interpolator_base-inl.h new file mode 100644 index 0000000..bd08b65 --- /dev/null +++ b/media/libeffects/loudness/dsp/core/interpolator_base-inl.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_BASE_INL_H_ +#define LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_BASE_INL_H_ + +#include "dsp/core/basic.h" + +//#define LOG_NDEBUG 0 +#include + + +namespace le_fx { + +namespace sigmod { + +template +InterpolatorBase::InterpolatorBase() { + status_ = false; + cached_index_ = 0; + x_data_ = NULL; + y_data_ = NULL; + data_length_ = 0; + own_x_data_ = false; + x_start_offset_ = 0.0; + last_element_index_ = -1; + x_inverse_sampling_interval_ = 0.0; + state_ = NULL; +} + +template +InterpolatorBase::~InterpolatorBase() { + delete [] state_; + if (own_x_data_) { + delete [] x_data_; + } +} + +template +bool InterpolatorBase::Initialize(const vector &x_data, + const vector &y_data) { +#ifndef NDEBUG + if (x_data.size() != y_data.size()) { + LoggerError("InterpolatorBase::Initialize: xData size (%d) != yData size" + " (%d)", x_data.size(), y_data.size()); + } +#endif + return Initialize(&x_data[0], &y_data[0], x_data.size()); +} + +template +bool InterpolatorBase::Initialize(double x_start_offset, + double x_sampling_interval, + const vector &y_data) { + return Initialize(x_start_offset, + x_sampling_interval, + &y_data[0], + y_data.size()); +} + +template +bool InterpolatorBase::Initialize(double x_start_offset, + double x_sampling_interval, + const T *y_data, + int data_length) { + // Constructs and populate x-axis data: `x_data_` + T *x_data_tmp = new T[data_length]; + float time_offset = x_start_offset; + for (int n = 0; n < data_length; n++) { + x_data_tmp[n] = time_offset; + time_offset += x_sampling_interval; + } + Initialize(x_data_tmp, y_data, data_length); + // Sets-up the regularly sampled interpolation mode + x_start_offset_ = x_start_offset; + x_inverse_sampling_interval_ = 1.0 / x_sampling_interval; + own_x_data_ = true; + return status_; +} + + +template +bool InterpolatorBase::Initialize( + const T *x_data, const T *y_data, int data_length) { + // Default settings + cached_index_ = 0; + data_length_ = 0; + x_start_offset_ = 0; + x_inverse_sampling_interval_ = 0; + state_ = NULL; + // Input data is externally owned + own_x_data_ = false; + x_data_ = x_data; + y_data_ = y_data; + data_length_ = data_length; + last_element_index_ = data_length - 1; + // Check input data sanity + for (int n = 0; n < last_element_index_; ++n) { + if (x_data_[n + 1] <= x_data_[n]) { + ALOGE("InterpolatorBase::Initialize: xData are not ordered or " + "contain equal values (X[%d] <= X[%d]) (%.5e <= %.5e)", + n + 1, n, x_data_[n + 1], x_data_[n]); + status_ = false; + return false; + } + } + // Pre-compute internal state by calling the corresponding function of the + // derived class. + status_ = static_cast(this)->SetInternalState(); + return status_; +} + +template +T InterpolatorBase::Interpolate(T x) { +#ifndef NDEBUG + if (cached_index_ < 0 || cached_index_ > data_length_ - 2) { + LoggerError("InterpolatorBase:Interpolate: CachedIndex_ out of bounds " + "[0, %d, %d]", cached_index_, data_length_ - 2); + } +#endif + // Search for the containing interval + if (x <= x_data_[cached_index_]) { + if (cached_index_ <= 0) { + cached_index_ = 0; + return y_data_[0]; + } + if (x >= x_data_[cached_index_ - 1]) { + cached_index_--; // Fast descending + } else { + if (x <= x_data_[0]) { + cached_index_ = 0; + return y_data_[0]; + } + cached_index_ = SearchIndex(x_data_, x, 0, cached_index_); + } + } else { + if (cached_index_ >= last_element_index_) { + cached_index_ = last_element_index_; + return y_data_[last_element_index_]; + } + if (x > x_data_[cached_index_ + 1]) { + if (cached_index_ + 2 > last_element_index_) { + cached_index_ = last_element_index_ - 1; + return y_data_[last_element_index_]; + } + if (x <= x_data_[cached_index_ + 2]) { + cached_index_++; // Fast ascending + } else { + if (x >= x_data_[last_element_index_]) { + cached_index_ = last_element_index_ - 1; + return y_data_[last_element_index_]; + } + cached_index_ = SearchIndex( + x_data_, x, cached_index_, last_element_index_); + } + } + } + // Compute interpolated value by calling the corresponding function of the + // derived class. + return static_cast(this)->MethodSpecificInterpolation(x); +} + +} // namespace sigmod + +} // namespace le_fx + +#endif // LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_BASE_INL_H_ diff --git a/media/libeffects/loudness/dsp/core/interpolator_base.h b/media/libeffects/loudness/dsp/core/interpolator_base.h new file mode 100644 index 0000000..0cd1a35 --- /dev/null +++ b/media/libeffects/loudness/dsp/core/interpolator_base.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_BASE_H_ +#define LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_BASE_H_ + +#include "common/core/types.h" + +namespace le_fx { + +namespace sigmod { + +// Interpolation base-class that provides the interface, while it is the derived +// class that provides the specific interpolation algorithm. The following list +// of interpolation algorithms are currently present: +// +// InterpolationSine: weighted interpolation between y_data[n] and +// y_data[n+1] using a sin(.) weighting factor from +// 0 to pi/4. +// InterpolationLinear: linear interpolation +// InterpolationSplines: spline-based interpolation +// +// Example (using derived spline-based interpolation class): +// InterpolatorSplines interp(x_data, y_data, data_length); +// for (int n = 0; n < data_length; n++) Y[n] = interp.Interpolate(X[n]); +// +template +class InterpolatorBase { + public: + InterpolatorBase(); + ~InterpolatorBase(); + + // Generic random-access interpolation with arbitrary spaced x-axis samples. + // Below X[0], the interpolator returns Y[0]. Above X[data_length-1], it + // returns Y[data_length-1]. + T Interpolate(T x); + + bool get_status() const { + return status_; + } + + // Initializes internal buffers. + // x_data: [(data_length)x1] x-axis coordinates (searching axis) + // y_data: [(data_length)x1] y-axis coordinates (interpolation axis) + // data_length: number of points + // returns `true` if everything is ok, `false`, otherwise + bool Initialize(const T *x_data, const T *y_data, int data_length); + + // Initializes internal buffers. + // x_data: x-axis coordinates (searching axis) + // y_data: y-axis coordinates (interpolating axis) + // returns `true` if everything is ok, `false`, otherwise + bool Initialize(const vector &x_data, const vector &y_data); + + // Initialization for regularly sampled sequences, where: + // x_data[i] = x_start_offset + i * x_sampling_interval + bool Initialize(double x_start_offset, + double x_sampling_interval, + const vector &y_data); + + // Initialization for regularly sampled sequences, where: + // x_data[i] = x_start_offset + i * x_sampling_interval + bool Initialize(double x_start_offset, + double x_sampling_interval, + const T *y_data, + int data_length); + + protected: + // Is set to false if something goes wrong, and to true if everything is ok. + bool status_; + + // The start-index of the previously searched interval + int cached_index_; + + // Data points + const T *x_data_; // Externally or internally owned, depending on own_x_data_ + const T *y_data_; // Externally owned (always) + int data_length_; + // Index of the last element `data_length_ - 1` kept here for optimization + int last_element_index_; + bool own_x_data_; + // For regularly-samples sequences, keep only the boundaries and the intervals + T x_start_offset_; + float x_inverse_sampling_interval_; + + // Algorithm state (internally owned) + double *state_; + + private: + LE_FX_DISALLOW_COPY_AND_ASSIGN(InterpolatorBase); +}; + +} // namespace sigmod + +} // namespace le_fx + +#include "dsp/core/interpolator_base-inl.h" + +#endif // LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_BASE_H_ diff --git a/media/libeffects/loudness/dsp/core/interpolator_linear.h b/media/libeffects/loudness/dsp/core/interpolator_linear.h new file mode 100644 index 0000000..434698a --- /dev/null +++ b/media/libeffects/loudness/dsp/core/interpolator_linear.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 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 LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_LINEAR_H_ +#define LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_LINEAR_H_ + +#include +#include "dsp/core/interpolator_base.h" + +namespace le_fx { + +namespace sigmod { + +// Linear interpolation class. +// +// The main functionality of this class is provided by it's base-class, so +// please refer to: InterpolatorBase +// +// Example: +// InterpolatorLinear interp(x_data, y_data, data_length); +// for (int n = 0; n < data_length; n++) Y[n] = interp.Interpolate(X[n]); +// +template +class InterpolatorLinear: public InterpolatorBase > { + public: + InterpolatorLinear() { } + ~InterpolatorLinear() { } + + protected: + // Provides the main implementation of the linear interpolation algorithm. + // Assumes that: X[cached_index_] < x < X[cached_index_ + 1] + T MethodSpecificInterpolation(T x); + + // Pre-compute internal state_ parameters. + bool SetInternalState(); + + private: + friend class InterpolatorBase >; + typedef InterpolatorBase > BaseClass; + using BaseClass::status_; + using BaseClass::cached_index_; + using BaseClass::x_data_; + using BaseClass::y_data_; + using BaseClass::data_length_; + using BaseClass::state_; + + LE_FX_DISALLOW_COPY_AND_ASSIGN(InterpolatorLinear); +}; + +template +inline T InterpolatorLinear::MethodSpecificInterpolation(T x) { + T dX = x_data_[cached_index_ + 1] - x_data_[cached_index_]; + T dY = y_data_[cached_index_ + 1] - y_data_[cached_index_]; + T dx = x - x_data_[cached_index_]; + return y_data_[cached_index_] + (dY * dx) / dX; +} + +template +bool InterpolatorLinear::SetInternalState() { + state_ = NULL; + return true; +} + +} // namespace sigmod + +} // namespace le_fx + +#endif // LE_FX_ENGINE_DSP_CORE_INTERPOLATOR_LINEAR_H_ -- cgit v1.1 From 530fdbdc1b5491f3fbf172752834d1515701e142 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 17 Sep 2013 19:07:40 -0700 Subject: Use changed MediaCodec.PARAMETER_KEY_VIDEO_BITRATE value Change-Id: I157bcafbf705865e66c81517b1eab10c3daa039e Signed-off-by: Lajos Molnar Bug: 10461617 --- media/libstagefright/ACodec.cpp | 2 +- media/libstagefright/wifi-display/source/Converter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 5c3abd0..bfb730c 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -4134,7 +4134,7 @@ bool ACodec::ExecutingState::onMessageReceived(const sp &msg) { status_t ACodec::setParameters(const sp ¶ms) { int32_t videoBitrate; - if (params->findInt32("videoBitrate", &videoBitrate)) { + if (params->findInt32("video-bitrate", &videoBitrate)) { OMX_VIDEO_CONFIG_BITRATETYPE configParams; InitOMXParams(&configParams); configParams.nPortIndex = kPortIndexOutput; diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 6f23854..753b3ec 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -833,7 +833,7 @@ int32_t Converter::getVideoBitrate() const { void Converter::setVideoBitrate(int32_t bitRate) { if (mIsVideo && mEncoder != NULL && bitRate != mPrevVideoBitrate) { sp params = new AMessage; - params->setInt32("videoBitrate", bitRate); + params->setInt32("video-bitrate", bitRate); mEncoder->setParameters(params); -- cgit v1.1 From 8d0fda9660aee7059f802f400875247b01226084 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 18 Sep 2013 10:33:39 -0700 Subject: Partial fix for SoundPool not terminating SoundPool was waiting for EVENT_UNDERRUN only to indicate end of clip. In J, AudioTrack delivered both EVENT_UNDERRUN followed by EVENT_BUFFER_END. However, as of K, AudioTrack is only delivering EVENT_BUFFER_END (this lack of EVENT_UNDERRUN is another bug which still needs to be fixed). The workaround is to also respond to EVENT_BUFFER_END in SoundPool. Bug: 10787103 Change-Id: Id68a23bddd6dd9df6c49c55138197260d71ca468 --- media/libmedia/SoundPool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 0985164..5239b2f 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -740,8 +740,8 @@ void SoundChannel::process(int event, void *info, unsigned long toggle) b->size = count; //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]); } - } else if (event == AudioTrack::EVENT_UNDERRUN) { - ALOGV("process %p channel %d EVENT_UNDERRUN", this, mChannelID); + } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) { + ALOGV("process %p channel %d EVENT_UNDERRUN or EVENT_BUFFER_END", this, mChannelID); mSoundPool->addToStopList(this); } else if (event == AudioTrack::EVENT_LOOP_END) { ALOGV("End loop %p channel %d count %d", this, mChannelID, *(int *)info); -- cgit v1.1 From 5baf2af52cd186633b7173196c1e4a4cd3435f22 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 12 Sep 2013 17:37:00 -0700 Subject: more support for audio effect offload Offloading of audio effects is now enabled for offloaded output threads. If an effect not supporting offload is enabled, the AudioTrack is invalidated so that it can be recreated in PCM mode. Fix some issues in effect proxy related to handling of effect commands to offloaded and non offloaded effects. Also fixed a bug on capture index in software Visualizer effect. Bug: 8174034. Change-Id: Ib23d3c2d5a652361b0aaec7faee09102f2b18fce --- media/libeffects/proxy/EffectProxy.cpp | 59 +++++++++++++++++++----- media/libeffects/visualizer/EffectVisualizer.cpp | 4 +- 2 files changed, 50 insertions(+), 13 deletions(-) (limited to 'media') diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp index 77c6e89..41640da 100644 --- a/media/libeffects/proxy/EffectProxy.cpp +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -48,6 +48,21 @@ static const effect_descriptor_t *const gDescriptors[] = &gProxyDescriptor, }; +static inline bool isGetterCmd(uint32_t cmdCode) +{ + switch (cmdCode) { + case EFFECT_CMD_GET_PARAM: + case EFFECT_CMD_GET_CONFIG: + case EFFECT_CMD_GET_CONFIG_REVERSE: + case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: + case EFFECT_CMD_GET_FEATURE_CONFIG: + return true; + default: + return false; + } +} + + int EffectProxyCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, @@ -155,7 +170,6 @@ int Effect_process(effect_handle_t self, int index = pContext->index; // if the index refers to HW , do not do anything. Just return. if (index == SUB_FX_HOST) { - ALOGV("Calling CoreProcess"); ret = (*pContext->eHandle[index])->process(pContext->eHandle[index], inBuffer, outBuffer); } @@ -172,7 +186,7 @@ int Effect_command(effect_handle_t self, void *pReplyData) { EffectContext *pContext = (EffectContext *) self; - int status; + int status = 0; if (pContext == NULL) { ALOGV("Effect_command() Proxy context is NULL"); return -EINVAL; @@ -237,23 +251,46 @@ int Effect_command(effect_handle_t self, ALOGV("Effect_command: effect index is neither offload nor host"); return -EINVAL; } - ALOGV("Effect_command: pContext->eHandle[%d]: %p", - index, pContext->eHandle[index]); - if (pContext->eHandle[SUB_FX_HOST]) - (*pContext->eHandle[SUB_FX_HOST])->command( + + // Getter commands are only sent to the active sub effect. + uint32_t hostReplySize = replySize != NULL ? *replySize : 0; + bool hostReplied = false; + int hostStatus = 0; + uint32_t offloadReplySize = replySize != NULL ? *replySize : 0; + bool offloadReplied = false; + int offloadStatus = 0; + + if (pContext->eHandle[SUB_FX_HOST] && (!isGetterCmd(cmdCode) || index == SUB_FX_HOST)) { + hostStatus = (*pContext->eHandle[SUB_FX_HOST])->command( pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize, - pCmdData, replySize, pReplyData); - if (pContext->eHandle[SUB_FX_OFFLOAD]) { + pCmdData, replySize != NULL ? &hostReplySize : NULL, pReplyData); + hostReplied = true; + } + if (pContext->eHandle[SUB_FX_OFFLOAD] && (!isGetterCmd(cmdCode) || index == SUB_FX_OFFLOAD)) { // In case of SET CMD, when the offload stream is unavailable, // we will store the effect param values in the DSP effect wrapper. // When the offload effects get enabled, we send these values to the // DSP during Effect_config. // So,we send the params to DSP wrapper also - (*pContext->eHandle[SUB_FX_OFFLOAD])->command( + offloadStatus = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, - pCmdData, replySize, pReplyData); + pCmdData, replySize != NULL ? &offloadReplySize : NULL, pReplyData); + offloadReplied = true; } - return 0; + // By convention the offloaded implementation reply is returned if command is processed by both + // host and offloaded sub effects + if (offloadReplied){ + status = offloadStatus; + if (replySize) { + *replySize = offloadReplySize; + } + } else if (hostReplied) { + status = hostStatus; + if (replySize) { + *replySize = hostReplySize; + } + } + return status; } /* end Effect_command */ diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index e7eccf1..96935e3 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -61,7 +61,7 @@ struct VisualizerContext { uint32_t mCaptureSize; uint32_t mScalingMode; uint8_t mState; - uint8_t mLastCaptureIdx; + uint32_t mLastCaptureIdx; uint32_t mLatency; struct timespec mBufferUpdateTime; uint8_t mCaptureBuf[CAPTURE_BUF_SIZE]; @@ -499,7 +499,7 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, memcpy(pReplyData, pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint, size); - pReplyData += size; + pReplyData = (char *)pReplyData + size; captureSize -= size; capturePoint = 0; } -- cgit v1.1 From 8bbbd7da02fac3de40139af19f7cf7a7cc3cc824 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 18 Sep 2013 14:15:42 -0700 Subject: Workaround slow AudioTrack destruction Bug: 10809586 Change-Id: I5f30d4deb1233e8ade8967568e40684ef680c395 --- media/libmedia/SoundPool.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'media') diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 5239b2f..37b400c 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -537,6 +537,18 @@ void SoundChannel::init(SoundPool* soundPool) mSoundPool = soundPool; } +// This class is used to destroy a RefBase asynchronously +class AsyncDestructThread : public Thread +{ +public: + AsyncDestructThread(sp refBase) : mRefBase(refBase) { } +protected: + virtual ~AsyncDestructThread() { } +private: + virtual bool threadLoop() { return false; } + const sp mRefBase; +}; + // call with sound pool lock held void SoundChannel::play(const sp& sample, int nextChannelID, float leftVolume, float rightVolume, int priority, int loop, float rate) @@ -641,6 +653,17 @@ exit: if (status != NO_ERROR) { mAudioTrack.clear(); } + // FIXME AudioTrack destruction should not be slow + if (oldTrack != 0) { + // must be a raw reference to avoid a race after run() + AsyncDestructThread *adt = new AsyncDestructThread(oldTrack); + // guaranteed to not run destructor + oldTrack.clear(); + // after the run(), adt thread will hold a strong reference to oldTrack, + // and the only strong reference to itself + adt->run("AsyncDestruct"); + // do not delete adt here: adt thread destroys itself, and oldTrack if needed + } } void SoundChannel::nextEvent() -- cgit v1.1 From e2773bb17bc5d01e05a77b8913539575ebd04500 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 20 Sep 2013 18:12:06 +0000 Subject: Revert "Workaround slow AudioTrack destruction" This reverts commit 8bbbd7da02fac3de40139af19f7cf7a7cc3cc824. Change-Id: I269a6c445cbce33451b6a9e74223e36e6abbdbe0 --- media/libmedia/SoundPool.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'media') diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 37b400c..5239b2f 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -537,18 +537,6 @@ void SoundChannel::init(SoundPool* soundPool) mSoundPool = soundPool; } -// This class is used to destroy a RefBase asynchronously -class AsyncDestructThread : public Thread -{ -public: - AsyncDestructThread(sp refBase) : mRefBase(refBase) { } -protected: - virtual ~AsyncDestructThread() { } -private: - virtual bool threadLoop() { return false; } - const sp mRefBase; -}; - // call with sound pool lock held void SoundChannel::play(const sp& sample, int nextChannelID, float leftVolume, float rightVolume, int priority, int loop, float rate) @@ -653,17 +641,6 @@ exit: if (status != NO_ERROR) { mAudioTrack.clear(); } - // FIXME AudioTrack destruction should not be slow - if (oldTrack != 0) { - // must be a raw reference to avoid a race after run() - AsyncDestructThread *adt = new AsyncDestructThread(oldTrack); - // guaranteed to not run destructor - oldTrack.clear(); - // after the run(), adt thread will hold a strong reference to oldTrack, - // and the only strong reference to itself - adt->run("AsyncDestruct"); - // do not delete adt here: adt thread destroys itself, and oldTrack if needed - } } void SoundChannel::nextEvent() -- cgit v1.1 From 5a6cd224d07c05b496b6aca050ce5ecf96f125af Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 20 Sep 2013 09:20:45 -0700 Subject: Fix slow AudioTrack and AudioRecord destruction There were two causes for the slowness: When thread was paused, it used nanosleep and sleep. These usually run to completion (except for POSIX signal, which we avoid because it is low-level). Instead, replace the nanosleep and sleep by condition timed wait, as that can be made to return early by a condition signal. Another advantage of condition timed wait is that a condition wait was already being used at top of thread loop, so it is a simpler change. The AudioRecord destructor was missing a proxy interrupt that was correct in AudioTrack. This proxy interrupt is needed in case another thread is blocked in proxy obtainBuffer. Does not address the 1 second polling for NS_WHENEVER. Bug: 10822765 Change-Id: Id665994551e87e4d7da9c7b015f424fd7a0b5560 --- media/libmedia/AudioRecord.cpp | 54 +++++++++++++++++++++++------------------- media/libmedia/AudioTrack.cpp | 53 ++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 48 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index e934a3e..fb731b9 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -105,6 +105,7 @@ AudioRecord::~AudioRecord() // Otherwise the callback thread will never exit. stop(); if (mAudioRecordThread != 0) { + mProxy->interrupt(); mAudioRecordThread->requestExit(); // see comment in AudioRecord.h mAudioRecordThread->requestExitAndWait(); mAudioRecordThread.clear(); @@ -960,7 +961,7 @@ void AudioRecord::DeathNotifier::binderDied(const wp& who) // ========================================================================= AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false) + : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL) { } @@ -977,25 +978,32 @@ bool AudioRecord::AudioRecordThread::threadLoop() // caller will check for exitPending() return true; } + if (mPausedInt) { + mPausedInt = false; + if (mPausedNs > 0) { + (void) mMyCond.waitRelative(mMyLock, mPausedNs); + } else { + mMyCond.wait(mMyLock); + } + return true; + } } nsecs_t ns = mReceiver.processAudioBuffer(this); switch (ns) { case 0: return true; - case NS_WHENEVER: - sleep(1); - return true; case NS_INACTIVE: - pauseConditional(); + pauseInternal(); return true; case NS_NEVER: return false; + case NS_WHENEVER: + // FIXME increase poll interval, or make event-driven + ns = 1000000000LL; + // fall through default: LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns); - struct timespec req; - req.tv_sec = ns / 1000000000LL; - req.tv_nsec = ns % 1000000000LL; - nanosleep(&req, NULL /*rem*/); + pauseInternal(ns); return true; } } @@ -1004,24 +1012,18 @@ void AudioRecord::AudioRecordThread::requestExit() { // must be in this order to avoid a race condition Thread::requestExit(); - resume(); + AutoMutex _l(mMyLock); + if (mPaused || mPausedInt) { + mPaused = false; + mPausedInt = false; + mMyCond.signal(); + } } void AudioRecord::AudioRecordThread::pause() { AutoMutex _l(mMyLock); mPaused = true; - mResumeLatch = false; -} - -void AudioRecord::AudioRecordThread::pauseConditional() -{ - AutoMutex _l(mMyLock); - if (mResumeLatch) { - mResumeLatch = false; - } else { - mPaused = true; - } } void AudioRecord::AudioRecordThread::resume() @@ -1029,13 +1031,17 @@ void AudioRecord::AudioRecordThread::resume() AutoMutex _l(mMyLock); if (mPaused) { mPaused = false; - mResumeLatch = false; mMyCond.signal(); - } else { - mResumeLatch = true; } } +void AudioRecord::AudioRecordThread::pauseInternal(nsecs_t ns) +{ + AutoMutex _l(mMyLock); + mPausedInt = true; + mPausedNs = ns; +} + // ------------------------------------------------------------------------- }; // namespace android diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 15249a4..fdcf911 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1782,7 +1782,7 @@ void AudioTrack::DeathNotifier::binderDied(const wp& who) // ========================================================================= AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false) + : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL) { } @@ -1799,25 +1799,32 @@ bool AudioTrack::AudioTrackThread::threadLoop() // caller will check for exitPending() return true; } + if (mPausedInt) { + mPausedInt = false; + if (mPausedNs > 0) { + (void) mMyCond.waitRelative(mMyLock, mPausedNs); + } else { + mMyCond.wait(mMyLock); + } + return true; + } } nsecs_t ns = mReceiver.processAudioBuffer(this); switch (ns) { case 0: return true; - case NS_WHENEVER: - sleep(1); - return true; case NS_INACTIVE: - pauseConditional(); + pauseInternal(); return true; case NS_NEVER: return false; + case NS_WHENEVER: + // FIXME increase poll interval, or make event-driven + ns = 1000000000LL; + // fall through default: LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns); - struct timespec req; - req.tv_sec = ns / 1000000000LL; - req.tv_nsec = ns % 1000000000LL; - nanosleep(&req, NULL /*rem*/); + pauseInternal(ns); return true; } } @@ -1826,24 +1833,18 @@ void AudioTrack::AudioTrackThread::requestExit() { // must be in this order to avoid a race condition Thread::requestExit(); - resume(); + AutoMutex _l(mMyLock); + if (mPaused || mPausedInt) { + mPaused = false; + mPausedInt = false; + mMyCond.signal(); + } } void AudioTrack::AudioTrackThread::pause() { AutoMutex _l(mMyLock); mPaused = true; - mResumeLatch = false; -} - -void AudioTrack::AudioTrackThread::pauseConditional() -{ - AutoMutex _l(mMyLock); - if (mResumeLatch) { - mResumeLatch = false; - } else { - mPaused = true; - } } void AudioTrack::AudioTrackThread::resume() @@ -1851,11 +1852,15 @@ void AudioTrack::AudioTrackThread::resume() AutoMutex _l(mMyLock); if (mPaused) { mPaused = false; - mResumeLatch = false; mMyCond.signal(); - } else { - mResumeLatch = true; } } +void AudioTrack::AudioTrackThread::pauseInternal(nsecs_t ns) +{ + AutoMutex _l(mMyLock); + mPausedInt = true; + mPausedNs = ns; +} + }; // namespace android -- cgit v1.1 From 9d2c78c4798ffd8c276c1bf0eaa0b34bc255a2da Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 23 Sep 2013 12:29:42 -0700 Subject: AudioTrack: fix music resume Fix regression introduced by commit 5a6cd22 in AudioTrack resume: the callback thread was not signaled if paused internaly. Bug: 10895013. Change-Id: Ic356b115132d6fccbcee2d9bb855e92671dc20c5 --- media/libmedia/AudioRecord.cpp | 5 +++-- media/libmedia/AudioTrack.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index fb731b9..c5a7777 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -979,12 +979,12 @@ bool AudioRecord::AudioRecordThread::threadLoop() return true; } if (mPausedInt) { - mPausedInt = false; if (mPausedNs > 0) { (void) mMyCond.waitRelative(mMyLock, mPausedNs); } else { mMyCond.wait(mMyLock); } + mPausedInt = false; return true; } } @@ -1029,8 +1029,9 @@ void AudioRecord::AudioRecordThread::pause() void AudioRecord::AudioRecordThread::resume() { AutoMutex _l(mMyLock); - if (mPaused) { + if (mPaused || mPausedInt) { mPaused = false; + mPausedInt = false; mMyCond.signal(); } } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index fdcf911..754a4e3 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1800,12 +1800,12 @@ bool AudioTrack::AudioTrackThread::threadLoop() return true; } if (mPausedInt) { - mPausedInt = false; if (mPausedNs > 0) { (void) mMyCond.waitRelative(mMyLock, mPausedNs); } else { mMyCond.wait(mMyLock); } + mPausedInt = false; return true; } } @@ -1850,8 +1850,9 @@ void AudioTrack::AudioTrackThread::pause() void AudioTrack::AudioTrackThread::resume() { AutoMutex _l(mMyLock); - if (mPaused) { + if (mPaused || mPausedInt) { mPaused = false; + mPausedInt = false; mMyCond.signal(); } } -- cgit v1.1 From 09647d29eaf429ce88c9c9709ff63dee62f2147a Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 20 Sep 2013 11:58:40 -0700 Subject: Add support for level measurements in Visualizer New commands to set a measurement mode and perform peak + RMS measurements. Bug 8413913 Change-Id: Ib25254065c79d365ebb34f9dc9caa0490e2d300d --- media/libeffects/visualizer/EffectVisualizer.cpp | 154 ++++++++++++++++++++--- media/libmedia/Visualizer.cpp | 68 ++++++++++ 2 files changed, 205 insertions(+), 17 deletions(-) (limited to 'media') diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 96935e3..0f27cbf 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -54,6 +55,18 @@ enum visualizer_state_e { #define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone" +#define DISCARD_MEASUREMENTS_TIME_MS 2000 // discard measurements older than this number of ms + +// maximum number of buffers for which we keep track of the measurements +#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 + + +struct BufferStats { + bool mIsValid; + uint16_t mPeakU16; // the positive peak of the absolute value of the samples in a buffer + float mRmsSquared; // the average square of the samples in a buffer +}; + struct VisualizerContext { const struct effect_interface_s *mItfe; effect_config_t mConfig; @@ -65,11 +78,34 @@ struct VisualizerContext { uint32_t mLatency; struct timespec mBufferUpdateTime; uint8_t mCaptureBuf[CAPTURE_BUF_SIZE]; + // for measurements + uint8_t mChannelCount; // to avoid recomputing it every time a buffer is processed + uint32_t mMeasurementMode; + uint8_t mMeasurementWindowSizeInBuffers; + uint8_t mMeasurementBufferIdx; + BufferStats mPastMeasurements[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS]; }; // //--- Local functions // +uint32_t Visualizer_getDeltaTimeMsFromUpdatedTime(VisualizerContext* pContext) { + uint32_t deltaMs = 0; + if (pContext->mBufferUpdateTime.tv_sec != 0) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec; + long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec; + if (nsec < 0) { + --secs; + nsec += 1000000000; + } + deltaMs = secs * 1000 + nsec / 1000000; + } + } + return deltaMs; +} + void Visualizer_reset(VisualizerContext *pContext) { @@ -165,9 +201,21 @@ int Visualizer_init(VisualizerContext *pContext) pContext->mConfig.outputCfg.bufferProvider.cookie = NULL; pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; + // visualization initialization pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX; pContext->mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED; + // measurement initialization + pContext->mChannelCount = popcount(pContext->mConfig.inputCfg.channels); + pContext->mMeasurementMode = MEASUREMENT_MODE_NONE; + pContext->mMeasurementWindowSizeInBuffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS; + pContext->mMeasurementBufferIdx = 0; + for (uint8_t i=0 ; imMeasurementWindowSizeInBuffers ; i++) { + pContext->mPastMeasurements[i].mIsValid = false; + pContext->mPastMeasurements[i].mPeakU16 = 0; + pContext->mPastMeasurements[i].mRmsSquared = 0; + } + Visualizer_setConfig(pContext, &pContext->mConfig); return 0; @@ -270,6 +318,30 @@ int Visualizer_process( return -EINVAL; } + // perform measurements if needed + if (pContext->mMeasurementMode & MEASUREMENT_MODE_PEAK_RMS) { + // find the peak and RMS squared for the new buffer + uint32_t inIdx; + int16_t maxSample = 0; + float rmsSqAcc = 0; + for (inIdx = 0 ; inIdx < inBuffer->frameCount * pContext->mChannelCount ; inIdx++) { + if (inBuffer->s16[inIdx] > maxSample) { + maxSample = inBuffer->s16[inIdx]; + } else if (-inBuffer->s16[inIdx] > maxSample) { + maxSample = -inBuffer->s16[inIdx]; + } + rmsSqAcc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]); + } + // store the measurement + pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mPeakU16 = (uint16_t)maxSample; + pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mRmsSquared = + rmsSqAcc / (inBuffer->frameCount * pContext->mChannelCount); + pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mIsValid = true; + if (++pContext->mMeasurementBufferIdx >= pContext->mMeasurementWindowSizeInBuffers) { + pContext->mMeasurementBufferIdx = 0; + } + } + // all code below assumes stereo 16 bit PCM output and input int32_t shift; @@ -423,6 +495,12 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, p->vsize = sizeof(uint32_t); *replySize += sizeof(uint32_t); break; + case VISUALIZER_PARAM_MEASUREMENT_MODE: + ALOGV("get mMeasurementMode = %d", pContext->mMeasurementMode); + *((uint32_t *)p->data + 1) = pContext->mMeasurementMode; + p->vsize = sizeof(uint32_t); + *replySize += sizeof(uint32_t); + break; default: p->status = -EINVAL; } @@ -452,6 +530,10 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, pContext->mLatency = *((uint32_t *)p->data + 1); ALOGV("set mLatency = %d", pContext->mLatency); break; + case VISUALIZER_PARAM_MEASUREMENT_MODE: + pContext->mMeasurementMode = *((uint32_t *)p->data + 1); + ALOGV("set mMeasurementMode = %d", pContext->mMeasurementMode); + break; default: *(int32_t *)pReplyData = -EINVAL; } @@ -470,24 +552,12 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, } if (pContext->mState == VISUALIZER_STATE_ACTIVE) { int32_t latencyMs = pContext->mLatency; - uint32_t deltaMs = 0; - if (pContext->mBufferUpdateTime.tv_sec != 0) { - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec; - long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec; - if (nsec < 0) { - --secs; - nsec += 1000000000; - } - deltaMs = secs * 1000 + nsec / 1000000; - latencyMs -= deltaMs; - if (latencyMs < 0) { - latencyMs = 0; - } - } + const uint32_t deltaMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext); + latencyMs -= deltaMs; + if (latencyMs < 0) { + latencyMs = 0; } - uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000; + const uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000; int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl; int32_t captureSize = pContext->mCaptureSize; @@ -525,6 +595,56 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, break; + case VISUALIZER_CMD_MEASURE: { + uint16_t peakU16 = 0; + float sumRmsSquared = 0.0f; + uint8_t nbValidMeasurements = 0; + // reset measurements if last measurement was too long ago (which implies stored + // measurements aren't relevant anymore and shouldn't bias the new one) + const int32_t delayMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext); + if (delayMs > DISCARD_MEASUREMENTS_TIME_MS) { + ALOGE("Discarding measurements, last measurement is %dms old", delayMs); + for (uint8_t i=0 ; imMeasurementWindowSizeInBuffers ; i++) { + pContext->mPastMeasurements[i].mIsValid = false; + pContext->mPastMeasurements[i].mPeakU16 = 0; + pContext->mPastMeasurements[i].mRmsSquared = 0; + } + pContext->mMeasurementBufferIdx = 0; + } else { + // only use actual measurements, otherwise the first RMS measure happening before + // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially + // low + for (uint8_t i=0 ; i < pContext->mMeasurementWindowSizeInBuffers ; i++) { + if (pContext->mPastMeasurements[i].mIsValid) { + if (pContext->mPastMeasurements[i].mPeakU16 > peakU16) { + peakU16 = pContext->mPastMeasurements[i].mPeakU16; + } + if (pContext->mMeasurementWindowSizeInBuffers != 0) { + sumRmsSquared += pContext->mPastMeasurements[i].mRmsSquared; + } + nbValidMeasurements++; + } + } + } + float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements); + int32_t* pIntReplyData = (int32_t*)pReplyData; + // convert from I16 sample values to mB and write results + if (rms < 0.000016f) { + pIntReplyData[MEASUREMENT_IDX_RMS] = -9600; //-96dB + } else { + pIntReplyData[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f)); + } + if (peakU16 == 0) { + pIntReplyData[MEASUREMENT_IDX_PEAK] = -9600; //-96dB + } else { + pIntReplyData[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peakU16 / 32767.0f)); + } + ALOGV("LEVEL_MONITOR_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)", + peakU16, pIntReplyData[MEASUREMENT_IDX_PEAK], + rms, pIntReplyData[MEASUREMENT_IDX_RMS]); + } + break; + default: ALOGW("Visualizer_command invalid command %d",cmdCode); return -EINVAL; diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index e519f13..c146b8d 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -43,6 +43,7 @@ Visualizer::Visualizer (int32_t priority, mCaptureSize(CAPTURE_SIZE_DEF), mSampleRate(44100000), mScalingMode(VISUALIZER_SCALING_MODE_NORMALIZED), + mMeasurementMode(MEASUREMENT_MODE_NONE), mCaptureCallBack(NULL), mCaptureCbkUser(NULL) { @@ -186,6 +187,73 @@ status_t Visualizer::setScalingMode(uint32_t mode) { return status; } +status_t Visualizer::setMeasurementMode(uint32_t mode) { + if ((mode != MEASUREMENT_MODE_NONE) + //Note: needs to be handled as a mask when more measurement modes are added + && ((mode & MEASUREMENT_MODE_PEAK_RMS) != mode)) { + return BAD_VALUE; + } + + Mutex::Autolock _l(mCaptureLock); + + uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2]; + effect_param_t *p = (effect_param_t *)buf32; + + p->psize = sizeof(uint32_t); + p->vsize = sizeof(uint32_t); + *(int32_t *)p->data = VISUALIZER_PARAM_MEASUREMENT_MODE; + *((int32_t *)p->data + 1)= mode; + status_t status = setParameter(p); + + ALOGV("setMeasurementMode mode %d status %d p->status %d", mode, status, p->status); + + if (status == NO_ERROR) { + status = p->status; + if (status == NO_ERROR) { + mMeasurementMode = mode; + } + } + return status; +} + +status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements) { + if (mMeasurementMode == MEASUREMENT_MODE_NONE) { + ALOGE("Cannot retrieve int measurements, no measurement mode set"); + return INVALID_OPERATION; + } + if (!(mMeasurementMode & type)) { + // measurement type has not been set on this Visualizer + ALOGE("Cannot retrieve int measurements, requested measurement mode 0x%x not set(0x%x)", + type, mMeasurementMode); + return INVALID_OPERATION; + } + // only peak+RMS measurement supported + if ((type != MEASUREMENT_MODE_PEAK_RMS) + // for peak+RMS measurement, the results are 2 int32_t values + || (number != 2)) { + ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d", + number); + return BAD_VALUE; + } + + status_t status = NO_ERROR; + if (mEnabled) { + uint32_t replySize = number * sizeof(int32_t); + status = command(VISUALIZER_CMD_MEASURE, + sizeof(uint32_t) /*cmdSize*/, + &type /*cmdData*/, + &replySize, measurements); + ALOGV("getMeasurements() command returned %d", status); + if ((status == NO_ERROR) && (replySize == 0)) { + status = NOT_ENOUGH_DATA; + } + } else { + ALOGV("getMeasurements() disabled"); + return INVALID_OPERATION; + } + return status; +} + status_t Visualizer::getWaveForm(uint8_t *waveform) { if (waveform == NULL) { -- cgit v1.1 From ffd5687c9ece8e28779793a20f06f99c7199ce44 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 24 Sep 2013 10:04:42 -0700 Subject: Send kWhatConnected in onTimeUpdate() before first access unit Bug: 10642588 Change-Id: If2b4fbbf250d5307e304f31c7aa4ac480e279484 --- media/libstagefright/rtsp/MyHandler.h | 40 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'media') diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 946f602..f4b5846 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -1681,6 +1681,26 @@ private: return true; } + void handleFirstAccessUnit() { + if (mFirstAccessUnit) { + sp msg = mNotify->dup(); + msg->setInt32("what", kWhatConnected); + msg->post(); + + if (mSeekable) { + for (size_t i = 0; i < mTracks.size(); ++i) { + TrackInfo *info = &mTracks.editItemAt(i); + + postNormalPlayTimeMapping( + i, + info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs); + } + } + + mFirstAccessUnit = false; + } + } + void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) { ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx", trackIndex, rtpTime, ntpTime); @@ -1712,6 +1732,8 @@ private: } } if (mAllTracksHaveTime && dataReceivedOnAllChannels()) { + handleFirstAccessUnit(); + // Time is now established, lets start timestamping immediately for (size_t i = 0; i < mTracks.size(); ++i) { TrackInfo *trackInfo = &mTracks.editItemAt(i); @@ -1745,23 +1767,7 @@ private: return; } - if (mFirstAccessUnit) { - sp msg = mNotify->dup(); - msg->setInt32("what", kWhatConnected); - msg->post(); - - if (mSeekable) { - for (size_t i = 0; i < mTracks.size(); ++i) { - TrackInfo *info = &mTracks.editItemAt(i); - - postNormalPlayTimeMapping( - i, - info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs); - } - } - - mFirstAccessUnit = false; - } + handleFirstAccessUnit(); TrackInfo *track = &mTracks.editItemAt(trackIndex); -- cgit v1.1 From 3d00aa6de95fb46e36f2bab4e3facdf0b96acf06 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 24 Sep 2013 09:53:27 -0700 Subject: soundpool: allocate shared memory heap by client Current SoundPool implementation allocates the shared memory heap containing decoded PCM samples in mediaserver process. When mediaserver process crashes, the shared memory heaps allocated by AudioCache cannot be mapped anymore in the new instance of mediaserver. This causes a silent failure to end playback of new sounds because AudioFlinger believes the new AudioTracks are opened in streaming mode and not static mode: it sees a NULL shared memory pointer when the track is created. The fix consists in allocating the memory heap in the client process. Thus the heap is not lost when mediaserver restarts. The global memory usage is the same as this is shared memory. Also added a way to detect that a shared memory is passed when the track is created but cannot be mapped on mediaserver side. Also fix a crash in SoundPool when ALOGV is enabled. Bug: 10894793. Change-Id: Ice6c66ec3b2a409d75dc903a508b6c6fbfb2e8a7 --- media/libmedia/IAudioFlinger.cpp | 27 ++++++-- media/libmedia/IMediaPlayerService.cpp | 73 +++++++++++++++------- media/libmedia/SoundPool.cpp | 40 ++++++------ media/libmedia/mediaplayer.cpp | 24 ++++--- media/libmediaplayerservice/MediaPlayerService.cpp | 49 ++++++++------- media/libmediaplayerservice/MediaPlayerService.h | 13 ++-- 6 files changed, 147 insertions(+), 79 deletions(-) (limited to 'media') diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index be818c6..68928f1 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -108,7 +108,12 @@ public: data.writeInt32(frameCount); track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT; data.writeInt32(lFlags); - data.writeStrongBinder(sharedBuffer->asBinder()); + if (sharedBuffer != 0) { + data.writeInt32(true); + data.writeStrongBinder(sharedBuffer->asBinder()); + } else { + data.writeInt32(false); + } data.writeInt32((int32_t) output); data.writeInt32((int32_t) tid); int lSessionId = 0; @@ -738,15 +743,27 @@ status_t BnAudioFlinger::onTransact( audio_channel_mask_t channelMask = data.readInt32(); size_t frameCount = data.readInt32(); track_flags_t flags = (track_flags_t) data.readInt32(); - sp buffer = interface_cast(data.readStrongBinder()); + bool haveSharedBuffer = data.readInt32() != 0; + sp buffer; + if (haveSharedBuffer) { + buffer = interface_cast(data.readStrongBinder()); + } audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); String8 name; status_t status; - sp track = createTrack( - (audio_stream_type_t) streamType, sampleRate, format, - channelMask, frameCount, &flags, buffer, output, tid, &sessionId, name, &status); + sp track; + if ((haveSharedBuffer && (buffer == 0)) || + ((buffer != 0) && (buffer->pointer() == NULL))) { + ALOGW("CREATE_TRACK: cannot retrieve shared memory"); + status = DEAD_OBJECT; + } else { + track = createTrack( + (audio_stream_type_t) streamType, sampleRate, format, + channelMask, frameCount, &flags, buffer, output, tid, + &sessionId, name, &status); + } reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeString8(name); diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 74f574d..3c22b4c 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -86,30 +86,48 @@ public: return interface_cast(reply.readStrongBinder()); } - virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) + virtual status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, + audio_format_t* pFormat, + const sp& heap, size_t *pSize) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); data.writeCString(url); - remote()->transact(DECODE_URL, data, &reply); - *pSampleRate = uint32_t(reply.readInt32()); - *pNumChannels = reply.readInt32(); - *pFormat = (audio_format_t) reply.readInt32(); - return interface_cast(reply.readStrongBinder()); + data.writeStrongBinder(heap->asBinder()); + status_t status = remote()->transact(DECODE_URL, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + *pSampleRate = uint32_t(reply.readInt32()); + *pNumChannels = reply.readInt32(); + *pFormat = (audio_format_t)reply.readInt32(); + *pSize = (size_t)reply.readInt32(); + } + } + return status; } - virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) + virtual status_t decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, + int* pNumChannels, audio_format_t* pFormat, + const sp& heap, size_t *pSize) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); data.writeFileDescriptor(fd); data.writeInt64(offset); data.writeInt64(length); - remote()->transact(DECODE_FD, data, &reply); - *pSampleRate = uint32_t(reply.readInt32()); - *pNumChannels = reply.readInt32(); - *pFormat = (audio_format_t) reply.readInt32(); - return interface_cast(reply.readStrongBinder()); + data.writeStrongBinder(heap->asBinder()); + status_t status = remote()->transact(DECODE_FD, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + if (status == NO_ERROR) { + *pSampleRate = uint32_t(reply.readInt32()); + *pNumChannels = reply.readInt32(); + *pFormat = (audio_format_t)reply.readInt32(); + *pSize = (size_t)reply.readInt32(); + } + } + return status; } virtual sp getOMX() { @@ -205,14 +223,19 @@ status_t BnMediaPlayerService::onTransact( case DECODE_URL: { CHECK_INTERFACE(IMediaPlayerService, data, reply); const char* url = data.readCString(); + sp heap = interface_cast(data.readStrongBinder()); uint32_t sampleRate; int numChannels; audio_format_t format; - sp player = decode(url, &sampleRate, &numChannels, &format); - reply->writeInt32(sampleRate); - reply->writeInt32(numChannels); - reply->writeInt32((int32_t) format); - reply->writeStrongBinder(player->asBinder()); + size_t size; + status_t status = decode(url, &sampleRate, &numChannels, &format, heap, &size); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + reply->writeInt32((int32_t)format); + reply->writeInt32((int32_t)size); + } return NO_ERROR; } break; case DECODE_FD: { @@ -220,14 +243,20 @@ status_t BnMediaPlayerService::onTransact( int fd = dup(data.readFileDescriptor()); int64_t offset = data.readInt64(); int64_t length = data.readInt64(); + sp heap = interface_cast(data.readStrongBinder()); uint32_t sampleRate; int numChannels; audio_format_t format; - sp player = decode(fd, offset, length, &sampleRate, &numChannels, &format); - reply->writeInt32(sampleRate); - reply->writeInt32(numChannels); - reply->writeInt32((int32_t) format); - reply->writeStrongBinder(player->asBinder()); + size_t size; + status_t status = decode(fd, offset, length, &sampleRate, &numChannels, &format, + heap, &size); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + reply->writeInt32((int32_t)format); + reply->writeInt32((int32_t)size); + } return NO_ERROR; } break; case CREATE_MEDIA_RECORDER: { diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 5239b2f..8434d43 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -32,6 +32,8 @@ int kDefaultBufferCount = 4; uint32_t kMaxSampleRate = 48000; uint32_t kDefaultSampleRate = 44100; uint32_t kDefaultFrameCount = 1200; +size_t kDefaultHeapSize = 1024 * 1024; // 1MB + SoundPool::SoundPool(int maxChannels, audio_stream_type_t streamType, int srcQuality) { @@ -464,7 +466,6 @@ Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length) void Sample::init() { - mData = 0; mSize = 0; mRefCount = 0; mSampleID = 0; @@ -482,7 +483,6 @@ Sample::~Sample() ALOGV("close(%d)", mFd); ::close(mFd); } - mData.clear(); free(mUrl); } @@ -491,44 +491,48 @@ status_t Sample::doLoad() uint32_t sampleRate; int numChannels; audio_format_t format; - sp p; + status_t status; + mHeap = new MemoryHeapBase(kDefaultHeapSize); + ALOGV("Start decode"); if (mUrl) { - p = MediaPlayer::decode(mUrl, &sampleRate, &numChannels, &format); + status = MediaPlayer::decode(mUrl, &sampleRate, &numChannels, &format, mHeap, &mSize); } else { - p = MediaPlayer::decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format); + status = MediaPlayer::decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format, + mHeap, &mSize); ALOGV("close(%d)", mFd); ::close(mFd); mFd = -1; } - if (p == 0) { + if (status != NO_ERROR) { ALOGE("Unable to load sample: %s", mUrl); - return -1; + goto error; } ALOGV("pointer = %p, size = %u, sampleRate = %u, numChannels = %d", - p->pointer(), p->size(), sampleRate, numChannels); + mHeap->getBase(), mSize, sampleRate, numChannels); if (sampleRate > kMaxSampleRate) { ALOGE("Sample rate (%u) out of range", sampleRate); - return - 1; + status = BAD_VALUE; + goto error; } if ((numChannels < 1) || (numChannels > 2)) { ALOGE("Sample channel count (%d) out of range", numChannels); - return - 1; + status = BAD_VALUE; + goto error; } - //_dumpBuffer(p->pointer(), p->size()); - uint8_t* q = static_cast(p->pointer()) + p->size() - 10; - //_dumpBuffer(q, 10, 10, false); - - mData = p; - mSize = p->size(); + mData = new MemoryBase(mHeap, 0, mSize); mSampleRate = sampleRate; mNumChannels = numChannels; mFormat = format; mState = READY; - return 0; + return NO_ERROR; + +error: + mHeap.clear(); + return status; } @@ -744,7 +748,7 @@ void SoundChannel::process(int event, void *info, unsigned long toggle) ALOGV("process %p channel %d EVENT_UNDERRUN or EVENT_BUFFER_END", this, mChannelID); mSoundPool->addToStopList(this); } else if (event == AudioTrack::EVENT_LOOP_END) { - ALOGV("End loop %p channel %d count %d", this, mChannelID, *(int *)info); + ALOGV("End loop %p channel %d", this, mChannelID); } } diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 4323d0c..0f6d897 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -776,17 +776,20 @@ void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) } } -/*static*/ sp MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) +/*static*/ status_t MediaPlayer::decode(const char* url, uint32_t *pSampleRate, + int* pNumChannels, audio_format_t* pFormat, + const sp& heap, size_t *pSize) { ALOGV("decode(%s)", url); - sp p; + status_t status; const sp& service = getMediaPlayerService(); if (service != 0) { - p = service->decode(url, pSampleRate, pNumChannels, pFormat); + status = service->decode(url, pSampleRate, pNumChannels, pFormat, heap, pSize); } else { ALOGE("Unable to locate media service"); + status = DEAD_OBJECT; } - return p; + return status; } @@ -796,17 +799,22 @@ void MediaPlayer::died() notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); } -/*static*/ sp MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) +/*static*/ status_t MediaPlayer::decode(int fd, int64_t offset, int64_t length, + uint32_t *pSampleRate, int* pNumChannels, + audio_format_t* pFormat, + const sp& heap, size_t *pSize) { ALOGV("decode(%d, %lld, %lld)", fd, offset, length); - sp p; + status_t status; const sp& service = getMediaPlayerService(); if (service != 0) { - p = service->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat); + status = service->decode(fd, offset, length, pSampleRate, + pNumChannels, pFormat, heap, pSize); } else { ALOGE("Unable to locate media service"); + status = DEAD_OBJECT; } - return p; + return status; } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 0dabd37..9553458 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -319,8 +319,8 @@ status_t MediaPlayerService::AudioCache::dump(int fd, const Vector& ar result.append(" AudioCache\n"); if (mHeap != 0) { - snprintf(buffer, 255, " heap base(%p), size(%d), flags(%d), device(%s)\n", - mHeap->getBase(), mHeap->getSize(), mHeap->getFlags(), mHeap->getDevice()); + snprintf(buffer, 255, " heap base(%p), size(%d), flags(%d)\n", + mHeap->getBase(), mHeap->getSize(), mHeap->getFlags()); result.append(buffer); } snprintf(buffer, 255, " msec per frame(%f), channel count(%d), format(%d), frame count(%zd)\n", @@ -1176,13 +1176,13 @@ int Antagonizer::callbackThread(void* user) } #endif -static size_t kDefaultHeapSize = 1024 * 1024; // 1MB - -sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) +status_t MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, + audio_format_t* pFormat, + const sp& heap, size_t *pSize) { ALOGV("decode(%s)", url); - sp mem; sp player; + status_t status = BAD_VALUE; // Protect our precious, precious DRMd ringtones by only allowing // decoding of http, but not filesystem paths or content Uris. @@ -1190,7 +1190,7 @@ sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, i // filedescriptor for them and use that. if (url != NULL && strncmp(url, "http://", 7) != 0) { ALOGD("Can't decode %s by path, use filedescriptor instead", url); - return mem; + return BAD_VALUE; } player_type playerType = @@ -1198,7 +1198,7 @@ sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, i ALOGV("player type = %d", playerType); // create the right type of player - sp cache = new AudioCache(url); + sp cache = new AudioCache(heap); player = MediaPlayerFactory::createPlayer(playerType, cache.get(), cache->notify); if (player == NULL) goto Exit; if (player->hardwareOutput()) goto Exit; @@ -1224,22 +1224,27 @@ sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, i goto Exit; } - mem = new MemoryBase(cache->getHeap(), 0, cache->size()); + *pSize = cache->size(); *pSampleRate = cache->sampleRate(); *pNumChannels = cache->channelCount(); *pFormat = cache->format(); - ALOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat); + ALOGV("return size %d sampleRate=%u, channelCount = %d, format = %d", + *pSize, *pSampleRate, *pNumChannels, *pFormat); + status = NO_ERROR; Exit: if (player != 0) player->reset(); - return mem; + return status; } -sp MediaPlayerService::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) +status_t MediaPlayerService::decode(int fd, int64_t offset, int64_t length, + uint32_t *pSampleRate, int* pNumChannels, + audio_format_t* pFormat, + const sp& heap, size_t *pSize) { ALOGV("decode(%d, %lld, %lld)", fd, offset, length); - sp mem; sp player; + status_t status = BAD_VALUE; player_type playerType = MediaPlayerFactory::getPlayerType(NULL /* client */, fd, @@ -1248,7 +1253,7 @@ sp MediaPlayerService::decode(int fd, int64_t offset, int64_t length, u ALOGV("player type = %d", playerType); // create the right type of player - sp cache = new AudioCache("decode_fd"); + sp cache = new AudioCache(heap); player = MediaPlayerFactory::createPlayer(playerType, cache.get(), cache->notify); if (player == NULL) goto Exit; if (player->hardwareOutput()) goto Exit; @@ -1274,16 +1279,18 @@ sp MediaPlayerService::decode(int fd, int64_t offset, int64_t length, u goto Exit; } - mem = new MemoryBase(cache->getHeap(), 0, cache->size()); + *pSize = cache->size(); *pSampleRate = cache->sampleRate(); *pNumChannels = cache->channelCount(); *pFormat = cache->format(); - ALOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat); + ALOGV("return size %d, sampleRate=%u, channelCount = %d, format = %d", + *pSize, *pSampleRate, *pNumChannels, *pFormat); + status = NO_ERROR; Exit: if (player != 0) player->reset(); ::close(fd); - return mem; + return status; } @@ -1803,12 +1810,10 @@ int MediaPlayerService::AudioOutput::getSessionId() const #undef LOG_TAG #define LOG_TAG "AudioCache" -MediaPlayerService::AudioCache::AudioCache(const char* name) : - mChannelCount(0), mFrameCount(1024), mSampleRate(0), mSize(0), - mError(NO_ERROR), mCommandComplete(false) +MediaPlayerService::AudioCache::AudioCache(const sp& heap) : + mHeap(heap), mChannelCount(0), mFrameCount(1024), mSampleRate(0), mSize(0), + mError(NO_ERROR), mCommandComplete(false) { - // create ashmem heap - mHeap = new MemoryHeapBase(kDefaultHeapSize, 0, name); } uint32_t MediaPlayerService::AudioCache::latency () const diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 7d27944..21f4117 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -177,7 +177,7 @@ class MediaPlayerService : public BnMediaPlayerService class AudioCache : public MediaPlayerBase::AudioSink { public: - AudioCache(const char* name); + AudioCache(const sp& heap); virtual ~AudioCache() {} virtual bool ready() const { return (mChannelCount > 0) && (mHeap->getHeapID() > 0); } @@ -224,7 +224,7 @@ class MediaPlayerService : public BnMediaPlayerService Mutex mLock; Condition mSignal; - sp mHeap; + sp mHeap; float mMsecsPerFrame; uint16_t mChannelCount; audio_format_t mFormat; @@ -247,8 +247,13 @@ public: virtual sp create(const sp& client, int audioSessionId); - virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); - virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat); + virtual status_t decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, + audio_format_t* pFormat, + const sp& heap, size_t *pSize); + virtual status_t decode(int fd, int64_t offset, int64_t length, + uint32_t *pSampleRate, int* pNumChannels, + audio_format_t* pFormat, + const sp& heap, size_t *pSize); virtual sp getOMX(); virtual sp makeCrypto(); virtual sp makeDrm(); -- cgit v1.1 From e93cf2ca27ae6f4a81d4ef548bbf10a34db6d98f Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 24 Sep 2013 11:52:37 -0700 Subject: Cleanup openRecord error handling Bug: 10888816 Change-Id: I84897dd7d30b370640b54e928f230604b873cb68 --- media/libmedia/AudioRecord.cpp | 9 +++++++-- media/libmedia/IAudioFlinger.cpp | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index c5a7777..666fafa 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -474,7 +474,7 @@ status_t AudioRecord::openRecord_l(size_t epoch) ALOGE_IF(originalSessionId != 0 && mSessionId != originalSessionId, "session ID changed from %d to %d", originalSessionId, mSessionId); - if (record == 0) { + if (record == 0 || status != NO_ERROR) { ALOGE("AudioFlinger could not create record track, status: %d", status); AudioSystem::releaseInput(input); return status; @@ -484,6 +484,11 @@ status_t AudioRecord::openRecord_l(size_t epoch) ALOGE("Could not get control block"); return NO_INIT; } + void *iMemPointer = iMem->pointer(); + if (iMemPointer == NULL) { + ALOGE("Could not get control block pointer"); + return NO_INIT; + } if (mAudioRecord != 0) { mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this); mDeathNotifier.clear(); @@ -491,7 +496,7 @@ status_t AudioRecord::openRecord_l(size_t epoch) mInput = input; mAudioRecord = record; mCblkMemory = iMem; - audio_track_cblk_t* cblk = static_cast(iMem->pointer()); + audio_track_cblk_t* cblk = static_cast(iMemPointer); mCblk = cblk; // FIXME missing fast track frameCount logic mAwaitBoost = false; diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index be818c6..2cc35c6 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -179,6 +179,17 @@ public: } lStatus = reply.readInt32(); record = interface_cast(reply.readStrongBinder()); + if (lStatus == NO_ERROR) { + if (record == 0) { + ALOGE("openRecord should have returned an IAudioRecord"); + lStatus = UNKNOWN_ERROR; + } + } else { + if (record != 0) { + ALOGE("openRecord returned an IAudioRecord but with status %d", lStatus); + record.clear(); + } + } } if (status) { *status = lStatus; @@ -767,6 +778,7 @@ status_t BnAudioFlinger::onTransact( status_t status; sp record = openRecord(input, sampleRate, format, channelMask, frameCount, &flags, tid, &sessionId, &status); + LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR)); reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeInt32(status); -- cgit v1.1 From ddfbfaeb00295fff7351711f0f044f17d6c40f3c Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 20 Sep 2013 12:27:32 -0700 Subject: fix oflload effect proxy commmand handling Implement a more generic command handling in offload effect proxy. All commands are sent to both sub effects but only the reply from the active one is returned to the caller. Bug: 8174034. Change-Id: Ia45f9933b3bf338257ec70b37732fa1578d26b9f --- media/libeffects/proxy/EffectProxy.cpp | 95 ++++++++++++++++++---------------- media/libeffects/proxy/EffectProxy.h | 5 ++ 2 files changed, 54 insertions(+), 46 deletions(-) (limited to 'media') diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp index 41640da..b3304b7 100644 --- a/media/libeffects/proxy/EffectProxy.cpp +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -48,20 +48,6 @@ static const effect_descriptor_t *const gDescriptors[] = &gProxyDescriptor, }; -static inline bool isGetterCmd(uint32_t cmdCode) -{ - switch (cmdCode) { - case EFFECT_CMD_GET_PARAM: - case EFFECT_CMD_GET_CONFIG: - case EFFECT_CMD_GET_CONFIG_REVERSE: - case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: - case EFFECT_CMD_GET_FEATURE_CONFIG: - return true; - default: - return false; - } -} - int EffectProxyCreate(const effect_uuid_t *uuid, int32_t sessionId, @@ -80,6 +66,7 @@ int EffectProxyCreate(const effect_uuid_t *uuid, pContext->ioId = ioId; pContext->uuid = *uuid; pContext->common_itfe = &gEffectInterface; + // The sub effects will be created in effect_command when the first command // for the effect is received pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL; @@ -124,6 +111,10 @@ int EffectProxyCreate(const effect_uuid_t *uuid, uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], uuid_print.node[4], uuid_print.node[5]); #endif + + pContext->replySize = PROXY_REPLY_SIZE_DEFAULT; + pContext->replyData = (char *)malloc(PROXY_REPLY_SIZE_DEFAULT); + *pHandle = (effect_handle_t)pContext; ALOGV("EffectCreate end"); return 0; @@ -137,6 +128,8 @@ int EffectProxyRelease(effect_handle_t handle) { } ALOGV("EffectRelease"); delete pContext->desc; + free(pContext->replyData); + if (pContext->eHandle[SUB_FX_HOST]) EffectRelease(pContext->eHandle[SUB_FX_HOST]); if (pContext->eHandle[SUB_FX_OFFLOAD]) @@ -253,43 +246,53 @@ int Effect_command(effect_handle_t self, } // Getter commands are only sent to the active sub effect. - uint32_t hostReplySize = replySize != NULL ? *replySize : 0; - bool hostReplied = false; - int hostStatus = 0; - uint32_t offloadReplySize = replySize != NULL ? *replySize : 0; - bool offloadReplied = false; - int offloadStatus = 0; + int *subStatus[SUB_FX_COUNT]; + uint32_t *subReplySize[SUB_FX_COUNT]; + void *subReplyData[SUB_FX_COUNT]; + uint32_t tmpSize; + int tmpStatus; - if (pContext->eHandle[SUB_FX_HOST] && (!isGetterCmd(cmdCode) || index == SUB_FX_HOST)) { - hostStatus = (*pContext->eHandle[SUB_FX_HOST])->command( - pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize, - pCmdData, replySize != NULL ? &hostReplySize : NULL, pReplyData); - hostReplied = true; - } - if (pContext->eHandle[SUB_FX_OFFLOAD] && (!isGetterCmd(cmdCode) || index == SUB_FX_OFFLOAD)) { - // In case of SET CMD, when the offload stream is unavailable, - // we will store the effect param values in the DSP effect wrapper. - // When the offload effects get enabled, we send these values to the - // DSP during Effect_config. - // So,we send the params to DSP wrapper also - offloadStatus = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( - pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, - pCmdData, replySize != NULL ? &offloadReplySize : NULL, pReplyData); - offloadReplied = true; + // grow temp reply buffer if needed + if (replySize != NULL) { + tmpSize = pContext->replySize; + while (tmpSize < *replySize && tmpSize < PROXY_REPLY_SIZE_MAX) { + tmpSize *= 2; + } + if (tmpSize > pContext->replySize) { + ALOGV("Effect_command grow reply buf to %d", tmpSize); + pContext->replyData = (char *)realloc(pContext->replyData, tmpSize); + pContext->replySize = tmpSize; + } + if (tmpSize > *replySize) { + tmpSize = *replySize; + } + } else { + tmpSize = 0; } - // By convention the offloaded implementation reply is returned if command is processed by both - // host and offloaded sub effects - if (offloadReplied){ - status = offloadStatus; - if (replySize) { - *replySize = offloadReplySize; + // tmpSize is now the actual reply size for the non active sub effect + + // Send command to sub effects. The command is sent to all sub effects so that their internal + // state is kept in sync. + // Only the reply from the active sub effect is returned to the caller. The reply from the + // other sub effect is lost in pContext->replyData + for (int i = 0; i < SUB_FX_COUNT; i++) { + if (pContext->eHandle[i] == NULL) { + continue; } - } else if (hostReplied) { - status = hostStatus; - if (replySize) { - *replySize = hostReplySize; + if (i == index) { + subStatus[i] = &status; + subReplySize[i] = replySize; + subReplyData[i] = pReplyData; + } else { + subStatus[i] = &tmpStatus; + subReplySize[i] = replySize == NULL ? NULL : &tmpSize; + subReplyData[i] = pReplyData == NULL ? NULL : pContext->replyData; } + *subStatus[i] = (*pContext->eHandle[i])->command( + pContext->eHandle[i], cmdCode, cmdSize, + pCmdData, subReplySize[i], subReplyData[i]); } + return status; } /* end Effect_command */ diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h index 8992f93..acbe17e 100644 --- a/media/libeffects/proxy/EffectProxy.h +++ b/media/libeffects/proxy/EffectProxy.h @@ -57,6 +57,9 @@ const struct effect_interface_s gEffectInterface = { NULL, }; +#define PROXY_REPLY_SIZE_MAX (64 * 1024) // must be power of two +#define PROXY_REPLY_SIZE_DEFAULT 32 // must be power of two + struct EffectContext { const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy effect_descriptor_t* desc; // Points to the sub effect descriptors @@ -67,6 +70,8 @@ struct EffectContext { int32_t ioId; // The ioId in which the effect is created. // Stored in context to pass on to sub effect creation effect_uuid_t uuid; // UUID of the Proxy + char* replyData; // temporary buffer for non active sub effect command reply + uint32_t replySize; // current size of temporary reply buffer }; #if __cplusplus -- cgit v1.1 From 6fbc9ef121b081f888163190bb13cbac31599900 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Tue, 24 Sep 2013 15:31:13 -0700 Subject: Fix log typos in Visualizer effect Fix errors in logs for Visualizer. Set loop counters on 32 bits Bug 8413913 Change-Id: Iad2140d003d15d45be46826a5e89baff14fe9e77 --- media/libeffects/visualizer/EffectVisualizer.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'media') diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 0f27cbf..dc403ab 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -58,7 +58,7 @@ enum visualizer_state_e { #define DISCARD_MEASUREMENTS_TIME_MS 2000 // discard measurements older than this number of ms // maximum number of buffers for which we keep track of the measurements -#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 +#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 // note: buffer index is stored in uint8_t struct BufferStats { @@ -210,7 +210,7 @@ int Visualizer_init(VisualizerContext *pContext) pContext->mMeasurementMode = MEASUREMENT_MODE_NONE; pContext->mMeasurementWindowSizeInBuffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS; pContext->mMeasurementBufferIdx = 0; - for (uint8_t i=0 ; imMeasurementWindowSizeInBuffers ; i++) { + for (uint32_t i=0 ; imMeasurementWindowSizeInBuffers ; i++) { pContext->mPastMeasurements[i].mIsValid = false; pContext->mPastMeasurements[i].mPeakU16 = 0; pContext->mPastMeasurements[i].mRmsSquared = 0; @@ -603,8 +603,8 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, // measurements aren't relevant anymore and shouldn't bias the new one) const int32_t delayMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext); if (delayMs > DISCARD_MEASUREMENTS_TIME_MS) { - ALOGE("Discarding measurements, last measurement is %dms old", delayMs); - for (uint8_t i=0 ; imMeasurementWindowSizeInBuffers ; i++) { + ALOGV("Discarding measurements, last measurement is %dms old", delayMs); + for (uint32_t i=0 ; imMeasurementWindowSizeInBuffers ; i++) { pContext->mPastMeasurements[i].mIsValid = false; pContext->mPastMeasurements[i].mPeakU16 = 0; pContext->mPastMeasurements[i].mRmsSquared = 0; @@ -614,14 +614,12 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, // only use actual measurements, otherwise the first RMS measure happening before // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially // low - for (uint8_t i=0 ; i < pContext->mMeasurementWindowSizeInBuffers ; i++) { + for (uint32_t i=0 ; i < pContext->mMeasurementWindowSizeInBuffers ; i++) { if (pContext->mPastMeasurements[i].mIsValid) { if (pContext->mPastMeasurements[i].mPeakU16 > peakU16) { peakU16 = pContext->mPastMeasurements[i].mPeakU16; } - if (pContext->mMeasurementWindowSizeInBuffers != 0) { - sumRmsSquared += pContext->mPastMeasurements[i].mRmsSquared; - } + sumRmsSquared += pContext->mPastMeasurements[i].mRmsSquared; nbValidMeasurements++; } } @@ -639,7 +637,7 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, } else { pIntReplyData[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peakU16 / 32767.0f)); } - ALOGV("LEVEL_MONITOR_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)", + ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)", peakU16, pIntReplyData[MEASUREMENT_IDX_PEAK], rms, pIntReplyData[MEASUREMENT_IDX_RMS]); } -- cgit v1.1 From cd0c4683947231a7d3dc7811bedb75c5a965103c Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 25 Sep 2013 18:43:55 -0700 Subject: LoudnessEnhancer compatible with stereo imaging Use a single compressor for both channels. Envelope of signal is determined by looking at both channels. Bug 8413913 Change-Id: Ia9b6f34923d2977c60a3352500b858dfa1fab33c --- .../libeffects/loudness/EffectLoudnessEnhancer.cpp | 42 +++++++++------------- .../dsp/core/dynamic_range_compression-inl.h | 2 +- .../dsp/core/dynamic_range_compression.cpp | 35 ++++++++++++++++++ .../loudness/dsp/core/dynamic_range_compression.h | 3 ++ 4 files changed, 56 insertions(+), 26 deletions(-) (limited to 'media') diff --git a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp index dfc25db..91ed677 100644 --- a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp +++ b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp @@ -56,8 +56,7 @@ struct LoudnessEnhancerContext { int32_t mTargetGainmB;// target gain in mB // in this implementation, there is no coupling between the compression on the left and right // channels - le_fx::AdaptiveDynamicRangeCompression* mCompressorL; - le_fx::AdaptiveDynamicRangeCompression* mCompressorR; + le_fx::AdaptiveDynamicRangeCompression* mCompressor; }; // @@ -68,11 +67,10 @@ void LE_reset(LoudnessEnhancerContext *pContext) { ALOGV(" > LE_reset(%p)", pContext); - if ((pContext->mCompressorL != NULL) && (pContext->mCompressorR != NULL)) { + if (pContext->mCompressor != NULL) { float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification ALOGV("LE_reset(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp); - pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); - pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); + pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); } else { ALOGE("LE_reset(%p): null compressors, can't apply target gain", pContext); } @@ -176,13 +174,9 @@ int LE_init(LoudnessEnhancerContext *pContext) float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification ALOGV("LE_init(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp); - if (pContext->mCompressorL == NULL) { - pContext->mCompressorL = new le_fx::AdaptiveDynamicRangeCompression(); - pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); - } - if (pContext->mCompressorR == NULL) { - pContext->mCompressorR = new le_fx::AdaptiveDynamicRangeCompression(); - pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); + if (pContext->mCompressor == NULL) { + pContext->mCompressor = new le_fx::AdaptiveDynamicRangeCompression(); + pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); } LE_setConfig(pContext, &pContext->mConfig); @@ -215,8 +209,7 @@ int LELib_Create(const effect_uuid_t *uuid, pContext->mItfe = &gLEInterface; pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED; - pContext->mCompressorL = NULL; - pContext->mCompressorR = NULL; + pContext->mCompressor = NULL; ret = LE_init(pContext); if (ret < 0) { ALOGW("LELib_Create() init failed"); @@ -242,13 +235,9 @@ int LELib_Release(effect_handle_t handle) { return -EINVAL; } pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED; - if (pContext->mCompressorL != NULL) { - delete pContext->mCompressorL; - pContext->mCompressorL = NULL; - } - if (pContext->mCompressorR != NULL) { - delete pContext->mCompressorR; - pContext->mCompressorR = NULL; + if (pContext->mCompressor != NULL) { + delete pContext->mCompressor; + pContext->mCompressor = NULL; } delete pContext; @@ -293,11 +282,14 @@ int LE_process( //ALOGV("LE about to process %d samples", inBuffer->frameCount); uint16_t inIdx; float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f); + float leftSample, rightSample; for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) { - inBuffer->s16[2*inIdx] = pContext->mCompressorL->Compress( - inputAmp * (float)inBuffer->s16[2*inIdx]); - inBuffer->s16[2*inIdx +1] = pContext->mCompressorR->Compress( - inputAmp * (float)inBuffer->s16[2*inIdx +1]); + // makeup gain is applied on the input of the compressor + leftSample = inputAmp * (float)inBuffer->s16[2*inIdx]; + rightSample = inputAmp * (float)inBuffer->s16[2*inIdx +1]; + pContext->mCompressor->Compress(&leftSample, &rightSample); + inBuffer->s16[2*inIdx] = (int16_t) leftSample; + inBuffer->s16[2*inIdx +1] = (int16_t) rightSample; } if (inBuffer->raw != outBuffer->raw) { diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h index fed8c2a..da75ceb 100644 --- a/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h +++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h @@ -35,7 +35,7 @@ inline void AdaptiveDynamicRangeCompression::set_knee_threshold_via_target_gain( float target_gain) { const float decibel = target_gain_to_knee_threshold_.Interpolate( target_gain); - ALOGE("set_knee_threshold_via_target_gain: decibel =%.3f", decibel); + ALOGV("set_knee_threshold_via_target_gain: decibel =%.3fdB", decibel); set_knee_threshold(decibel); } diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp index 2bbd043..7bd068e 100644 --- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp +++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp @@ -102,5 +102,40 @@ float AdaptiveDynamicRangeCompression::Compress(float x) { return x; } +void AdaptiveDynamicRangeCompression::Compress(float *x1, float *x2) { + // Taking the maximum amplitude of both channels + const float max_abs_x = std::max(std::fabs(*x1), + std::max(std::fabs(*x2), kMinLogAbsValue)); + const float max_abs_x_dB = math::fast_log(max_abs_x); + // Subtract Threshold from log-encoded input to get the amount of overshoot + const float overshoot = max_abs_x_dB - knee_threshold_; + // Hard half-wave rectifier + const float rect = std::max(overshoot, 0.0f); + // Multiply rectified overshoot with slope + const float cv = rect * slope_; + const float prev_state = state_; + if (cv <= state_) { + state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv; + } else { + state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv; + } + compressor_gain_ *= + math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state); + *x1 *= compressor_gain_; + if (*x1 > kFixedPointLimit) { + *x1 = kFixedPointLimit; + } + if (*x1 < -kFixedPointLimit) { + *x1 = -kFixedPointLimit; + } + *x2 *= compressor_gain_; + if (*x2 > kFixedPointLimit) { + *x2 = kFixedPointLimit; + } + if (*x2 < -kFixedPointLimit) { + *x2 = -kFixedPointLimit; + } +} + } // namespace le_fx diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h index 4c015df..2821a78 100644 --- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.h +++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h @@ -55,6 +55,9 @@ class AdaptiveDynamicRangeCompression { // log(.) and exp(.). float Compress(float x); + // Stereo channel version of the compressor + void Compress(float *x1, float *x2); + // This version is slower than Compress(.) but faster than CompressSlow(.) float CompressNormalSpeed(float x); -- cgit v1.1 From 465da60d885c8fa4e7cea4626478574ce17a54a9 Mon Sep 17 00:00:00 2001 From: Johann Date: Thu, 26 Sep 2013 17:37:51 -0700 Subject: Indicate sync frames returned by encoder Set the appropriate OMX flag when the encoder generates a keyframe. This is necessary for any muxer which needs to indicate which frames are seekable. Bug: 8422347 Change-Id: I744a0b3023db24d3de2210bce82f41e50d259505 --- media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index 5f2b5c8..16f0f30 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -803,6 +803,8 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) { outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts; outputBufferHeader->nFlags = 0; + if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY) + outputBufferHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; outputBufferHeader->nOffset = 0; outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz; memcpy(outputBufferHeader->pBuffer, -- cgit v1.1 From 91b0ca1a5bea44dd9b5196910186dd2927821994 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 26 Sep 2013 17:23:10 -0700 Subject: fix playback position after switching to offload After switching from offloaded track to PCM track while paused (e.g. when connecting A2DP), playback restarts from the beginning of the song when resuming. Save current position before recreating an AudioPlayer in AwesomePlayer::play_l() and seek to the saved position before starting playback. Also fix a problem where the position is not reported properly by AudioPlayer if a seek is pending and queried just after start and before the first buffer is read from the MediaSource. Bug: 8174034. Change-Id: I254e65418ff903a9bf2e2111b89a00e2e54876c5 --- media/libstagefright/AudioPlayer.cpp | 34 +++++++++++++++++++++------------- media/libstagefright/AwesomePlayer.cpp | 7 +++++++ 2 files changed, 28 insertions(+), 13 deletions(-) (limited to 'media') diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index e38e261..a8a8786 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -363,6 +363,7 @@ void AudioPlayer::reset() { mPositionTimeMediaUs = -1; mPositionTimeRealUs = -1; mSeeking = false; + mSeekTimeUs = 0; mReachedEOS = false; mFinalStatus = OK; mStarted = false; @@ -602,15 +603,24 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { // need to adjust the mStartPosUs for offload decoding since parser // might not be able to get the exact seek time requested. - if (refreshSeekTime && useOffload()) { - if (postSeekComplete) { - ALOGV("fillBuffer is going to post SEEK_COMPLETE"); - mObserver->postAudioSeekComplete(); - postSeekComplete = false; - } + if (refreshSeekTime) { + if (useOffload()) { + if (postSeekComplete) { + ALOGV("fillBuffer is going to post SEEK_COMPLETE"); + mObserver->postAudioSeekComplete(); + postSeekComplete = false; + } - mStartPosUs = mPositionTimeMediaUs; - ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6); + mStartPosUs = mPositionTimeMediaUs; + ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6); + } + // clear seek time with mLock locked and once we have valid mPositionTimeMediaUs + // and mPositionTimeRealUs + // before clearing mSeekTimeUs check if a new seek request has been received while + // we were reading from the source with mLock released. + if (!mSeeking) { + mSeekTimeUs = 0; + } } if (!useOffload()) { @@ -741,12 +751,10 @@ int64_t AudioPlayer::getMediaTimeUs() { return mPositionTimeRealUs; } - if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) { - if (mSeeking) { - return mSeekTimeUs; - } - return 0; + if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) { + // mSeekTimeUs is either seek time while seeking or 0 if playback did not start. + return mSeekTimeUs; } int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs; diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 5fbee7e..9b0c69a 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -927,6 +927,9 @@ status_t AwesomePlayer::play_l() { if ((err != OK) && mOffloadAudio) { ALOGI("play_l() cannot create offload output, fallback to sw decode"); + int64_t curTimeUs; + getPosition(&curTimeUs); + delete mAudioPlayer; mAudioPlayer = NULL; // if the player was started it will take care of stopping the source when destroyed @@ -942,6 +945,10 @@ status_t AwesomePlayer::play_l() { if (err != OK) { mAudioSource.clear(); } else { + mSeekNotificationSent = true; + if (mExtractorFlags & MediaExtractor::CAN_SEEK) { + seekTo_l(curTimeUs); + } createAudioPlayer_l(); err = startAudioPlayer_l(false); } -- cgit v1.1 From 42c5ae81036d4002da3fe1e3b1016131ba737e74 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 27 Sep 2013 12:13:52 -0700 Subject: Prevent onPrepared callback from being called twice b/10891995 Change-Id: If6845c832d114629282f0b03f904a37c3325208e --- media/libstagefright/AwesomePlayer.cpp | 36 +++++++++++++++------------- media/libstagefright/include/AwesomePlayer.h | 2 ++ 2 files changed, 21 insertions(+), 17 deletions(-) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 5fbee7e..bcf9cd3 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -259,6 +259,7 @@ void AwesomePlayer::cancelPlayerEvents(bool keepNotifications) { mQueue.cancelEvent(mBufferingEvent->eventID()); mBufferingEventPending = false; + mAudioTearDown = false; } } @@ -2301,6 +2302,7 @@ void AwesomePlayer::abortPrepare(status_t err) { modifyFlags((PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED), CLEAR); mAsyncPrepareEvent = NULL; mPreparedCondition.broadcast(); + mAudioTearDown = false; } // static @@ -2374,6 +2376,20 @@ void AwesomePlayer::finishAsyncPrepare_l() { modifyFlags(PREPARED, SET); mAsyncPrepareEvent = NULL; mPreparedCondition.broadcast(); + + if (mAudioTearDown) { + if (mPrepareResult == OK) { + if (mExtractorFlags & MediaExtractor::CAN_SEEK) { + seekTo_l(mAudioTearDownPosition); + } + + if (mAudioTearDownWasPlaying) { + modifyFlags(CACHE_UNDERRUN, CLEAR); + play_l(); + } + } + mAudioTearDown = false; + } } uint32_t AwesomePlayer::flags() const { @@ -2791,7 +2807,7 @@ void AwesomePlayer::onAudioTearDownEvent() { ALOGV("onAudioTearDownEvent"); // stream info is cleared by reset_l() so copy what we need - const bool wasPlaying = (mFlags & PLAYING); + mAudioTearDownWasPlaying = (mFlags & PLAYING); KeyedVector uriHeaders(mUriHeaders); sp fileSource(mFileSource); @@ -2800,8 +2816,7 @@ void AwesomePlayer::onAudioTearDownEvent() { mStatsLock.unlock(); // get current position so we can start recreated stream from here - int64_t position = 0; - getPosition(&position); + getPosition(&mAudioTearDownPosition); // Reset and recreate reset_l(); @@ -2825,21 +2840,8 @@ void AwesomePlayer::onAudioTearDownEvent() { mAudioTearDown = true; mIsAsyncPrepare = true; - // Call parepare for the host decoding + // Call prepare for the host decoding beginPrepareAsync_l(); - - if (mPrepareResult == OK) { - if (mExtractorFlags & MediaExtractor::CAN_SEEK) { - seekTo_l(position); - } - - if (wasPlaying) { - modifyFlags(CACHE_UNDERRUN, CLEAR); - play_l(); - } - } - - mAudioTearDown = false; } } // namespace android diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index b001cf4..271df8e 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -342,6 +342,8 @@ private: bool mOffloadAudio; bool mAudioTearDown; + bool mAudioTearDownWasPlaying; + int64_t mAudioTearDownPosition; status_t setVideoScalingMode(int32_t mode); status_t setVideoScalingMode_l(int32_t mode); -- cgit v1.1 From 5b8ce24b849f6cd5629b4ba508f7c78d6227d250 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 27 Sep 2013 14:50:48 -0700 Subject: Revert "Effect Offload Proxy for effects offload" This reverts commit 60c60df7db278d2fa5c90b0fa14f99a61d50272b. Change-Id: Iafba9e02a9f3bfde6248d802e96c4e649686a87d --- media/libeffects/data/audio_effects.conf | 2 +- media/libeffects/proxy/Android.mk | 34 ---- media/libeffects/proxy/EffectProxy.cpp | 338 ------------------------------- media/libeffects/proxy/EffectProxy.h | 80 -------- 4 files changed, 1 insertion(+), 453 deletions(-) delete mode 100644 media/libeffects/proxy/Android.mk delete mode 100644 media/libeffects/proxy/EffectProxy.cpp delete mode 100644 media/libeffects/proxy/EffectProxy.h (limited to 'media') diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index c3c4b67..f1c5f5b 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -10,7 +10,7 @@ libraries { # the HW and SW effects #proxy { - #path /system/lib/soundfx/libeffectproxy.so + #path /system/lib/soundfx/libProxy.so #} # This is the SW implementation library of the effect diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk deleted file mode 100644 index 01b3be1..0000000 --- a/media/libeffects/proxy/Android.mk +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2013 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. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE:= libeffectproxy -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx -LOCAL_MODULE_TAGS := optional - - -LOCAL_SRC_FILES := \ - EffectProxy.cpp - -LOCAL_CFLAGS+= -fvisibility=hidden - -LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libdl libeffects - -LOCAL_C_INCLUDES := \ - system/media/audio_effects/include \ - bionic/libc/include - -include $(BUILD_SHARED_LIBRARY) - diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp deleted file mode 100644 index b3304b7..0000000 --- a/media/libeffects/proxy/EffectProxy.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "EffectProxy" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { -// This is a dummy proxy descriptor just to return to Factory during the initial -// GetDescriptor call. Later in the factory, it is replaced with the -// SW sub effect descriptor -const effect_descriptor_t gProxyDescriptor = { - EFFECT_UUID_INITIALIZER, // type - EFFECT_UUID_INITIALIZER, // uuid - EFFECT_CONTROL_API_VERSION, //version of effect control API - (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST | - EFFECT_FLAG_VOLUME_CTRL), // effect capability flags - 0, // CPU load - 1, // Data memory - "Proxy", //effect name - "AOSP", //implementor name -}; - - -static const effect_descriptor_t *const gDescriptors[] = -{ - &gProxyDescriptor, -}; - - -int EffectProxyCreate(const effect_uuid_t *uuid, - int32_t sessionId, - int32_t ioId, - effect_handle_t *pHandle) { - - effect_descriptor_t* desc; - EffectContext* pContext; - if (pHandle == NULL || uuid == NULL) { - ALOGE("EffectProxyCreate() called with NULL pointer"); - return -EINVAL; - } - ALOGV("EffectProxyCreate start.."); - pContext = new EffectContext; - pContext->sessionId = sessionId; - pContext->ioId = ioId; - pContext->uuid = *uuid; - pContext->common_itfe = &gEffectInterface; - - // The sub effects will be created in effect_command when the first command - // for the effect is received - pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL; - - // Get the HW and SW sub effect descriptors from the effects factory - desc = new effect_descriptor_t[SUB_FX_COUNT]; - pContext->desc = new effect_descriptor_t[SUB_FX_COUNT]; - int retValue = EffectGetSubEffects(uuid, desc, - sizeof(effect_descriptor_t) * SUB_FX_COUNT); - // EffectGetSubEffects returns the number of sub-effects copied. - if (retValue != SUB_FX_COUNT) { - ALOGE("EffectCreate() could not get the sub effects"); - delete desc; - delete pContext->desc; - return -EINVAL; - } - // Check which is the HW descriptor and copy the descriptors - // to the Context desc array - // Also check if there is only one HW and one SW descriptor. - // HW descriptor alone has the HW_TUNNEL flag. - if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && - !(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { - pContext->desc[SUB_FX_OFFLOAD] = desc[0]; - pContext->desc[SUB_FX_HOST] = desc[1]; - } - else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && - !(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { - pContext->desc[SUB_FX_HOST] = desc[0]; - pContext->desc[SUB_FX_OFFLOAD] = desc[1]; - } - delete desc; -#if (LOG_NDEBUG == 0) - effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid; - ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X" - "%02X%02X\n",uuid_print.timeLow, uuid_print.timeMid, - uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0], - uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], - uuid_print.node[4], uuid_print.node[5]); - ALOGV("EffectCreate() UUID of OFFLOAD: %08X-%04X-%04X-%04X-%02X%02X%02X%02X" - "%02X%02X\n", uuid_print.timeLow, uuid_print.timeMid, - uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0], - uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], - uuid_print.node[4], uuid_print.node[5]); -#endif - - pContext->replySize = PROXY_REPLY_SIZE_DEFAULT; - pContext->replyData = (char *)malloc(PROXY_REPLY_SIZE_DEFAULT); - - *pHandle = (effect_handle_t)pContext; - ALOGV("EffectCreate end"); - return 0; -} //end EffectProxyCreate - -int EffectProxyRelease(effect_handle_t handle) { - EffectContext * pContext = (EffectContext *)handle; - if (pContext == NULL) { - ALOGV("ERROR : EffectRelease called with NULL pointer"); - return -EINVAL; - } - ALOGV("EffectRelease"); - delete pContext->desc; - free(pContext->replyData); - - if (pContext->eHandle[SUB_FX_HOST]) - EffectRelease(pContext->eHandle[SUB_FX_HOST]); - if (pContext->eHandle[SUB_FX_OFFLOAD]) - EffectRelease(pContext->eHandle[SUB_FX_OFFLOAD]); - delete pContext; - pContext = NULL; - return 0; -} /*end EffectProxyRelease */ - -int EffectProxyGetDescriptor(const effect_uuid_t *uuid, - effect_descriptor_t *pDescriptor) { - const effect_descriptor_t *desc = NULL; - - if (pDescriptor == NULL || uuid == NULL) { - ALOGV("EffectGetDescriptor() called with NULL pointer"); - return -EINVAL; - } - desc = &gProxyDescriptor; - *pDescriptor = *desc; - return 0; -} /* end EffectProxyGetDescriptor */ - -/* Effect Control Interface Implementation: Process */ -int Effect_process(effect_handle_t self, - audio_buffer_t *inBuffer, - audio_buffer_t *outBuffer) { - - EffectContext *pContext = (EffectContext *) self; - int ret = 0; - if (pContext != NULL) { - int index = pContext->index; - // if the index refers to HW , do not do anything. Just return. - if (index == SUB_FX_HOST) { - ret = (*pContext->eHandle[index])->process(pContext->eHandle[index], - inBuffer, outBuffer); - } - } - return ret; -} /* end Effect_process */ - -/* Effect Control Interface Implementation: Command */ -int Effect_command(effect_handle_t self, - uint32_t cmdCode, - uint32_t cmdSize, - void *pCmdData, - uint32_t *replySize, - void *pReplyData) { - - EffectContext *pContext = (EffectContext *) self; - int status = 0; - if (pContext == NULL) { - ALOGV("Effect_command() Proxy context is NULL"); - return -EINVAL; - } - if (pContext->eHandle[SUB_FX_HOST] == NULL) { - ALOGV("Effect_command() Calling HOST EffectCreate"); - status = EffectCreate(&pContext->desc[SUB_FX_HOST].uuid, - pContext->sessionId, pContext->ioId, - &(pContext->eHandle[SUB_FX_HOST])); - if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) { - ALOGV("Effect_command() Error creating SW sub effect"); - return status; - } - } - if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) { - ALOGV("Effect_command() Calling OFFLOAD EffectCreate"); - status = EffectCreate(&pContext->desc[SUB_FX_OFFLOAD].uuid, - pContext->sessionId, pContext->ioId, - &(pContext->eHandle[SUB_FX_OFFLOAD])); - if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) { - ALOGV("Effect_command() Error creating HW effect"); - // Do not return error here as SW effect is created - // Return error if the CMD_OFFLOAD sends the index as OFFLOAD - } - pContext->index = SUB_FX_HOST; - } - // EFFECT_CMD_OFFLOAD used to (1) send whether the thread is offload or not - // (2) Send the ioHandle of the effectThread when the effect - // is moved from one type of thread to another. - // pCmdData points to a memory holding effect_offload_param_t structure - if (cmdCode == EFFECT_CMD_OFFLOAD) { - ALOGV("Effect_command() cmdCode = EFFECT_CMD_OFFLOAD"); - if (cmdSize == 0 || pCmdData == NULL) { - ALOGV("effectsOffload: Effect_command: CMD_OFFLOAD has no data"); - *(int*)pReplyData = FAILED_TRANSACTION; - return FAILED_TRANSACTION; - } - effect_offload_param_t* offloadParam = (effect_offload_param_t*)pCmdData; - // Assign the effect context index based on isOffload field of the structure - pContext->index = offloadParam->isOffload ? SUB_FX_OFFLOAD : SUB_FX_HOST; - // if the index is HW and the HW effect is unavailable, return error - // and reset the index to SW - if (pContext->eHandle[pContext->index] == NULL) { - ALOGV("Effect_command()CMD_OFFLOAD sub effect unavailable"); - *(int*)pReplyData = FAILED_TRANSACTION; - return FAILED_TRANSACTION; - } - pContext->ioId = offloadParam->ioHandle; - ALOGV("Effect_command()CMD_OFFLOAD index:%d io %d", pContext->index, pContext->ioId); - // Update the DSP wrapper with the new ioHandle. - // Pass the OFFLOAD command to the wrapper. - // The DSP wrapper needs to handle this CMD - if (pContext->eHandle[SUB_FX_OFFLOAD]) - status = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( - pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, - pCmdData, replySize, pReplyData); - return status; - } - - int index = pContext->index; - if (index != SUB_FX_HOST && index != SUB_FX_OFFLOAD) { - ALOGV("Effect_command: effect index is neither offload nor host"); - return -EINVAL; - } - - // Getter commands are only sent to the active sub effect. - int *subStatus[SUB_FX_COUNT]; - uint32_t *subReplySize[SUB_FX_COUNT]; - void *subReplyData[SUB_FX_COUNT]; - uint32_t tmpSize; - int tmpStatus; - - // grow temp reply buffer if needed - if (replySize != NULL) { - tmpSize = pContext->replySize; - while (tmpSize < *replySize && tmpSize < PROXY_REPLY_SIZE_MAX) { - tmpSize *= 2; - } - if (tmpSize > pContext->replySize) { - ALOGV("Effect_command grow reply buf to %d", tmpSize); - pContext->replyData = (char *)realloc(pContext->replyData, tmpSize); - pContext->replySize = tmpSize; - } - if (tmpSize > *replySize) { - tmpSize = *replySize; - } - } else { - tmpSize = 0; - } - // tmpSize is now the actual reply size for the non active sub effect - - // Send command to sub effects. The command is sent to all sub effects so that their internal - // state is kept in sync. - // Only the reply from the active sub effect is returned to the caller. The reply from the - // other sub effect is lost in pContext->replyData - for (int i = 0; i < SUB_FX_COUNT; i++) { - if (pContext->eHandle[i] == NULL) { - continue; - } - if (i == index) { - subStatus[i] = &status; - subReplySize[i] = replySize; - subReplyData[i] = pReplyData; - } else { - subStatus[i] = &tmpStatus; - subReplySize[i] = replySize == NULL ? NULL : &tmpSize; - subReplyData[i] = pReplyData == NULL ? NULL : pContext->replyData; - } - *subStatus[i] = (*pContext->eHandle[i])->command( - pContext->eHandle[i], cmdCode, cmdSize, - pCmdData, subReplySize[i], subReplyData[i]); - } - - return status; -} /* end Effect_command */ - - -/* Effect Control Interface Implementation: get_descriptor */ -int Effect_getDescriptor(effect_handle_t self, - effect_descriptor_t *pDescriptor) { - - EffectContext * pContext = (EffectContext *) self; - const effect_descriptor_t *desc; - - ALOGV("Effect_getDescriptor"); - if (pContext == NULL || pDescriptor == NULL) { - ALOGV("Effect_getDescriptor() invalid param"); - return -EINVAL; - } - if (pContext->desc == NULL) { - ALOGV("Effect_getDescriptor() could not get descriptor"); - return -EINVAL; - } - desc = &pContext->desc[SUB_FX_HOST]; - *pDescriptor = *desc; - pDescriptor->uuid = pContext->uuid; // Replace the uuid with the Proxy UUID - // Also set/clear the EFFECT_FLAG_OFFLOAD_SUPPORTED flag based on the sub effects availability - if (pContext->eHandle[SUB_FX_OFFLOAD] != NULL) - pDescriptor->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; - else - pDescriptor->flags &= ~EFFECT_FLAG_OFFLOAD_SUPPORTED; - return 0; -} /* end Effect_getDescriptor */ - -} // namespace android - -__attribute__ ((visibility ("default"))) -audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { - tag : AUDIO_EFFECT_LIBRARY_TAG, - version : EFFECT_LIBRARY_API_VERSION, - name : "Effect Proxy", - implementor : "AOSP", - create_effect : android::EffectProxyCreate, - release_effect : android::EffectProxyRelease, - get_descriptor : android::EffectProxyGetDescriptor, -}; diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h deleted file mode 100644 index acbe17e..0000000 --- a/media/libeffects/proxy/EffectProxy.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -namespace android { -enum { - SUB_FX_HOST, // Index of HOST in the descriptor and handle arrays - // of the Proxy context - SUB_FX_OFFLOAD, // Index of OFFLOAD in the descriptor and handle arrays - // of the Proxy context - SUB_FX_COUNT // The number of sub effects for a Proxy(1 HW, 1 SW) -}; -#if __cplusplus -extern "C" { -#endif - -int EffectProxyCreate(const effect_uuid_t *uuid, - int32_t sessionId, - int32_t ioId, - effect_handle_t *pHandle); -int EffectProxyRelease(effect_handle_t handle); -int EffectProxyGetDescriptor(const effect_uuid_t *uuid, - effect_descriptor_t *pDescriptor); -/* Effect Control Interface Implementation: Process */ -int Effect_process(effect_handle_t self, - audio_buffer_t *inBuffer, - audio_buffer_t *outBuffer); - -/* Effect Control Interface Implementation: Command */ -int Effect_command(effect_handle_t self, - uint32_t cmdCode, - uint32_t cmdSize, - void *pCmdData, - uint32_t *replySize, - void *pReplyData); -int Effect_getDescriptor(effect_handle_t self, - effect_descriptor_t *pDescriptor); - -const struct effect_interface_s gEffectInterface = { - Effect_process, - Effect_command, - Effect_getDescriptor, - NULL, -}; - -#define PROXY_REPLY_SIZE_MAX (64 * 1024) // must be power of two -#define PROXY_REPLY_SIZE_DEFAULT 32 // must be power of two - -struct EffectContext { - const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy - effect_descriptor_t* desc; // Points to the sub effect descriptors - effect_handle_t eHandle[SUB_FX_COUNT]; // The effect handles of the sub effects - int index; // The index that is currently active - HOST or OFFLOAD - int32_t sessionId; // The sessiond in which the effect is created. - // Stored in context to pass on to sub effect creation - int32_t ioId; // The ioId in which the effect is created. - // Stored in context to pass on to sub effect creation - effect_uuid_t uuid; // UUID of the Proxy - char* replyData; // temporary buffer for non active sub effect command reply - uint32_t replySize; // current size of temporary reply buffer -}; - -#if __cplusplus -} // extern "C" -#endif -} //namespace android -- cgit v1.1 From 83f400056ac913250f0926326ff78697c68d18a1 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 27 Sep 2013 14:53:24 -0700 Subject: Revert "Effects Factory changes for effects offload" This reverts commit 284c17e73bbff51cb5b1adcee98386d47733757a. Change-Id: I31db21e1ad4758b21356bfe4c4c64f15b2da8737 --- media/libeffects/data/audio_effects.conf | 39 ------ media/libeffects/factory/EffectsFactory.c | 218 +----------------------------- media/libeffects/factory/EffectsFactory.h | 19 --- 3 files changed, 2 insertions(+), 274 deletions(-) (limited to 'media') diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index f1c5f5b..0c3c687 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -6,23 +6,6 @@ # } # } libraries { -# This is a proxy library that will be an abstraction for -# the HW and SW effects - - #proxy { - #path /system/lib/soundfx/libProxy.so - #} - -# This is the SW implementation library of the effect - #libSW { - #path /system/lib/soundfx/libswwrapper.so - #} - -# This is the HW implementation library for the effect - #libHW { - #path /system/lib/soundfx/libhwwrapper.so - #} - bundle { path /system/lib/soundfx/libbundlewrapper.so } @@ -63,28 +46,6 @@ libraries { # } effects { - -# additions for the proxy implementation -# Proxy implementation - #effectname { - #library proxy - #uuid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - - # SW implemetation of the effect. Added as a node under the proxy to - # indicate this as a sub effect. - #libsw { - #library libSW - #uuid yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy - #} End of SW effect - - # HW implementation of the effect. Added as a node under the proxy to - # indicate this as a sub effect. - #libhw { - #library libHW - #uuid zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz - #}End of HW effect - #} End of effect proxy - bassboost { library bundle uuid 8631f300-72e2-11df-b57e-0002a5d5c51b diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c index f8d6041..f158929 100644 --- a/media/libeffects/factory/EffectsFactory.c +++ b/media/libeffects/factory/EffectsFactory.c @@ -28,9 +28,6 @@ static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries -// list of effect_descriptor and list of sub effects : all currently loaded -// It does not contain effects without sub effects. -static list_sub_elem_t *gSubEffectList; static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList static uint32_t gNumEffects; // total number number of effects static list_elem_t *gCurLib; // current library in enumeration process @@ -53,8 +50,6 @@ static int loadLibraries(cnode *root); static int loadLibrary(cnode *root, const char *name); static int loadEffects(cnode *root); static int loadEffect(cnode *node); -// To get and add the effect pointed by the passed node to the gSubEffectList -static int addSubEffect(cnode *root); static lib_entry_t *getLibrary(const char *path); static void resetEffectEnumeration(); static uint32_t updateNumEffects(); @@ -62,10 +57,6 @@ static int findEffect(const effect_uuid_t *type, const effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc); -// To search a subeffect in the gSubEffectList -int findSubEffect(const effect_uuid_t *uuid, - lib_entry_t **lib, - effect_descriptor_t **desc); static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len); static int stringToUuid(const char *str, effect_uuid_t *uuid); static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen); @@ -296,12 +287,7 @@ int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, eff ret = findEffect(NULL, uuid, &l, &d); if (ret < 0){ - // Sub effects are not associated with the library->effects, - // so, findEffect will fail. Search for the effect in gSubEffectList. - ret = findSubEffect(uuid, &l, &d); - if (ret < 0 ) { - goto exit; - } + goto exit; } // create effect in library @@ -368,27 +354,21 @@ int EffectRelease(effect_handle_t handle) } if (e1 == NULL) { ret = -ENOENT; - pthread_mutex_unlock(&gLibLock); goto exit; } // release effect in library if (fx->lib == NULL) { ALOGW("EffectRelease() fx %p library already unloaded", handle); - pthread_mutex_unlock(&gLibLock); } else { pthread_mutex_lock(&fx->lib->lock); - // Releasing the gLibLock here as the list access is over as the - // effect is removed from the list. - // If the gLibLock is not released, we will have a deadlock situation - // since we call the sub effect release inside the EffectRelease of Proxy - pthread_mutex_unlock(&gLibLock); fx->lib->desc->release_effect(fx->subItfe); pthread_mutex_unlock(&fx->lib->lock); } free(fx); exit: + pthread_mutex_unlock(&gLibLock); return ret; } @@ -400,49 +380,6 @@ int EffectIsNullUuid(const effect_uuid_t *uuid) return 1; } -// Function to get the sub effect descriptors of the effect whose uuid -// is pointed by the first argument. It searches the gSubEffectList for the -// matching uuid and then copies the corresponding sub effect descriptors -// to the inout param -int EffectGetSubEffects(const effect_uuid_t *uuid, - effect_descriptor_t *pDescriptors, size_t size) -{ - ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X" - "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, - uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2], - uuid->node[3],uuid->node[4],uuid->node[5]); - - // Check if the size of the desc buffer is large enough for 2 subeffects - if ((uuid == NULL) || (pDescriptors == NULL) || - (size < 2*sizeof(effect_descriptor_t))) { - ALOGW("NULL pointer or insufficient memory. Cannot query subeffects"); - return -EINVAL; - } - int ret = init(); - if (ret < 0) - return ret; - list_sub_elem_t *e = gSubEffectList; - sub_effect_entry_t *subeffect; - effect_descriptor_t *d; - int count = 0; - while (e != NULL) { - d = (effect_descriptor_t*)e->object; - if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) { - ALOGV("EffectGetSubEffects: effect found in the list"); - list_elem_t *subefx = e->sub_elem; - while (subefx != NULL) { - subeffect = (sub_effect_entry_t*)subefx->object; - d = (effect_descriptor_t*)(subeffect->object); - pDescriptors[count++] = *d; - subefx = subefx->next; - } - ALOGV("EffectGetSubEffects end - copied the sub effect descriptors"); - return count; - } - e = e->next; - } - return -ENOENT; -} ///////////////////////////////////////////////// // Local functions ///////////////////////////////////////////////// @@ -566,65 +503,6 @@ error: return -EINVAL; } -// This will find the library and UUID tags of the sub effect pointed by the -// node, gets the effect descriptor and lib_entry_t and adds the subeffect - -// sub_entry_t to the gSubEffectList -int addSubEffect(cnode *root) -{ - ALOGV("addSubEffect"); - cnode *node; - effect_uuid_t uuid; - effect_descriptor_t *d; - lib_entry_t *l; - list_elem_t *e; - node = config_find(root, LIBRARY_TAG); - if (node == NULL) { - return -EINVAL; - } - l = getLibrary(node->value); - if (l == NULL) { - ALOGW("addSubEffect() could not get library %s", node->value); - return -EINVAL; - } - node = config_find(root, UUID_TAG); - if (node == NULL) { - return -EINVAL; - } - if (stringToUuid(node->value, &uuid) != 0) { - ALOGW("addSubEffect() invalid uuid %s", node->value); - return -EINVAL; - } - d = malloc(sizeof(effect_descriptor_t)); - if (l->desc->get_descriptor(&uuid, d) != 0) { - char s[40]; - uuidToString(&uuid, s, 40); - ALOGW("Error querying effect %s on lib %s", s, l->name); - free(d); - return -EINVAL; - } -#if (LOG_NDEBUG==0) - char s[256]; - dumpEffectDescriptor(d, s, 256); - ALOGV("addSubEffect() read descriptor %p:%s",d, s); -#endif - if (EFFECT_API_VERSION_MAJOR(d->apiVersion) != - EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) { - ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name); - free(d); - return -EINVAL; - } - sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t)); - sub_effect->object = d; - // lib_entry_t is stored since the sub effects are not linked to the library - sub_effect->lib = l; - e = malloc(sizeof(list_elem_t)); - e->object = sub_effect; - e->next = gSubEffectList->sub_elem; - gSubEffectList->sub_elem = e; - ALOGV("addSubEffect end"); - return 0; -} - int loadEffects(cnode *root) { cnode *node; @@ -693,101 +571,9 @@ int loadEffect(cnode *root) e->next = l->effects; l->effects = e; - // After the UUID node in the config_tree, if node->next is valid, - // that would be sub effect node. - // Find the sub effects and add them to the gSubEffectList - node = node->next; - int count = 2; - bool hwSubefx = false, swSubefx = false; - list_sub_elem_t *sube = NULL; - if (node != NULL) { - ALOGV("Adding the effect to gEffectSubList as there are sub effects"); - sube = malloc(sizeof(list_sub_elem_t)); - sube->object = d; - sube->sub_elem = NULL; - sube->next = gSubEffectList; - gSubEffectList = sube; - } - while (node != NULL && count) { - if (addSubEffect(node)) { - ALOGW("loadEffect() could not add subEffect %s", node->value); - // Change the gSubEffectList to point to older list; - gSubEffectList = sube->next; - free(sube->sub_elem);// Free an already added sub effect - sube->sub_elem = NULL; - free(sube); - return -ENOENT; - } - sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object; - effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object); - // Since we return a dummy descriptor for the proxy during - // get_descriptor call,we replace it with the correspoding - // sw effect descriptor, but with Proxy UUID - // check for Sw desc - if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) == - EFFECT_FLAG_HW_ACC_TUNNEL)) { - swSubefx = true; - *d = *subEffectDesc; - d->uuid = uuid; - ALOGV("loadEffect() Changed the Proxy desc"); - } else - hwSubefx = true; - count--; - node = node->next; - } - // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc - if (hwSubefx && swSubefx) { - d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; - } return 0; } -// Searches the sub effect matching to the specified uuid -// in the gSubEffectList. It gets the lib_entry_t for -// the matched sub_effect . Used in EffectCreate of sub effects -int findSubEffect(const effect_uuid_t *uuid, - lib_entry_t **lib, - effect_descriptor_t **desc) -{ - list_sub_elem_t *e = gSubEffectList; - list_elem_t *subefx; - sub_effect_entry_t *effect; - lib_entry_t *l = NULL; - effect_descriptor_t *d = NULL; - int found = 0; - int ret = 0; - - if (uuid == NULL) - return -EINVAL; - - while (e != NULL && !found) { - subefx = (list_elem_t*)(e->sub_elem); - while (subefx != NULL) { - effect = (sub_effect_entry_t*)subefx->object; - l = (lib_entry_t *)effect->lib; - d = (effect_descriptor_t *)effect->object; - if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) { - ALOGV("uuid matched"); - found = 1; - break; - } - subefx = subefx->next; - } - e = e->next; - } - if (!found) { - ALOGV("findSubEffect() effect not found"); - ret = -ENOENT; - } else { - ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name); - *lib = l; - if (desc != NULL) { - *desc = d; - } - } - return ret; -} - lib_entry_t *getLibrary(const char *name) { list_elem_t *e; diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h index 147ff18..c1d4319 100644 --- a/media/libeffects/factory/EffectsFactory.h +++ b/media/libeffects/factory/EffectsFactory.h @@ -32,15 +32,6 @@ typedef struct list_elem_s { struct list_elem_s *next; } list_elem_t; -// Structure used for storing effects with their sub effects. -// Used in creating gSubEffectList. Here, -// object holds the effect desc and the list sub_elem holds the sub effects -typedef struct list_sub_elem_s { - void *object; - list_elem_t *sub_elem; - struct list_sub_elem_s *next; -} list_sub_elem_t; - typedef struct lib_entry_s { audio_effect_library_t *desc; char *name; @@ -56,16 +47,6 @@ typedef struct effect_entry_s { lib_entry_t *lib; } effect_entry_t; -// Structure used to store the lib entry -// and the descriptor of the sub effects. -// The library entry is to be stored in case of -// sub effects as the sub effects are not linked -// to the library list - gLibraryList. -typedef struct sub_effect_entry_s { - lib_entry_t *lib; - void *object; -} sub_effect_entry_t; - #if __cplusplus } // extern "C" #endif -- cgit v1.1 From 2eab94f7dfd41a65e13aca379a1aed97447f8884 Mon Sep 17 00:00:00 2001 From: jpadmana Date: Tue, 4 Jun 2013 16:08:29 +0530 Subject: Effects Factory changes for effects offload audio_effects.conf - commented changes to illustrate the addition of Proxy and sub effects to the conf file Added an effectFactoryApi - EffectGetSubEffects for querying the sub effect descriptors from the factory. This api is used by the Proxy to get the sub effects Added functions and data structures in factory code for loading the sub effects gSubEffectList - has the Proxies and their corresponding sub effects - addSubEffect() - reads a sub effect node and adds to the gSubEffectList - findSubEffect() - searches through the gSubEffectList to find a SubEffect Bug: 8174034. Change-Id: Id7f6aa67c41db370d32beaf43a979ba4ac925928 Signed-off-by: jpadmana --- media/libeffects/data/audio_effects.conf | 39 ++++++ media/libeffects/factory/EffectsFactory.c | 218 +++++++++++++++++++++++++++++- media/libeffects/factory/EffectsFactory.h | 19 +++ 3 files changed, 274 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index 0c3c687..f1c5f5b 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -6,6 +6,23 @@ # } # } libraries { +# This is a proxy library that will be an abstraction for +# the HW and SW effects + + #proxy { + #path /system/lib/soundfx/libProxy.so + #} + +# This is the SW implementation library of the effect + #libSW { + #path /system/lib/soundfx/libswwrapper.so + #} + +# This is the HW implementation library for the effect + #libHW { + #path /system/lib/soundfx/libhwwrapper.so + #} + bundle { path /system/lib/soundfx/libbundlewrapper.so } @@ -46,6 +63,28 @@ libraries { # } effects { + +# additions for the proxy implementation +# Proxy implementation + #effectname { + #library proxy + #uuid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + + # SW implemetation of the effect. Added as a node under the proxy to + # indicate this as a sub effect. + #libsw { + #library libSW + #uuid yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy + #} End of SW effect + + # HW implementation of the effect. Added as a node under the proxy to + # indicate this as a sub effect. + #libhw { + #library libHW + #uuid zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz + #}End of HW effect + #} End of effect proxy + bassboost { library bundle uuid 8631f300-72e2-11df-b57e-0002a5d5c51b diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c index f158929..f8d6041 100644 --- a/media/libeffects/factory/EffectsFactory.c +++ b/media/libeffects/factory/EffectsFactory.c @@ -28,6 +28,9 @@ static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries +// list of effect_descriptor and list of sub effects : all currently loaded +// It does not contain effects without sub effects. +static list_sub_elem_t *gSubEffectList; static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList static uint32_t gNumEffects; // total number number of effects static list_elem_t *gCurLib; // current library in enumeration process @@ -50,6 +53,8 @@ static int loadLibraries(cnode *root); static int loadLibrary(cnode *root, const char *name); static int loadEffects(cnode *root); static int loadEffect(cnode *node); +// To get and add the effect pointed by the passed node to the gSubEffectList +static int addSubEffect(cnode *root); static lib_entry_t *getLibrary(const char *path); static void resetEffectEnumeration(); static uint32_t updateNumEffects(); @@ -57,6 +62,10 @@ static int findEffect(const effect_uuid_t *type, const effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc); +// To search a subeffect in the gSubEffectList +int findSubEffect(const effect_uuid_t *uuid, + lib_entry_t **lib, + effect_descriptor_t **desc); static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len); static int stringToUuid(const char *str, effect_uuid_t *uuid); static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen); @@ -287,7 +296,12 @@ int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, eff ret = findEffect(NULL, uuid, &l, &d); if (ret < 0){ - goto exit; + // Sub effects are not associated with the library->effects, + // so, findEffect will fail. Search for the effect in gSubEffectList. + ret = findSubEffect(uuid, &l, &d); + if (ret < 0 ) { + goto exit; + } } // create effect in library @@ -354,21 +368,27 @@ int EffectRelease(effect_handle_t handle) } if (e1 == NULL) { ret = -ENOENT; + pthread_mutex_unlock(&gLibLock); goto exit; } // release effect in library if (fx->lib == NULL) { ALOGW("EffectRelease() fx %p library already unloaded", handle); + pthread_mutex_unlock(&gLibLock); } else { pthread_mutex_lock(&fx->lib->lock); + // Releasing the gLibLock here as the list access is over as the + // effect is removed from the list. + // If the gLibLock is not released, we will have a deadlock situation + // since we call the sub effect release inside the EffectRelease of Proxy + pthread_mutex_unlock(&gLibLock); fx->lib->desc->release_effect(fx->subItfe); pthread_mutex_unlock(&fx->lib->lock); } free(fx); exit: - pthread_mutex_unlock(&gLibLock); return ret; } @@ -380,6 +400,49 @@ int EffectIsNullUuid(const effect_uuid_t *uuid) return 1; } +// Function to get the sub effect descriptors of the effect whose uuid +// is pointed by the first argument. It searches the gSubEffectList for the +// matching uuid and then copies the corresponding sub effect descriptors +// to the inout param +int EffectGetSubEffects(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptors, size_t size) +{ + ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X" + "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, + uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2], + uuid->node[3],uuid->node[4],uuid->node[5]); + + // Check if the size of the desc buffer is large enough for 2 subeffects + if ((uuid == NULL) || (pDescriptors == NULL) || + (size < 2*sizeof(effect_descriptor_t))) { + ALOGW("NULL pointer or insufficient memory. Cannot query subeffects"); + return -EINVAL; + } + int ret = init(); + if (ret < 0) + return ret; + list_sub_elem_t *e = gSubEffectList; + sub_effect_entry_t *subeffect; + effect_descriptor_t *d; + int count = 0; + while (e != NULL) { + d = (effect_descriptor_t*)e->object; + if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) { + ALOGV("EffectGetSubEffects: effect found in the list"); + list_elem_t *subefx = e->sub_elem; + while (subefx != NULL) { + subeffect = (sub_effect_entry_t*)subefx->object; + d = (effect_descriptor_t*)(subeffect->object); + pDescriptors[count++] = *d; + subefx = subefx->next; + } + ALOGV("EffectGetSubEffects end - copied the sub effect descriptors"); + return count; + } + e = e->next; + } + return -ENOENT; +} ///////////////////////////////////////////////// // Local functions ///////////////////////////////////////////////// @@ -503,6 +566,65 @@ error: return -EINVAL; } +// This will find the library and UUID tags of the sub effect pointed by the +// node, gets the effect descriptor and lib_entry_t and adds the subeffect - +// sub_entry_t to the gSubEffectList +int addSubEffect(cnode *root) +{ + ALOGV("addSubEffect"); + cnode *node; + effect_uuid_t uuid; + effect_descriptor_t *d; + lib_entry_t *l; + list_elem_t *e; + node = config_find(root, LIBRARY_TAG); + if (node == NULL) { + return -EINVAL; + } + l = getLibrary(node->value); + if (l == NULL) { + ALOGW("addSubEffect() could not get library %s", node->value); + return -EINVAL; + } + node = config_find(root, UUID_TAG); + if (node == NULL) { + return -EINVAL; + } + if (stringToUuid(node->value, &uuid) != 0) { + ALOGW("addSubEffect() invalid uuid %s", node->value); + return -EINVAL; + } + d = malloc(sizeof(effect_descriptor_t)); + if (l->desc->get_descriptor(&uuid, d) != 0) { + char s[40]; + uuidToString(&uuid, s, 40); + ALOGW("Error querying effect %s on lib %s", s, l->name); + free(d); + return -EINVAL; + } +#if (LOG_NDEBUG==0) + char s[256]; + dumpEffectDescriptor(d, s, 256); + ALOGV("addSubEffect() read descriptor %p:%s",d, s); +#endif + if (EFFECT_API_VERSION_MAJOR(d->apiVersion) != + EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) { + ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name); + free(d); + return -EINVAL; + } + sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t)); + sub_effect->object = d; + // lib_entry_t is stored since the sub effects are not linked to the library + sub_effect->lib = l; + e = malloc(sizeof(list_elem_t)); + e->object = sub_effect; + e->next = gSubEffectList->sub_elem; + gSubEffectList->sub_elem = e; + ALOGV("addSubEffect end"); + return 0; +} + int loadEffects(cnode *root) { cnode *node; @@ -571,9 +693,101 @@ int loadEffect(cnode *root) e->next = l->effects; l->effects = e; + // After the UUID node in the config_tree, if node->next is valid, + // that would be sub effect node. + // Find the sub effects and add them to the gSubEffectList + node = node->next; + int count = 2; + bool hwSubefx = false, swSubefx = false; + list_sub_elem_t *sube = NULL; + if (node != NULL) { + ALOGV("Adding the effect to gEffectSubList as there are sub effects"); + sube = malloc(sizeof(list_sub_elem_t)); + sube->object = d; + sube->sub_elem = NULL; + sube->next = gSubEffectList; + gSubEffectList = sube; + } + while (node != NULL && count) { + if (addSubEffect(node)) { + ALOGW("loadEffect() could not add subEffect %s", node->value); + // Change the gSubEffectList to point to older list; + gSubEffectList = sube->next; + free(sube->sub_elem);// Free an already added sub effect + sube->sub_elem = NULL; + free(sube); + return -ENOENT; + } + sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object; + effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object); + // Since we return a dummy descriptor for the proxy during + // get_descriptor call,we replace it with the correspoding + // sw effect descriptor, but with Proxy UUID + // check for Sw desc + if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) == + EFFECT_FLAG_HW_ACC_TUNNEL)) { + swSubefx = true; + *d = *subEffectDesc; + d->uuid = uuid; + ALOGV("loadEffect() Changed the Proxy desc"); + } else + hwSubefx = true; + count--; + node = node->next; + } + // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc + if (hwSubefx && swSubefx) { + d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; + } return 0; } +// Searches the sub effect matching to the specified uuid +// in the gSubEffectList. It gets the lib_entry_t for +// the matched sub_effect . Used in EffectCreate of sub effects +int findSubEffect(const effect_uuid_t *uuid, + lib_entry_t **lib, + effect_descriptor_t **desc) +{ + list_sub_elem_t *e = gSubEffectList; + list_elem_t *subefx; + sub_effect_entry_t *effect; + lib_entry_t *l = NULL; + effect_descriptor_t *d = NULL; + int found = 0; + int ret = 0; + + if (uuid == NULL) + return -EINVAL; + + while (e != NULL && !found) { + subefx = (list_elem_t*)(e->sub_elem); + while (subefx != NULL) { + effect = (sub_effect_entry_t*)subefx->object; + l = (lib_entry_t *)effect->lib; + d = (effect_descriptor_t *)effect->object; + if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) { + ALOGV("uuid matched"); + found = 1; + break; + } + subefx = subefx->next; + } + e = e->next; + } + if (!found) { + ALOGV("findSubEffect() effect not found"); + ret = -ENOENT; + } else { + ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name); + *lib = l; + if (desc != NULL) { + *desc = d; + } + } + return ret; +} + lib_entry_t *getLibrary(const char *name) { list_elem_t *e; diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h index c1d4319..147ff18 100644 --- a/media/libeffects/factory/EffectsFactory.h +++ b/media/libeffects/factory/EffectsFactory.h @@ -32,6 +32,15 @@ typedef struct list_elem_s { struct list_elem_s *next; } list_elem_t; +// Structure used for storing effects with their sub effects. +// Used in creating gSubEffectList. Here, +// object holds the effect desc and the list sub_elem holds the sub effects +typedef struct list_sub_elem_s { + void *object; + list_elem_t *sub_elem; + struct list_sub_elem_s *next; +} list_sub_elem_t; + typedef struct lib_entry_s { audio_effect_library_t *desc; char *name; @@ -47,6 +56,16 @@ typedef struct effect_entry_s { lib_entry_t *lib; } effect_entry_t; +// Structure used to store the lib entry +// and the descriptor of the sub effects. +// The library entry is to be stored in case of +// sub effects as the sub effects are not linked +// to the library list - gLibraryList. +typedef struct sub_effect_entry_s { + lib_entry_t *lib; + void *object; +} sub_effect_entry_t; + #if __cplusplus } // extern "C" #endif -- cgit v1.1 From faca05e96744dfaa2f352e3dbb29eead4e55cfa0 Mon Sep 17 00:00:00 2001 From: jpadmana Date: Tue, 4 Jun 2013 16:03:29 +0530 Subject: Effect Offload Proxy for effects offload Effect Proxy abstracts the sub effects to the upper layers. It has the following functionalities: - creation and release of sub effects - routing the effect commands and process to the appropriate sub effect Bug: 8174034. Change-Id: Iec34b61104f0bbec4ef67c62f0710a5536dc325b Signed-off-by: jpadmana --- media/libeffects/data/audio_effects.conf | 2 +- media/libeffects/proxy/Android.mk | 34 ++++ media/libeffects/proxy/EffectProxy.cpp | 298 +++++++++++++++++++++++++++++++ media/libeffects/proxy/EffectProxy.h | 75 ++++++++ 4 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 media/libeffects/proxy/Android.mk create mode 100644 media/libeffects/proxy/EffectProxy.cpp create mode 100644 media/libeffects/proxy/EffectProxy.h (limited to 'media') diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf index f1c5f5b..c3c4b67 100644 --- a/media/libeffects/data/audio_effects.conf +++ b/media/libeffects/data/audio_effects.conf @@ -10,7 +10,7 @@ libraries { # the HW and SW effects #proxy { - #path /system/lib/soundfx/libProxy.so + #path /system/lib/soundfx/libeffectproxy.so #} # This is the SW implementation library of the effect diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk new file mode 100644 index 0000000..01b3be1 --- /dev/null +++ b/media/libeffects/proxy/Android.mk @@ -0,0 +1,34 @@ +# Copyright 2013 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE:= libeffectproxy +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_TAGS := optional + + +LOCAL_SRC_FILES := \ + EffectProxy.cpp + +LOCAL_CFLAGS+= -fvisibility=hidden + +LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libdl libeffects + +LOCAL_C_INCLUDES := \ + system/media/audio_effects/include \ + bionic/libc/include + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp new file mode 100644 index 0000000..77c6e89 --- /dev/null +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "EffectProxy" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +// This is a dummy proxy descriptor just to return to Factory during the initial +// GetDescriptor call. Later in the factory, it is replaced with the +// SW sub effect descriptor +const effect_descriptor_t gProxyDescriptor = { + EFFECT_UUID_INITIALIZER, // type + EFFECT_UUID_INITIALIZER, // uuid + EFFECT_CONTROL_API_VERSION, //version of effect control API + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST | + EFFECT_FLAG_VOLUME_CTRL), // effect capability flags + 0, // CPU load + 1, // Data memory + "Proxy", //effect name + "AOSP", //implementor name +}; + + +static const effect_descriptor_t *const gDescriptors[] = +{ + &gProxyDescriptor, +}; + +int EffectProxyCreate(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle) { + + effect_descriptor_t* desc; + EffectContext* pContext; + if (pHandle == NULL || uuid == NULL) { + ALOGE("EffectProxyCreate() called with NULL pointer"); + return -EINVAL; + } + ALOGV("EffectProxyCreate start.."); + pContext = new EffectContext; + pContext->sessionId = sessionId; + pContext->ioId = ioId; + pContext->uuid = *uuid; + pContext->common_itfe = &gEffectInterface; + // The sub effects will be created in effect_command when the first command + // for the effect is received + pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL; + + // Get the HW and SW sub effect descriptors from the effects factory + desc = new effect_descriptor_t[SUB_FX_COUNT]; + pContext->desc = new effect_descriptor_t[SUB_FX_COUNT]; + int retValue = EffectGetSubEffects(uuid, desc, + sizeof(effect_descriptor_t) * SUB_FX_COUNT); + // EffectGetSubEffects returns the number of sub-effects copied. + if (retValue != SUB_FX_COUNT) { + ALOGE("EffectCreate() could not get the sub effects"); + delete desc; + delete pContext->desc; + return -EINVAL; + } + // Check which is the HW descriptor and copy the descriptors + // to the Context desc array + // Also check if there is only one HW and one SW descriptor. + // HW descriptor alone has the HW_TUNNEL flag. + if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && + !(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { + pContext->desc[SUB_FX_OFFLOAD] = desc[0]; + pContext->desc[SUB_FX_HOST] = desc[1]; + } + else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && + !(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { + pContext->desc[SUB_FX_HOST] = desc[0]; + pContext->desc[SUB_FX_OFFLOAD] = desc[1]; + } + delete desc; +#if (LOG_NDEBUG == 0) + effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid; + ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X" + "%02X%02X\n",uuid_print.timeLow, uuid_print.timeMid, + uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0], + uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], + uuid_print.node[4], uuid_print.node[5]); + ALOGV("EffectCreate() UUID of OFFLOAD: %08X-%04X-%04X-%04X-%02X%02X%02X%02X" + "%02X%02X\n", uuid_print.timeLow, uuid_print.timeMid, + uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0], + uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], + uuid_print.node[4], uuid_print.node[5]); +#endif + *pHandle = (effect_handle_t)pContext; + ALOGV("EffectCreate end"); + return 0; +} //end EffectProxyCreate + +int EffectProxyRelease(effect_handle_t handle) { + EffectContext * pContext = (EffectContext *)handle; + if (pContext == NULL) { + ALOGV("ERROR : EffectRelease called with NULL pointer"); + return -EINVAL; + } + ALOGV("EffectRelease"); + delete pContext->desc; + if (pContext->eHandle[SUB_FX_HOST]) + EffectRelease(pContext->eHandle[SUB_FX_HOST]); + if (pContext->eHandle[SUB_FX_OFFLOAD]) + EffectRelease(pContext->eHandle[SUB_FX_OFFLOAD]); + delete pContext; + pContext = NULL; + return 0; +} /*end EffectProxyRelease */ + +int EffectProxyGetDescriptor(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor) { + const effect_descriptor_t *desc = NULL; + + if (pDescriptor == NULL || uuid == NULL) { + ALOGV("EffectGetDescriptor() called with NULL pointer"); + return -EINVAL; + } + desc = &gProxyDescriptor; + *pDescriptor = *desc; + return 0; +} /* end EffectProxyGetDescriptor */ + +/* Effect Control Interface Implementation: Process */ +int Effect_process(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer) { + + EffectContext *pContext = (EffectContext *) self; + int ret = 0; + if (pContext != NULL) { + int index = pContext->index; + // if the index refers to HW , do not do anything. Just return. + if (index == SUB_FX_HOST) { + ALOGV("Calling CoreProcess"); + ret = (*pContext->eHandle[index])->process(pContext->eHandle[index], + inBuffer, outBuffer); + } + } + return ret; +} /* end Effect_process */ + +/* Effect Control Interface Implementation: Command */ +int Effect_command(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData) { + + EffectContext *pContext = (EffectContext *) self; + int status; + if (pContext == NULL) { + ALOGV("Effect_command() Proxy context is NULL"); + return -EINVAL; + } + if (pContext->eHandle[SUB_FX_HOST] == NULL) { + ALOGV("Effect_command() Calling HOST EffectCreate"); + status = EffectCreate(&pContext->desc[SUB_FX_HOST].uuid, + pContext->sessionId, pContext->ioId, + &(pContext->eHandle[SUB_FX_HOST])); + if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) { + ALOGV("Effect_command() Error creating SW sub effect"); + return status; + } + } + if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) { + ALOGV("Effect_command() Calling OFFLOAD EffectCreate"); + status = EffectCreate(&pContext->desc[SUB_FX_OFFLOAD].uuid, + pContext->sessionId, pContext->ioId, + &(pContext->eHandle[SUB_FX_OFFLOAD])); + if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) { + ALOGV("Effect_command() Error creating HW effect"); + // Do not return error here as SW effect is created + // Return error if the CMD_OFFLOAD sends the index as OFFLOAD + } + pContext->index = SUB_FX_HOST; + } + // EFFECT_CMD_OFFLOAD used to (1) send whether the thread is offload or not + // (2) Send the ioHandle of the effectThread when the effect + // is moved from one type of thread to another. + // pCmdData points to a memory holding effect_offload_param_t structure + if (cmdCode == EFFECT_CMD_OFFLOAD) { + ALOGV("Effect_command() cmdCode = EFFECT_CMD_OFFLOAD"); + if (cmdSize == 0 || pCmdData == NULL) { + ALOGV("effectsOffload: Effect_command: CMD_OFFLOAD has no data"); + *(int*)pReplyData = FAILED_TRANSACTION; + return FAILED_TRANSACTION; + } + effect_offload_param_t* offloadParam = (effect_offload_param_t*)pCmdData; + // Assign the effect context index based on isOffload field of the structure + pContext->index = offloadParam->isOffload ? SUB_FX_OFFLOAD : SUB_FX_HOST; + // if the index is HW and the HW effect is unavailable, return error + // and reset the index to SW + if (pContext->eHandle[pContext->index] == NULL) { + ALOGV("Effect_command()CMD_OFFLOAD sub effect unavailable"); + *(int*)pReplyData = FAILED_TRANSACTION; + return FAILED_TRANSACTION; + } + pContext->ioId = offloadParam->ioHandle; + ALOGV("Effect_command()CMD_OFFLOAD index:%d io %d", pContext->index, pContext->ioId); + // Update the DSP wrapper with the new ioHandle. + // Pass the OFFLOAD command to the wrapper. + // The DSP wrapper needs to handle this CMD + if (pContext->eHandle[SUB_FX_OFFLOAD]) + status = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( + pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + return status; + } + + int index = pContext->index; + if (index != SUB_FX_HOST && index != SUB_FX_OFFLOAD) { + ALOGV("Effect_command: effect index is neither offload nor host"); + return -EINVAL; + } + ALOGV("Effect_command: pContext->eHandle[%d]: %p", + index, pContext->eHandle[index]); + if (pContext->eHandle[SUB_FX_HOST]) + (*pContext->eHandle[SUB_FX_HOST])->command( + pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + if (pContext->eHandle[SUB_FX_OFFLOAD]) { + // In case of SET CMD, when the offload stream is unavailable, + // we will store the effect param values in the DSP effect wrapper. + // When the offload effects get enabled, we send these values to the + // DSP during Effect_config. + // So,we send the params to DSP wrapper also + (*pContext->eHandle[SUB_FX_OFFLOAD])->command( + pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + } + return 0; +} /* end Effect_command */ + + +/* Effect Control Interface Implementation: get_descriptor */ +int Effect_getDescriptor(effect_handle_t self, + effect_descriptor_t *pDescriptor) { + + EffectContext * pContext = (EffectContext *) self; + const effect_descriptor_t *desc; + + ALOGV("Effect_getDescriptor"); + if (pContext == NULL || pDescriptor == NULL) { + ALOGV("Effect_getDescriptor() invalid param"); + return -EINVAL; + } + if (pContext->desc == NULL) { + ALOGV("Effect_getDescriptor() could not get descriptor"); + return -EINVAL; + } + desc = &pContext->desc[SUB_FX_HOST]; + *pDescriptor = *desc; + pDescriptor->uuid = pContext->uuid; // Replace the uuid with the Proxy UUID + // Also set/clear the EFFECT_FLAG_OFFLOAD_SUPPORTED flag based on the sub effects availability + if (pContext->eHandle[SUB_FX_OFFLOAD] != NULL) + pDescriptor->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; + else + pDescriptor->flags &= ~EFFECT_FLAG_OFFLOAD_SUPPORTED; + return 0; +} /* end Effect_getDescriptor */ + +} // namespace android + +__attribute__ ((visibility ("default"))) +audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { + tag : AUDIO_EFFECT_LIBRARY_TAG, + version : EFFECT_LIBRARY_API_VERSION, + name : "Effect Proxy", + implementor : "AOSP", + create_effect : android::EffectProxyCreate, + release_effect : android::EffectProxyRelease, + get_descriptor : android::EffectProxyGetDescriptor, +}; diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h new file mode 100644 index 0000000..8992f93 --- /dev/null +++ b/media/libeffects/proxy/EffectProxy.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +namespace android { +enum { + SUB_FX_HOST, // Index of HOST in the descriptor and handle arrays + // of the Proxy context + SUB_FX_OFFLOAD, // Index of OFFLOAD in the descriptor and handle arrays + // of the Proxy context + SUB_FX_COUNT // The number of sub effects for a Proxy(1 HW, 1 SW) +}; +#if __cplusplus +extern "C" { +#endif + +int EffectProxyCreate(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle); +int EffectProxyRelease(effect_handle_t handle); +int EffectProxyGetDescriptor(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor); +/* Effect Control Interface Implementation: Process */ +int Effect_process(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer); + +/* Effect Control Interface Implementation: Command */ +int Effect_command(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData); +int Effect_getDescriptor(effect_handle_t self, + effect_descriptor_t *pDescriptor); + +const struct effect_interface_s gEffectInterface = { + Effect_process, + Effect_command, + Effect_getDescriptor, + NULL, +}; + +struct EffectContext { + const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy + effect_descriptor_t* desc; // Points to the sub effect descriptors + effect_handle_t eHandle[SUB_FX_COUNT]; // The effect handles of the sub effects + int index; // The index that is currently active - HOST or OFFLOAD + int32_t sessionId; // The sessiond in which the effect is created. + // Stored in context to pass on to sub effect creation + int32_t ioId; // The ioId in which the effect is created. + // Stored in context to pass on to sub effect creation + effect_uuid_t uuid; // UUID of the Proxy +}; + +#if __cplusplus +} // extern "C" +#endif +} //namespace android -- cgit v1.1 From eba9bf72fb5e036bb15ca4a1dc126883a2cb938d Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 27 Sep 2013 15:04:26 -0700 Subject: fix command handling in effect offload proxy Fix some issues in effect proxy related to handling of effect commands to offloaded and non offloaded effects. Also fixed a bug on capture index in software Visualizer effect. Bug: 8174034. Change-Id: I119458fea597cc3acbc0ef9ec315f67aa211cbd9 --- media/libeffects/proxy/EffectProxy.cpp | 59 +++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 11 deletions(-) (limited to 'media') diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp index 77c6e89..41640da 100644 --- a/media/libeffects/proxy/EffectProxy.cpp +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -48,6 +48,21 @@ static const effect_descriptor_t *const gDescriptors[] = &gProxyDescriptor, }; +static inline bool isGetterCmd(uint32_t cmdCode) +{ + switch (cmdCode) { + case EFFECT_CMD_GET_PARAM: + case EFFECT_CMD_GET_CONFIG: + case EFFECT_CMD_GET_CONFIG_REVERSE: + case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: + case EFFECT_CMD_GET_FEATURE_CONFIG: + return true; + default: + return false; + } +} + + int EffectProxyCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, @@ -155,7 +170,6 @@ int Effect_process(effect_handle_t self, int index = pContext->index; // if the index refers to HW , do not do anything. Just return. if (index == SUB_FX_HOST) { - ALOGV("Calling CoreProcess"); ret = (*pContext->eHandle[index])->process(pContext->eHandle[index], inBuffer, outBuffer); } @@ -172,7 +186,7 @@ int Effect_command(effect_handle_t self, void *pReplyData) { EffectContext *pContext = (EffectContext *) self; - int status; + int status = 0; if (pContext == NULL) { ALOGV("Effect_command() Proxy context is NULL"); return -EINVAL; @@ -237,23 +251,46 @@ int Effect_command(effect_handle_t self, ALOGV("Effect_command: effect index is neither offload nor host"); return -EINVAL; } - ALOGV("Effect_command: pContext->eHandle[%d]: %p", - index, pContext->eHandle[index]); - if (pContext->eHandle[SUB_FX_HOST]) - (*pContext->eHandle[SUB_FX_HOST])->command( + + // Getter commands are only sent to the active sub effect. + uint32_t hostReplySize = replySize != NULL ? *replySize : 0; + bool hostReplied = false; + int hostStatus = 0; + uint32_t offloadReplySize = replySize != NULL ? *replySize : 0; + bool offloadReplied = false; + int offloadStatus = 0; + + if (pContext->eHandle[SUB_FX_HOST] && (!isGetterCmd(cmdCode) || index == SUB_FX_HOST)) { + hostStatus = (*pContext->eHandle[SUB_FX_HOST])->command( pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize, - pCmdData, replySize, pReplyData); - if (pContext->eHandle[SUB_FX_OFFLOAD]) { + pCmdData, replySize != NULL ? &hostReplySize : NULL, pReplyData); + hostReplied = true; + } + if (pContext->eHandle[SUB_FX_OFFLOAD] && (!isGetterCmd(cmdCode) || index == SUB_FX_OFFLOAD)) { // In case of SET CMD, when the offload stream is unavailable, // we will store the effect param values in the DSP effect wrapper. // When the offload effects get enabled, we send these values to the // DSP during Effect_config. // So,we send the params to DSP wrapper also - (*pContext->eHandle[SUB_FX_OFFLOAD])->command( + offloadStatus = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, - pCmdData, replySize, pReplyData); + pCmdData, replySize != NULL ? &offloadReplySize : NULL, pReplyData); + offloadReplied = true; } - return 0; + // By convention the offloaded implementation reply is returned if command is processed by both + // host and offloaded sub effects + if (offloadReplied){ + status = offloadStatus; + if (replySize) { + *replySize = offloadReplySize; + } + } else if (hostReplied) { + status = hostStatus; + if (replySize) { + *replySize = hostReplySize; + } + } + return status; } /* end Effect_command */ -- cgit v1.1 From 5d6d86a4d102704f49b9235eaf282c428d7100b6 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 20 Sep 2013 12:27:32 -0700 Subject: fix oflload effect proxy commmand handling Implement a more generic command handling in offload effect proxy. All commands are sent to both sub effects but only the reply from the active one is returned to the caller. Bug: 8174034. Change-Id: I28aa0f0d806e846332bc29801ee40d34e4ea0c43 --- media/libeffects/proxy/EffectProxy.cpp | 95 ++++++++++++++++++---------------- media/libeffects/proxy/EffectProxy.h | 5 ++ 2 files changed, 54 insertions(+), 46 deletions(-) (limited to 'media') diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp index 41640da..b3304b7 100644 --- a/media/libeffects/proxy/EffectProxy.cpp +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -48,20 +48,6 @@ static const effect_descriptor_t *const gDescriptors[] = &gProxyDescriptor, }; -static inline bool isGetterCmd(uint32_t cmdCode) -{ - switch (cmdCode) { - case EFFECT_CMD_GET_PARAM: - case EFFECT_CMD_GET_CONFIG: - case EFFECT_CMD_GET_CONFIG_REVERSE: - case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: - case EFFECT_CMD_GET_FEATURE_CONFIG: - return true; - default: - return false; - } -} - int EffectProxyCreate(const effect_uuid_t *uuid, int32_t sessionId, @@ -80,6 +66,7 @@ int EffectProxyCreate(const effect_uuid_t *uuid, pContext->ioId = ioId; pContext->uuid = *uuid; pContext->common_itfe = &gEffectInterface; + // The sub effects will be created in effect_command when the first command // for the effect is received pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL; @@ -124,6 +111,10 @@ int EffectProxyCreate(const effect_uuid_t *uuid, uuid_print.node[1], uuid_print.node[2], uuid_print.node[3], uuid_print.node[4], uuid_print.node[5]); #endif + + pContext->replySize = PROXY_REPLY_SIZE_DEFAULT; + pContext->replyData = (char *)malloc(PROXY_REPLY_SIZE_DEFAULT); + *pHandle = (effect_handle_t)pContext; ALOGV("EffectCreate end"); return 0; @@ -137,6 +128,8 @@ int EffectProxyRelease(effect_handle_t handle) { } ALOGV("EffectRelease"); delete pContext->desc; + free(pContext->replyData); + if (pContext->eHandle[SUB_FX_HOST]) EffectRelease(pContext->eHandle[SUB_FX_HOST]); if (pContext->eHandle[SUB_FX_OFFLOAD]) @@ -253,43 +246,53 @@ int Effect_command(effect_handle_t self, } // Getter commands are only sent to the active sub effect. - uint32_t hostReplySize = replySize != NULL ? *replySize : 0; - bool hostReplied = false; - int hostStatus = 0; - uint32_t offloadReplySize = replySize != NULL ? *replySize : 0; - bool offloadReplied = false; - int offloadStatus = 0; + int *subStatus[SUB_FX_COUNT]; + uint32_t *subReplySize[SUB_FX_COUNT]; + void *subReplyData[SUB_FX_COUNT]; + uint32_t tmpSize; + int tmpStatus; - if (pContext->eHandle[SUB_FX_HOST] && (!isGetterCmd(cmdCode) || index == SUB_FX_HOST)) { - hostStatus = (*pContext->eHandle[SUB_FX_HOST])->command( - pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize, - pCmdData, replySize != NULL ? &hostReplySize : NULL, pReplyData); - hostReplied = true; - } - if (pContext->eHandle[SUB_FX_OFFLOAD] && (!isGetterCmd(cmdCode) || index == SUB_FX_OFFLOAD)) { - // In case of SET CMD, when the offload stream is unavailable, - // we will store the effect param values in the DSP effect wrapper. - // When the offload effects get enabled, we send these values to the - // DSP during Effect_config. - // So,we send the params to DSP wrapper also - offloadStatus = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( - pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, - pCmdData, replySize != NULL ? &offloadReplySize : NULL, pReplyData); - offloadReplied = true; + // grow temp reply buffer if needed + if (replySize != NULL) { + tmpSize = pContext->replySize; + while (tmpSize < *replySize && tmpSize < PROXY_REPLY_SIZE_MAX) { + tmpSize *= 2; + } + if (tmpSize > pContext->replySize) { + ALOGV("Effect_command grow reply buf to %d", tmpSize); + pContext->replyData = (char *)realloc(pContext->replyData, tmpSize); + pContext->replySize = tmpSize; + } + if (tmpSize > *replySize) { + tmpSize = *replySize; + } + } else { + tmpSize = 0; } - // By convention the offloaded implementation reply is returned if command is processed by both - // host and offloaded sub effects - if (offloadReplied){ - status = offloadStatus; - if (replySize) { - *replySize = offloadReplySize; + // tmpSize is now the actual reply size for the non active sub effect + + // Send command to sub effects. The command is sent to all sub effects so that their internal + // state is kept in sync. + // Only the reply from the active sub effect is returned to the caller. The reply from the + // other sub effect is lost in pContext->replyData + for (int i = 0; i < SUB_FX_COUNT; i++) { + if (pContext->eHandle[i] == NULL) { + continue; } - } else if (hostReplied) { - status = hostStatus; - if (replySize) { - *replySize = hostReplySize; + if (i == index) { + subStatus[i] = &status; + subReplySize[i] = replySize; + subReplyData[i] = pReplyData; + } else { + subStatus[i] = &tmpStatus; + subReplySize[i] = replySize == NULL ? NULL : &tmpSize; + subReplyData[i] = pReplyData == NULL ? NULL : pContext->replyData; } + *subStatus[i] = (*pContext->eHandle[i])->command( + pContext->eHandle[i], cmdCode, cmdSize, + pCmdData, subReplySize[i], subReplyData[i]); } + return status; } /* end Effect_command */ diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h index 8992f93..acbe17e 100644 --- a/media/libeffects/proxy/EffectProxy.h +++ b/media/libeffects/proxy/EffectProxy.h @@ -57,6 +57,9 @@ const struct effect_interface_s gEffectInterface = { NULL, }; +#define PROXY_REPLY_SIZE_MAX (64 * 1024) // must be power of two +#define PROXY_REPLY_SIZE_DEFAULT 32 // must be power of two + struct EffectContext { const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy effect_descriptor_t* desc; // Points to the sub effect descriptors @@ -67,6 +70,8 @@ struct EffectContext { int32_t ioId; // The ioId in which the effect is created. // Stored in context to pass on to sub effect creation effect_uuid_t uuid; // UUID of the Proxy + char* replyData; // temporary buffer for non active sub effect command reply + uint32_t replySize; // current size of temporary reply buffer }; #if __cplusplus -- cgit v1.1 From 30c08634416a99a0f627e4de3a5f49dcf0a72fd3 Mon Sep 17 00:00:00 2001 From: Johann Date: Fri, 27 Sep 2013 17:42:12 -0700 Subject: Change VP8 encoder bitrate Allow the bitrate to be updated while the encoder is running. Bug: 8422347 Change-Id: I8427fe20921b00f92b8f99fe21691709fab354b0 --- .../codecs/on2/enc/SoftVPXEncoder.cpp | 32 ++++++++++++++++++++++ .../libstagefright/codecs/on2/enc/SoftVPXEncoder.h | 5 +++- 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index 16f0f30..8375cac 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -141,6 +141,7 @@ SoftVPXEncoder::SoftVPXEncoder(const char *name, mWidth(176), mHeight(144), mBitrate(192000), // in bps + mBitrateUpdated(false), mBitrateControlMode(VPX_VBR), // variable bitrate mFrameDurationUs(33333), // Defaults to 30 fps mDCTPartitions(0), @@ -536,6 +537,22 @@ OMX_ERRORTYPE SoftVPXEncoder::setConfig( return OMX_ErrorNone; } + case OMX_IndexConfigVideoBitrate: + { + OMX_VIDEO_CONFIG_BITRATETYPE *params = + (OMX_VIDEO_CONFIG_BITRATETYPE *)_params; + + if (params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + if (mBitrate != params->nEncodeBitrate) { + mBitrate = params->nEncodeBitrate; + mBitrateUpdated = true; + } + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::setConfig(index, _params); } @@ -779,6 +796,21 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { mKeyFrameRequested = false; } + if (mBitrateUpdated) { + mCodecConfiguration->rc_target_bitrate = mBitrate/1000; + vpx_codec_err_t res = vpx_codec_enc_config_set(mCodecContext, + mCodecConfiguration); + if (res != VPX_CODEC_OK) { + ALOGE("vp8 encoder failed to update bitrate: %s", + vpx_codec_err_to_string(res)); + notify(OMX_EventError, + OMX_ErrorUndefined, + 0, // Extra notification data + NULL); // Notification data pointer + } + mBitrateUpdated = false; + } + codec_return = vpx_codec_encode( mCodecContext, &raw_frame, diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index 4ee5e51..076830f 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -128,7 +128,10 @@ private: int32_t mHeight; // Target bitrate set for the encoder, in bits per second. - int32_t mBitrate; + uint32_t mBitrate; + + // If a request for a change it bitrate has been received. + bool mBitrateUpdated; // Bitrate control mode, either constant or variable vpx_rc_mode mBitrateControlMode; -- cgit v1.1 From 59fe010bcc072597852454a2ec53d7b0a2002a3b Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 27 Sep 2013 18:48:26 -0700 Subject: fix volume and effect enable delay on offloaded tracks Volume: add a method to wake up the mediaserver playback thread when a volume command is received on an offloaded track. Effects: call effect chain process on offloaded playback threads asynchronously from writes to allow effect state updates while waiting for async write callback. Bug: 10796540. Change-Id: Id2747ae88783575d1d7ffd6fc86fbd054ab2c739 --- media/libmedia/AudioTrack.cpp | 3 +++ media/libmedia/IAudioTrack.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 754a4e3..37d50cf 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -534,6 +534,9 @@ status_t AudioTrack::setVolume(float left, float right) mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000)); + if (isOffloaded()) { + mAudioTrack->signal(); + } return NO_ERROR; } diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index f0d75ba..3cd9cfd 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -41,6 +41,7 @@ enum { SET_MEDIA_TIME_TRANSFORM, SET_PARAMETERS, GET_TIMESTAMP, + SIGNAL, }; class BpAudioTrack : public BpInterface @@ -182,6 +183,12 @@ public: } return status; } + + virtual void signal() { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(SIGNAL, data, &reply); + } }; IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack"); @@ -269,6 +276,11 @@ status_t BnAudioTrack::onTransact( } return NO_ERROR; } break; + case SIGNAL: { + CHECK_INTERFACE(IAudioTrack, data, reply); + signal(); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } -- cgit v1.1 From c16c203047ca0f0d76573ead2c42764a78baf521 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 30 Sep 2013 13:18:55 -0700 Subject: MediaCodecInfo: report supported color formats for non-native-window mode Revert the change in behavior when checking for adaptive playback was introduced. Change-Id: I59dc2450a4299b912015f2e4c9ec018a19a20b84 Signed-off-by: Lajos Molnar Bug: 10921537 --- media/libstagefright/OMXCodec.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'media') diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 7b37365..2c95ab4 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -4585,12 +4585,6 @@ status_t QueryCodec( caps->mFlags = 0; caps->mComponentName = componentName; - if (!isEncoder && !strncmp(mime, "video/", 6) && - omx->storeMetaDataInBuffers( - node, 1 /* port index */, OMX_TRUE) == OK) { - caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback; - } - OMX_VIDEO_PARAM_PROFILELEVELTYPE param; InitOMXParams(¶m); @@ -4626,6 +4620,12 @@ status_t QueryCodec( caps->mColorFormats.push(portFormat.eColorFormat); } + if (!isEncoder && !strncmp(mime, "video/", 6) && + omx->storeMetaDataInBuffers( + node, 1 /* port index */, OMX_TRUE) == OK) { + caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback; + } + CHECK_EQ(omx->freeNode(node), (status_t)OK); return OK; -- cgit v1.1 From 6218fdc2bef7b9c912bc4d132c12ee43b7b2dd37 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 25 Sep 2013 08:09:41 -0700 Subject: MediaTimeProvider support fixes Add MEDIA_SKIPPED event when resuming at a different time than seeked to. Send MEDIA_STARTED/PAUSED events only when playing (vs. when doing seek previews) Change-Id: I243ebf054303755ea8863229c3211694f2c204a7 Signed-off-by: Lajos Molnar Bug: 10954008 --- media/libstagefright/AwesomePlayer.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 3b516af..e7cfc78 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -1413,8 +1413,10 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) { mSeekTimeUs = timeUs; modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR); - notifyListener_l(MEDIA_PAUSED); - mMediaRenderingStartGeneration = ++mStartGeneration; + if (mFlags & PLAYING) { + notifyListener_l(MEDIA_PAUSED); + mMediaRenderingStartGeneration = ++mStartGeneration; + } seekAudioIfNecessary_l(); @@ -1659,6 +1661,16 @@ void AwesomePlayer::finishSeekIfNecessary(int64_t videoTimeUs) { return; } + // If we paused, then seeked, then resumed, it is possible that we have + // signaled SEEK_COMPLETE at a copmletely different media time than where + // we are now resuming. Signal new position to media time provider. + // Cannot signal another SEEK_COMPLETE, as existing clients may not expect + // multiple SEEK_COMPLETE responses to a single seek() request. + if (mSeekNotificationSent && abs(mSeekTimeUs - videoTimeUs) > 10000) { + // notify if we are resuming more than 10ms away from desired seek time + notifyListener_l(MEDIA_SKIPPED); + } + if (mAudioPlayer != NULL) { ALOGV("seeking audio to %lld us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6); @@ -1930,7 +1942,9 @@ void AwesomePlayer::onVideoEvent() { notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START); } - notifyIfMediaStarted_l(); + if (mFlags & PLAYING) { + notifyIfMediaStarted_l(); + } } mVideoBuffer->release(); -- cgit v1.1 From 6ea551fa13b69e5ce359a7dba7485d857a005304 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 2 Oct 2013 13:06:06 -0700 Subject: Remove obsolete miracast sink code and friends. Change-Id: I8bbb22fb0cfe2d73881d9f05bf8112ae86d8040b related-to-bug: 11047222 --- media/libstagefright/wifi-display/Android.mk | 70 -- .../libstagefright/wifi-display/MediaReceiver.cpp | 328 ------ media/libstagefright/wifi-display/MediaReceiver.h | 111 -- media/libstagefright/wifi-display/SNTPClient.cpp | 174 --- media/libstagefright/wifi-display/SNTPClient.h | 62 -- media/libstagefright/wifi-display/TimeSyncer.cpp | 337 ------ media/libstagefright/wifi-display/TimeSyncer.h | 109 -- media/libstagefright/wifi-display/nettest.cpp | 400 ------- .../wifi-display/rtp/RTPAssembler.cpp | 328 ------ .../libstagefright/wifi-display/rtp/RTPAssembler.h | 92 -- .../wifi-display/rtp/RTPReceiver.cpp | 1152 -------------------- .../libstagefright/wifi-display/rtp/RTPReceiver.h | 125 --- media/libstagefright/wifi-display/rtptest.cpp | 565 ---------- .../wifi-display/sink/DirectRenderer.cpp | 653 ----------- .../wifi-display/sink/DirectRenderer.h | 87 -- .../wifi-display/sink/WifiDisplaySink.cpp | 917 ---------------- .../wifi-display/sink/WifiDisplaySink.h | 195 ---- .../wifi-display/source/WifiDisplaySource.cpp | 16 +- .../wifi-display/source/WifiDisplaySource.h | 3 - media/libstagefright/wifi-display/udptest.cpp | 116 -- 20 files changed, 1 insertion(+), 5839 deletions(-) delete mode 100644 media/libstagefright/wifi-display/MediaReceiver.cpp delete mode 100644 media/libstagefright/wifi-display/MediaReceiver.h delete mode 100644 media/libstagefright/wifi-display/SNTPClient.cpp delete mode 100644 media/libstagefright/wifi-display/SNTPClient.h delete mode 100644 media/libstagefright/wifi-display/TimeSyncer.cpp delete mode 100644 media/libstagefright/wifi-display/TimeSyncer.h delete mode 100644 media/libstagefright/wifi-display/nettest.cpp delete mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.cpp delete mode 100644 media/libstagefright/wifi-display/rtp/RTPAssembler.h delete mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.cpp delete mode 100644 media/libstagefright/wifi-display/rtp/RTPReceiver.h delete mode 100644 media/libstagefright/wifi-display/rtptest.cpp delete mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.cpp delete mode 100644 media/libstagefright/wifi-display/sink/DirectRenderer.h delete mode 100644 media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp delete mode 100644 media/libstagefright/wifi-display/sink/WifiDisplaySink.h delete mode 100644 media/libstagefright/wifi-display/udptest.cpp (limited to 'media') diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index c7d107e..3abe8a8 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -3,16 +3,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - MediaReceiver.cpp \ MediaSender.cpp \ Parameters.cpp \ - rtp/RTPAssembler.cpp \ - rtp/RTPReceiver.cpp \ rtp/RTPSender.cpp \ - sink/DirectRenderer.cpp \ - sink/WifiDisplaySink.cpp \ - SNTPClient.cpp \ - TimeSyncer.cpp \ source/Converter.cpp \ source/MediaPuller.cpp \ source/PlaybackSession.cpp \ @@ -63,66 +56,3 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= wfd include $(BUILD_EXECUTABLE) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - udptest.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= udptest - -include $(BUILD_EXECUTABLE) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - rtptest.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= rtptest - -include $(BUILD_EXECUTABLE) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - nettest.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= nettest - -include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp deleted file mode 100644 index 5524235..0000000 --- a/media/libstagefright/wifi-display/MediaReceiver.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2013, 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 "MediaReceiver" -#include - -#include "MediaReceiver.h" - -#include "AnotherPacketSource.h" -#include "rtp/RTPReceiver.h" - -#include -#include -#include -#include -#include -#include - -namespace android { - -MediaReceiver::MediaReceiver( - const sp &netSession, - const sp ¬ify) - : mNetSession(netSession), - mNotify(notify), - mMode(MODE_UNDEFINED), - mGeneration(0), - mInitStatus(OK), - mInitDoneCount(0) { -} - -MediaReceiver::~MediaReceiver() { -} - -ssize_t MediaReceiver::addTrack( - RTPReceiver::TransportMode rtpMode, - RTPReceiver::TransportMode rtcpMode, - int32_t *localRTPPort) { - if (mMode != MODE_UNDEFINED) { - return INVALID_OPERATION; - } - - size_t trackIndex = mTrackInfos.size(); - - TrackInfo info; - - sp notify = new AMessage(kWhatReceiverNotify, id()); - notify->setInt32("generation", mGeneration); - notify->setSize("trackIndex", trackIndex); - - info.mReceiver = new RTPReceiver(mNetSession, notify); - looper()->registerHandler(info.mReceiver); - - info.mReceiver->registerPacketType( - 33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM); - - info.mReceiver->registerPacketType( - 96, RTPReceiver::PACKETIZATION_AAC); - - info.mReceiver->registerPacketType( - 97, RTPReceiver::PACKETIZATION_H264); - - status_t err = info.mReceiver->initAsync( - rtpMode, - rtcpMode, - localRTPPort); - - if (err != OK) { - looper()->unregisterHandler(info.mReceiver->id()); - info.mReceiver.clear(); - - return err; - } - - mTrackInfos.push_back(info); - - return trackIndex; -} - -status_t MediaReceiver::connectTrack( - size_t trackIndex, - const char *remoteHost, - int32_t remoteRTPPort, - int32_t remoteRTCPPort) { - if (trackIndex >= mTrackInfos.size()) { - return -ERANGE; - } - - TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); - return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort); -} - -status_t MediaReceiver::initAsync(Mode mode) { - if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW) - && mTrackInfos.size() > 1) { - return INVALID_OPERATION; - } - - sp msg = new AMessage(kWhatInit, id()); - msg->setInt32("mode", mode); - msg->post(); - - return OK; -} - -void MediaReceiver::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatInit: - { - int32_t mode; - CHECK(msg->findInt32("mode", &mode)); - - CHECK_EQ(mMode, MODE_UNDEFINED); - mMode = (Mode)mode; - - if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { - notifyInitDone(mInitStatus); - } - - mTSParser = new ATSParser( - ATSParser::ALIGNED_VIDEO_DATA - | ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE); - - mFormatKnownMask = 0; - break; - } - - case kWhatReceiverNotify: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - if (generation != mGeneration) { - break; - } - - onReceiverNotify(msg); - break; - } - - default: - TRESPASS(); - } -} - -void MediaReceiver::onReceiverNotify(const sp &msg) { - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case RTPReceiver::kWhatInitDone: - { - ++mInitDoneCount; - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - if (err != OK) { - mInitStatus = err; - ++mGeneration; - } - - if (mMode != MODE_UNDEFINED) { - if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) { - notifyInitDone(mInitStatus); - } - } - break; - } - - case RTPReceiver::kWhatError: - { - int32_t err; - CHECK(msg->findInt32("err", &err)); - - notifyError(err); - break; - } - - case RTPReceiver::kWhatAccessUnit: - { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - int32_t followsDiscontinuity; - if (!msg->findInt32( - "followsDiscontinuity", &followsDiscontinuity)) { - followsDiscontinuity = 0; - } - - if (mMode == MODE_TRANSPORT_STREAM) { - if (followsDiscontinuity) { - mTSParser->signalDiscontinuity( - ATSParser::DISCONTINUITY_TIME, NULL /* extra */); - } - - for (size_t offset = 0; - offset < accessUnit->size(); offset += 188) { - status_t err = mTSParser->feedTSPacket( - accessUnit->data() + offset, 188); - - if (err != OK) { - notifyError(err); - break; - } - } - - drainPackets(0 /* trackIndex */, ATSParser::VIDEO); - drainPackets(1 /* trackIndex */, ATSParser::AUDIO); - } else { - postAccessUnit(trackIndex, accessUnit, NULL); - } - break; - } - - case RTPReceiver::kWhatPacketLost: - { - notifyPacketLost(); - break; - } - - default: - TRESPASS(); - } -} - -void MediaReceiver::drainPackets( - size_t trackIndex, ATSParser::SourceType type) { - sp source = - static_cast( - mTSParser->getSource(type).get()); - - if (source == NULL) { - return; - } - - sp format; - if (!(mFormatKnownMask & (1ul << trackIndex))) { - sp meta = source->getFormat(); - CHECK(meta != NULL); - - CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format)); - - mFormatKnownMask |= 1ul << trackIndex; - } - - status_t finalResult; - while (source->hasBufferAvailable(&finalResult)) { - sp accessUnit; - status_t err = source->dequeueAccessUnit(&accessUnit); - if (err == OK) { - postAccessUnit(trackIndex, accessUnit, format); - format.clear(); - } else if (err != INFO_DISCONTINUITY) { - notifyError(err); - } - } - - if (finalResult != OK) { - notifyError(finalResult); - } -} - -void MediaReceiver::notifyInitDone(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatInitDone); - notify->setInt32("err", err); - notify->post(); -} - -void MediaReceiver::notifyError(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatError); - notify->setInt32("err", err); - notify->post(); -} - -void MediaReceiver::notifyPacketLost() { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatPacketLost); - notify->post(); -} - -void MediaReceiver::postAccessUnit( - size_t trackIndex, - const sp &accessUnit, - const sp &format) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatAccessUnit); - notify->setSize("trackIndex", trackIndex); - notify->setBuffer("accessUnit", accessUnit); - - if (format != NULL) { - notify->setMessage("format", format); - } - - notify->post(); -} - -status_t MediaReceiver::informSender( - size_t trackIndex, const sp ¶ms) { - if (trackIndex >= mTrackInfos.size()) { - return -ERANGE; - } - - TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); - return info->mReceiver->informSender(params); -} - -} // namespace android - - diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h deleted file mode 100644 index afbb407..0000000 --- a/media/libstagefright/wifi-display/MediaReceiver.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "ATSParser.h" -#include "rtp/RTPReceiver.h" - -namespace android { - -struct ABuffer; -struct ANetworkSession; -struct AMessage; -struct ATSParser; - -// This class facilitates receiving of media data for one or more tracks -// over RTP. Either a 1:1 track to RTP channel mapping is used or a single -// RTP channel provides the data for a transport stream that is consequently -// demuxed and its track's data provided to the observer. -struct MediaReceiver : public AHandler { - enum { - kWhatInitDone, - kWhatError, - kWhatAccessUnit, - kWhatPacketLost, - }; - - MediaReceiver( - const sp &netSession, - const sp ¬ify); - - ssize_t addTrack( - RTPReceiver::TransportMode rtpMode, - RTPReceiver::TransportMode rtcpMode, - int32_t *localRTPPort); - - status_t connectTrack( - size_t trackIndex, - const char *remoteHost, - int32_t remoteRTPPort, - int32_t remoteRTCPPort); - - enum Mode { - MODE_UNDEFINED, - MODE_TRANSPORT_STREAM, - MODE_TRANSPORT_STREAM_RAW, - MODE_ELEMENTARY_STREAMS, - }; - status_t initAsync(Mode mode); - - status_t informSender(size_t trackIndex, const sp ¶ms); - -protected: - virtual void onMessageReceived(const sp &msg); - virtual ~MediaReceiver(); - -private: - enum { - kWhatInit, - kWhatReceiverNotify, - }; - - struct TrackInfo { - sp mReceiver; - }; - - sp mNetSession; - sp mNotify; - - Mode mMode; - int32_t mGeneration; - - Vector mTrackInfos; - - status_t mInitStatus; - size_t mInitDoneCount; - - sp mTSParser; - uint32_t mFormatKnownMask; - - void onReceiverNotify(const sp &msg); - - void drainPackets(size_t trackIndex, ATSParser::SourceType type); - - void notifyInitDone(status_t err); - void notifyError(status_t err); - void notifyPacketLost(); - - void postAccessUnit( - size_t trackIndex, - const sp &accessUnit, - const sp &format); - - DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver); -}; - -} // namespace android - diff --git a/media/libstagefright/wifi-display/SNTPClient.cpp b/media/libstagefright/wifi-display/SNTPClient.cpp deleted file mode 100644 index 5c0af6a..0000000 --- a/media/libstagefright/wifi-display/SNTPClient.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2013, 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 "SNTPClient.h" - -#include -#include - -#include -#include -#include -#include -#include - -namespace android { - -SNTPClient::SNTPClient() { -} - -status_t SNTPClient::requestTime(const char *host) { - struct hostent *ent; - int64_t requestTimeNTP, requestTimeUs; - ssize_t n; - int64_t responseTimeUs, responseTimeNTP; - int64_t originateTimeNTP, receiveTimeNTP, transmitTimeNTP; - int64_t roundTripTimeNTP, clockOffsetNTP; - - status_t err = UNKNOWN_ERROR; - - int s = socket(AF_INET, SOCK_DGRAM, 0); - - if (s < 0) { - err = -errno; - - goto bail; - } - - ent = gethostbyname(host); - - if (ent == NULL) { - err = -ENOENT; - goto bail2; - } - - struct sockaddr_in hostAddr; - memset(hostAddr.sin_zero, 0, sizeof(hostAddr.sin_zero)); - hostAddr.sin_family = AF_INET; - hostAddr.sin_port = htons(kNTPPort); - hostAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - - uint8_t packet[kNTPPacketSize]; - memset(packet, 0, sizeof(packet)); - - packet[0] = kNTPModeClient | (kNTPVersion << 3); - - requestTimeNTP = getNowNTP(); - requestTimeUs = ALooper::GetNowUs(); - writeTimeStamp(&packet[kNTPTransmitTimeOffset], requestTimeNTP); - - n = sendto( - s, packet, sizeof(packet), 0, - (const struct sockaddr *)&hostAddr, sizeof(hostAddr)); - - if (n < 0) { - err = -errno; - goto bail2; - } - - memset(packet, 0, sizeof(packet)); - - do { - n = recv(s, packet, sizeof(packet), 0); - } while (n < 0 && errno == EINTR); - - if (n < 0) { - err = -errno; - goto bail2; - } - - responseTimeUs = ALooper::GetNowUs(); - - responseTimeNTP = requestTimeNTP + makeNTP(responseTimeUs - requestTimeUs); - - originateTimeNTP = readTimeStamp(&packet[kNTPOriginateTimeOffset]); - receiveTimeNTP = readTimeStamp(&packet[kNTPReceiveTimeOffset]); - transmitTimeNTP = readTimeStamp(&packet[kNTPTransmitTimeOffset]); - - roundTripTimeNTP = - makeNTP(responseTimeUs - requestTimeUs) - - (transmitTimeNTP - receiveTimeNTP); - - clockOffsetNTP = - ((receiveTimeNTP - originateTimeNTP) - + (transmitTimeNTP - responseTimeNTP)) / 2; - - mTimeReferenceNTP = responseTimeNTP + clockOffsetNTP; - mTimeReferenceUs = responseTimeUs; - mRoundTripTimeNTP = roundTripTimeNTP; - - err = OK; - -bail2: - close(s); - s = -1; - -bail: - return err; -} - -int64_t SNTPClient::adjustTimeUs(int64_t timeUs) const { - uint64_t nowNTP = - mTimeReferenceNTP + makeNTP(timeUs - mTimeReferenceUs); - - int64_t nowUs = - (nowNTP >> 32) * 1000000ll - + ((nowNTP & 0xffffffff) * 1000000ll) / (1ll << 32); - - nowUs -= ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; - - return nowUs; -} - -// static -void SNTPClient::writeTimeStamp(uint8_t *dst, uint64_t ntpTime) { - *dst++ = (ntpTime >> 56) & 0xff; - *dst++ = (ntpTime >> 48) & 0xff; - *dst++ = (ntpTime >> 40) & 0xff; - *dst++ = (ntpTime >> 32) & 0xff; - *dst++ = (ntpTime >> 24) & 0xff; - *dst++ = (ntpTime >> 16) & 0xff; - *dst++ = (ntpTime >> 8) & 0xff; - *dst++ = ntpTime & 0xff; -} - -// static -uint64_t SNTPClient::readTimeStamp(const uint8_t *dst) { - return U64_AT(dst); -} - -// static -uint64_t SNTPClient::getNowNTP() { - struct timeval tv; - gettimeofday(&tv, NULL /* time zone */); - - uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec; - - nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll; - - return makeNTP(nowUs); -} - -// static -uint64_t SNTPClient::makeNTP(uint64_t deltaUs) { - uint64_t hi = deltaUs / 1000000ll; - uint64_t lo = ((1ll << 32) * (deltaUs % 1000000ll)) / 1000000ll; - - return (hi << 32) | lo; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/SNTPClient.h b/media/libstagefright/wifi-display/SNTPClient.h deleted file mode 100644 index 967d1fc..0000000 --- a/media/libstagefright/wifi-display/SNTPClient.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2013, 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 SNTP_CLIENT_H_ - -#define SNTP_CLIENT_H_ - -#include -#include - -namespace android { - -// Implementation of the SNTP (Simple Network Time Protocol) -struct SNTPClient { - SNTPClient(); - - status_t requestTime(const char *host); - - // given a time obtained from ALooper::GetNowUs() - // return the number of us elapsed since Jan 1 1970 00:00:00 (UTC). - int64_t adjustTimeUs(int64_t timeUs) const; - -private: - enum { - kNTPPort = 123, - kNTPPacketSize = 48, - kNTPModeClient = 3, - kNTPVersion = 3, - kNTPTransmitTimeOffset = 40, - kNTPOriginateTimeOffset = 24, - kNTPReceiveTimeOffset = 32, - }; - - uint64_t mTimeReferenceNTP; - int64_t mTimeReferenceUs; - int64_t mRoundTripTimeNTP; - - static void writeTimeStamp(uint8_t *dst, uint64_t ntpTime); - static uint64_t readTimeStamp(const uint8_t *dst); - - static uint64_t getNowNTP(); - static uint64_t makeNTP(uint64_t deltaUs); - - DISALLOW_EVIL_CONSTRUCTORS(SNTPClient); -}; - -} // namespace android - -#endif // SNTP_CLIENT_H_ diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp deleted file mode 100644 index 0f4d93a..0000000 --- a/media/libstagefright/wifi-display/TimeSyncer.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2013, 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_NEBUG 0 -#define LOG_TAG "TimeSyncer" -#include - -#include "TimeSyncer.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -TimeSyncer::TimeSyncer( - const sp &netSession, const sp ¬ify) - : mNetSession(netSession), - mNotify(notify), - mIsServer(false), - mConnected(false), - mUDPSession(0), - mSeqNo(0), - mTotalTimeUs(0.0), - mPendingT1(0ll), - mTimeoutGeneration(0) { -} - -TimeSyncer::~TimeSyncer() { -} - -void TimeSyncer::startServer(unsigned localPort) { - sp msg = new AMessage(kWhatStartServer, id()); - msg->setInt32("localPort", localPort); - msg->post(); -} - -void TimeSyncer::startClient(const char *remoteHost, unsigned remotePort) { - sp msg = new AMessage(kWhatStartClient, id()); - msg->setString("remoteHost", remoteHost); - msg->setInt32("remotePort", remotePort); - msg->post(); -} - -void TimeSyncer::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatStartClient: - { - AString remoteHost; - CHECK(msg->findString("remoteHost", &remoteHost)); - - int32_t remotePort; - CHECK(msg->findInt32("remotePort", &remotePort)); - - sp notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - 0 /* localPort */, - remoteHost.c_str(), - remotePort, - notify, - &mUDPSession)); - - postSendPacket(); - break; - } - - case kWhatStartServer: - { - mIsServer = true; - - int32_t localPort; - CHECK(msg->findInt32("localPort", &localPort)); - - sp notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - localPort, notify, &mUDPSession)); - - break; - } - - case kWhatSendPacket: - { - if (mHistory.size() == 0) { - ALOGI("starting batch"); - } - - TimeInfo ti; - memset(&ti, 0, sizeof(ti)); - - ti.mT1 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, &ti, sizeof(ti))); - - mPendingT1 = ti.mT1; - postTimeout(); - break; - } - - case kWhatTimedOut: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mTimeoutGeneration) { - break; - } - - ALOGI("timed out, sending another request"); - postSendPacket(); - break; - } - - case kWhatUDPNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred in session %d (%d, '%s/%s').", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - cancelTimeout(); - - notifyError(err); - break; - } - - case ANetworkSession::kWhatDatagram: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp packet; - CHECK(msg->findBuffer("data", &packet)); - - int64_t arrivalTimeUs; - CHECK(packet->meta()->findInt64( - "arrivalTimeUs", &arrivalTimeUs)); - - CHECK_EQ(packet->size(), sizeof(TimeInfo)); - - TimeInfo *ti = (TimeInfo *)packet->data(); - - if (mIsServer) { - if (!mConnected) { - AString fromAddr; - CHECK(msg->findString("fromAddr", &fromAddr)); - - int32_t fromPort; - CHECK(msg->findInt32("fromPort", &fromPort)); - - CHECK_EQ((status_t)OK, - mNetSession->connectUDPSession( - mUDPSession, fromAddr.c_str(), fromPort)); - - mConnected = true; - } - - ti->mT2 = arrivalTimeUs; - ti->mT3 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, ti, sizeof(*ti))); - } else { - if (ti->mT1 != mPendingT1) { - break; - } - - cancelTimeout(); - mPendingT1 = 0; - - ti->mT4 = arrivalTimeUs; - - // One way delay for a packet to travel from client - // to server or back (assumed to be the same either way). - int64_t delay = - (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; - - // Offset between the client clock (T1, T4) and the - // server clock (T2, T3) timestamps. - int64_t offset = - (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; - - mHistory.push_back(*ti); - - ALOGV("delay = %lld us,\toffset %lld us", - delay, - offset); - - if (mHistory.size() < kNumPacketsPerBatch) { - postSendPacket(1000000ll / 30); - } else { - notifyOffset(); - - ALOGI("batch done"); - - mHistory.clear(); - postSendPacket(kBatchDelayUs); - } - } - break; - } - - default: - TRESPASS(); - } - - break; - } - - default: - TRESPASS(); - } -} - -void TimeSyncer::postSendPacket(int64_t delayUs) { - (new AMessage(kWhatSendPacket, id()))->post(delayUs); -} - -void TimeSyncer::postTimeout() { - sp msg = new AMessage(kWhatTimedOut, id()); - msg->setInt32("generation", mTimeoutGeneration); - msg->post(kTimeoutDelayUs); -} - -void TimeSyncer::cancelTimeout() { - ++mTimeoutGeneration; -} - -void TimeSyncer::notifyError(status_t err) { - if (mNotify == NULL) { - looper()->stop(); - return; - } - - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatError); - notify->setInt32("err", err); - notify->post(); -} - -// static -int TimeSyncer::CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2) { - int64_t rt1 = ti1->mT4 - ti1->mT1; - int64_t rt2 = ti2->mT4 - ti2->mT1; - - if (rt1 < rt2) { - return -1; - } else if (rt1 > rt2) { - return 1; - } - - return 0; -} - -void TimeSyncer::notifyOffset() { - mHistory.sort(CompareRountripTime); - - int64_t sum = 0ll; - size_t count = 0; - - // Only consider the third of the information associated with the best - // (smallest) roundtrip times. - for (size_t i = 0; i < mHistory.size() / 3; ++i) { - const TimeInfo *ti = &mHistory[i]; - -#if 0 - // One way delay for a packet to travel from client - // to server or back (assumed to be the same either way). - int64_t delay = - (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; -#endif - - // Offset between the client clock (T1, T4) and the - // server clock (T2, T3) timestamps. - int64_t offset = - (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; - - ALOGV("(%d) RT: %lld us, offset: %lld us", - i, ti->mT4 - ti->mT1, offset); - - sum += offset; - ++count; - } - - if (mNotify == NULL) { - ALOGI("avg. offset is %lld", sum / count); - return; - } - - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatTimeOffset); - notify->setInt64("offset", sum / count); - notify->post(); -} - -} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h deleted file mode 100644 index 4e7571f..0000000 --- a/media/libstagefright/wifi-display/TimeSyncer.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2013, 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 TIME_SYNCER_H_ - -#define TIME_SYNCER_H_ - -#include - -namespace android { - -struct ANetworkSession; - -/* - TimeSyncer allows us to synchronize time between a client and a server. - The client sends a UDP packet containing its send-time to the server, - the server sends that packet back to the client amended with information - about when it was received as well as the time the reply was sent back. - Finally the client receives the reply and has now enough information to - compute the clock offset between client and server assuming that packet - exchange is symmetric, i.e. time for a packet client->server and - server->client is roughly equal. - This exchange is repeated a number of times and the average offset computed - over the 30% of packets that had the lowest roundtrip times. - The offset is determined every 10 secs to account for slight differences in - clock frequency. -*/ -struct TimeSyncer : public AHandler { - enum { - kWhatError, - kWhatTimeOffset, - }; - TimeSyncer( - const sp &netSession, - const sp ¬ify); - - void startServer(unsigned localPort); - void startClient(const char *remoteHost, unsigned remotePort); - -protected: - virtual ~TimeSyncer(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatStartServer, - kWhatStartClient, - kWhatUDPNotify, - kWhatSendPacket, - kWhatTimedOut, - }; - - struct TimeInfo { - int64_t mT1; // client timestamp at send - int64_t mT2; // server timestamp at receive - int64_t mT3; // server timestamp at send - int64_t mT4; // client timestamp at receive - }; - - enum { - kNumPacketsPerBatch = 30, - }; - static const int64_t kTimeoutDelayUs = 500000ll; - static const int64_t kBatchDelayUs = 60000000ll; // every minute - - sp mNetSession; - sp mNotify; - - bool mIsServer; - bool mConnected; - int32_t mUDPSession; - uint32_t mSeqNo; - double mTotalTimeUs; - - Vector mHistory; - - int64_t mPendingT1; - int32_t mTimeoutGeneration; - - void postSendPacket(int64_t delayUs = 0ll); - - void postTimeout(); - void cancelTimeout(); - - void notifyError(status_t err); - void notifyOffset(); - - static int CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2); - - DISALLOW_EVIL_CONSTRUCTORS(TimeSyncer); -}; - -} // namespace android - -#endif // TIME_SYNCER_H_ diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp deleted file mode 100644 index 73c0d80..0000000 --- a/media/libstagefright/wifi-display/nettest.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright 2013, 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_NEBUG 0 -#define LOG_TAG "nettest" -#include - -#include "TimeSyncer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -struct TestHandler : public AHandler { - TestHandler(const sp &netSession); - - void listen(int32_t port); - void connect(const char *host, int32_t port); - -protected: - virtual ~TestHandler(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kTimeSyncerPort = 8123, - }; - - enum { - kWhatListen, - kWhatConnect, - kWhatTimeSyncerNotify, - kWhatNetNotify, - kWhatSendMore, - kWhatStop, - }; - - sp mNetSession; - sp mTimeSyncer; - - int32_t mServerSessionID; - int32_t mSessionID; - - int64_t mTimeOffsetUs; - bool mTimeOffsetValid; - - int32_t mCounter; - - int64_t mMaxDelayMs; - - void dumpDelay(int32_t counter, int64_t delayMs); - - DISALLOW_EVIL_CONSTRUCTORS(TestHandler); -}; - -TestHandler::TestHandler(const sp &netSession) - : mNetSession(netSession), - mServerSessionID(0), - mSessionID(0), - mTimeOffsetUs(-1ll), - mTimeOffsetValid(false), - mCounter(0), - mMaxDelayMs(-1ll) { -} - -TestHandler::~TestHandler() { -} - -void TestHandler::listen(int32_t port) { - sp msg = new AMessage(kWhatListen, id()); - msg->setInt32("port", port); - msg->post(); -} - -void TestHandler::connect(const char *host, int32_t port) { - sp msg = new AMessage(kWhatConnect, id()); - msg->setString("host", host); - msg->setInt32("port", port); - msg->post(); -} - -void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) { - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - if (delayMs > mMaxDelayMs) { - mMaxDelayMs = delayMs; - } - - ALOGI("[%d] (%4lld ms / %4lld ms) %s", - counter, - delayMs, - mMaxDelayMs, - kPattern + kPatternSize - n); -} - -void TestHandler::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatListen: - { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - notify = new AMessage(kWhatNetNotify, id()); - - int32_t port; - CHECK(msg->findInt32("port", &port)); - - struct in_addr ifaceAddr; - ifaceAddr.s_addr = INADDR_ANY; - - CHECK_EQ((status_t)OK, - mNetSession->createTCPDatagramSession( - ifaceAddr, - port, - notify, - &mServerSessionID)); - break; - } - - case kWhatConnect: - { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - mTimeSyncer->startServer(kTimeSyncerPort); - - AString host; - CHECK(msg->findString("host", &host)); - - int32_t port; - CHECK(msg->findInt32("port", &port)); - - notify = new AMessage(kWhatNetNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createTCPDatagramSession( - 0 /* localPort */, - host.c_str(), - port, - notify, - &mSessionID)); - break; - } - - case kWhatNetNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatConnected: - { - ALOGI("kWhatConnected"); - - (new AMessage(kWhatSendMore, id()))->post(); - break; - } - - case ANetworkSession::kWhatClientConnected: - { - ALOGI("kWhatClientConnected"); - - CHECK_EQ(mSessionID, 0); - CHECK(msg->findInt32("sessionID", &mSessionID)); - - AString clientIP; - CHECK(msg->findString("client-ip", &clientIP)); - - mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort); - break; - } - - case ANetworkSession::kWhatDatagram: - { - sp packet; - CHECK(msg->findBuffer("data", &packet)); - - CHECK_EQ(packet->size(), 12u); - - int32_t counter = U32_AT(packet->data()); - int64_t timeUs = U64_AT(packet->data() + 4); - - if (mTimeOffsetValid) { - timeUs -= mTimeOffsetUs; - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayMs = (nowUs - timeUs) / 1000ll; - - dumpDelay(counter, delayMs); - } else { - ALOGI("received %d", counter); - } - break; - } - - case ANetworkSession::kWhatError: - { - ALOGE("kWhatError"); - break; - } - - default: - TRESPASS(); - } - break; - } - - case kWhatTimeSyncerNotify: - { - CHECK(msg->findInt64("offset", &mTimeOffsetUs)); - mTimeOffsetValid = true; - break; - } - - case kWhatSendMore: - { - uint8_t buffer[4 + 8]; - buffer[0] = mCounter >> 24; - buffer[1] = (mCounter >> 16) & 0xff; - buffer[2] = (mCounter >> 8) & 0xff; - buffer[3] = mCounter & 0xff; - - int64_t nowUs = ALooper::GetNowUs(); - - buffer[4] = nowUs >> 56; - buffer[5] = (nowUs >> 48) & 0xff; - buffer[6] = (nowUs >> 40) & 0xff; - buffer[7] = (nowUs >> 32) & 0xff; - buffer[8] = (nowUs >> 24) & 0xff; - buffer[9] = (nowUs >> 16) & 0xff; - buffer[10] = (nowUs >> 8) & 0xff; - buffer[11] = nowUs & 0xff; - - ++mCounter; - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mSessionID, - buffer, - sizeof(buffer), - true /* timeValid */, - nowUs)); - - msg->post(100000ll); - break; - } - - case kWhatStop: - { - if (mSessionID != 0) { - mNetSession->destroySession(mSessionID); - mSessionID = 0; - } - - if (mServerSessionID != 0) { - mNetSession->destroySession(mServerSessionID); - mServerSessionID = 0; - } - - looper()->stop(); - break; - } - - default: - TRESPASS(); - } -} - -} // namespace android - -static void usage(const char *me) { - fprintf(stderr, - "usage: %s -c host:port\tconnect to remote host\n" - " -l port \tlisten\n", - me); -} - -int main(int argc, char **argv) { - using namespace android; - - // srand(time(NULL)); - - ProcessState::self()->startThreadPool(); - - DataSource::RegisterDefaultSniffers(); - - int32_t connectToPort = -1; - AString connectToHost; - - int32_t listenOnPort = -1; - - int res; - while ((res = getopt(argc, argv, "hc:l:")) >= 0) { - switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - usage(argv[0]); - exit(1); - } - - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 0 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case 'l': - { - char *end; - listenOnPort = strtol(optarg, &end, 10); - - if (*end != '\0' || end == optarg - || listenOnPort < 0 || listenOnPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case '?': - case 'h': - usage(argv[0]); - exit(1); - } - } - - if ((listenOnPort < 0 && connectToPort < 0) - || (listenOnPort >= 0 && connectToPort >= 0)) { - fprintf(stderr, - "You need to select either client or server mode.\n"); - exit(1); - } - - sp netSession = new ANetworkSession; - netSession->start(); - - sp looper = new ALooper; - - sp handler = new TestHandler(netSession); - looper->registerHandler(handler); - - if (listenOnPort) { - handler->listen(listenOnPort); - } - - if (connectToPort >= 0) { - handler->connect(connectToHost.c_str(), connectToPort); - } - - looper->start(true /* runOnCallingThread */); - - return 0; -} diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp deleted file mode 100644 index 7a96081..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2013, 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 "RTPAssembler" -#include - -#include "RTPAssembler.h" - -#include -#include -#include -#include -#include - -namespace android { - -RTPReceiver::Assembler::Assembler(const sp ¬ify) - : mNotify(notify) { -} - -void RTPReceiver::Assembler::postAccessUnit( - const sp &accessUnit, bool followsDiscontinuity) { - sp notify = mNotify->dup(); - notify->setInt32("what", RTPReceiver::kWhatAccessUnit); - notify->setBuffer("accessUnit", accessUnit); - notify->setInt32("followsDiscontinuity", followsDiscontinuity); - notify->post(); -} -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::TSAssembler::TSAssembler(const sp ¬ify) - : Assembler(notify), - mSawDiscontinuity(false) { -} - -void RTPReceiver::TSAssembler::signalDiscontinuity() { - mSawDiscontinuity = true; -} - -status_t RTPReceiver::TSAssembler::processPacket(const sp &packet) { - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - - packet->meta()->setInt64("timeUs", (rtpTime * 100ll) / 9); - - postAccessUnit(packet, mSawDiscontinuity); - - if (mSawDiscontinuity) { - mSawDiscontinuity = false; - } - - return OK; -} - -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::H264Assembler::H264Assembler(const sp ¬ify) - : Assembler(notify), - mState(0), - mIndicator(0), - mNALType(0), - mAccessUnitRTPTime(0) { -} - -void RTPReceiver::H264Assembler::signalDiscontinuity() { - reset(); -} - -status_t RTPReceiver::H264Assembler::processPacket(const sp &packet) { - status_t err = internalProcessPacket(packet); - - if (err != OK) { - reset(); - } - - return err; -} - -status_t RTPReceiver::H264Assembler::internalProcessPacket( - const sp &packet) { - const uint8_t *data = packet->data(); - size_t size = packet->size(); - - switch (mState) { - case 0: - { - if (size < 1 || (data[0] & 0x80)) { - ALOGV("Malformed H264 RTP packet (empty or F-bit set)"); - return ERROR_MALFORMED; - } - - unsigned nalType = data[0] & 0x1f; - if (nalType >= 1 && nalType <= 23) { - addSingleNALUnit(packet); - ALOGV("added single NAL packet"); - } else if (nalType == 28) { - // FU-A - unsigned indicator = data[0]; - CHECK((indicator & 0x1f) == 28); - - if (size < 2) { - ALOGV("Malformed H264 FU-A packet (single byte)"); - return ERROR_MALFORMED; - } - - if (!(data[1] & 0x80)) { - ALOGV("Malformed H264 FU-A packet (no start bit)"); - return ERROR_MALFORMED; - } - - mIndicator = data[0]; - mNALType = data[1] & 0x1f; - uint32_t nri = (data[0] >> 5) & 3; - - clearAccumulator(); - - uint8_t byte = mNALType | (nri << 5); - appendToAccumulator(&byte, 1); - appendToAccumulator(data + 2, size - 2); - - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - mAccumulator->meta()->setInt32("rtp-time", rtpTime); - - if (data[1] & 0x40) { - // Huh? End bit also set on the first buffer. - addSingleNALUnit(mAccumulator); - clearAccumulator(); - - ALOGV("added FU-A"); - break; - } - - mState = 1; - } else if (nalType == 24) { - // STAP-A - - status_t err = addSingleTimeAggregationPacket(packet); - if (err != OK) { - return err; - } - } else { - ALOGV("Malformed H264 packet (unknown type %d)", nalType); - return ERROR_UNSUPPORTED; - } - break; - } - - case 1: - { - if (size < 2 - || data[0] != mIndicator - || (data[1] & 0x1f) != mNALType - || (data[1] & 0x80)) { - ALOGV("Malformed H264 FU-A packet (indicator, " - "type or start bit mismatch)"); - - return ERROR_MALFORMED; - } - - appendToAccumulator(data + 2, size - 2); - - if (data[1] & 0x40) { - addSingleNALUnit(mAccumulator); - - clearAccumulator(); - mState = 0; - - ALOGV("added FU-A"); - } - break; - } - - default: - TRESPASS(); - } - - int32_t marker; - CHECK(packet->meta()->findInt32("M", &marker)); - - if (marker) { - flushAccessUnit(); - } - - return OK; -} - -void RTPReceiver::H264Assembler::reset() { - mNALUnits.clear(); - - clearAccumulator(); - mState = 0; -} - -void RTPReceiver::H264Assembler::clearAccumulator() { - if (mAccumulator != NULL) { - // XXX Too expensive. - mAccumulator.clear(); - } -} - -void RTPReceiver::H264Assembler::appendToAccumulator( - const void *data, size_t size) { - if (mAccumulator == NULL) { - mAccumulator = new ABuffer(size); - memcpy(mAccumulator->data(), data, size); - return; - } - - if (mAccumulator->size() + size > mAccumulator->capacity()) { - sp buf = new ABuffer(mAccumulator->size() + size); - memcpy(buf->data(), mAccumulator->data(), mAccumulator->size()); - buf->setRange(0, mAccumulator->size()); - - int32_t rtpTime; - if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) { - buf->meta()->setInt32("rtp-time", rtpTime); - } - - mAccumulator = buf; - } - - memcpy(mAccumulator->data() + mAccumulator->size(), data, size); - mAccumulator->setRange(0, mAccumulator->size() + size); -} - -void RTPReceiver::H264Assembler::addSingleNALUnit(const sp &packet) { - if (mNALUnits.empty()) { - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - - mAccessUnitRTPTime = rtpTime; - } - - mNALUnits.push_back(packet); -} - -void RTPReceiver::H264Assembler::flushAccessUnit() { - if (mNALUnits.empty()) { - return; - } - - size_t totalSize = 0; - for (List >::iterator it = mNALUnits.begin(); - it != mNALUnits.end(); ++it) { - totalSize += 4 + (*it)->size(); - } - - sp accessUnit = new ABuffer(totalSize); - size_t offset = 0; - for (List >::iterator it = mNALUnits.begin(); - it != mNALUnits.end(); ++it) { - const sp nalUnit = *it; - - memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4); - - memcpy(accessUnit->data() + offset + 4, - nalUnit->data(), - nalUnit->size()); - - offset += 4 + nalUnit->size(); - } - - mNALUnits.clear(); - - accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll); - postAccessUnit(accessUnit, false /* followsDiscontinuity */); -} - -status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket( - const sp &packet) { - const uint8_t *data = packet->data(); - size_t size = packet->size(); - - if (size < 3) { - ALOGV("Malformed H264 STAP-A packet (too small)"); - return ERROR_MALFORMED; - } - - int32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); - - ++data; - --size; - while (size >= 2) { - size_t nalSize = (data[0] << 8) | data[1]; - - if (size < nalSize + 2) { - ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)"); - return ERROR_MALFORMED; - } - - sp unit = new ABuffer(nalSize); - memcpy(unit->data(), &data[2], nalSize); - - unit->meta()->setInt32("rtp-time", rtpTime); - - addSingleNALUnit(unit); - - data += 2 + nalSize; - size -= 2 + nalSize; - } - - if (size != 0) { - ALOGV("Unexpected padding at end of STAP-A packet."); - } - - ALOGV("added STAP-A"); - - return OK; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.h b/media/libstagefright/wifi-display/rtp/RTPAssembler.h deleted file mode 100644 index e456d32..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPAssembler.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2013, 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 RTP_ASSEMBLER_H_ - -#define RTP_ASSEMBLER_H_ - -#include "RTPReceiver.h" - -namespace android { - -// A helper class to reassemble the payload of RTP packets into access -// units depending on the packetization scheme. -struct RTPReceiver::Assembler : public RefBase { - Assembler(const sp ¬ify); - - virtual void signalDiscontinuity() = 0; - virtual status_t processPacket(const sp &packet) = 0; - -protected: - virtual ~Assembler() {} - - void postAccessUnit( - const sp &accessUnit, bool followsDiscontinuity); - -private: - sp mNotify; - - DISALLOW_EVIL_CONSTRUCTORS(Assembler); -}; - -struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler { - TSAssembler(const sp ¬ify); - - virtual void signalDiscontinuity(); - virtual status_t processPacket(const sp &packet); - -private: - bool mSawDiscontinuity; - - DISALLOW_EVIL_CONSTRUCTORS(TSAssembler); -}; - -struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler { - H264Assembler(const sp ¬ify); - - virtual void signalDiscontinuity(); - virtual status_t processPacket(const sp &packet); - -private: - int32_t mState; - - uint8_t mIndicator; - uint8_t mNALType; - - sp mAccumulator; - - List > mNALUnits; - int32_t mAccessUnitRTPTime; - - status_t internalProcessPacket(const sp &packet); - - void addSingleNALUnit(const sp &packet); - status_t addSingleTimeAggregationPacket(const sp &packet); - - void flushAccessUnit(); - - void clearAccumulator(); - void appendToAccumulator(const void *data, size_t size); - - void reset(); - - DISALLOW_EVIL_CONSTRUCTORS(H264Assembler); -}; - -} // namespace android - -#endif // RTP_ASSEMBLER_H_ - diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp deleted file mode 100644 index 3b3bd63..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ /dev/null @@ -1,1152 +0,0 @@ -/* - * Copyright 2013, 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 "RTPReceiver" -#include - -#include "RTPAssembler.h" -#include "RTPReceiver.h" - -#include -#include -#include -#include -#include -#include -#include - -#define TRACK_PACKET_LOSS 0 - -namespace android { - -//////////////////////////////////////////////////////////////////////////////// - -struct RTPReceiver::Source : public AHandler { - Source(RTPReceiver *receiver, uint32_t ssrc); - - void onPacketReceived(uint16_t seq, const sp &buffer); - - void addReportBlock(uint32_t ssrc, const sp &buf); - -protected: - virtual ~Source(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatRetransmit, - kWhatDeclareLost, - }; - - static const uint32_t kMinSequential = 2; - static const uint32_t kMaxDropout = 3000; - static const uint32_t kMaxMisorder = 100; - static const uint32_t kRTPSeqMod = 1u << 16; - static const int64_t kReportIntervalUs = 10000000ll; - - RTPReceiver *mReceiver; - uint32_t mSSRC; - bool mFirst; - uint16_t mMaxSeq; - uint32_t mCycles; - uint32_t mBaseSeq; - uint32_t mReceived; - uint32_t mExpectedPrior; - uint32_t mReceivedPrior; - - int64_t mFirstArrivalTimeUs; - int64_t mFirstRTPTimeUs; - - // Ordered by extended seq number. - List > mPackets; - - enum StatusBits { - STATUS_DECLARED_LOST = 1, - STATUS_REQUESTED_RETRANSMISSION = 2, - STATUS_ARRIVED_LATE = 4, - }; -#if TRACK_PACKET_LOSS - KeyedVector mLostPackets; -#endif - - void modifyPacketStatus(int32_t extSeqNo, uint32_t mask); - - int32_t mAwaitingExtSeqNo; - bool mRequestedRetransmission; - - int32_t mActivePacketType; - sp mActiveAssembler; - - int64_t mNextReportTimeUs; - - int32_t mNumDeclaredLost; - int32_t mNumDeclaredLostPrior; - - int32_t mRetransmitGeneration; - int32_t mDeclareLostGeneration; - bool mDeclareLostTimerPending; - - void queuePacket(const sp &packet); - void dequeueMore(); - - sp getNextPacket(); - void resync(); - - void postRetransmitTimer(int64_t delayUs); - void postDeclareLostTimer(int64_t delayUs); - void cancelTimers(); - - DISALLOW_EVIL_CONSTRUCTORS(Source); -}; - -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc) - : mReceiver(receiver), - mSSRC(ssrc), - mFirst(true), - mMaxSeq(0), - mCycles(0), - mBaseSeq(0), - mReceived(0), - mExpectedPrior(0), - mReceivedPrior(0), - mFirstArrivalTimeUs(-1ll), - mFirstRTPTimeUs(-1ll), - mAwaitingExtSeqNo(-1), - mRequestedRetransmission(false), - mActivePacketType(-1), - mNextReportTimeUs(-1ll), - mNumDeclaredLost(0), - mNumDeclaredLostPrior(0), - mRetransmitGeneration(0), - mDeclareLostGeneration(0), - mDeclareLostTimerPending(false) { -} - -RTPReceiver::Source::~Source() { -} - -void RTPReceiver::Source::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatRetransmit: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mRetransmitGeneration) { - break; - } - - mRequestedRetransmission = true; - mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo); - - modifyPacketStatus( - mAwaitingExtSeqNo, STATUS_REQUESTED_RETRANSMISSION); - break; - } - - case kWhatDeclareLost: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mDeclareLostGeneration) { - break; - } - - cancelTimers(); - - ALOGV("Lost packet extSeqNo %d %s", - mAwaitingExtSeqNo, - mRequestedRetransmission ? "*" : ""); - - mRequestedRetransmission = false; - if (mActiveAssembler != NULL) { - mActiveAssembler->signalDiscontinuity(); - } - - modifyPacketStatus(mAwaitingExtSeqNo, STATUS_DECLARED_LOST); - - // resync(); - ++mAwaitingExtSeqNo; - ++mNumDeclaredLost; - - mReceiver->notifyPacketLost(); - - dequeueMore(); - break; - } - - default: - TRESPASS(); - } -} - -void RTPReceiver::Source::onPacketReceived( - uint16_t seq, const sp &buffer) { - if (mFirst) { - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); - - mFirst = false; - mBaseSeq = seq; - mMaxSeq = seq; - ++mReceived; - return; - } - - uint16_t udelta = seq - mMaxSeq; - - if (udelta < kMaxDropout) { - // In order, with permissible gap. - - if (seq < mMaxSeq) { - // Sequence number wrapped - count another 64K cycle - mCycles += kRTPSeqMod; - } - - mMaxSeq = seq; - - ++mReceived; - } else if (udelta <= kRTPSeqMod - kMaxMisorder) { - // The sequence number made a very large jump - return; - } else { - // Duplicate or reordered packet. - } - - buffer->setInt32Data(mCycles | seq); - queuePacket(buffer); -} - -void RTPReceiver::Source::queuePacket(const sp &packet) { - int32_t newExtendedSeqNo = packet->int32Data(); - - if (mFirstArrivalTimeUs < 0ll) { - mFirstArrivalTimeUs = ALooper::GetNowUs(); - - uint32_t rtpTime; - CHECK(packet->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); - - mFirstRTPTimeUs = (rtpTime * 100ll) / 9ll; - } - - if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) { - // We're no longer interested in these. They're old. - ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo); - - modifyPacketStatus(newExtendedSeqNo, STATUS_ARRIVED_LATE); - return; - } - - if (mPackets.empty()) { - mPackets.push_back(packet); - dequeueMore(); - return; - } - - List >::iterator firstIt = mPackets.begin(); - List >::iterator it = --mPackets.end(); - for (;;) { - int32_t extendedSeqNo = (*it)->int32Data(); - - if (extendedSeqNo == newExtendedSeqNo) { - // Duplicate packet. - return; - } - - if (extendedSeqNo < newExtendedSeqNo) { - // Insert new packet after the one at "it". - mPackets.insert(++it, packet); - break; - } - - if (it == firstIt) { - // Insert new packet before the first existing one. - mPackets.insert(it, packet); - break; - } - - --it; - } - - dequeueMore(); -} - -void RTPReceiver::Source::dequeueMore() { - int64_t nowUs = ALooper::GetNowUs(); - if (mNextReportTimeUs < 0ll || nowUs >= mNextReportTimeUs) { - if (mNextReportTimeUs >= 0ll) { - uint32_t expected = (mMaxSeq | mCycles) - mBaseSeq + 1; - - uint32_t expectedInterval = expected - mExpectedPrior; - mExpectedPrior = expected; - - uint32_t receivedInterval = mReceived - mReceivedPrior; - mReceivedPrior = mReceived; - - int64_t lostInterval = - (int64_t)expectedInterval - (int64_t)receivedInterval; - - int32_t declaredLostInterval = - mNumDeclaredLost - mNumDeclaredLostPrior; - - mNumDeclaredLostPrior = mNumDeclaredLost; - - if (declaredLostInterval > 0) { - ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", - lostInterval, - 100.0f * lostInterval / expectedInterval, - declaredLostInterval); - } - } - - mNextReportTimeUs = nowUs + kReportIntervalUs; - -#if TRACK_PACKET_LOSS - for (size_t i = 0; i < mLostPackets.size(); ++i) { - int32_t key = mLostPackets.keyAt(i); - uint32_t value = mLostPackets.valueAt(i); - - AString status; - if (value & STATUS_REQUESTED_RETRANSMISSION) { - status.append("retrans "); - } - if (value & STATUS_ARRIVED_LATE) { - status.append("arrived-late "); - } - ALOGI("Packet %d declared lost %s", key, status.c_str()); - } -#endif - } - - sp packet; - while ((packet = getNextPacket()) != NULL) { - if (mDeclareLostTimerPending) { - cancelTimers(); - } - - CHECK_GE(mAwaitingExtSeqNo, 0); -#if TRACK_PACKET_LOSS - mLostPackets.removeItem(mAwaitingExtSeqNo); -#endif - - int32_t packetType; - CHECK(packet->meta()->findInt32("PT", &packetType)); - - if (packetType != mActivePacketType) { - mActiveAssembler = mReceiver->makeAssembler(packetType); - mActivePacketType = packetType; - } - - if (mActiveAssembler != NULL) { - status_t err = mActiveAssembler->processPacket(packet); - if (err != OK) { - ALOGV("assembler returned error %d", err); - } - } - - ++mAwaitingExtSeqNo; - } - - if (mDeclareLostTimerPending) { - return; - } - - if (mPackets.empty()) { - return; - } - - CHECK_GE(mAwaitingExtSeqNo, 0); - - const sp &firstPacket = *mPackets.begin(); - - uint32_t rtpTime; - CHECK(firstPacket->meta()->findInt32( - "rtp-time", (int32_t *)&rtpTime)); - - - int64_t rtpUs = (rtpTime * 100ll) / 9ll; - - int64_t maxArrivalTimeUs = - mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs; - - nowUs = ALooper::GetNowUs(); - - CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data()); - - ALOGV("waiting for %d, comparing against %d, %lld us left", - mAwaitingExtSeqNo, - firstPacket->int32Data(), - maxArrivalTimeUs - nowUs); - - postDeclareLostTimer(maxArrivalTimeUs + kPacketLostAfterUs); - - if (kRequestRetransmissionAfterUs > 0ll) { - postRetransmitTimer( - maxArrivalTimeUs + kRequestRetransmissionAfterUs); - } -} - -sp RTPReceiver::Source::getNextPacket() { - if (mPackets.empty()) { - return NULL; - } - - int32_t extSeqNo = (*mPackets.begin())->int32Data(); - - if (mAwaitingExtSeqNo < 0) { - mAwaitingExtSeqNo = extSeqNo; - } else if (extSeqNo != mAwaitingExtSeqNo) { - return NULL; - } - - sp packet = *mPackets.begin(); - mPackets.erase(mPackets.begin()); - - return packet; -} - -void RTPReceiver::Source::resync() { - mAwaitingExtSeqNo = -1; -} - -void RTPReceiver::Source::addReportBlock( - uint32_t ssrc, const sp &buf) { - uint32_t extMaxSeq = mMaxSeq | mCycles; - uint32_t expected = extMaxSeq - mBaseSeq + 1; - - int64_t lost = (int64_t)expected - (int64_t)mReceived; - if (lost > 0x7fffff) { - lost = 0x7fffff; - } else if (lost < -0x800000) { - lost = -0x800000; - } - - uint32_t expectedInterval = expected - mExpectedPrior; - mExpectedPrior = expected; - - uint32_t receivedInterval = mReceived - mReceivedPrior; - mReceivedPrior = mReceived; - - int64_t lostInterval = expectedInterval - receivedInterval; - - uint8_t fractionLost; - if (expectedInterval == 0 || lostInterval <=0) { - fractionLost = 0; - } else { - fractionLost = (lostInterval << 8) / expectedInterval; - } - - uint8_t *ptr = buf->data() + buf->size(); - - ptr[0] = ssrc >> 24; - ptr[1] = (ssrc >> 16) & 0xff; - ptr[2] = (ssrc >> 8) & 0xff; - ptr[3] = ssrc & 0xff; - - ptr[4] = fractionLost; - - ptr[5] = (lost >> 16) & 0xff; - ptr[6] = (lost >> 8) & 0xff; - ptr[7] = lost & 0xff; - - ptr[8] = extMaxSeq >> 24; - ptr[9] = (extMaxSeq >> 16) & 0xff; - ptr[10] = (extMaxSeq >> 8) & 0xff; - ptr[11] = extMaxSeq & 0xff; - - // XXX TODO: - - ptr[12] = 0x00; // interarrival jitter - ptr[13] = 0x00; - ptr[14] = 0x00; - ptr[15] = 0x00; - - ptr[16] = 0x00; // last SR - ptr[17] = 0x00; - ptr[18] = 0x00; - ptr[19] = 0x00; - - ptr[20] = 0x00; // delay since last SR - ptr[21] = 0x00; - ptr[22] = 0x00; - ptr[23] = 0x00; - - buf->setRange(buf->offset(), buf->size() + 24); -} - -//////////////////////////////////////////////////////////////////////////////// - -RTPReceiver::RTPReceiver( - const sp &netSession, - const sp ¬ify, - uint32_t flags) - : mNetSession(netSession), - mNotify(notify), - mFlags(flags), - mRTPMode(TRANSPORT_UNDEFINED), - mRTCPMode(TRANSPORT_UNDEFINED), - mRTPSessionID(0), - mRTCPSessionID(0), - mRTPConnected(false), - mRTCPConnected(false), - mRTPClientSessionID(0), - mRTCPClientSessionID(0) { -} - -RTPReceiver::~RTPReceiver() { - if (mRTCPClientSessionID != 0) { - mNetSession->destroySession(mRTCPClientSessionID); - mRTCPClientSessionID = 0; - } - - if (mRTPClientSessionID != 0) { - mNetSession->destroySession(mRTPClientSessionID); - mRTPClientSessionID = 0; - } - - if (mRTCPSessionID != 0) { - mNetSession->destroySession(mRTCPSessionID); - mRTCPSessionID = 0; - } - - if (mRTPSessionID != 0) { - mNetSession->destroySession(mRTPSessionID); - mRTPSessionID = 0; - } -} - -status_t RTPReceiver::initAsync( - TransportMode rtpMode, - TransportMode rtcpMode, - int32_t *outLocalRTPPort) { - if (mRTPMode != TRANSPORT_UNDEFINED - || rtpMode == TRANSPORT_UNDEFINED - || rtpMode == TRANSPORT_NONE - || rtcpMode == TRANSPORT_UNDEFINED) { - return INVALID_OPERATION; - } - - CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED); - CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED); - - sp rtpNotify = new AMessage(kWhatRTPNotify, id()); - - sp rtcpNotify; - if (rtcpMode != TRANSPORT_NONE) { - rtcpNotify = new AMessage(kWhatRTCPNotify, id()); - } - - CHECK_EQ(mRTPSessionID, 0); - CHECK_EQ(mRTCPSessionID, 0); - - int32_t localRTPPort; - - struct in_addr ifaceAddr; - ifaceAddr.s_addr = INADDR_ANY; - - for (;;) { - localRTPPort = PickRandomRTPPort(); - - status_t err; - if (rtpMode == TRANSPORT_UDP) { - err = mNetSession->createUDPSession( - localRTPPort, - rtpNotify, - &mRTPSessionID); - } else { - CHECK_EQ(rtpMode, TRANSPORT_TCP); - err = mNetSession->createTCPDatagramSession( - ifaceAddr, - localRTPPort, - rtpNotify, - &mRTPSessionID); - } - - if (err != OK) { - continue; - } - - if (rtcpMode == TRANSPORT_NONE) { - break; - } else if (rtcpMode == TRANSPORT_UDP) { - err = mNetSession->createUDPSession( - localRTPPort + 1, - rtcpNotify, - &mRTCPSessionID); - } else { - CHECK_EQ(rtpMode, TRANSPORT_TCP); - err = mNetSession->createTCPDatagramSession( - ifaceAddr, - localRTPPort + 1, - rtcpNotify, - &mRTCPSessionID); - } - - if (err == OK) { - break; - } - - mNetSession->destroySession(mRTPSessionID); - mRTPSessionID = 0; - } - - mRTPMode = rtpMode; - mRTCPMode = rtcpMode; - *outLocalRTPPort = localRTPPort; - - return OK; -} - -status_t RTPReceiver::connect( - const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) { - status_t err; - - if (mRTPMode == TRANSPORT_UDP) { - CHECK(!mRTPConnected); - - err = mNetSession->connectUDPSession( - mRTPSessionID, remoteHost, remoteRTPPort); - - if (err != OK) { - notifyInitDone(err); - return err; - } - - ALOGI("connectUDPSession RTP successful."); - - mRTPConnected = true; - } - - if (mRTCPMode == TRANSPORT_UDP) { - CHECK(!mRTCPConnected); - - err = mNetSession->connectUDPSession( - mRTCPSessionID, remoteHost, remoteRTCPPort); - - if (err != OK) { - notifyInitDone(err); - return err; - } - - scheduleSendRR(); - - ALOGI("connectUDPSession RTCP successful."); - - mRTCPConnected = true; - } - - if (mRTPConnected - && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { - notifyInitDone(OK); - } - - return OK; -} - -status_t RTPReceiver::informSender(const sp ¶ms) { - if (!mRTCPConnected) { - return INVALID_OPERATION; - } - - int64_t avgLatencyUs; - CHECK(params->findInt64("avgLatencyUs", &avgLatencyUs)); - - int64_t maxLatencyUs; - CHECK(params->findInt64("maxLatencyUs", &maxLatencyUs)); - - sp buf = new ABuffer(28); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 0; - ptr[1] = 204; // APP - ptr[2] = 0; - - CHECK((buf->size() % 4) == 0u); - ptr[3] = (buf->size() / 4) - 1; - - ptr[4] = kSourceID >> 24; // SSRC - ptr[5] = (kSourceID >> 16) & 0xff; - ptr[6] = (kSourceID >> 8) & 0xff; - ptr[7] = kSourceID & 0xff; - ptr[8] = 'l'; - ptr[9] = 'a'; - ptr[10] = 't'; - ptr[11] = 'e'; - - ptr[12] = avgLatencyUs >> 56; - ptr[13] = (avgLatencyUs >> 48) & 0xff; - ptr[14] = (avgLatencyUs >> 40) & 0xff; - ptr[15] = (avgLatencyUs >> 32) & 0xff; - ptr[16] = (avgLatencyUs >> 24) & 0xff; - ptr[17] = (avgLatencyUs >> 16) & 0xff; - ptr[18] = (avgLatencyUs >> 8) & 0xff; - ptr[19] = avgLatencyUs & 0xff; - - ptr[20] = maxLatencyUs >> 56; - ptr[21] = (maxLatencyUs >> 48) & 0xff; - ptr[22] = (maxLatencyUs >> 40) & 0xff; - ptr[23] = (maxLatencyUs >> 32) & 0xff; - ptr[24] = (maxLatencyUs >> 24) & 0xff; - ptr[25] = (maxLatencyUs >> 16) & 0xff; - ptr[26] = (maxLatencyUs >> 8) & 0xff; - ptr[27] = maxLatencyUs & 0xff; - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); - - return OK; -} - -void RTPReceiver::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatRTPNotify: - case kWhatRTCPNotify: - onNetNotify(msg->what() == kWhatRTPNotify, msg); - break; - - case kWhatSendRR: - { - onSendRR(); - break; - } - - default: - TRESPASS(); - } -} - -void RTPReceiver::onNetNotify(bool isRTP, const sp &msg) { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - int32_t errorOccuredDuringSend; - CHECK(msg->findInt32("send", &errorOccuredDuringSend)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred during %s in session %d " - "(%d, '%s' (%s)).", - errorOccuredDuringSend ? "send" : "receive", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - if (sessionID == mRTPSessionID) { - mRTPSessionID = 0; - } else if (sessionID == mRTCPSessionID) { - mRTCPSessionID = 0; - } else if (sessionID == mRTPClientSessionID) { - mRTPClientSessionID = 0; - } else if (sessionID == mRTCPClientSessionID) { - mRTCPClientSessionID = 0; - } - - if (!mRTPConnected - || (mRTCPMode != TRANSPORT_NONE && !mRTCPConnected)) { - notifyInitDone(err); - break; - } - - notifyError(err); - break; - } - - case ANetworkSession::kWhatDatagram: - { - sp data; - CHECK(msg->findBuffer("data", &data)); - - if (isRTP) { - if (mFlags & FLAG_AUTO_CONNECT) { - AString fromAddr; - CHECK(msg->findString("fromAddr", &fromAddr)); - - int32_t fromPort; - CHECK(msg->findInt32("fromPort", &fromPort)); - - CHECK_EQ((status_t)OK, - connect( - fromAddr.c_str(), fromPort, fromPort + 1)); - - mFlags &= ~FLAG_AUTO_CONNECT; - } - - onRTPData(data); - } else { - onRTCPData(data); - } - break; - } - - case ANetworkSession::kWhatClientConnected: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - if (isRTP) { - CHECK_EQ(mRTPMode, TRANSPORT_TCP); - - if (mRTPClientSessionID != 0) { - // We only allow a single client connection. - mNetSession->destroySession(sessionID); - sessionID = 0; - break; - } - - mRTPClientSessionID = sessionID; - mRTPConnected = true; - } else { - CHECK_EQ(mRTCPMode, TRANSPORT_TCP); - - if (mRTCPClientSessionID != 0) { - // We only allow a single client connection. - mNetSession->destroySession(sessionID); - sessionID = 0; - break; - } - - mRTCPClientSessionID = sessionID; - mRTCPConnected = true; - } - - if (mRTPConnected - && (mRTCPConnected || mRTCPMode == TRANSPORT_NONE)) { - notifyInitDone(OK); - } - break; - } - } -} - -void RTPReceiver::notifyInitDone(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatInitDone); - notify->setInt32("err", err); - notify->post(); -} - -void RTPReceiver::notifyError(status_t err) { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatError); - notify->setInt32("err", err); - notify->post(); -} - -void RTPReceiver::notifyPacketLost() { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatPacketLost); - notify->post(); -} - -status_t RTPReceiver::onRTPData(const sp &buffer) { - size_t size = buffer->size(); - if (size < 12) { - // Too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - const uint8_t *data = buffer->data(); - - if ((data[0] >> 6) != 2) { - // Unsupported version. - return ERROR_UNSUPPORTED; - } - - if (data[0] & 0x20) { - // Padding present. - - size_t paddingLength = data[size - 1]; - - if (paddingLength + 12 > size) { - // If we removed this much padding we'd end up with something - // that's too short to be a valid RTP header. - return ERROR_MALFORMED; - } - - size -= paddingLength; - } - - int numCSRCs = data[0] & 0x0f; - - size_t payloadOffset = 12 + 4 * numCSRCs; - - if (size < payloadOffset) { - // Not enough data to fit the basic header and all the CSRC entries. - return ERROR_MALFORMED; - } - - if (data[0] & 0x10) { - // Header eXtension present. - - if (size < payloadOffset + 4) { - // Not enough data to fit the basic header, all CSRC entries - // and the first 4 bytes of the extension header. - - return ERROR_MALFORMED; - } - - const uint8_t *extensionData = &data[payloadOffset]; - - size_t extensionLength = - 4 * (extensionData[2] << 8 | extensionData[3]); - - if (size < payloadOffset + 4 + extensionLength) { - return ERROR_MALFORMED; - } - - payloadOffset += 4 + extensionLength; - } - - uint32_t srcId = U32_AT(&data[8]); - uint32_t rtpTime = U32_AT(&data[4]); - uint16_t seqNo = U16_AT(&data[2]); - - sp meta = buffer->meta(); - meta->setInt32("ssrc", srcId); - meta->setInt32("rtp-time", rtpTime); - meta->setInt32("PT", data[1] & 0x7f); - meta->setInt32("M", data[1] >> 7); - - buffer->setRange(payloadOffset, size - payloadOffset); - - ssize_t index = mSources.indexOfKey(srcId); - sp source; - if (index < 0) { - source = new Source(this, srcId); - looper()->registerHandler(source); - - mSources.add(srcId, source); - } else { - source = mSources.valueAt(index); - } - - source->onPacketReceived(seqNo, buffer); - - return OK; -} - -status_t RTPReceiver::onRTCPData(const sp &data) { - ALOGI("onRTCPData"); - return OK; -} - -void RTPReceiver::addSDES(const sp &buffer) { - uint8_t *data = buffer->data() + buffer->size(); - data[0] = 0x80 | 1; - data[1] = 202; // SDES - data[4] = kSourceID >> 24; // SSRC - data[5] = (kSourceID >> 16) & 0xff; - data[6] = (kSourceID >> 8) & 0xff; - data[7] = kSourceID & 0xff; - - size_t offset = 8; - - data[offset++] = 1; // CNAME - - AString cname = "stagefright@somewhere"; - data[offset++] = cname.size(); - - memcpy(&data[offset], cname.c_str(), cname.size()); - offset += cname.size(); - - data[offset++] = 6; // TOOL - - AString tool = "stagefright/1.0"; - data[offset++] = tool.size(); - - memcpy(&data[offset], tool.c_str(), tool.size()); - offset += tool.size(); - - data[offset++] = 0; - - if ((offset % 4) > 0) { - size_t count = 4 - (offset % 4); - switch (count) { - case 3: - data[offset++] = 0; - case 2: - data[offset++] = 0; - case 1: - data[offset++] = 0; - } - } - - size_t numWords = (offset / 4) - 1; - data[2] = numWords >> 8; - data[3] = numWords & 0xff; - - buffer->setRange(buffer->offset(), buffer->size() + offset); -} - -void RTPReceiver::scheduleSendRR() { - (new AMessage(kWhatSendRR, id()))->post(5000000ll); -} - -void RTPReceiver::onSendRR() { - sp buf = new ABuffer(kMaxUDPPacketSize); - buf->setRange(0, 0); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 0; - ptr[1] = 201; // RR - ptr[2] = 0; - ptr[3] = 1; - ptr[4] = kSourceID >> 24; // SSRC - ptr[5] = (kSourceID >> 16) & 0xff; - ptr[6] = (kSourceID >> 8) & 0xff; - ptr[7] = kSourceID & 0xff; - - buf->setRange(0, 8); - - size_t numReportBlocks = 0; - for (size_t i = 0; i < mSources.size(); ++i) { - uint32_t ssrc = mSources.keyAt(i); - sp source = mSources.valueAt(i); - - if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) { - // Cannot fit another report block. - break; - } - - source->addReportBlock(ssrc, buf); - ++numReportBlocks; - } - - ptr[0] |= numReportBlocks; // 5 bit - - size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks; - ptr[2] = sizeInWordsMinus1 >> 8; - ptr[3] = sizeInWordsMinus1 & 0xff; - - buf->setRange(0, (sizeInWordsMinus1 + 1) * 4); - - addSDES(buf); - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); - - scheduleSendRR(); -} - -status_t RTPReceiver::registerPacketType( - uint8_t packetType, PacketizationMode mode) { - mPacketTypes.add(packetType, mode); - - return OK; -} - -sp RTPReceiver::makeAssembler(uint8_t packetType) { - ssize_t index = mPacketTypes.indexOfKey(packetType); - if (index < 0) { - return NULL; - } - - PacketizationMode mode = mPacketTypes.valueAt(index); - - switch (mode) { - case PACKETIZATION_NONE: - case PACKETIZATION_TRANSPORT_STREAM: - return new TSAssembler(mNotify); - - case PACKETIZATION_H264: - return new H264Assembler(mNotify); - - default: - return NULL; - } -} - -void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) { - int32_t blp = 0; - - sp buf = new ABuffer(16); - buf->setRange(0, 0); - - uint8_t *ptr = buf->data(); - ptr[0] = 0x80 | 1; // generic NACK - ptr[1] = 205; // TSFB - ptr[2] = 0; - ptr[3] = 3; - ptr[8] = (senderSSRC >> 24) & 0xff; - ptr[9] = (senderSSRC >> 16) & 0xff; - ptr[10] = (senderSSRC >> 8) & 0xff; - ptr[11] = (senderSSRC & 0xff); - ptr[8] = (kSourceID >> 24) & 0xff; - ptr[9] = (kSourceID >> 16) & 0xff; - ptr[10] = (kSourceID >> 8) & 0xff; - ptr[11] = (kSourceID & 0xff); - ptr[12] = (extSeqNo >> 8) & 0xff; - ptr[13] = (extSeqNo & 0xff); - ptr[14] = (blp >> 8) & 0xff; - ptr[15] = (blp & 0xff); - - buf->setRange(0, 16); - - mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); -} - -void RTPReceiver::Source::modifyPacketStatus(int32_t extSeqNo, uint32_t mask) { -#if TRACK_PACKET_LOSS - ssize_t index = mLostPackets.indexOfKey(extSeqNo); - if (index < 0) { - mLostPackets.add(extSeqNo, mask); - } else { - mLostPackets.editValueAt(index) |= mask; - } -#endif -} - -void RTPReceiver::Source::postRetransmitTimer(int64_t timeUs) { - int64_t delayUs = timeUs - ALooper::GetNowUs(); - sp msg = new AMessage(kWhatRetransmit, id()); - msg->setInt32("generation", mRetransmitGeneration); - msg->post(delayUs); -} - -void RTPReceiver::Source::postDeclareLostTimer(int64_t timeUs) { - CHECK(!mDeclareLostTimerPending); - mDeclareLostTimerPending = true; - - int64_t delayUs = timeUs - ALooper::GetNowUs(); - sp msg = new AMessage(kWhatDeclareLost, id()); - msg->setInt32("generation", mDeclareLostGeneration); - msg->post(delayUs); -} - -void RTPReceiver::Source::cancelTimers() { - ++mRetransmitGeneration; - ++mDeclareLostGeneration; - mDeclareLostTimerPending = false; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h deleted file mode 100644 index 240ab2e..0000000 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2013, 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 RTP_RECEIVER_H_ - -#define RTP_RECEIVER_H_ - -#include "RTPBase.h" - -#include - -namespace android { - -struct ABuffer; -struct ANetworkSession; - -// An object of this class facilitates receiving of media data on an RTP -// channel. The channel is established over a UDP or TCP connection depending -// on which "TransportMode" was chosen. In addition different RTP packetization -// schemes are supported such as "Transport Stream Packets over RTP", -// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)" -struct RTPReceiver : public RTPBase, public AHandler { - enum { - kWhatInitDone, - kWhatError, - kWhatAccessUnit, - kWhatPacketLost, - }; - - enum Flags { - FLAG_AUTO_CONNECT = 1, - }; - RTPReceiver( - const sp &netSession, - const sp ¬ify, - uint32_t flags = 0); - - status_t registerPacketType( - uint8_t packetType, PacketizationMode mode); - - status_t initAsync( - TransportMode rtpMode, - TransportMode rtcpMode, - int32_t *outLocalRTPPort); - - status_t connect( - const char *remoteHost, - int32_t remoteRTPPort, - int32_t remoteRTCPPort); - - status_t informSender(const sp ¶ms); - -protected: - virtual ~RTPReceiver(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatRTPNotify, - kWhatRTCPNotify, - kWhatSendRR, - }; - - enum { - kSourceID = 0xdeadbeef, - kPacketLostAfterUs = 100000, - kRequestRetransmissionAfterUs = -1, - }; - - struct Assembler; - struct H264Assembler; - struct Source; - struct TSAssembler; - - sp mNetSession; - sp mNotify; - uint32_t mFlags; - TransportMode mRTPMode; - TransportMode mRTCPMode; - int32_t mRTPSessionID; - int32_t mRTCPSessionID; - bool mRTPConnected; - bool mRTCPConnected; - - int32_t mRTPClientSessionID; // in TRANSPORT_TCP mode. - int32_t mRTCPClientSessionID; // in TRANSPORT_TCP mode. - - KeyedVector mPacketTypes; - KeyedVector > mSources; - - void onNetNotify(bool isRTP, const sp &msg); - status_t onRTPData(const sp &data); - status_t onRTCPData(const sp &data); - void onSendRR(); - - void scheduleSendRR(); - void addSDES(const sp &buffer); - - void notifyInitDone(status_t err); - void notifyError(status_t err); - void notifyPacketLost(); - - sp makeAssembler(uint8_t packetType); - - void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo); - - DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver); -}; - -} // namespace android - -#endif // RTP_RECEIVER_H_ diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp deleted file mode 100644 index b902f29..0000000 --- a/media/libstagefright/wifi-display/rtptest.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/* - * Copyright 2013, 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_NEBUG 0 -#define LOG_TAG "rtptest" -#include - -#include "rtp/RTPSender.h" -#include "rtp/RTPReceiver.h" -#include "TimeSyncer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4" - -namespace android { - -struct PacketSource : public RefBase { - PacketSource() {} - - virtual sp getNextAccessUnit() = 0; - -protected: - virtual ~PacketSource() {} - -private: - DISALLOW_EVIL_CONSTRUCTORS(PacketSource); -}; - -struct MediaPacketSource : public PacketSource { - MediaPacketSource() - : mMaxSampleSize(1024 * 1024) { - mExtractor = new NuMediaExtractor; - CHECK_EQ((status_t)OK, - mExtractor->setDataSource(MEDIA_FILENAME)); - - bool haveVideo = false; - for (size_t i = 0; i < mExtractor->countTracks(); ++i) { - sp format; - CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format)); - - AString mime; - CHECK(format->findString("mime", &mime)); - - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) { - mExtractor->selectTrack(i); - haveVideo = true; - break; - } - } - - CHECK(haveVideo); - } - - virtual sp getNextAccessUnit() { - int64_t timeUs; - status_t err = mExtractor->getSampleTime(&timeUs); - - if (err != OK) { - return NULL; - } - - sp accessUnit = new ABuffer(mMaxSampleSize); - CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit)); - - accessUnit->meta()->setInt64("timeUs", timeUs); - - CHECK_EQ((status_t)OK, mExtractor->advance()); - - return accessUnit; - } - -protected: - virtual ~MediaPacketSource() { - } - -private: - sp mExtractor; - size_t mMaxSampleSize; - - DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource); -}; - -struct SimplePacketSource : public PacketSource { - SimplePacketSource() - : mCounter(0) { - } - - virtual sp getNextAccessUnit() { - sp buffer = new ABuffer(4); - uint8_t *dst = buffer->data(); - dst[0] = mCounter >> 24; - dst[1] = (mCounter >> 16) & 0xff; - dst[2] = (mCounter >> 8) & 0xff; - dst[3] = mCounter & 0xff; - - buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate); - - ++mCounter; - - return buffer; - } - -protected: - virtual ~SimplePacketSource() { - } - -private: - enum { - kFrameRate = 30 - }; - - uint32_t mCounter; - - DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource); -}; - -struct TestHandler : public AHandler { - TestHandler(const sp &netSession); - - void listen(); - void connect(const char *host, int32_t port); - -protected: - virtual ~TestHandler(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatListen, - kWhatConnect, - kWhatReceiverNotify, - kWhatSenderNotify, - kWhatSendMore, - kWhatStop, - kWhatTimeSyncerNotify, - }; - -#if 1 - static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP; - static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP; -#else - static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP; - static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE; -#endif - -#if 1 - static const RTPBase::PacketizationMode kPacketizationMode - = RTPBase::PACKETIZATION_H264; -#else - static const RTPBase::PacketizationMode kPacketizationMode - = RTPBase::PACKETIZATION_NONE; -#endif - - sp mNetSession; - sp mSource; - sp mSender; - sp mReceiver; - - sp mTimeSyncer; - bool mTimeSyncerStarted; - - int64_t mFirstTimeRealUs; - int64_t mFirstTimeMediaUs; - - int64_t mTimeOffsetUs; - bool mTimeOffsetValid; - - status_t readMore(); - - DISALLOW_EVIL_CONSTRUCTORS(TestHandler); -}; - -TestHandler::TestHandler(const sp &netSession) - : mNetSession(netSession), - mTimeSyncerStarted(false), - mFirstTimeRealUs(-1ll), - mFirstTimeMediaUs(-1ll), - mTimeOffsetUs(-1ll), - mTimeOffsetValid(false) { -} - -TestHandler::~TestHandler() { -} - -void TestHandler::listen() { - sp msg = new AMessage(kWhatListen, id()); - msg->post(); -} - -void TestHandler::connect(const char *host, int32_t port) { - sp msg = new AMessage(kWhatConnect, id()); - msg->setString("host", host); - msg->setInt32("port", port); - msg->post(); -} - -static void dumpDelay(int64_t delayMs) { - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - ALOGI("(%4lld ms) %s\n", - delayMs, - kPattern + kPatternSize - n); -} - -void TestHandler::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatListen: - { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - notify = new AMessage(kWhatReceiverNotify, id()); - mReceiver = new RTPReceiver( - mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT); - looper()->registerHandler(mReceiver); - - CHECK_EQ((status_t)OK, - mReceiver->registerPacketType(33, kPacketizationMode)); - - int32_t receiverRTPPort; - CHECK_EQ((status_t)OK, - mReceiver->initAsync( - kRTPMode, - kRTCPMode, - &receiverRTPPort)); - - printf("picked receiverRTPPort %d\n", receiverRTPPort); - -#if 0 - CHECK_EQ((status_t)OK, - mReceiver->connect( - "127.0.0.1", senderRTPPort, senderRTPPort + 1)); -#endif - break; - } - - case kWhatConnect: - { - AString host; - CHECK(msg->findString("host", &host)); - - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - mTimeSyncer->startServer(8123); - - int32_t receiverRTPPort; - CHECK(msg->findInt32("port", &receiverRTPPort)); - -#if 1 - mSource = new MediaPacketSource; -#else - mSource = new SimplePacketSource; -#endif - - notify = new AMessage(kWhatSenderNotify, id()); - mSender = new RTPSender(mNetSession, notify); - - looper()->registerHandler(mSender); - - int32_t senderRTPPort; - CHECK_EQ((status_t)OK, - mSender->initAsync( - host.c_str(), - receiverRTPPort, - kRTPMode, - kRTCPMode == RTPBase::TRANSPORT_NONE - ? -1 : receiverRTPPort + 1, - kRTCPMode, - &senderRTPPort)); - - printf("picked senderRTPPort %d\n", senderRTPPort); - break; - } - - case kWhatSenderNotify: - { - ALOGI("kWhatSenderNotify"); - - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case RTPSender::kWhatInitDone: - { - int32_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGI("RTPSender::initAsync completed w/ err %d", err); - - if (err == OK) { - err = readMore(); - - if (err != OK) { - (new AMessage(kWhatStop, id()))->post(); - } - } - break; - } - - case RTPSender::kWhatError: - break; - } - break; - } - - case kWhatReceiverNotify: - { - ALOGV("kWhatReceiverNotify"); - - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case RTPReceiver::kWhatInitDone: - { - int32_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGI("RTPReceiver::initAsync completed w/ err %d", err); - break; - } - - case RTPReceiver::kWhatError: - break; - - case RTPReceiver::kWhatAccessUnit: - { -#if 0 - if (!mTimeSyncerStarted) { - mTimeSyncer->startClient("172.18.41.216", 8123); - mTimeSyncerStarted = true; - } - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - if (mTimeOffsetValid) { - timeUs -= mTimeOffsetUs; - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayMs = (nowUs - timeUs) / 1000ll; - - dumpDelay(delayMs); - } -#endif - break; - } - - case RTPReceiver::kWhatPacketLost: - ALOGV("kWhatPacketLost"); - break; - - default: - TRESPASS(); - } - break; - } - - case kWhatSendMore: - { - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - CHECK_EQ((status_t)OK, - mSender->queueBuffer( - accessUnit, - 33, - kPacketizationMode)); - - status_t err = readMore(); - - if (err != OK) { - (new AMessage(kWhatStop, id()))->post(); - } - break; - } - - case kWhatStop: - { - if (mReceiver != NULL) { - looper()->unregisterHandler(mReceiver->id()); - mReceiver.clear(); - } - - if (mSender != NULL) { - looper()->unregisterHandler(mSender->id()); - mSender.clear(); - } - - mSource.clear(); - - looper()->stop(); - break; - } - - case kWhatTimeSyncerNotify: - { - CHECK(msg->findInt64("offset", &mTimeOffsetUs)); - mTimeOffsetValid = true; - break; - } - - default: - TRESPASS(); - } -} - -status_t TestHandler::readMore() { - sp accessUnit = mSource->getNextAccessUnit(); - - if (accessUnit == NULL) { - return ERROR_END_OF_STREAM; - } - - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - int64_t nowUs = ALooper::GetNowUs(); - int64_t whenUs; - - if (mFirstTimeRealUs < 0ll) { - mFirstTimeRealUs = whenUs = nowUs; - mFirstTimeMediaUs = timeUs; - } else { - whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs; - } - - accessUnit->meta()->setInt64("timeUs", whenUs); - - sp msg = new AMessage(kWhatSendMore, id()); - msg->setBuffer("accessUnit", accessUnit); - msg->post(whenUs - nowUs); - - return OK; -} - -} // namespace android - -static void usage(const char *me) { - fprintf(stderr, - "usage: %s -c host:port\tconnect to remote host\n" - " -l \tlisten\n", - me); -} - -int main(int argc, char **argv) { - using namespace android; - - // srand(time(NULL)); - - ProcessState::self()->startThreadPool(); - - DataSource::RegisterDefaultSniffers(); - - bool listen = false; - int32_t connectToPort = -1; - AString connectToHost; - - int res; - while ((res = getopt(argc, argv, "hc:l")) >= 0) { - switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - usage(argv[0]); - exit(1); - } - - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 1 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case 'l': - { - listen = true; - break; - } - - case '?': - case 'h': - usage(argv[0]); - exit(1); - } - } - - if (!listen && connectToPort < 0) { - fprintf(stderr, - "You need to select either client or server mode.\n"); - exit(1); - } - - sp netSession = new ANetworkSession; - netSession->start(); - - sp looper = new ALooper; - - sp handler = new TestHandler(netSession); - looper->registerHandler(handler); - - if (listen) { - handler->listen(); - } - - if (connectToPort >= 0) { - handler->connect(connectToHost.c_str(), connectToPort); - } - - looper->start(true /* runOnCallingThread */); - - return 0; -} - diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp deleted file mode 100644 index cdb2267..0000000 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright 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 "DirectRenderer" -#include - -#include "DirectRenderer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -/* - Drives the decoding process using a MediaCodec instance. Input buffers - queued by calls to "queueInputBuffer" are fed to the decoder as soon - as the decoder is ready for them, the client is notified about output - buffers as the decoder spits them out. -*/ -struct DirectRenderer::DecoderContext : public AHandler { - enum { - kWhatOutputBufferReady, - }; - DecoderContext(const sp ¬ify); - - status_t init( - const sp &format, - const sp &surfaceTex); - - void queueInputBuffer(const sp &accessUnit); - - status_t renderOutputBufferAndRelease(size_t index); - status_t releaseOutputBuffer(size_t index); - -protected: - virtual ~DecoderContext(); - - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatDecoderNotify, - }; - - sp mNotify; - sp mDecoderLooper; - sp mDecoder; - Vector > mDecoderInputBuffers; - Vector > mDecoderOutputBuffers; - List mDecoderInputBuffersAvailable; - bool mDecoderNotificationPending; - - List > mAccessUnits; - - void onDecoderNotify(); - void scheduleDecoderNotification(); - void queueDecoderInputBuffers(); - - void queueOutputBuffer( - size_t index, int64_t timeUs, const sp &buffer); - - DISALLOW_EVIL_CONSTRUCTORS(DecoderContext); -}; - -//////////////////////////////////////////////////////////////////////////////// - -/* - A "push" audio renderer. The primary function of this renderer is to use - an AudioTrack in push mode and making sure not to block the event loop - be ensuring that calls to AudioTrack::write never block. This is done by - estimating an upper bound of data that can be written to the AudioTrack - buffer without delay. -*/ -struct DirectRenderer::AudioRenderer : public AHandler { - AudioRenderer(const sp &decoderContext); - - void queueInputBuffer( - size_t index, int64_t timeUs, const sp &buffer); - -protected: - virtual ~AudioRenderer(); - virtual void onMessageReceived(const sp &msg); - -private: - enum { - kWhatPushAudio, - }; - - struct BufferInfo { - size_t mIndex; - int64_t mTimeUs; - sp mBuffer; - }; - - sp mDecoderContext; - sp mAudioTrack; - - List mInputBuffers; - bool mPushPending; - - size_t mNumFramesWritten; - - void schedulePushIfNecessary(); - void onPushAudio(); - - ssize_t writeNonBlocking(const uint8_t *data, size_t size); - - DISALLOW_EVIL_CONSTRUCTORS(AudioRenderer); -}; - -//////////////////////////////////////////////////////////////////////////////// - -DirectRenderer::DecoderContext::DecoderContext(const sp ¬ify) - : mNotify(notify), - mDecoderNotificationPending(false) { -} - -DirectRenderer::DecoderContext::~DecoderContext() { - if (mDecoder != NULL) { - mDecoder->release(); - mDecoder.clear(); - - mDecoderLooper->stop(); - mDecoderLooper.clear(); - } -} - -status_t DirectRenderer::DecoderContext::init( - const sp &format, - const sp &surfaceTex) { - CHECK(mDecoder == NULL); - - AString mime; - CHECK(format->findString("mime", &mime)); - - mDecoderLooper = new ALooper; - mDecoderLooper->setName("video codec looper"); - - mDecoderLooper->start( - false /* runOnCallingThread */, - false /* canCallJava */, - PRIORITY_DEFAULT); - - mDecoder = MediaCodec::CreateByType( - mDecoderLooper, mime.c_str(), false /* encoder */); - - CHECK(mDecoder != NULL); - - status_t err = mDecoder->configure( - format, - surfaceTex == NULL - ? NULL : new Surface(surfaceTex), - NULL /* crypto */, - 0 /* flags */); - CHECK_EQ(err, (status_t)OK); - - err = mDecoder->start(); - CHECK_EQ(err, (status_t)OK); - - err = mDecoder->getInputBuffers( - &mDecoderInputBuffers); - CHECK_EQ(err, (status_t)OK); - - err = mDecoder->getOutputBuffers( - &mDecoderOutputBuffers); - CHECK_EQ(err, (status_t)OK); - - scheduleDecoderNotification(); - - return OK; -} - -void DirectRenderer::DecoderContext::queueInputBuffer( - const sp &accessUnit) { - CHECK(mDecoder != NULL); - - mAccessUnits.push_back(accessUnit); - queueDecoderInputBuffers(); -} - -status_t DirectRenderer::DecoderContext::renderOutputBufferAndRelease( - size_t index) { - return mDecoder->renderOutputBufferAndRelease(index); -} - -status_t DirectRenderer::DecoderContext::releaseOutputBuffer(size_t index) { - return mDecoder->releaseOutputBuffer(index); -} - -void DirectRenderer::DecoderContext::queueDecoderInputBuffers() { - if (mDecoder == NULL) { - return; - } - - bool submittedMore = false; - - while (!mAccessUnits.empty() - && !mDecoderInputBuffersAvailable.empty()) { - size_t index = *mDecoderInputBuffersAvailable.begin(); - - mDecoderInputBuffersAvailable.erase( - mDecoderInputBuffersAvailable.begin()); - - sp srcBuffer = *mAccessUnits.begin(); - mAccessUnits.erase(mAccessUnits.begin()); - - const sp &dstBuffer = - mDecoderInputBuffers.itemAt(index); - - memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size()); - - int64_t timeUs; - CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs)); - - status_t err = mDecoder->queueInputBuffer( - index, - 0 /* offset */, - srcBuffer->size(), - timeUs, - 0 /* flags */); - CHECK_EQ(err, (status_t)OK); - - submittedMore = true; - } - - if (submittedMore) { - scheduleDecoderNotification(); - } -} - -void DirectRenderer::DecoderContext::onMessageReceived( - const sp &msg) { - switch (msg->what()) { - case kWhatDecoderNotify: - { - onDecoderNotify(); - break; - } - - default: - TRESPASS(); - } -} - -void DirectRenderer::DecoderContext::onDecoderNotify() { - mDecoderNotificationPending = false; - - for (;;) { - size_t index; - status_t err = mDecoder->dequeueInputBuffer(&index); - - if (err == OK) { - mDecoderInputBuffersAvailable.push_back(index); - } else if (err == -EAGAIN) { - break; - } else { - TRESPASS(); - } - } - - queueDecoderInputBuffers(); - - for (;;) { - size_t index; - size_t offset; - size_t size; - int64_t timeUs; - uint32_t flags; - status_t err = mDecoder->dequeueOutputBuffer( - &index, - &offset, - &size, - &timeUs, - &flags); - - if (err == OK) { - queueOutputBuffer( - index, timeUs, mDecoderOutputBuffers.itemAt(index)); - } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { - err = mDecoder->getOutputBuffers( - &mDecoderOutputBuffers); - CHECK_EQ(err, (status_t)OK); - } else if (err == INFO_FORMAT_CHANGED) { - // We don't care. - } else if (err == -EAGAIN) { - break; - } else { - TRESPASS(); - } - } - - scheduleDecoderNotification(); -} - -void DirectRenderer::DecoderContext::scheduleDecoderNotification() { - if (mDecoderNotificationPending) { - return; - } - - sp notify = - new AMessage(kWhatDecoderNotify, id()); - - mDecoder->requestActivityNotification(notify); - mDecoderNotificationPending = true; -} - -void DirectRenderer::DecoderContext::queueOutputBuffer( - size_t index, int64_t timeUs, const sp &buffer) { - sp msg = mNotify->dup(); - msg->setInt32("what", kWhatOutputBufferReady); - msg->setSize("index", index); - msg->setInt64("timeUs", timeUs); - msg->setBuffer("buffer", buffer); - msg->post(); -} - -//////////////////////////////////////////////////////////////////////////////// - -DirectRenderer::AudioRenderer::AudioRenderer( - const sp &decoderContext) - : mDecoderContext(decoderContext), - mPushPending(false), - mNumFramesWritten(0) { - mAudioTrack = new AudioTrack( - AUDIO_STREAM_DEFAULT, - 48000.0f, - AUDIO_FORMAT_PCM, - AUDIO_CHANNEL_OUT_STEREO, - (int)0 /* frameCount */); - - CHECK_EQ((status_t)OK, mAudioTrack->initCheck()); - - mAudioTrack->start(); -} - -DirectRenderer::AudioRenderer::~AudioRenderer() { -} - -void DirectRenderer::AudioRenderer::queueInputBuffer( - size_t index, int64_t timeUs, const sp &buffer) { - BufferInfo info; - info.mIndex = index; - info.mTimeUs = timeUs; - info.mBuffer = buffer; - - mInputBuffers.push_back(info); - schedulePushIfNecessary(); -} - -void DirectRenderer::AudioRenderer::onMessageReceived( - const sp &msg) { - switch (msg->what()) { - case kWhatPushAudio: - { - onPushAudio(); - break; - } - - default: - break; - } -} - -void DirectRenderer::AudioRenderer::schedulePushIfNecessary() { - if (mPushPending || mInputBuffers.empty()) { - return; - } - - mPushPending = true; - - uint32_t numFramesPlayed; - CHECK_EQ(mAudioTrack->getPosition(&numFramesPlayed), - (status_t)OK); - - uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed; - - // This is how long the audio sink will have data to - // play back. - const float msecsPerFrame = 1000.0f / mAudioTrack->getSampleRate(); - - int64_t delayUs = - msecsPerFrame * numFramesPendingPlayout * 1000ll; - - // Let's give it more data after about half that time - // has elapsed. - (new AMessage(kWhatPushAudio, id()))->post(delayUs / 2); -} - -void DirectRenderer::AudioRenderer::onPushAudio() { - mPushPending = false; - - while (!mInputBuffers.empty()) { - const BufferInfo &info = *mInputBuffers.begin(); - - ssize_t n = writeNonBlocking( - info.mBuffer->data(), info.mBuffer->size()); - - if (n < (ssize_t)info.mBuffer->size()) { - CHECK_GE(n, 0); - - info.mBuffer->setRange( - info.mBuffer->offset() + n, info.mBuffer->size() - n); - break; - } - - mDecoderContext->releaseOutputBuffer(info.mIndex); - - mInputBuffers.erase(mInputBuffers.begin()); - } - - schedulePushIfNecessary(); -} - -ssize_t DirectRenderer::AudioRenderer::writeNonBlocking( - const uint8_t *data, size_t size) { - uint32_t numFramesPlayed; - status_t err = mAudioTrack->getPosition(&numFramesPlayed); - if (err != OK) { - return err; - } - - ssize_t numFramesAvailableToWrite = - mAudioTrack->frameCount() - (mNumFramesWritten - numFramesPlayed); - - size_t numBytesAvailableToWrite = - numFramesAvailableToWrite * mAudioTrack->frameSize(); - - if (size > numBytesAvailableToWrite) { - size = numBytesAvailableToWrite; - } - - CHECK_EQ(mAudioTrack->write(data, size), (ssize_t)size); - - size_t numFramesWritten = size / mAudioTrack->frameSize(); - mNumFramesWritten += numFramesWritten; - - return size; -} - -//////////////////////////////////////////////////////////////////////////////// - -DirectRenderer::DirectRenderer( - const sp &bufferProducer) - : mSurfaceTex(bufferProducer), - mVideoRenderPending(false), - mNumFramesLate(0), - mNumFrames(0) { -} - -DirectRenderer::~DirectRenderer() { -} - -void DirectRenderer::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatDecoderNotify: - { - onDecoderNotify(msg); - break; - } - - case kWhatRenderVideo: - { - onRenderVideo(); - break; - } - - case kWhatQueueAccessUnit: - onQueueAccessUnit(msg); - break; - - case kWhatSetFormat: - onSetFormat(msg); - break; - - default: - TRESPASS(); - } -} - -void DirectRenderer::setFormat(size_t trackIndex, const sp &format) { - sp msg = new AMessage(kWhatSetFormat, id()); - msg->setSize("trackIndex", trackIndex); - msg->setMessage("format", format); - msg->post(); -} - -void DirectRenderer::onSetFormat(const sp &msg) { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - sp format; - CHECK(msg->findMessage("format", &format)); - - internalSetFormat(trackIndex, format); -} - -void DirectRenderer::internalSetFormat( - size_t trackIndex, const sp &format) { - CHECK_LT(trackIndex, 2u); - - CHECK(mDecoderContext[trackIndex] == NULL); - - sp notify = new AMessage(kWhatDecoderNotify, id()); - notify->setSize("trackIndex", trackIndex); - - mDecoderContext[trackIndex] = new DecoderContext(notify); - looper()->registerHandler(mDecoderContext[trackIndex]); - - CHECK_EQ((status_t)OK, - mDecoderContext[trackIndex]->init( - format, trackIndex == 0 ? mSurfaceTex : NULL)); - - if (trackIndex == 1) { - // Audio - mAudioRenderer = new AudioRenderer(mDecoderContext[1]); - looper()->registerHandler(mAudioRenderer); - } -} - -void DirectRenderer::queueAccessUnit( - size_t trackIndex, const sp &accessUnit) { - sp msg = new AMessage(kWhatQueueAccessUnit, id()); - msg->setSize("trackIndex", trackIndex); - msg->setBuffer("accessUnit", accessUnit); - msg->post(); -} - -void DirectRenderer::onQueueAccessUnit(const sp &msg) { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - CHECK_LT(trackIndex, 2u); - CHECK(mDecoderContext[trackIndex] != NULL); - - mDecoderContext[trackIndex]->queueInputBuffer(accessUnit); -} - -void DirectRenderer::onDecoderNotify(const sp &msg) { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case DecoderContext::kWhatOutputBufferReady: - { - size_t index; - CHECK(msg->findSize("index", &index)); - - int64_t timeUs; - CHECK(msg->findInt64("timeUs", &timeUs)); - - sp buffer; - CHECK(msg->findBuffer("buffer", &buffer)); - - queueOutputBuffer(trackIndex, index, timeUs, buffer); - break; - } - - default: - TRESPASS(); - } -} - -void DirectRenderer::queueOutputBuffer( - size_t trackIndex, - size_t index, int64_t timeUs, const sp &buffer) { - if (trackIndex == 1) { - // Audio - mAudioRenderer->queueInputBuffer(index, timeUs, buffer); - return; - } - - OutputInfo info; - info.mIndex = index; - info.mTimeUs = timeUs; - info.mBuffer = buffer; - mVideoOutputBuffers.push_back(info); - - scheduleVideoRenderIfNecessary(); -} - -void DirectRenderer::scheduleVideoRenderIfNecessary() { - if (mVideoRenderPending || mVideoOutputBuffers.empty()) { - return; - } - - mVideoRenderPending = true; - - int64_t timeUs = (*mVideoOutputBuffers.begin()).mTimeUs; - int64_t nowUs = ALooper::GetNowUs(); - - int64_t delayUs = timeUs - nowUs; - - (new AMessage(kWhatRenderVideo, id()))->post(delayUs); -} - -void DirectRenderer::onRenderVideo() { - mVideoRenderPending = false; - - int64_t nowUs = ALooper::GetNowUs(); - - while (!mVideoOutputBuffers.empty()) { - const OutputInfo &info = *mVideoOutputBuffers.begin(); - - if (info.mTimeUs > nowUs) { - break; - } - - if (info.mTimeUs + 15000ll < nowUs) { - ++mNumFramesLate; - } - ++mNumFrames; - - status_t err = - mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex); - CHECK_EQ(err, (status_t)OK); - - mVideoOutputBuffers.erase(mVideoOutputBuffers.begin()); - } - - scheduleVideoRenderIfNecessary(); -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h deleted file mode 100644 index 07c2170..0000000 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 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 DIRECT_RENDERER_H_ - -#define DIRECT_RENDERER_H_ - -#include - -namespace android { - -struct ABuffer; -struct IGraphicBufferProducer; - -// Renders audio and video data queued by calls to "queueAccessUnit". -struct DirectRenderer : public AHandler { - DirectRenderer(const sp &bufferProducer); - - void setFormat(size_t trackIndex, const sp &format); - void queueAccessUnit(size_t trackIndex, const sp &accessUnit); - -protected: - virtual void onMessageReceived(const sp &msg); - virtual ~DirectRenderer(); - -private: - struct DecoderContext; - struct AudioRenderer; - - enum { - kWhatDecoderNotify, - kWhatRenderVideo, - kWhatQueueAccessUnit, - kWhatSetFormat, - }; - - struct OutputInfo { - size_t mIndex; - int64_t mTimeUs; - sp mBuffer; - }; - - sp mSurfaceTex; - - sp mDecoderContext[2]; - List mVideoOutputBuffers; - - bool mVideoRenderPending; - - sp mAudioRenderer; - - int32_t mNumFramesLate; - int32_t mNumFrames; - - void onDecoderNotify(const sp &msg); - - void queueOutputBuffer( - size_t trackIndex, - size_t index, int64_t timeUs, const sp &buffer); - - void scheduleVideoRenderIfNecessary(); - void onRenderVideo(); - - void onSetFormat(const sp &msg); - void onQueueAccessUnit(const sp &msg); - - void internalSetFormat(size_t trackIndex, const sp &format); - - DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); -}; - -} // namespace android - -#endif // DIRECT_RENDERER_H_ diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp deleted file mode 100644 index bc88f1e..0000000 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ /dev/null @@ -1,917 +0,0 @@ -/* - * Copyright 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 "WifiDisplaySink" -#include - -#include "WifiDisplaySink.h" - -#include "DirectRenderer.h" -#include "MediaReceiver.h" -#include "TimeSyncer.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -// static -const AString WifiDisplaySink::sUserAgent = MakeUserAgent(); - -WifiDisplaySink::WifiDisplaySink( - uint32_t flags, - const sp &netSession, - const sp &bufferProducer, - const sp ¬ify) - : mState(UNDEFINED), - mFlags(flags), - mNetSession(netSession), - mSurfaceTex(bufferProducer), - mNotify(notify), - mUsingTCPTransport(false), - mUsingTCPInterleaving(false), - mSessionID(0), - mNextCSeq(1), - mIDRFrameRequestPending(false), - mTimeOffsetUs(0ll), - mTimeOffsetValid(false), - mSetupDeferred(false), - mLatencyCount(0), - mLatencySumUs(0ll), - mLatencyMaxUs(0ll), - mMaxDelayMs(-1ll) { - // We support any and all resolutions, but prefer 720p30 - mSinkSupportedVideoFormats.setNativeResolution( - VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 - - mSinkSupportedVideoFormats.enableAll(); -} - -WifiDisplaySink::~WifiDisplaySink() { -} - -void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) { - sp msg = new AMessage(kWhatStart, id()); - msg->setString("sourceHost", sourceHost); - msg->setInt32("sourcePort", sourcePort); - msg->post(); -} - -void WifiDisplaySink::start(const char *uri) { - sp msg = new AMessage(kWhatStart, id()); - msg->setString("setupURI", uri); - msg->post(); -} - -// static -bool WifiDisplaySink::ParseURL( - const char *url, AString *host, int32_t *port, AString *path, - AString *user, AString *pass) { - host->clear(); - *port = 0; - path->clear(); - user->clear(); - pass->clear(); - - if (strncasecmp("rtsp://", url, 7)) { - return false; - } - - const char *slashPos = strchr(&url[7], '/'); - - if (slashPos == NULL) { - host->setTo(&url[7]); - path->setTo("/"); - } else { - host->setTo(&url[7], slashPos - &url[7]); - path->setTo(slashPos); - } - - ssize_t atPos = host->find("@"); - - if (atPos >= 0) { - // Split of user:pass@ from hostname. - - AString userPass(*host, 0, atPos); - host->erase(0, atPos + 1); - - ssize_t colonPos = userPass.find(":"); - - if (colonPos < 0) { - *user = userPass; - } else { - user->setTo(userPass, 0, colonPos); - pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1); - } - } - - const char *colonPos = strchr(host->c_str(), ':'); - - if (colonPos != NULL) { - char *end; - unsigned long x = strtoul(colonPos + 1, &end, 10); - - if (end == colonPos + 1 || *end != '\0' || x >= 65536) { - return false; - } - - *port = x; - - size_t colonOffset = colonPos - host->c_str(); - size_t trailing = host->size() - colonOffset; - host->erase(colonOffset, trailing); - } else { - *port = 554; - } - - return true; -} - -void WifiDisplaySink::onMessageReceived(const sp &msg) { - switch (msg->what()) { - case kWhatStart: - { - sleep(2); // XXX - - int32_t sourcePort; - CHECK(msg->findString("sourceHost", &mRTSPHost)); - CHECK(msg->findInt32("sourcePort", &sourcePort)); - - sp notify = new AMessage(kWhatRTSPNotify, id()); - - status_t err = mNetSession->createRTSPClient( - mRTSPHost.c_str(), sourcePort, notify, &mSessionID); - CHECK_EQ(err, (status_t)OK); - - mState = CONNECTING; - break; - } - - case kWhatRTSPNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred in session %d (%d, '%s/%s').", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - if (sessionID == mSessionID) { - ALOGI("Lost control connection."); - - // The control connection is dead now. - mNetSession->destroySession(mSessionID); - mSessionID = 0; - - if (mNotify == NULL) { - looper()->stop(); - } else { - sp notify = mNotify->dup(); - notify->setInt32("what", kWhatDisconnected); - notify->post(); - } - } - break; - } - - case ANetworkSession::kWhatConnected: - { - ALOGI("We're now connected."); - mState = CONNECTED; - - if (mFlags & FLAG_SPECIAL_MODE) { - sp notify = new AMessage( - kWhatTimeSyncerNotify, id()); - - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - mTimeSyncer->startClient(mRTSPHost.c_str(), 8123); - } - break; - } - - case ANetworkSession::kWhatData: - { - onReceiveClientData(msg); - break; - } - - default: - TRESPASS(); - } - break; - } - - case kWhatStop: - { - looper()->stop(); - break; - } - - case kWhatMediaReceiverNotify: - { - onMediaReceiverNotify(msg); - break; - } - - case kWhatTimeSyncerNotify: - { - int32_t what; - CHECK(msg->findInt32("what", &what)); - - if (what == TimeSyncer::kWhatTimeOffset) { - CHECK(msg->findInt64("offset", &mTimeOffsetUs)); - mTimeOffsetValid = true; - - if (mSetupDeferred) { - CHECK_EQ((status_t)OK, - sendSetup( - mSessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0")); - - mSetupDeferred = false; - } - } - break; - } - - case kWhatReportLateness: - { - if (mLatencyCount > 0) { - int64_t avgLatencyUs = mLatencySumUs / mLatencyCount; - - ALOGV("avg. latency = %lld ms (max %lld ms)", - avgLatencyUs / 1000ll, - mLatencyMaxUs / 1000ll); - - sp params = new AMessage; - params->setInt64("avgLatencyUs", avgLatencyUs); - params->setInt64("maxLatencyUs", mLatencyMaxUs); - mMediaReceiver->informSender(0 /* trackIndex */, params); - } - - mLatencyCount = 0; - mLatencySumUs = 0ll; - mLatencyMaxUs = 0ll; - - msg->post(kReportLatenessEveryUs); - break; - } - - default: - TRESPASS(); - } -} - -void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) { - int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll; - - if (delayMs > mMaxDelayMs) { - mMaxDelayMs = delayMs; - } - - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - ALOGI("[%lld]: (%4lld ms / %4lld ms) %s", - timeUs / 1000, - delayMs, - mMaxDelayMs, - kPattern + kPatternSize - n); -} - -void WifiDisplaySink::onMediaReceiverNotify(const sp &msg) { - int32_t what; - CHECK(msg->findInt32("what", &what)); - - switch (what) { - case MediaReceiver::kWhatInitDone: - { - status_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGI("MediaReceiver initialization completed w/ err %d", err); - break; - } - - case MediaReceiver::kWhatError: - { - status_t err; - CHECK(msg->findInt32("err", &err)); - - ALOGE("MediaReceiver signaled error %d", err); - break; - } - - case MediaReceiver::kWhatAccessUnit: - { - if (mRenderer == NULL) { - mRenderer = new DirectRenderer(mSurfaceTex); - looper()->registerHandler(mRenderer); - } - - sp accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) { - mTimeOffsetUs = timeUs - ALooper::GetNowUs(); - mTimeOffsetValid = true; - } - - CHECK(mTimeOffsetValid); - - // We are the timesync _client_, - // client time = server time - time offset. - timeUs -= mTimeOffsetUs; - - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayUs = nowUs - timeUs; - - mLatencySumUs += delayUs; - if (mLatencyCount == 0 || delayUs > mLatencyMaxUs) { - mLatencyMaxUs = delayUs; - } - ++mLatencyCount; - - // dumpDelay(trackIndex, timeUs); - - timeUs += 220000ll; // Assume 220 ms of latency - accessUnit->meta()->setInt64("timeUs", timeUs); - - sp format; - if (msg->findMessage("format", &format)) { - mRenderer->setFormat(trackIndex, format); - } - - mRenderer->queueAccessUnit(trackIndex, accessUnit); - break; - } - - case MediaReceiver::kWhatPacketLost: - { -#if 0 - if (!mIDRFrameRequestPending) { - ALOGI("requesting IDR frame"); - - sendIDRFrameRequest(mSessionID); - } -#endif - break; - } - - default: - TRESPASS(); - } -} - -void WifiDisplaySink::registerResponseHandler( - int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) { - ResponseID id; - id.mSessionID = sessionID; - id.mCSeq = cseq; - mResponseHandlers.add(id, func); -} - -status_t WifiDisplaySink::sendM2(int32_t sessionID) { - AString request = "OPTIONS * RTSP/1.0\r\n"; - AppendCommonResponse(&request, mNextCSeq); - - request.append( - "Require: org.wfa.wfd1.0\r\n" - "\r\n"); - - status_t err = - mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response); - - ++mNextCSeq; - - return OK; -} - -status_t WifiDisplaySink::onReceiveM2Response( - int32_t sessionID, const sp &msg) { - int32_t statusCode; - if (!msg->getStatusCode(&statusCode)) { - return ERROR_MALFORMED; - } - - if (statusCode != 200) { - return ERROR_UNSUPPORTED; - } - - return OK; -} - -status_t WifiDisplaySink::onReceiveSetupResponse( - int32_t sessionID, const sp &msg) { - int32_t statusCode; - if (!msg->getStatusCode(&statusCode)) { - return ERROR_MALFORMED; - } - - if (statusCode != 200) { - return ERROR_UNSUPPORTED; - } - - if (!msg->findString("session", &mPlaybackSessionID)) { - return ERROR_MALFORMED; - } - - if (!ParsedMessage::GetInt32Attribute( - mPlaybackSessionID.c_str(), - "timeout", - &mPlaybackSessionTimeoutSecs)) { - mPlaybackSessionTimeoutSecs = -1; - } - - ssize_t colonPos = mPlaybackSessionID.find(";"); - if (colonPos >= 0) { - // Strip any options from the returned session id. - mPlaybackSessionID.erase( - colonPos, mPlaybackSessionID.size() - colonPos); - } - - status_t err = configureTransport(msg); - - if (err != OK) { - return err; - } - - mState = PAUSED; - - return sendPlay( - sessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); -} - -status_t WifiDisplaySink::configureTransport(const sp &msg) { - if (mUsingTCPTransport && !(mFlags & FLAG_SPECIAL_MODE)) { - // In "special" mode we still use a UDP RTCP back-channel that - // needs connecting. - return OK; - } - - AString transport; - if (!msg->findString("transport", &transport)) { - ALOGE("Missing 'transport' field in SETUP response."); - return ERROR_MALFORMED; - } - - AString sourceHost; - if (!ParsedMessage::GetAttribute( - transport.c_str(), "source", &sourceHost)) { - sourceHost = mRTSPHost; - } - - AString serverPortStr; - if (!ParsedMessage::GetAttribute( - transport.c_str(), "server_port", &serverPortStr)) { - ALOGE("Missing 'server_port' in Transport field."); - return ERROR_MALFORMED; - } - - int rtpPort, rtcpPort; - if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2 - || rtpPort <= 0 || rtpPort > 65535 - || rtcpPort <=0 || rtcpPort > 65535 - || rtcpPort != rtpPort + 1) { - ALOGE("Invalid server_port description '%s'.", - serverPortStr.c_str()); - - return ERROR_MALFORMED; - } - - if (rtpPort & 1) { - ALOGW("Server picked an odd numbered RTP port."); - } - - return mMediaReceiver->connectTrack( - 0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort); -} - -status_t WifiDisplaySink::onReceivePlayResponse( - int32_t sessionID, const sp &msg) { - int32_t statusCode; - if (!msg->getStatusCode(&statusCode)) { - return ERROR_MALFORMED; - } - - if (statusCode != 200) { - return ERROR_UNSUPPORTED; - } - - mState = PLAYING; - - (new AMessage(kWhatReportLateness, id()))->post(kReportLatenessEveryUs); - - return OK; -} - -status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse( - int32_t sessionID, const sp &msg) { - CHECK(mIDRFrameRequestPending); - mIDRFrameRequestPending = false; - - return OK; -} - -void WifiDisplaySink::onReceiveClientData(const sp &msg) { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp obj; - CHECK(msg->findObject("data", &obj)); - - sp data = - static_cast(obj.get()); - - ALOGV("session %d received '%s'", - sessionID, data->debugString().c_str()); - - AString method; - AString uri; - data->getRequestField(0, &method); - - int32_t cseq; - if (!data->findInt32("cseq", &cseq)) { - sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */); - return; - } - - if (method.startsWith("RTSP/")) { - // This is a response. - - ResponseID id; - id.mSessionID = sessionID; - id.mCSeq = cseq; - - ssize_t index = mResponseHandlers.indexOfKey(id); - - if (index < 0) { - ALOGW("Received unsolicited server response, cseq %d", cseq); - return; - } - - HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index); - mResponseHandlers.removeItemsAt(index); - - status_t err = (this->*func)(sessionID, data); - CHECK_EQ(err, (status_t)OK); - } else { - AString version; - data->getRequestField(2, &version); - if (!(version == AString("RTSP/1.0"))) { - sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq); - return; - } - - if (method == "OPTIONS") { - onOptionsRequest(sessionID, cseq, data); - } else if (method == "GET_PARAMETER") { - onGetParameterRequest(sessionID, cseq, data); - } else if (method == "SET_PARAMETER") { - onSetParameterRequest(sessionID, cseq, data); - } else { - sendErrorResponse(sessionID, "405 Method Not Allowed", cseq); - } - } -} - -void WifiDisplaySink::onOptionsRequest( - int32_t sessionID, - int32_t cseq, - const sp &data) { - AString response = "RTSP/1.0 200 OK\r\n"; - AppendCommonResponse(&response, cseq); - response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n"); - response.append("\r\n"); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); - - err = sendM2(sessionID); - CHECK_EQ(err, (status_t)OK); -} - -void WifiDisplaySink::onGetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data) { - AString body; - - if (mState == CONNECTED) { - mUsingTCPTransport = false; - mUsingTCPInterleaving = false; - - char val[PROPERTY_VALUE_MAX]; - if (property_get("media.wfd-sink.tcp-mode", val, NULL)) { - if (!strcasecmp("true", val) || !strcmp("1", val)) { - ALOGI("Using TCP unicast transport."); - mUsingTCPTransport = true; - mUsingTCPInterleaving = false; - } else if (!strcasecmp("interleaved", val)) { - ALOGI("Using TCP interleaved transport."); - mUsingTCPTransport = true; - mUsingTCPInterleaving = true; - } - } else if (mFlags & FLAG_SPECIAL_MODE) { - mUsingTCPTransport = true; - } - - body = "wfd_video_formats: "; - body.append(mSinkSupportedVideoFormats.getFormatSpec()); - - body.append( - "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n" - "wfd_client_rtp_ports: RTP/AVP/"); - - if (mUsingTCPTransport) { - body.append("TCP;"); - if (mUsingTCPInterleaving) { - body.append("interleaved"); - } else { - body.append("unicast 19000 0"); - } - } else { - body.append("UDP;unicast 19000 0"); - } - - body.append(" mode=play\r\n"); - } - - AString response = "RTSP/1.0 200 OK\r\n"; - AppendCommonResponse(&response, cseq); - response.append("Content-Type: text/parameters\r\n"); - response.append(StringPrintf("Content-Length: %d\r\n", body.size())); - response.append("\r\n"); - response.append(body); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); -} - -status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { - sp notify = new AMessage(kWhatMediaReceiverNotify, id()); - - mMediaReceiverLooper = new ALooper; - mMediaReceiverLooper->setName("media_receiver"); - - mMediaReceiverLooper->start( - false /* runOnCallingThread */, - false /* canCallJava */, - PRIORITY_AUDIO); - - mMediaReceiver = new MediaReceiver(mNetSession, notify); - mMediaReceiverLooper->registerHandler(mMediaReceiver); - - RTPReceiver::TransportMode rtpMode = RTPReceiver::TRANSPORT_UDP; - if (mUsingTCPTransport) { - if (mUsingTCPInterleaving) { - rtpMode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED; - } else { - rtpMode = RTPReceiver::TRANSPORT_TCP; - } - } - - int32_t localRTPPort; - status_t err = mMediaReceiver->addTrack( - rtpMode, RTPReceiver::TRANSPORT_UDP /* rtcpMode */, &localRTPPort); - - if (err == OK) { - err = mMediaReceiver->initAsync(MediaReceiver::MODE_TRANSPORT_STREAM); - } - - if (err != OK) { - mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id()); - mMediaReceiver.clear(); - - mMediaReceiverLooper->stop(); - mMediaReceiverLooper.clear(); - - return err; - } - - AString request = StringPrintf("SETUP %s RTSP/1.0\r\n", uri); - - AppendCommonResponse(&request, mNextCSeq); - - if (rtpMode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) { - request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n"); - } else if (rtpMode == RTPReceiver::TRANSPORT_TCP) { - if (mFlags & FLAG_SPECIAL_MODE) { - // This isn't quite true, since the RTP connection is through TCP - // and the RTCP connection through UDP... - request.append( - StringPrintf( - "Transport: RTP/AVP/TCP;unicast;client_port=%d-%d\r\n", - localRTPPort, localRTPPort + 1)); - } else { - request.append( - StringPrintf( - "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n", - localRTPPort)); - } - } else { - request.append( - StringPrintf( - "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", - localRTPPort, - localRTPPort + 1)); - } - - request.append("\r\n"); - - ALOGV("request = '%s'", request.c_str()); - - err = mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse); - - ++mNextCSeq; - - return OK; -} - -status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) { - AString request = StringPrintf("PLAY %s RTSP/1.0\r\n", uri); - - AppendCommonResponse(&request, mNextCSeq); - - request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); - request.append("\r\n"); - - status_t err = - mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse); - - ++mNextCSeq; - - return OK; -} - -status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) { - CHECK(!mIDRFrameRequestPending); - - AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; - - AppendCommonResponse(&request, mNextCSeq); - - AString content = "wfd_idr_request\r\n"; - - request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); - request.append(StringPrintf("Content-Length: %d\r\n", content.size())); - request.append("\r\n"); - request.append(content); - - status_t err = - mNetSession->sendRequest(sessionID, request.c_str(), request.size()); - - if (err != OK) { - return err; - } - - registerResponseHandler( - sessionID, - mNextCSeq, - &WifiDisplaySink::onReceiveIDRFrameRequestResponse); - - ++mNextCSeq; - - mIDRFrameRequestPending = true; - - return OK; -} - -void WifiDisplaySink::onSetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data) { - const char *content = data->getContent(); - - if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) { - if ((mFlags & FLAG_SPECIAL_MODE) && !mTimeOffsetValid) { - mSetupDeferred = true; - } else { - status_t err = - sendSetup( - sessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); - - CHECK_EQ(err, (status_t)OK); - } - } - - AString response = "RTSP/1.0 200 OK\r\n"; - AppendCommonResponse(&response, cseq); - response.append("\r\n"); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); -} - -void WifiDisplaySink::sendErrorResponse( - int32_t sessionID, - const char *errorDetail, - int32_t cseq) { - AString response; - response.append("RTSP/1.0 "); - response.append(errorDetail); - response.append("\r\n"); - - AppendCommonResponse(&response, cseq); - - response.append("\r\n"); - - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); -} - -// static -void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) { - time_t now = time(NULL); - struct tm *now2 = gmtime(&now); - char buf[128]; - strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2); - - response->append("Date: "); - response->append(buf); - response->append("\r\n"); - - response->append(StringPrintf("User-Agent: %s\r\n", sUserAgent.c_str())); - - if (cseq >= 0) { - response->append(StringPrintf("CSeq: %d\r\n", cseq)); - } -} - -} // namespace android diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h deleted file mode 100644 index dc1fc32..0000000 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 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 WIFI_DISPLAY_SINK_H_ - -#define WIFI_DISPLAY_SINK_H_ - -#include "VideoFormats.h" - -#include -#include -#include - -namespace android { - -struct AMessage; -struct DirectRenderer; -struct MediaReceiver; -struct ParsedMessage; -struct TimeSyncer; - -// Represents the RTSP client acting as a wifi display sink. -// Connects to a wifi display source and renders the incoming -// transport stream using a MediaPlayer instance. -struct WifiDisplaySink : public AHandler { - enum { - kWhatDisconnected, - }; - - enum Flags { - FLAG_SPECIAL_MODE = 1, - }; - - // If no notification message is specified (notify == NULL) - // the sink will stop its looper() once the session ends, - // otherwise it will post an appropriate notification but leave - // the looper() running. - WifiDisplaySink( - uint32_t flags, - const sp &netSession, - const sp &bufferProducer = NULL, - const sp ¬ify = NULL); - - void start(const char *sourceHost, int32_t sourcePort); - void start(const char *uri); - -protected: - virtual ~WifiDisplaySink(); - virtual void onMessageReceived(const sp &msg); - -private: - enum State { - UNDEFINED, - CONNECTING, - CONNECTED, - PAUSED, - PLAYING, - }; - - enum { - kWhatStart, - kWhatRTSPNotify, - kWhatStop, - kWhatMediaReceiverNotify, - kWhatTimeSyncerNotify, - kWhatReportLateness, - }; - - struct ResponseID { - int32_t mSessionID; - int32_t mCSeq; - - bool operator<(const ResponseID &other) const { - return mSessionID < other.mSessionID - || (mSessionID == other.mSessionID - && mCSeq < other.mCSeq); - } - }; - - typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)( - int32_t sessionID, const sp &msg); - - static const int64_t kReportLatenessEveryUs = 1000000ll; - - static const AString sUserAgent; - - State mState; - uint32_t mFlags; - VideoFormats mSinkSupportedVideoFormats; - sp mNetSession; - sp mSurfaceTex; - sp mNotify; - sp mTimeSyncer; - bool mUsingTCPTransport; - bool mUsingTCPInterleaving; - AString mRTSPHost; - int32_t mSessionID; - - int32_t mNextCSeq; - - KeyedVector mResponseHandlers; - - sp mMediaReceiverLooper; - sp mMediaReceiver; - sp mRenderer; - - AString mPlaybackSessionID; - int32_t mPlaybackSessionTimeoutSecs; - - bool mIDRFrameRequestPending; - - int64_t mTimeOffsetUs; - bool mTimeOffsetValid; - - bool mSetupDeferred; - - size_t mLatencyCount; - int64_t mLatencySumUs; - int64_t mLatencyMaxUs; - - int64_t mMaxDelayMs; - - status_t sendM2(int32_t sessionID); - status_t sendSetup(int32_t sessionID, const char *uri); - status_t sendPlay(int32_t sessionID, const char *uri); - status_t sendIDRFrameRequest(int32_t sessionID); - - status_t onReceiveM2Response( - int32_t sessionID, const sp &msg); - - status_t onReceiveSetupResponse( - int32_t sessionID, const sp &msg); - - status_t configureTransport(const sp &msg); - - status_t onReceivePlayResponse( - int32_t sessionID, const sp &msg); - - status_t onReceiveIDRFrameRequestResponse( - int32_t sessionID, const sp &msg); - - void registerResponseHandler( - int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func); - - void onReceiveClientData(const sp &msg); - - void onOptionsRequest( - int32_t sessionID, - int32_t cseq, - const sp &data); - - void onGetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data); - - void onSetParameterRequest( - int32_t sessionID, - int32_t cseq, - const sp &data); - - void onMediaReceiverNotify(const sp &msg); - - void sendErrorResponse( - int32_t sessionID, - const char *errorDetail, - int32_t cseq); - - static void AppendCommonResponse(AString *response, int32_t cseq); - - bool ParseURL( - const char *url, AString *host, int32_t *port, AString *path, - AString *user, AString *pass); - - void dumpDelay(size_t trackIndex, int64_t timeUs); - - DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink); -}; - -} // namespace android - -#endif // WIFI_DISPLAY_SINK_H_ diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index d72349d..05e4018 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -22,7 +22,6 @@ #include "PlaybackSession.h" #include "Parameters.h" #include "rtp/RTPSender.h" -#include "TimeSyncer.h" #include #include @@ -173,15 +172,7 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { } } - if (err == OK) { - sp notify = new AMessage(kWhatTimeSyncerNotify, id()); - mTimeSyncer = new TimeSyncer(mNetSession, notify); - looper()->registerHandler(mTimeSyncer); - - mTimeSyncer->startServer(8123); - - mState = AWAITING_CLIENT_CONNECTION; - } + mState = AWAITING_CLIENT_CONNECTION; sp response = new AMessage; response->setInt32("err", err); @@ -556,11 +547,6 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { break; } - case kWhatTimeSyncerNotify: - { - break; - } - default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 4f11712..750265f 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -30,7 +30,6 @@ namespace android { struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; -struct TimeSyncer; // Represents the RTSP server acting as a wifi display source. // Manages incoming connections, sets up Playback sessions as necessary. @@ -83,7 +82,6 @@ private: kWhatHDCPNotify, kWhatFinishStop2, kWhatTeardownTriggerTimedOut, - kWhatTimeSyncerNotify, }; struct ResponseID { @@ -120,7 +118,6 @@ private: sp mNetSession; sp mClient; AString mMediaPath; - sp mTimeSyncer; struct in_addr mInterfaceAddr; int32_t mSessionID; diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp deleted file mode 100644 index 61eb9f9..0000000 --- a/media/libstagefright/wifi-display/udptest.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 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_NEBUG 0 -#define LOG_TAG "udptest" -#include - -#include "TimeSyncer.h" - -#include -#include -#include - -namespace android { - -} // namespace android - -static void usage(const char *me) { - fprintf(stderr, - "usage: %s -c host[:port]\tconnect to test server\n" - " -l \tcreate a test server\n", - me); -} - -int main(int argc, char **argv) { - using namespace android; - - ProcessState::self()->startThreadPool(); - - int32_t localPort = -1; - int32_t connectToPort = -1; - AString connectToHost; - - int res; - while ((res = getopt(argc, argv, "hc:l:")) >= 0) { - switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - connectToHost = optarg; - connectToPort = 49152; - } else { - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 1 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - } - break; - } - - case 'l': - { - char *end; - localPort = strtol(optarg, &end, 10); - - if (*end != '\0' || end == optarg - || localPort < 1 || localPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - break; - } - - case '?': - case 'h': - usage(argv[0]); - exit(1); - } - } - - if (localPort < 0 && connectToPort < 0) { - fprintf(stderr, - "You need to select either client or server mode.\n"); - exit(1); - } - - sp netSession = new ANetworkSession; - netSession->start(); - - sp looper = new ALooper; - - sp handler = new TimeSyncer(netSession, NULL /* notify */); - looper->registerHandler(handler); - - if (localPort >= 0) { - handler->startServer(localPort); - } else { - handler->startClient(connectToHost.c_str(), connectToPort); - } - - looper->start(true /* runOnCallingThread */); - - return 0; -} - -- cgit v1.1 From a73c954d947748a3b6f630cf2c160fe55ec596e3 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 2 Oct 2013 11:25:20 -0700 Subject: MediaCodec: avoid codec EOS if flushing during port reconfiguration Change-Id: Ic2e93f38feeb2e906f8d6b400ed6df2b7580ca87 Signed-off-by: Lajos Molnar Bug: 11045434 --- media/libstagefright/ACodec.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index bfb730c..8158d70 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3026,16 +3026,17 @@ void ACodec::BaseState::onInputBufferFilled(const sp &msg) { sp buffer; int32_t err = OK; bool eos = false; + PortMode mode = getPortMode(kPortIndexInput); if (!msg->findBuffer("buffer", &buffer)) { + /* these are unfilled buffers returned by client */ CHECK(msg->findInt32("err", &err)); ALOGV("[%s] saw error %d instead of an input buffer", mCodec->mComponentName.c_str(), err); buffer.clear(); - - eos = true; + mode = KEEP_BUFFERS; } int32_t tmp; @@ -3049,8 +3050,6 @@ void ACodec::BaseState::onInputBufferFilled(const sp &msg) { info->mStatus = BufferInfo::OWNED_BY_US; - PortMode mode = getPortMode(kPortIndexInput); - switch (mode) { case KEEP_BUFFERS: { -- cgit v1.1 From a306ee6bc1aef463f8984be26b8a4214490b6c55 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 3 Oct 2013 08:43:20 -0700 Subject: Remove now unused wfd commandline tool. (also unbreaks the build) Change-Id: I03320342976e4e355cc6aeb3d2c485985613b3b5 related-to-bug: 11047222 --- media/libstagefright/wifi-display/Android.mk | 21 -- media/libstagefright/wifi-display/wfd.cpp | 363 --------------------------- 2 files changed, 384 deletions(-) delete mode 100644 media/libstagefright/wifi-display/wfd.cpp (limited to 'media') diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 3abe8a8..f70454a 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -35,24 +35,3 @@ LOCAL_MODULE:= libstagefright_wfd LOCAL_MODULE_TAGS:= optional include $(BUILD_SHARED_LIBRARY) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - wfd.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= wfd - -include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp deleted file mode 100644 index 52e4e26..0000000 --- a/media/libstagefright/wifi-display/wfd.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright 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 "wfd" -#include - -#include "sink/WifiDisplaySink.h" -#include "source/WifiDisplaySource.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -static void usage(const char *me) { - fprintf(stderr, - "usage:\n" - " %s -c host[:port]\tconnect to wifi source\n" - " -u uri \tconnect to an rtsp uri\n" - " -l ip[:port] \tlisten on the specified port " - " -f(ilename) \tstream media " - "(create a sink)\n" - " -s(pecial) \trun in 'special' mode\n", - me); -} - -struct RemoteDisplayClient : public BnRemoteDisplayClient { - RemoteDisplayClient(); - - virtual void onDisplayConnected( - const sp &bufferProducer, - uint32_t width, - uint32_t height, - uint32_t flags, - uint32_t session); - - virtual void onDisplayDisconnected(); - virtual void onDisplayError(int32_t error); - - void waitUntilDone(); - -protected: - virtual ~RemoteDisplayClient(); - -private: - Mutex mLock; - Condition mCondition; - - bool mDone; - - sp mComposerClient; - sp mSurfaceTexture; - sp mDisplayBinder; - - DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient); -}; - -RemoteDisplayClient::RemoteDisplayClient() - : mDone(false) { - mComposerClient = new SurfaceComposerClient; - CHECK_EQ(mComposerClient->initCheck(), (status_t)OK); -} - -RemoteDisplayClient::~RemoteDisplayClient() { -} - -void RemoteDisplayClient::onDisplayConnected( - const sp &bufferProducer, - uint32_t width, - uint32_t height, - uint32_t flags, - uint32_t session) { - ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x, session = %d", - width, height, flags, session); - - if (bufferProducer != NULL) { - mSurfaceTexture = bufferProducer; - mDisplayBinder = mComposerClient->createDisplay( - String8("foo"), false /* secure */); - - SurfaceComposerClient::openGlobalTransaction(); - mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); - - Rect layerStackRect(1280, 720); // XXX fix this. - Rect displayRect(1280, 720); - - mComposerClient->setDisplayProjection( - mDisplayBinder, 0 /* 0 degree rotation */, - layerStackRect, - displayRect); - - SurfaceComposerClient::closeGlobalTransaction(); - } -} - -void RemoteDisplayClient::onDisplayDisconnected() { - ALOGI("onDisplayDisconnected"); - - Mutex::Autolock autoLock(mLock); - mDone = true; - mCondition.broadcast(); -} - -void RemoteDisplayClient::onDisplayError(int32_t error) { - ALOGI("onDisplayError error=%d", error); - - Mutex::Autolock autoLock(mLock); - mDone = true; - mCondition.broadcast(); -} - -void RemoteDisplayClient::waitUntilDone() { - Mutex::Autolock autoLock(mLock); - while (!mDone) { - mCondition.wait(mLock); - } -} - -static void createSource(const AString &addr, int32_t port) { - sp sm = defaultServiceManager(); - sp binder = sm->getService(String16("media.player")); - sp service = - interface_cast(binder); - - CHECK(service.get() != NULL); - - String8 iface; - iface.append(addr.c_str()); - iface.append(StringPrintf(":%d", port).c_str()); - - sp client = new RemoteDisplayClient; - sp display = - service->listenForRemoteDisplay(client, iface); - - client->waitUntilDone(); - - display->dispose(); - display.clear(); -} - -static void createFileSource( - const AString &addr, int32_t port, const char *path) { - sp session = new ANetworkSession; - session->start(); - - sp looper = new ALooper; - looper->start(); - - sp client = new RemoteDisplayClient; - sp source = new WifiDisplaySource(session, client, path); - looper->registerHandler(source); - - AString iface = StringPrintf("%s:%d", addr.c_str(), port); - CHECK_EQ((status_t)OK, source->start(iface.c_str())); - - client->waitUntilDone(); - - source->stop(); -} - -} // namespace android - -int main(int argc, char **argv) { - using namespace android; - - ProcessState::self()->startThreadPool(); - - DataSource::RegisterDefaultSniffers(); - - AString connectToHost; - int32_t connectToPort = -1; - AString uri; - - AString listenOnAddr; - int32_t listenOnPort = -1; - - AString path; - - bool specialMode = false; - - int res; - while ((res = getopt(argc, argv, "hc:l:u:f:s")) >= 0) { - switch (res) { - case 'c': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - connectToHost = optarg; - connectToPort = WifiDisplaySource::kWifiDisplayDefaultPort; - } else { - connectToHost.setTo(optarg, colonPos - optarg); - - char *end; - connectToPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || connectToPort < 1 || connectToPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - } - break; - } - - case 'u': - { - uri = optarg; - break; - } - - case 'f': - { - path = optarg; - break; - } - - case 'l': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - listenOnAddr = optarg; - listenOnPort = WifiDisplaySource::kWifiDisplayDefaultPort; - } else { - listenOnAddr.setTo(optarg, colonPos - optarg); - - char *end; - listenOnPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || listenOnPort < 1 || listenOnPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - } - break; - } - - case 's': - { - specialMode = true; - break; - } - - case '?': - case 'h': - default: - usage(argv[0]); - exit(1); - } - } - - if (connectToPort >= 0 && listenOnPort >= 0) { - fprintf(stderr, - "You can connect to a source or create one, " - "but not both at the same time.\n"); - exit(1); - } - - if (listenOnPort >= 0) { - if (path.empty()) { - createSource(listenOnAddr, listenOnPort); - } else { - createFileSource(listenOnAddr, listenOnPort, path.c_str()); - } - - exit(0); - } - - if (connectToPort < 0 && uri.empty()) { - fprintf(stderr, - "You need to select either source host or uri.\n"); - - exit(1); - } - - if (connectToPort >= 0 && !uri.empty()) { - fprintf(stderr, - "You need to either connect to a wfd host or an rtsp url, " - "not both.\n"); - exit(1); - } - - sp composerClient = new SurfaceComposerClient; - CHECK_EQ(composerClient->initCheck(), (status_t)OK); - - sp display(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); - DisplayInfo info; - SurfaceComposerClient::getDisplayInfo(display, &info); - ssize_t displayWidth = info.w; - ssize_t displayHeight = info.h; - - ALOGV("display is %d x %d\n", displayWidth, displayHeight); - - sp control = - composerClient->createSurface( - String8("A Surface"), - displayWidth, - displayHeight, - PIXEL_FORMAT_RGB_565, - 0); - - CHECK(control != NULL); - CHECK(control->isValid()); - - SurfaceComposerClient::openGlobalTransaction(); - CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); - CHECK_EQ(control->show(), (status_t)OK); - SurfaceComposerClient::closeGlobalTransaction(); - - sp surface = control->getSurface(); - CHECK(surface != NULL); - - sp session = new ANetworkSession; - session->start(); - - sp looper = new ALooper; - - sp sink = new WifiDisplaySink( - specialMode ? WifiDisplaySink::FLAG_SPECIAL_MODE : 0 /* flags */, - session, - surface->getIGraphicBufferProducer()); - - looper->registerHandler(sink); - - if (connectToPort >= 0) { - sink->start(connectToHost.c_str(), connectToPort); - } else { - sink->start(uri.c_str()); - } - - looper->start(true /* runOnCallingThread */); - - composerClient->dispose(); - - return 0; -} -- cgit v1.1 From 56ce726019f700a95ce5b45beebceadae4836e30 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 2 May 2013 16:30:48 -0700 Subject: IOMX: Add prepareForAdaptivePlayback method prepareForAdaptivePlayback is the fallback mechanism to support seamless resolution change for devices that do not support dynamic output buffers. It is up to the codecs to handle this appropriately, but codecs that do not handle dynamic output buffers would request enough buffers up to the requested size in this method to avoid port reconfiguration on resolution changes. Change-Id: I58d4aa8ef1359ea3472735bbe9140c3132039b3d Signed-off-by: Lajos Molnar Bug: 10192531 Related-to-bug: 7093648 --- media/libmedia/IOMX.cpp | 34 ++++++++++++++++++++++++++ media/libstagefright/OMXClient.cpp | 11 +++++++++ media/libstagefright/include/OMX.h | 4 +++ media/libstagefright/include/OMXNodeInstance.h | 4 +++ media/libstagefright/omx/OMX.cpp | 7 ++++++ media/libstagefright/omx/OMXNodeInstance.cpp | 34 ++++++++++++++++++++++++++ 6 files changed, 94 insertions(+) (limited to 'media') diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index ef99f4f..71ce320 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -43,6 +43,7 @@ enum { CREATE_INPUT_SURFACE, SIGNAL_END_OF_INPUT_STREAM, STORE_META_DATA_IN_BUFFERS, + PREPARE_FOR_ADAPTIVE_PLAYBACK, ALLOC_BUFFER, ALLOC_BUFFER_WITH_BACKUP, FREE_BUFFER, @@ -351,6 +352,22 @@ public: return err; } + virtual status_t prepareForAdaptivePlayback( + node_id node, OMX_U32 port_index, OMX_BOOL enable, + OMX_U32 max_width, OMX_U32 max_height) { + Parcel data, reply; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeIntPtr((intptr_t)node); + data.writeInt32(port_index); + data.writeInt32((int32_t)enable); + data.writeInt32(max_width); + data.writeInt32(max_height); + remote()->transact(PREPARE_FOR_ADAPTIVE_PLAYBACK, data, &reply); + + status_t err = reply.readInt32(); + return err; + } + virtual status_t allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data) { @@ -770,6 +787,23 @@ status_t BnOMX::onTransact( return NO_ERROR; } + case PREPARE_FOR_ADAPTIVE_PLAYBACK: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + node_id node = (void*)data.readIntPtr(); + OMX_U32 port_index = data.readInt32(); + OMX_BOOL enable = (OMX_BOOL)data.readInt32(); + OMX_U32 max_width = data.readInt32(); + OMX_U32 max_height = data.readInt32(); + + status_t err = prepareForAdaptivePlayback( + node, port_index, enable, max_width, max_height); + reply->writeInt32(err); + + return NO_ERROR; + } + case ALLOC_BUFFER: { CHECK_OMX_INTERFACE(IOMX, data, reply); diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index 9820ef5..9f9352d 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -69,6 +69,10 @@ struct MuxOMX : public IOMX { virtual status_t storeMetaDataInBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable); + virtual status_t prepareForAdaptivePlayback( + node_id node, OMX_U32 port_index, OMX_BOOL enable, + OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight); + virtual status_t enableGraphicBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable); @@ -268,6 +272,13 @@ status_t MuxOMX::storeMetaDataInBuffers( return getOMX(node)->storeMetaDataInBuffers(node, port_index, enable); } +status_t MuxOMX::prepareForAdaptivePlayback( + node_id node, OMX_U32 port_index, OMX_BOOL enable, + OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) { + return getOMX(node)->prepareForAdaptivePlayback( + node, port_index, enable, maxFrameWidth, maxFrameHeight); +} + status_t MuxOMX::enableGraphicBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable) { return getOMX(node)->enableGraphicBuffers(node, port_index, enable); diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index 7e53af3..31a5077 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -71,6 +71,10 @@ public: virtual status_t storeMetaDataInBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable); + virtual status_t prepareForAdaptivePlayback( + node_id node, OMX_U32 portIndex, OMX_BOOL enable, + OMX_U32 max_frame_width, OMX_U32 max_frame_height); + virtual status_t useBuffer( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer); diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index ae498b4..339179e 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -58,6 +58,10 @@ struct OMXNodeInstance { status_t storeMetaDataInBuffers(OMX_U32 portIndex, OMX_BOOL enable); + status_t prepareForAdaptivePlayback( + OMX_U32 portIndex, OMX_BOOL enable, + OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight); + status_t useBuffer( OMX_U32 portIndex, const sp ¶ms, OMX::buffer_id *buffer); diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index aaa9f89..84a0e10 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -331,6 +331,13 @@ status_t OMX::storeMetaDataInBuffers( return findInstance(node)->storeMetaDataInBuffers(port_index, enable); } +status_t OMX::prepareForAdaptivePlayback( + node_id node, OMX_U32 portIndex, OMX_BOOL enable, + OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) { + return findInstance(node)->prepareForAdaptivePlayback( + portIndex, enable, maxFrameWidth, maxFrameHeight); +} + status_t OMX::useBuffer( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer) { diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index ef683a0..46e5d71 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -417,6 +417,40 @@ status_t OMXNodeInstance::storeMetaDataInBuffers_l( return err; } +status_t OMXNodeInstance::prepareForAdaptivePlayback( + OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth, + OMX_U32 maxFrameHeight) { + Mutex::Autolock autolock(mLock); + + OMX_INDEXTYPE index; + OMX_STRING name = const_cast( + "OMX.google.android.index.prepareForAdaptivePlayback"); + + OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); + if (err != OMX_ErrorNone) { + ALOGW_IF(enable, "OMX_GetExtensionIndex %s failed", name); + return StatusFromOMXError(err); + } + + PrepareForAdaptivePlaybackParams params; + params.nSize = sizeof(params); + params.nVersion.s.nVersionMajor = 1; + params.nVersion.s.nVersionMinor = 0; + params.nVersion.s.nRevision = 0; + params.nVersion.s.nStep = 0; + + params.nPortIndex = portIndex; + params.bEnable = enable; + params.nMaxFrameWidth = maxFrameWidth; + params.nMaxFrameHeight = maxFrameHeight; + if ((err = OMX_SetParameter(mHandle, index, ¶ms)) != OMX_ErrorNone) { + ALOGW("OMX_SetParameter failed for PrepareForAdaptivePlayback " + "with error %d (0x%08x)", err, err); + return UNKNOWN_ERROR; + } + return err; +} + status_t OMXNodeInstance::useBuffer( OMX_U32 portIndex, const sp ¶ms, OMX::buffer_id *buffer) { -- cgit v1.1 From fce0d1883cdbcb7d501625fb43844043cd28a267 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 2 May 2013 16:46:41 -0700 Subject: stagefright: enable adaptive playback based on codec format key Added support for prepareForAdaptivePlayback() call in configureCodec, if max-width and max-height keys are specified in the format. It is OK for this call to fail, if component does not implement adaptive playback. Change-Id: Ie15892bc666df103b635890a0fda799b204bb06c Signed-off-by: Lajos Molnar Bug: 7093648 Bug: 10192531 --- media/libstagefright/ACodec.cpp | 42 ++++++++++++++++++++++++++++++++++++++- media/libstagefright/OMXCodec.cpp | 12 +++++++---- 2 files changed, 49 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index bfb730c..9fa8a00 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1106,9 +1106,49 @@ status_t ACodec::configureCodec( if (!encoder && video && haveNativeWindow) { err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexOutput, OMX_TRUE); if (err != OK) { - // allow failure ALOGE("[%s] storeMetaDataInBuffers failed w/ err %d", mComponentName.c_str(), err); + + // if adaptive playback has been requested, try JB fallback + // NOTE: THIS FALLBACK MECHANISM WILL BE REMOVED DUE TO ITS + // LARGE MEMORY REQUIREMENT + + // we will not do adaptive playback on software accessed + // surfaces as they never had to respond to changes in the + // crop window, and we don't trust that they will be able to. + int usageBits = 0; + bool canDoAdaptivePlayback; + + sp windowWrapper( + static_cast(obj.get())); + sp nativeWindow = windowWrapper->getNativeWindow(); + + if (nativeWindow->query( + nativeWindow.get(), + NATIVE_WINDOW_CONSUMER_USAGE_BITS, + &usageBits) != OK) { + canDoAdaptivePlayback = false; + } else { + canDoAdaptivePlayback = + (usageBits & + (GRALLOC_USAGE_SW_READ_MASK | + GRALLOC_USAGE_SW_WRITE_MASK)) == 0; + } + + int32_t maxWidth = 0, maxHeight = 0; + if (canDoAdaptivePlayback && + msg->findInt32("max-width", &maxWidth) && + msg->findInt32("max-height", &maxHeight)) { + ALOGV("[%s] prepareForAdaptivePlayback(%ldx%ld)", + mComponentName.c_str(), maxWidth, maxHeight); + + err = mOMX->prepareForAdaptivePlayback( + mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight); + ALOGW_IF(err != OK, + "[%s] prepareForAdaptivePlayback failed w/ err %d", + mComponentName.c_str(), err); + } + // allow failure err = OK; } else { ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str()); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 2c95ab4..7f56af8 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -4620,10 +4620,14 @@ status_t QueryCodec( caps->mColorFormats.push(portFormat.eColorFormat); } - if (!isEncoder && !strncmp(mime, "video/", 6) && - omx->storeMetaDataInBuffers( - node, 1 /* port index */, OMX_TRUE) == OK) { - caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback; + if (!isEncoder && !strncmp(mime, "video/", 6)) { + if (omx->storeMetaDataInBuffers( + node, 1 /* port index */, OMX_TRUE) == OK || + omx->prepareForAdaptivePlayback( + node, 1 /* port index */, OMX_TRUE, + 1280 /* width */, 720 /* height */) == OK) { + caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback; + } } CHECK_EQ(omx->freeNode(node), (status_t)OK); -- cgit v1.1 From 385e7509eb563c983647e72b1232225c2200435f Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 4 Oct 2013 08:36:52 -0700 Subject: fix offload audio effect proxy implementation uuid The proxy implementation UUID should not be the NULL UUID as AudioFlinger will reject effect creation if the AudioEffect is constructed by passing the implementation UUID and not the type UUID. Bug: 11070481. Change-Id: Ia9049d974e76303c5b63a607ee594b7dc1f182d4 --- media/libeffects/proxy/EffectProxy.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp index b3304b7..dd4ad08 100644 --- a/media/libeffects/proxy/EffectProxy.cpp +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -30,9 +30,10 @@ namespace android { // This is a dummy proxy descriptor just to return to Factory during the initial // GetDescriptor call. Later in the factory, it is replaced with the // SW sub effect descriptor +// proxy UUID af8da7e0-2ca1-11e3-b71d-0002a5d5c51b const effect_descriptor_t gProxyDescriptor = { EFFECT_UUID_INITIALIZER, // type - EFFECT_UUID_INITIALIZER, // uuid + {0xaf8da7e0, 0x2ca1, 0x11e3, 0xb71d, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }}, // uuid EFFECT_CONTROL_API_VERSION, //version of effect control API (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST | EFFECT_FLAG_VOLUME_CTRL), // effect capability flags -- cgit v1.1 From 8db188489871c770d5d56cf67b0001222415db41 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 3 Oct 2013 17:54:26 -0700 Subject: TimedEventQueue takes a wake lock Take a wake lock when events present in a TimedEventQueue to prevent from going idle before all delayed events are processed. Bug: 11057387. Change-Id: I26a17df68068fde5e879a2fe7568dec439fc540f --- media/libstagefright/Android.mk | 1 + media/libstagefright/TimedEventQueue.cpp | 76 +++++++++++++++++++++++++- media/libstagefright/include/TimedEventQueue.h | 25 +++++++++ 3 files changed, 99 insertions(+), 3 deletions(-) (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 1f68b51..6a2a696 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -89,6 +89,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libvorbisidec \ libz \ + libpowermanager LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 7e9c4bf..74c5905 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -31,17 +31,24 @@ #include #include +#include +#include namespace android { TimedEventQueue::TimedEventQueue() : mNextEventID(1), mRunning(false), - mStopped(false) { + mStopped(false), + mDeathRecipient(new PMDeathRecipient(this)) { } TimedEventQueue::~TimedEventQueue() { stop(); + if (mPowerManager != 0) { + sp binder = mPowerManager->asBinder(); + binder->unlinkToDeath(mDeathRecipient); + } } void TimedEventQueue::start() { @@ -76,6 +83,11 @@ void TimedEventQueue::stop(bool flush) { void *dummy; pthread_join(mThread, &dummy); + // some events may be left in the queue if we did not flush and the wake lock + // must be released. + if (!mQueue.empty()) { + releaseWakeLock_l(); + } mQueue.clear(); mRunning = false; @@ -117,6 +129,9 @@ TimedEventQueue::event_id TimedEventQueue::postTimedEvent( mQueueHeadChangedCondition.signal(); } + if (mQueue.empty()) { + acquireWakeLock_l(); + } mQueue.insert(it, item); mQueueNotEmptyCondition.signal(); @@ -172,7 +187,9 @@ void TimedEventQueue::cancelEvents( (*it).event->setEventID(0); it = mQueue.erase(it); - + if (mQueue.empty()) { + releaseWakeLock_l(); + } if (stopAfterFirstMatch) { return; } @@ -280,7 +297,9 @@ sp TimedEventQueue::removeEventFromQueue_l( event->setEventID(0); mQueue.erase(it); - + if (mQueue.empty()) { + releaseWakeLock_l(); + } return event; } } @@ -290,5 +309,56 @@ sp TimedEventQueue::removeEventFromQueue_l( return NULL; } +void TimedEventQueue::acquireWakeLock_l() +{ + if (mWakeLockToken != 0) { + return; + } + if (mPowerManager == 0) { + // use checkService() to avoid blocking if power service is not up yet + sp binder = + defaultServiceManager()->checkService(String16("power")); + if (binder == 0) { + ALOGW("cannot connect to the power manager service"); + } else { + mPowerManager = interface_cast(binder); + binder->linkToDeath(mDeathRecipient); + } + } + if (mPowerManager != 0) { + sp binder = new BBinder(); + status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, + binder, + String16("TimedEventQueue"), + String16("media")); + if (status == NO_ERROR) { + mWakeLockToken = binder; + } + } +} + +void TimedEventQueue::releaseWakeLock_l() +{ + if (mWakeLockToken == 0) { + return; + } + if (mPowerManager != 0) { + mPowerManager->releaseWakeLock(mWakeLockToken, 0); + } + mWakeLockToken.clear(); +} + +void TimedEventQueue::clearPowerManager() +{ + Mutex::Autolock _l(mLock); + releaseWakeLock_l(); + mPowerManager.clear(); +} + +void TimedEventQueue::PMDeathRecipient::binderDied(const wp& who) +{ + mQueue->clearPowerManager(); +} + } // namespace android diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h index 11f844c..4e49c83 100644 --- a/media/libstagefright/include/TimedEventQueue.h +++ b/media/libstagefright/include/TimedEventQueue.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace android { @@ -57,6 +58,21 @@ struct TimedEventQueue { Event &operator=(const Event &); }; + class PMDeathRecipient : public IBinder::DeathRecipient { + public: + PMDeathRecipient(TimedEventQueue *queue) : mQueue(queue) {} + virtual ~PMDeathRecipient() {} + + // IBinder::DeathRecipient + virtual void binderDied(const wp& who); + + private: + PMDeathRecipient(const PMDeathRecipient&); + PMDeathRecipient& operator = (const PMDeathRecipient&); + + TimedEventQueue *mQueue; + }; + TimedEventQueue(); ~TimedEventQueue(); @@ -96,6 +112,8 @@ struct TimedEventQueue { static int64_t getRealTimeUs(); + void clearPowerManager(); + private: struct QueueItem { sp event; @@ -118,11 +136,18 @@ private: bool mRunning; bool mStopped; + sp mPowerManager; + sp mWakeLockToken; + const sp mDeathRecipient; + static void *ThreadWrapper(void *me); void threadEntry(); sp removeEventFromQueue_l(event_id id); + void acquireWakeLock_l(); + void releaseWakeLock_l(); + TimedEventQueue(const TimedEventQueue &); TimedEventQueue &operator=(const TimedEventQueue &); }; -- cgit v1.1 From aab5b08cb4a3b5a47daece6168f41ec918020739 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 8 Oct 2013 09:22:31 -0700 Subject: AwesomePlayer: do not send events when paused. When streaming audio and paused, AwesomePlayer should stop sending BufferingEvents as they will keep a wake lock for no reason. TimedEventQueue should always acquire the wakelock with mediaserver identity so that it is released with the same identity by the event handler thread. Bug: 11104408. Change-Id: Ied0e03acd6ad2f5a4c0ec82d5c2aa4e1c6da772c --- media/libstagefright/AwesomePlayer.cpp | 8 +++++++- media/libstagefright/TimedEventQueue.cpp | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index e7cfc78..be6719a 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -798,7 +798,9 @@ void AwesomePlayer::onBufferingUpdate() { } } - postBufferingEvent_l(); + if (mFlags & (PLAYING | PREPARING)) { + postBufferingEvent_l(); + } } void AwesomePlayer::sendCacheStats() { @@ -1001,6 +1003,10 @@ status_t AwesomePlayer::play_l() { } addBatteryData(params); + if (isStreamingHTTP()) { + postBufferingEvent_l(); + } + return OK; } diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 74c5905..6a16bb4 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include namespace android { @@ -327,10 +329,12 @@ void TimedEventQueue::acquireWakeLock_l() } if (mPowerManager != 0) { sp binder = new BBinder(); + int64_t token = IPCThreadState::self()->clearCallingIdentity(); status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, binder, String16("TimedEventQueue"), String16("media")); + IPCThreadState::self()->restoreCallingIdentity(token); if (status == NO_ERROR) { mWakeLockToken = binder; } @@ -343,7 +347,9 @@ void TimedEventQueue::releaseWakeLock_l() return; } if (mPowerManager != 0) { + int64_t token = IPCThreadState::self()->clearCallingIdentity(); mPowerManager->releaseWakeLock(mWakeLockToken, 0); + IPCThreadState::self()->restoreCallingIdentity(token); } mWakeLockToken.clear(); } -- cgit v1.1 From b3cb72a17d9a472883e9e2faa18b42eac533fe99 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Sat, 12 Oct 2013 17:05:19 -0700 Subject: SoundPool: handle new audio track event If the AudioTrack is torn down, SoundPool will never receive the buffer end event and the track will stay active for ever. The fix consists in stopping the AudioTrack when a new audiotrack event is received. Bug: 11193583. Change-Id: I9876eb2a8f75c601368f669acd67b0accf6e2736 --- media/libmedia/SoundPool.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'media') diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp index 8434d43..22e9fad 100644 --- a/media/libmedia/SoundPool.cpp +++ b/media/libmedia/SoundPool.cpp @@ -744,11 +744,16 @@ void SoundChannel::process(int event, void *info, unsigned long toggle) b->size = count; //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]); } - } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) { - ALOGV("process %p channel %d EVENT_UNDERRUN or EVENT_BUFFER_END", this, mChannelID); + } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END || + event == AudioTrack::EVENT_NEW_IAUDIOTRACK) { + ALOGV("process %p channel %d event %s", + this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" : + (event == AudioTrack::EVENT_BUFFER_END) ? "BUFFER_END" : "NEW_IAUDIOTRACK"); mSoundPool->addToStopList(this); } else if (event == AudioTrack::EVENT_LOOP_END) { ALOGV("End loop %p channel %d", this, mChannelID); + } else { + ALOGW("SoundChannel::process unexpected event %d", event); } } -- cgit v1.1 From d576687570f19f3956d91c1d3da10d965e34c407 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 15 Oct 2013 16:11:24 -0700 Subject: AwesomePlayer: fix concurrent pause and teardown There was a race condition if a pause request was received while we were executing a teardown event. Although we hold a mutex while processing the teardown event, there is a step of the prepare sequence where we release the mutex if streaming. In this case, the pause request is executed but ignored because the player state is still preparing. At the end of the teardown event processing we restore previous playback state and resume. The fix consists in clearing the saved teardown playback state when a pause request is received while processing a teardown event. Bug: 11225491. Change-Id: If0e61855ce5a336322f1ba8e5559bdc190beeb76 --- media/libstagefright/AwesomePlayer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'media') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index be6719a..c912f75 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -1225,6 +1225,12 @@ status_t AwesomePlayer::pause() { status_t AwesomePlayer::pause_l(bool at_eos) { if (!(mFlags & PLAYING)) { + if (mAudioTearDown && mAudioTearDownWasPlaying) { + ALOGV("pause_l() during teardown and finishSetDataSource_l() mFlags %x" , mFlags); + mAudioTearDownWasPlaying = false; + notifyListener_l(MEDIA_PAUSED); + mMediaRenderingStartGeneration = ++mStartGeneration; + } return OK; } -- cgit v1.1 From cc21e4f1e41dfa17e7e2bef995fcd22c45f6bcd0 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 16 Oct 2013 15:12:32 -0700 Subject: AudioTrack: fix head position after restore The head position transfered to the new track by restoreTrack_l() must take into account the frames that are dropped from the old track to avoid a non recoverable offset in the playback head position returned to applications. Bug: 11230062. Change-Id: I51143a08b95e8f264ed709ae2054360315f2b8b1 --- media/libmedia/AudioTrack.cpp | 4 +++- media/libmedia/AudioTrackShared.cpp | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 37d50cf..507f9bc 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1662,7 +1662,9 @@ status_t AudioTrack::restoreTrack_l(const char *from) // if the new IAudioTrack is created, createTrack_l() will modify the // following member variables: mAudioTrack, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioTrack and IMemory - size_t position = mProxy->getPosition(); + + // take the frames that will be lost by track recreation into account in saved position + size_t position = mProxy->getPosition() + mProxy->getFramesFilled(); mNewPosition = position + mUpdatePeriod; size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0; result = createTrack_l(mStreamType, diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 4fd92b2..da73d65 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -316,6 +316,27 @@ size_t ClientProxy::getMisalignment() (mFrameCountP2 - 1); } +size_t ClientProxy::getFramesFilled() { + audio_track_cblk_t* cblk = mCblk; + int32_t front; + int32_t rear; + + if (mIsOut) { + front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); + rear = cblk->u.mStreaming.mRear; + } else { + rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); + front = cblk->u.mStreaming.mFront; + } + ssize_t filled = rear - front; + // pipe should not be overfull + if (!(0 <= filled && (size_t) filled <= mFrameCount)) { + ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled); + return 0; + } + return (size_t)filled; +} + // --------------------------------------------------------------------------- void AudioTrackClientProxy::flush() -- cgit v1.1 From 598de6c701e989385eeffa7c5dfd61f0459a2631 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 16 Oct 2013 17:02:13 -0700 Subject: Fix race condition in AudioTrack::pause followed by start Bug: 11148722 Change-Id: Iec88f00c8510363d4418e4b8d5b34feb06ecf04d --- media/libmedia/AudioTrack.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'media') diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 37d50cf..80f5155 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1785,7 +1785,8 @@ void AudioTrack::DeathNotifier::binderDied(const wp& who) // ========================================================================= AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL) + : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL), + mIgnoreNextPausedInt(false) { } @@ -1802,6 +1803,10 @@ bool AudioTrack::AudioTrackThread::threadLoop() // caller will check for exitPending() return true; } + if (mIgnoreNextPausedInt) { + mIgnoreNextPausedInt = false; + mPausedInt = false; + } if (mPausedInt) { if (mPausedNs > 0) { (void) mMyCond.waitRelative(mMyLock, mPausedNs); @@ -1836,12 +1841,7 @@ void AudioTrack::AudioTrackThread::requestExit() { // must be in this order to avoid a race condition Thread::requestExit(); - AutoMutex _l(mMyLock); - if (mPaused || mPausedInt) { - mPaused = false; - mPausedInt = false; - mMyCond.signal(); - } + resume(); } void AudioTrack::AudioTrackThread::pause() @@ -1853,6 +1853,7 @@ void AudioTrack::AudioTrackThread::pause() void AudioTrack::AudioTrackThread::resume() { AutoMutex _l(mMyLock); + mIgnoreNextPausedInt = true; if (mPaused || mPausedInt) { mPaused = false; mPausedInt = false; -- cgit v1.1