From 4b3913a3e43d3180b21d77cc2f717b446350354f Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 11 May 2011 14:13:42 -0700 Subject: Squashed commit of the following: commit c80992e419ed567abef451042f09c4958534b90d Author: Andreas Huber Date: Wed May 11 14:00:07 2011 -0700 Support for the mp3 audio decoder as a software OMX component. Change-Id: I66e10c4d0be4c3aecdef1c21b15a2c7359ceb807 commit a358d0e1bf2a88897887445f42ccdda0f5f2f528 Author: Andreas Huber Date: Wed May 11 13:11:23 2011 -0700 Support for G.711 alaw and mulaw decoders as software OMX components Change-Id: Ia5c76c02cb83a9f94ce39a27b2251e5880218f03 commit 79088b9c9a5c8b8c97ea66cb4f90a2b0f0d34553 Author: Andreas Huber Date: Thu May 5 15:43:32 2011 -0700 Instead of using an RGB surface and conversion yuv420->rgb565 convert from OMX_COLOR_FormatYUV420Planar to HAL_PIXEL_FORMAT_YV12 instead. Change-Id: I8c4fc3c54c963f0d4ba6377f3c4ab4e0013152e5 related-to-bug: 4394005 commit 69469d3bd84425777b11b9fc938c5e0c61af26a7 Author: Andreas Huber Date: Tue May 10 15:46:42 2011 -0700 voip mustn't link against libstagefright.so Change-Id: I4d0ba9a8b9dc9380b792a1bd04bcda231964862c commit 2a9a9eeeeeb36ae3a9e680469c3016d509ff08c3 Author: Andreas Huber Date: Tue May 10 14:37:10 2011 -0700 Remove most non-OMX software decoders by default Change-Id: Ic56514bc1b56b8fa952e8c4a164ea7379ecb69d0 commit a4de62c37b335c318217765403a9fb282b20a216 Author: Andreas Huber Date: Mon May 9 16:50:02 2011 -0700 Conditionally build the old-style software decoders. Change-Id: I5de609e1d76c92d26d6eb81d1551462258f3f15f commit 5d8b039f9449dc3dad1e77c42c80cc0b54b0c846 Author: Andreas Huber Date: Mon May 9 16:13:12 2011 -0700 Support for MPEG4 and H.263 video decoders as soft OMX components. Change-Id: I5e3a4835afab89f98e3aa128d013628f5830eafe commit b25a1bfbeb0ff6e62e1cc694ce2599c91489c7d0 Author: Andreas Huber Date: Mon May 9 11:49:10 2011 -0700 Boost Soft OMX thread priority, fix timestamp handling in vorbis Soft OMX decoder. Change-Id: I68d26d4999f06fcc451d69e5303663fab0cba9e8 commit c0574362f8dc3319ce84d981097867062a698527 Author: Andreas Huber Date: Mon May 9 11:28:53 2011 -0700 Support for the AMR decoders (NB and WB) as Soft OMX components. Change-Id: Ia565f59833fb52653e23f26536e7e41fc329a754 commit 3e5575a8f0e27a490cb7bde77bd9456087837f08 Author: Andreas Huber Date: Wed May 4 13:41:25 2011 -0700 Signal an error if the aac decoder failed to initialize from codec specific data. Change-Id: I01da7831bdf722edd7d6dc5974486daa2cf2b209 related-to-bug: 4272179 commit f94aeaa9886e772ff4823e671ed237096649f4af Author: Andreas Huber Date: Tue May 3 13:07:38 2011 -0700 Software OMX nodes don't (yet?) support native_window mode. Change-Id: I7d9ca9164ef4abf66b573ca21dba12d672f8b12d commit eefdfabac8dc659e00daa56da69aea705c49cb67 Author: Andreas Huber Date: Tue May 3 12:57:16 2011 -0700 Fixing the OMX tests to refer to appropriate files from test content. Change-Id: I5b61c3498749bfb876abbd3946a5132356e3f6ff commit f31b7326aef14b6a1b7946520a9688f092e844d5 Author: Andreas Huber Date: Tue May 3 11:08:38 2011 -0700 Soft OMX components are now dynamiclly loaded/unloaded, not directly linked against. Change-Id: I1e2ecfbfab67a8869886f738eaf0c7b3c948b6d9 commit b7f0343879e4df06f0a1c9bfece24df557954e2f Author: Andreas Huber Date: Mon May 2 15:58:36 2011 -0700 Support for the AVC software decoder as an OMX component. Change-Id: I13c12df435ba4afbd968a9fc659f66b91c818bc2 commit 5bb9e616d6c8e1b13d531fe996b9a9affdfb2977 Author: Andreas Huber Date: Fri Apr 29 12:05:37 2011 -0700 Fix Vorbis OMX decoder's component role. Change-Id: I5e871e5e11b3f951c93590210e63fd7987c467b5 commit 089c91f2333062e196c7afd5fb0ca914878aa474 Author: Andreas Huber Date: Fri Apr 29 12:05:18 2011 -0700 Support vorbis_decoder OMX testing. Change-Id: I1985be178a12ae3f8768bc72067d9236238be170 commit 56e241fa36fc37219bc536b823bdc2ab82dc1fad Author: Andreas Huber Date: Fri Apr 29 12:01:46 2011 -0700 SoftVorbis OMX component now respects the number of valid frames per page. Change-Id: I82a117a064d9b083fc58a54ad900a987a763ef03 commit fcd618ec520c376fdb78f4cbb44b8d9f5d213e2b Author: Andreas Huber Date: Fri Apr 29 10:59:38 2011 -0700 Support for the vorbis audio decoder as a soft OMX component. Change-Id: Iaeb057e58ca306d3dce205c0445b74d5aefef492 commit d1fcc3203fc8003ad79c6e96b3a1fc4261743f16 Author: Andreas Huber Date: Fri Apr 29 10:07:50 2011 -0700 VPX decoder now properly resizes buffers after a port settings change. Change-Id: I110749a31b6cba087891d8e5dfe420830bdbf831 commit 35c7168243cb69849d88911144a2c7fdfed5c54e Author: Andreas Huber Date: Thu Apr 28 13:23:34 2011 -0700 Support for the VPX video decoder as a Software OMX component. Change-Id: Ic345add2d6d768d4af631160153f2e9b97fcea71 commit 923b2534b4211fc5405377b5190bfa6f2dd27f32 Author: Andreas Huber Date: Thu Apr 28 11:34:40 2011 -0700 Table-based registration of soft omx components. Change-Id: I7f45f0fa5b3a7950776e69c66349731f7674e937 commit 04a88f3edb2266a463da9c4481b80178be460902 Author: Andreas Huber Date: Thu Apr 28 11:22:31 2011 -0700 Apparently OMX_GetParameter is valid in any state other than OMX_StateInvalid OMX_SetParameter is still constrained to OMX_StateLoaded or a disabled port. Change-Id: I1032d7cf4011982d306aa369d4158a82830d26fb commit 9d70ca68445e7c40f5c9b2d12466e468f514de88 Author: Andreas Huber Date: Wed Apr 27 15:03:18 2011 -0700 Use the new soft OMX aac decoder for HTTP live playback. Change-Id: Ifbcfb732a9edb855cb46b49f6d0ac942170ee28f commit 213fe4a10ea93cce08e8622dc3908053f29878a1 Author: Andreas Huber Date: Tue Apr 12 16:39:45 2011 -0700 Foundation for supporting software decoders as OMX components Change-Id: I7fdab256563b35d1d090617abaea9a26b198d816 Change-Id: I83e9236beed4af985d10333c203f065df9e09a42 --- media/libstagefright/ACodec.cpp | 2 +- media/libstagefright/Android.mk | 36 +- media/libstagefright/AwesomePlayer.cpp | 4 +- media/libstagefright/OMXCodec.cpp | 134 +++- media/libstagefright/WAVExtractor.cpp | 2 + media/libstagefright/codecs/aacdec/Android.mk | 29 +- media/libstagefright/codecs/aacdec/SoftAAC.cpp | 449 ++++++++++++++ media/libstagefright/codecs/aacdec/SoftAAC.h | 76 +++ media/libstagefright/codecs/amrnb/dec/Android.mk | 30 + media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp | 434 +++++++++++++ media/libstagefright/codecs/amrnb/dec/SoftAMR.h | 87 +++ media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp | 2 +- media/libstagefright/codecs/amrwb/src/mime_io.cpp | 2 +- .../codecs/amrwb/src/pvamrwbdecoder.h | 4 +- media/libstagefright/codecs/avc/dec/Android.mk | 55 +- media/libstagefright/codecs/avc/dec/SoftAVC.cpp | 690 +++++++++++++++++++++ media/libstagefright/codecs/avc/dec/SoftAVC.h | 109 ++++ media/libstagefright/codecs/g711/dec/Android.mk | 19 + media/libstagefright/codecs/g711/dec/SoftG711.cpp | 302 +++++++++ media/libstagefright/codecs/g711/dec/SoftG711.h | 63 ++ .../libstagefright/codecs/m4v_h263/dec/Android.mk | 26 + .../codecs/m4v_h263/dec/SoftMPEG4.cpp | 528 ++++++++++++++++ .../libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h | 92 +++ media/libstagefright/codecs/mp3dec/Android.mk | 23 + media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 325 ++++++++++ media/libstagefright/codecs/mp3dec/SoftMP3.h | 80 +++ media/libstagefright/codecs/on2/dec/Android.mk | 31 +- media/libstagefright/codecs/on2/dec/SoftVPX.cpp | 366 +++++++++++ media/libstagefright/codecs/on2/dec/SoftVPX.h | 70 +++ media/libstagefright/codecs/vorbis/dec/Android.mk | 27 +- .../codecs/vorbis/dec/SoftVorbis.cpp | 445 +++++++++++++ .../libstagefright/codecs/vorbis/dec/SoftVorbis.h | 78 +++ .../include/SimpleSoftOMXComponent.h | 143 +++++ media/libstagefright/include/SoftOMXComponent.h | 171 +++++ media/libstagefright/omx/Android.mk | 50 +- media/libstagefright/omx/OMXMaster.cpp | 17 +- media/libstagefright/omx/OMXMaster.h | 1 + media/libstagefright/omx/OMXNodeInstance.cpp | 1 + media/libstagefright/omx/OMXPVCodecsPlugin.cpp | 101 --- media/libstagefright/omx/OMXPVCodecsPlugin.h | 54 -- .../libstagefright/omx/SimpleSoftOMXComponent.cpp | 640 +++++++++++++++++++ media/libstagefright/omx/SoftOMXComponent.cpp | 326 ++++++++++ media/libstagefright/omx/SoftOMXPlugin.cpp | 170 +++++ media/libstagefright/omx/SoftOMXPlugin.h | 53 ++ media/libstagefright/omx/tests/OMXHarness.cpp | 21 +- 45 files changed, 6105 insertions(+), 263 deletions(-) create mode 100644 media/libstagefright/codecs/aacdec/SoftAAC.cpp create mode 100644 media/libstagefright/codecs/aacdec/SoftAAC.h create mode 100644 media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp create mode 100644 media/libstagefright/codecs/amrnb/dec/SoftAMR.h create mode 100644 media/libstagefright/codecs/avc/dec/SoftAVC.cpp create mode 100644 media/libstagefright/codecs/avc/dec/SoftAVC.h create mode 100644 media/libstagefright/codecs/g711/dec/SoftG711.cpp create mode 100644 media/libstagefright/codecs/g711/dec/SoftG711.h create mode 100644 media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp create mode 100644 media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h create mode 100644 media/libstagefright/codecs/mp3dec/SoftMP3.cpp create mode 100644 media/libstagefright/codecs/mp3dec/SoftMP3.h create mode 100644 media/libstagefright/codecs/on2/dec/SoftVPX.cpp create mode 100644 media/libstagefright/codecs/on2/dec/SoftVPX.h create mode 100644 media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp create mode 100644 media/libstagefright/codecs/vorbis/dec/SoftVorbis.h create mode 100644 media/libstagefright/include/SimpleSoftOMXComponent.h create mode 100644 media/libstagefright/include/SoftOMXComponent.h delete mode 100644 media/libstagefright/omx/OMXPVCodecsPlugin.cpp delete mode 100644 media/libstagefright/omx/OMXPVCodecsPlugin.h create mode 100644 media/libstagefright/omx/SimpleSoftOMXComponent.cpp create mode 100644 media/libstagefright/omx/SoftOMXComponent.cpp create mode 100644 media/libstagefright/omx/SoftOMXPlugin.cpp create mode 100644 media/libstagefright/omx/SoftOMXPlugin.h (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index e52f6d1..4189354 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1644,7 +1644,7 @@ void ACodec::UninitializedState::onSetup( if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) { componentName = "OMX.Nvidia.h264.decode"; } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { - componentName = "OMX.Nvidia.aac.decoder"; + componentName = "OMX.google.aac.decoder"; } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) { componentName = "OMX.Nvidia.mp3.decoder"; } else { diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 10ce00c..d8cf810 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -3,6 +3,8 @@ include $(CLEAR_VARS) include frameworks/base/media/libstagefright/codecs/common/Config.mk +BUILD_WITH_SOFTWARE_DECODERS := true + LOCAL_SRC_FILES:= \ ACodec.cpp \ AACExtractor.cpp \ @@ -45,7 +47,6 @@ LOCAL_SRC_FILES:= \ ShoutcastSource.cpp \ StagefrightMediaScanner.cpp \ StagefrightMetadataRetriever.cpp \ - ThreadedSource.cpp \ ThrottledSource.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ @@ -82,28 +83,39 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ - libstagefright_aacdec \ libstagefright_aacenc \ - libstagefright_amrnbdec \ libstagefright_amrnbenc \ - libstagefright_amrwbdec \ libstagefright_amrwbenc \ - libstagefright_avcdec \ libstagefright_avcenc \ - libstagefright_m4vh263dec \ libstagefright_m4vh263enc \ - libstagefright_mp3dec \ - libstagefright_vorbisdec \ libstagefright_matroska \ - libstagefright_vpxdec \ libvpx \ libstagefright_mpeg2ts \ libstagefright_httplive \ libstagefright_rtsp \ libstagefright_id3 \ - libstagefright_g711dec \ libFLAC \ +ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true) + +LOCAL_SRC_FILES += \ + ThreadedSource.cpp \ + +LOCAL_STATIC_LIBRARIES += \ + libstagefright_aacdec \ + libstagefright_amrnbdec \ + libstagefright_amrwbdec \ + libstagefright_avcdec \ + libstagefright_g711dec \ + libstagefright_mp3dec \ + libstagefright_m4vh263dec \ + libstagefright_vorbisdec \ + libstagefright_vpxdec \ + libvpx \ + +endif + + ################################################################################ # The following was shamelessly copied from external/webkit/Android.mk and @@ -180,6 +192,10 @@ endif LOCAL_CFLAGS += -Wno-multichar +ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true) + LOCAL_CFLAGS += -DHAVE_SOFTWARE_DECODERS +endif + LOCAL_MODULE:= libstagefright include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index dcc268f..b45e5d3 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -942,7 +942,9 @@ void AwesomePlayer::initRenderer_l() { // before creating a new one. IPCThreadState::self()->flushCommands(); - if (USE_SURFACE_ALLOC && strncmp(component, "OMX.", 4) == 0) { + if (USE_SURFACE_ALLOC + && !strncmp(component, "OMX.", 4) + && strncmp(component, "OMX.google.", 11)) { // Hardware decoders avoid the CPU color conversion by decoding // directly to ANativeBuffers, so we must use a renderer that // just pushes those buffers to the ANativeWindow. diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 78d13b2..cd5bdfb 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -53,7 +53,10 @@ #include #include +#if HAVE_SOFTWARE_DECODERS #include "include/ThreadedSource.h" +#endif + #include "include/avc_utils.h" namespace android { @@ -65,11 +68,6 @@ struct CodecInfo { const char *codec; }; -#define FACTORY_CREATE(name) \ -static sp Make##name(const sp &source) { \ - return new name(source); \ -} - #define FACTORY_CREATE_ENCODER(name) \ static sp Make##name(const sp &source, const sp &meta) { \ return new name(source, meta); \ @@ -77,20 +75,29 @@ static sp Make##name(const sp &source, const sp Make##name(const sp &source) { \ + return new name(source); \ +} + FACTORY_CREATE(AMRNBDecoder) FACTORY_CREATE(AMRWBDecoder) FACTORY_CREATE(AACDecoder) FACTORY_CREATE(AVCDecoder) FACTORY_CREATE(G711Decoder) +FACTORY_CREATE(MP3Decoder) FACTORY_CREATE(M4vH263Decoder) FACTORY_CREATE(VorbisDecoder) FACTORY_CREATE(VPXDecoder) -FACTORY_CREATE_ENCODER(AMRNBEncoder) -FACTORY_CREATE_ENCODER(AMRWBEncoder) -FACTORY_CREATE_ENCODER(AACEncoder) -FACTORY_CREATE_ENCODER(AVCEncoder) -FACTORY_CREATE_ENCODER(M4vH263Encoder) +#endif static sp InstantiateSoftwareEncoder( const char *name, const sp &source, @@ -119,18 +126,19 @@ static sp InstantiateSoftwareEncoder( static sp InstantiateSoftwareCodec( const char *name, const sp &source) { +#if HAVE_SOFTWARE_DECODERS struct FactoryInfo { const char *name; sp (*CreateFunc)(const sp &); }; static const FactoryInfo kFactoryInfo[] = { - FACTORY_REF(MP3Decoder) FACTORY_REF(AMRNBDecoder) FACTORY_REF(AMRWBDecoder) FACTORY_REF(AACDecoder) FACTORY_REF(AVCDecoder) FACTORY_REF(G711Decoder) + FACTORY_REF(MP3Decoder) FACTORY_REF(M4vH263Decoder) FACTORY_REF(VorbisDecoder) FACTORY_REF(VPXDecoder) @@ -145,6 +153,7 @@ static sp InstantiateSoftwareCodec( return (*kFactoryInfo[i].CreateFunc)(source); } } +#endif return NULL; } @@ -156,36 +165,47 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" }, // { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.Nvidia.mp3.decoder" }, // { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" }, + { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" }, { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" }, + { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" }, + { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" }, { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" }, + { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.decoder" }, { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" }, + { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "OMX.google.g711.alaw.decoder" }, { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" }, + { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "OMX.google.g711.mlaw.decoder" }, { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.google.mpeg4.decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" }, + { MEDIA_MIMETYPE_VIDEO_H263, "OMX.google.h263.decoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" }, + { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.avc.decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" }, + { MEDIA_MIMETYPE_AUDIO_VORBIS, "OMX.google.vorbis.decoder" }, { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" }, + { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" }, { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" }, }; @@ -277,6 +297,10 @@ static void InitOMXParams(T *params) { } static bool IsSoftwareCodec(const char *componentName) { + if (!strncmp("OMX.google.", componentName, 11)) { + return true; + } + if (!strncmp("OMX.", componentName, 4)) { return false; } @@ -284,26 +308,29 @@ static bool IsSoftwareCodec(const char *componentName) { return true; } -// A sort order in which non-OMX components are first, -// followed by software codecs, and followed by all the others. +// A sort order in which OMX software codecs are first, followed +// by other (non-OMX) software codecs, followed by everything else. static int CompareSoftwareCodecsFirst( const String8 *elem1, const String8 *elem2) { - bool isNotOMX1 = strncmp(elem1->string(), "OMX.", 4); - bool isNotOMX2 = strncmp(elem2->string(), "OMX.", 4); - - if (isNotOMX1) { - if (isNotOMX2) { return 0; } - return -1; - } - if (isNotOMX2) { - return 1; - } + bool isOMX1 = !strncmp(elem1->string(), "OMX.", 4); + bool isOMX2 = !strncmp(elem2->string(), "OMX.", 4); bool isSoftwareCodec1 = IsSoftwareCodec(elem1->string()); bool isSoftwareCodec2 = IsSoftwareCodec(elem2->string()); if (isSoftwareCodec1) { - if (isSoftwareCodec2) { return 0; } + if (!isSoftwareCodec2) { return -1; } + + if (isOMX1) { + if (isOMX2) { return 0; } + + return -1; + } else { + if (isOMX2) { return 0; } + + return 1; + } + return -1; } @@ -622,6 +649,11 @@ status_t OMXCodec::configureCodec(const sp &meta, uint32_t flags) { LOGE("Profile and/or level exceed the decoder's capabilities."); return ERROR_UNSUPPORTED; } + } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { + addCodecSpecificData(data, size); + + CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size)); + addCodecSpecificData(data, size); } } @@ -631,16 +663,23 @@ status_t OMXCodec::configureCodec(const sp &meta, uint32_t flags) { } if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mMIME)) { setAMRFormat(false /* isWAMR */, bitRate); - } - if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) { + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) { setAMRFormat(true /* isWAMR */, bitRate); - } - if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) { + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) { int32_t numChannels, sampleRate; CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); setAACFormat(numChannels, sampleRate, bitRate); + } 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 + // a variable number of channels. + + int32_t numChannels; + CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); + + setG711Format(numChannels); } if (!strncasecmp(mMIME, "video/", 6)) { @@ -1316,6 +1355,8 @@ 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 { LOGE("Not a supported video mime type: %s", mime); CHECK(!"Should not be here. Not a supported video mime type."); @@ -1443,7 +1484,8 @@ OMXCodec::OMXCodec( mOutputPortSettingsChangedPending(false), mLeftOverBuffer(NULL), mPaused(false), - mNativeWindow(nativeWindow) { + mNativeWindow(!strncmp(componentName, "OMX.google.", 11) + ? NULL : nativeWindow) { mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexOutput] = ENABLED; @@ -2899,6 +2941,23 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { offset += srcBuffer->range_length(); + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_VORBIS, mMIME)) { + CHECK(!(mQuirks & kSupportsMultipleFramesPerInputBuffer)); + CHECK_GE(info->mSize, offset + sizeof(int32_t)); + + int32_t numPageSamples; + if (!srcBuffer->meta_data()->findInt32( + kKeyValidSamples, &numPageSamples)) { + numPageSamples = -1; + } + + memcpy((uint8_t *)info->mData + offset, + &numPageSamples, + sizeof(numPageSamples)); + + offset += sizeof(numPageSamples); + } + if (releaseBuffer) { srcBuffer->release(); srcBuffer = NULL; @@ -3224,6 +3283,11 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bit } } +void OMXCodec::setG711Format(int32_t numChannels) { + CHECK(!mIsEncoder); + setRawAudioFormat(kPortIndexInput, 8000, numChannels); +} + void OMXCodec::setImageOutputFormat( OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) { CODEC_LOGV("setImageOutputFormat(%ld, %ld)", width, height); @@ -4013,6 +4077,13 @@ void OMXCodec::initOutputFormat(const sp &inputFormat) { numChannels, params.nChannels); } + if (sampleRate != params.nSamplingRate) { + LOGW("Codec outputs at different sampling rate than " + "what the input stream contains (contains data at " + "%d Hz, codec outputs %d Hz)", + sampleRate, params.nSamplingRate); + } + mOutputFormat->setCString( kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); @@ -4025,8 +4096,7 @@ void OMXCodec::initOutputFormat(const sp &inputFormat) { (mQuirks & kDecoderLiesAboutNumberOfChannels) ? numChannels : params.nChannels); - // The codec-reported sampleRate is not reliable... - mOutputFormat->setInt32(kKeySampleRate, sampleRate); + mOutputFormat->setInt32(kKeySampleRate, params.nSamplingRate); } else if (audio_def->eEncoding == OMX_AUDIO_CodingAMR) { OMX_AUDIO_PARAM_AMRTYPE amr; InitOMXParams(&amr); diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index 76f47f7..bf978d7 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -264,6 +264,8 @@ WAVSource::WAVSource( mGroup(NULL) { CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate)); CHECK(mMeta->findInt32(kKeyChannelCount, &mNumChannels)); + + mMeta->setInt32(kKeyMaxInputSize, kMaxFrameSize); } WAVSource::~WAVSource() { diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk index 69e331f..5ef54fd 100644 --- a/media/libstagefright/codecs/aacdec/Android.mk +++ b/media/libstagefright/codecs/aacdec/Android.mk @@ -143,14 +143,39 @@ LOCAL_SRC_FILES := \ unpack_idx.cpp \ window_tables_fxp.cpp \ pvmp4setaudioconfig.cpp \ - AACDecoder.cpp + AACDecoder.cpp \ LOCAL_CFLAGS := -DAAC_PLUS -DHQ_SBR -DPARAMETRICSTEREO -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= -DOSCL_UNUSED_ARG= -LOCAL_C_INCLUDES := frameworks/base/media/libstagefright/include +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ LOCAL_ARM_MODE := arm LOCAL_MODULE := libstagefright_aacdec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftAAC.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_CFLAGS := -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_aacdec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_aacdec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp new file mode 100644 index 0000000..7ce6128 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftAAC" +#include + +#include "SoftAAC.h" + +#include "pvmp4audiodecoder_api.h" + +#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; +} + +SoftAAC::SoftAAC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mConfig(new tPVMP4AudioDecoderExternal), + mDecoderBuf(NULL), + mInputBufferCount(0), + mUpsamplingFactor(2), + mAnchorTimeUs(0), + mNumSamplesOutput(0), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAAC::~SoftAAC() { + free(mDecoderBuf); + mDecoderBuf = NULL; + + delete mConfig; + mConfig = NULL; +} + +void SoftAAC::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + 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("audio/aac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + 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); +} + +status_t SoftAAC::initDecoder() { + memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal)); + mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED; + mConfig->aacPlusEnabled = 1; + + // The software decoder doesn't properly support mono output on + // AACplus files. Always output stereo. + mConfig->desiredChannels = 2; + + UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements(); + mDecoderBuf = malloc(memRequirements); + + Int err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf); + if (err != MP4AUDEC_SUCCESS) { + LOGE("Failed to initialize MP4 audio decoder"); + return UNKNOWN_ERROR; + } + + return OK; +} + +OMX_ERRORTYPE SoftAAC::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + aacParams->nBitRate = 0; + aacParams->nAudioBandWidth = 0; + aacParams->nAACtools = 0; + aacParams->nAACERtools = 0; + aacParams->eAACProfile = OMX_AUDIO_AACObjectMain; + aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; + + if (!isConfigured()) { + aacParams->nChannels = 1; + aacParams->nSampleRate = 44100; + aacParams->nFrameLength = 0; + } else { + aacParams->nChannels = mConfig->encodedChannels; + aacParams->nSampleRate = mConfig->samplingRate; + aacParams->nFrameLength = mConfig->frameLength; + } + + return OMX_ErrorNone; + } + + 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; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 44100; + } else { + pcmParams->nChannels = mConfig->desiredChannels; + pcmParams->nSamplingRate = mConfig->samplingRate; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAAC::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, + "audio_decoder.aac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftAAC::isConfigured() const { + return mInputBufferCount > 0; +} + +void SoftAAC::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (portIndex == 0 && mInputBufferCount == 0) { + ++mInputBufferCount; + + BufferInfo *info = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *header = info->mHeader; + + mConfig->pInputBuffer = header->pBuffer + header->nOffset; + mConfig->inputBufferCurrentLength = header->nFilledLen; + mConfig->inputBufferMaxLength = 0; + + Int err = PVMP4AudioDecoderConfig(mConfig, mDecoderBuf); + if (err != MP4AUDEC_SUCCESS) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, err, NULL); + return; + } + + inQueue.erase(inQueue.begin()); + info->mOwnedByUs = false; + notifyEmptyBufferDone(header); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + 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->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } + + mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset; + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + mConfig->inputBufferMaxLength = 0; + mConfig->inputBufferUsedLength = 0; + mConfig->remainderBits = 0; + + mConfig->pOutputBuffer = + reinterpret_cast(outHeader->pBuffer + outHeader->nOffset); + + mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048]; + mConfig->repositionFlag = false; + + Int32 prevSamplingRate = mConfig->samplingRate; + Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf); + + /* + * AAC+/eAAC+ streams can be signalled in two ways: either explicitly + * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual + * rate system and the sampling rate in the final output is actually + * doubled compared with the core AAC decoder sampling rate. + * + * Explicit signalling is done by explicitly defining SBR audio object + * type in the bitstream. Implicit signalling is done by embedding + * SBR content in AAC extension payload specific to SBR, and hence + * requires an AAC decoder to perform pre-checks on actual audio frames. + * + * Thus, we could not say for sure whether a stream is + * AAC+/eAAC+ until the first data frame is decoded. + */ + if (mInputBufferCount <= 2) { + LOGV("audio/extended audio object type: %d + %d", + mConfig->audioObjectType, mConfig->extendedAudioObjectType); + LOGV("aac+ upsampling factor: %d desired channels: %d", + mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels); + + if (mInputBufferCount == 1) { + mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor; + // Check on the sampling rate to see whether it is changed. + if (mConfig->samplingRate != prevSamplingRate) { + LOGW("Sample rate was %d Hz, but now is %d Hz", + prevSamplingRate, mConfig->samplingRate); + + // We'll hold onto the input buffer and will decode + // it again once the output port has been reconfigured. + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + } else { // mInputBufferCount == 2 + if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC || + mConfig->extendedAudioObjectType == MP4AUDIO_LTP) { + if (mUpsamplingFactor == 2) { + // The stream turns out to be not aacPlus mode anyway + LOGW("Disable AAC+/eAAC+ since extended audio object " + "type is %d", + mConfig->extendedAudioObjectType); + mConfig->aacPlusEnabled = 0; + } + } else { + if (mUpsamplingFactor == 1) { + // aacPlus mode does not buy us anything, but to cause + // 1. CPU load to increase, and + // 2. a half speed of decoding + LOGW("Disable AAC+/eAAC+ since upsampling factor is 1"); + mConfig->aacPlusEnabled = 0; + } + } + } + } + + size_t numOutBytes = + mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels; + + if (decoderErr == MP4AUDEC_SUCCESS) { + CHECK_LE(mConfig->inputBufferUsedLength, inHeader->nFilledLen); + + inHeader->nFilledLen -= mConfig->inputBufferUsedLength; + inHeader->nOffset += mConfig->inputBufferUsedLength; + } else { + memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); + } + + if (mUpsamplingFactor == 2) { + if (mConfig->desiredChannels == 1) { + memcpy(&mConfig->pOutputBuffer[1024], + &mConfig->pOutputBuffer[2048], + numOutBytes * 2); + } + numOutBytes *= 2; + } + + outHeader->nFilledLen = numOutBytes; + outHeader->nFlags = 0; + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate; + + mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor; + + 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; + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mInputBufferCount; + } +} + +void SoftAAC::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. + PVMP4AudioDecoderResetBuffer(mDecoderBuf); + } +} + +void SoftAAC::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; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAAC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h new file mode 100644 index 0000000..963fd27 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/SoftAAC.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_AAC_H_ + +#define SOFT_AAC_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tPVMP4AudioDecoderExternal; + +namespace android { + +struct SoftAAC : public SimpleSoftOMXComponent { + SoftAAC(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAAC(); + + 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); + +private: + enum { + kNumBuffers = 4 + }; + + tPVMP4AudioDecoderExternal *mConfig; + void *mDecoderBuf; + + size_t mInputBufferCount; + size_t mUpsamplingFactor; + int64_t mAnchorTimeUs; + int64_t mNumSamplesOutput; + + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + + DISALLOW_EVIL_CONSTRUCTORS(SoftAAC); +}; + +} // namespace android + +#endif // SOFT_AAC_H_ diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk index a545762..296cae4 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.mk +++ b/media/libstagefright/codecs/amrnb/dec/Android.mk @@ -52,3 +52,33 @@ LOCAL_CFLAGS := \ LOCAL_MODULE := libstagefright_amrnbdec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftAMR.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../common/include \ + $(LOCAL_PATH)/../common \ + frameworks/base/media/libstagefright/codecs/amrwb/src \ + +LOCAL_CFLAGS := -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_amrnbdec libstagefright_amrwbdec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright_omx libstagefright_foundation libutils \ + libstagefright_amrnb_common + +LOCAL_MODULE := libstagefright_soft_amrdec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp new file mode 100644 index 0000000..c0a588f --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftAMR" +#include + +#include "SoftAMR.h" + +#include "gsmamr_dec.h" +#include "pvamrwbdecoder.h" + +#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; +} + +SoftAMR::SoftAMR( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mMode(MODE_NARROW), + mState(NULL), + mDecoderBuf(NULL), + mDecoderCookie(NULL), + mInputBufferCount(0), + mAnchorTimeUs(0), + mNumSamplesOutput(0), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + if (!strcmp(name, "OMX.google.amrwb.decoder")) { + mMode = MODE_WIDE; + } else { + CHECK(!strcmp(name, "OMX.google.amrnb.decoder")); + } + + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAMR::~SoftAMR() { + if (mMode == MODE_NARROW) { + GSMDecodeFrameExit(&mState); + mState = NULL; + } else { + free(mDecoderBuf); + mDecoderBuf = NULL; + + mState = NULL; + mDecoderCookie = NULL; + } +} + +void SoftAMR::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + mMode == MODE_NARROW + ? const_cast("audio/amr") + : const_cast("audio/amrwb"); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAMR; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + + def.nBufferSize = + (mMode == MODE_NARROW ? kNumSamplesPerFrameNB : kNumSamplesPerFrameWB) + * 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); +} + +status_t SoftAMR::initDecoder() { + if (mMode == MODE_NARROW) { + Word16 err = GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder"); + + if (err != 0) { + return UNKNOWN_ERROR; + } + } else { + int32_t memReq = pvDecoder_AmrWbMemRequirements(); + mDecoderBuf = malloc(memReq); + + pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie); + } + + return OK; +} + +OMX_ERRORTYPE SoftAMR::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParams = + (OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (amrParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + amrParams->nChannels = 1; + amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; + amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatConformance; + + if (!isConfigured()) { + amrParams->nBitRate = 0; + amrParams->eAMRBandMode = OMX_AUDIO_AMRBandModeUnused; + } else { + amrParams->nBitRate = 0; + amrParams->eAMRBandMode = + mMode == MODE_NARROW + ? OMX_AUDIO_AMRBandModeNB0 : OMX_AUDIO_AMRBandModeWB0; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->nChannels = 1; + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + + pcmParams->nSamplingRate = + (mMode == MODE_NARROW) ? kSampleRateNB : kSampleRateWB; + + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAMR::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_NARROW) { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.amrnb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } else { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.amrwb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + const OMX_AUDIO_PARAM_AMRTYPE *aacParams = + (const OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftAMR::isConfigured() const { + return mInputBufferCount > 0; +} + +static size_t getFrameSize(unsigned FT) { + static const size_t kFrameSizeWB[9] = { + 132, 177, 253, 285, 317, 365, 397, 461, 477 + }; + + size_t frameSize = kFrameSizeWB[FT]; + + // Round up bits to bytes and add 1 for the header byte. + frameSize = (frameSize + 7) / 8 + 1; + + return frameSize; +} + +void SoftAMR::onQueueFilled(OMX_U32 portIndex) { + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + 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->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } + + const uint8_t *inputPtr = inHeader->pBuffer + inHeader->nOffset; + int32_t numBytesRead; + + if (mMode == MODE_NARROW) { + numBytesRead = + AMRDecode(mState, + (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f), + (UWord8 *)&inputPtr[1], + reinterpret_cast(outHeader->pBuffer), + MIME_IETF); + + if (numBytesRead == -1) { + LOGE("PV AMR decoder AMRDecode() call failed"); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + + return; + } + + ++numBytesRead; // Include the frame type header byte. + + if (static_cast(numBytesRead) > inHeader->nFilledLen) { + // This is bad, should never have happened, but did. Abort now. + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + + return; + } + } else { + int16 mode = ((inputPtr[0] >> 3) & 0x0f); + size_t frameSize = getFrameSize(mode); + CHECK_GE(inHeader->nFilledLen, frameSize); + + int16 frameType; + RX_State_wb rx_state; + mime_unsorting( + const_cast(&inputPtr[1]), + mInputSampleBuffer, + &frameType, &mode, 1, &rx_state); + + int16_t *outPtr = (int16_t *)outHeader->pBuffer; + + int16_t numSamplesOutput; + pvDecoder_AmrWb( + mode, mInputSampleBuffer, + outPtr, + &numSamplesOutput, + mDecoderBuf, frameType, mDecoderCookie); + + CHECK_EQ((int)numSamplesOutput, (int)kNumSamplesPerFrameWB); + + for (int i = 0; i < kNumSamplesPerFrameWB; ++i) { + /* Delete the 2 LSBs (14-bit output) */ + outPtr[i] &= 0xfffC; + } + + numBytesRead = frameSize; + } + + inHeader->nOffset += numBytesRead; + inHeader->nFilledLen -= numBytesRead; + + outHeader->nFlags = 0; + outHeader->nOffset = 0; + + if (mMode == MODE_NARROW) { + outHeader->nFilledLen = kNumSamplesPerFrameNB * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000ll) / kSampleRateNB; + + mNumSamplesOutput += kNumSamplesPerFrameNB; + } else { + outHeader->nFilledLen = kNumSamplesPerFrameWB * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000ll) / kSampleRateWB; + + mNumSamplesOutput += kNumSamplesPerFrameWB; + } + + 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; + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mInputBufferCount; + } +} + +void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) { +} + +void SoftAMR::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; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAMR(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h new file mode 100644 index 0000000..9a596e5 --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_AMR_H_ + +#define SOFT_AMR_H_ + +#include "SimpleSoftOMXComponent.h" + +namespace android { + +struct SoftAMR : public SimpleSoftOMXComponent { + SoftAMR(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAMR(); + + 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); + +private: + enum { + kNumBuffers = 4, + kSampleRateNB = 8000, + kSampleRateWB = 16000, + kNumSamplesPerFrameNB = 160, + kNumSamplesPerFrameWB = 320, + }; + + enum { + MODE_NARROW, + MODE_WIDE + + } mMode; + + void *mState; + void *mDecoderBuf; + int16_t *mDecoderCookie; + + size_t mInputBufferCount; + int64_t mAnchorTimeUs; + int64_t mNumSamplesOutput; + + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + int16_t mInputSampleBuffer[477]; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + + DISALLOW_EVIL_CONSTRUCTORS(SoftAMR); +}; + +} // namespace android + +#endif // SOFT_AMR_H_ + diff --git a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp index 2a21472..5b111ef 100644 --- a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp +++ b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp @@ -177,7 +177,7 @@ status_t AMRWBDecoder::read( CHECK(mInputBuffer->range_length() >= frameSize); int16 frameType; - RX_State rx_state; + RX_State_wb rx_state; mime_unsorting( const_cast(&inputPtr[1]), mInputSampleBuffer, diff --git a/media/libstagefright/codecs/amrwb/src/mime_io.cpp b/media/libstagefright/codecs/amrwb/src/mime_io.cpp index 9ff8816..e1966c6 100644 --- a/media/libstagefright/codecs/amrwb/src/mime_io.cpp +++ b/media/libstagefright/codecs/amrwb/src/mime_io.cpp @@ -531,7 +531,7 @@ void mime_unsorting(uint8 unsorted_bits[], int16 * frame_type, int16 * mode, uint8 quality, - RX_State *st) + RX_State_wb *st) { int16 i; diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h index 433fc92..c40bc10 100644 --- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h +++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h @@ -101,7 +101,7 @@ typedef struct { int16 prev_ft; int16 prev_mode; -} RX_State; +} RX_State_wb; /*---------------------------------------------------------------------------- ; ENUMERATED TYPEDEF'S @@ -141,7 +141,7 @@ typedef struct int16 *frame_type, int16 *mode, uint8 q, - RX_State *st); + RX_State_wb *st); /*---------------------------------------------------------------------------- diff --git a/media/libstagefright/codecs/avc/dec/Android.mk b/media/libstagefright/codecs/avc/dec/Android.mk index 1b00347..afecdc4 100644 --- a/media/libstagefright/codecs/avc/dec/Android.mk +++ b/media/libstagefright/codecs/avc/dec/Android.mk @@ -3,25 +3,54 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ AVCDecoder.cpp \ - src/avcdec_api.cpp \ - src/avc_bitstream.cpp \ - src/header.cpp \ - src/itrans.cpp \ - src/pred_inter.cpp \ - src/pred_intra.cpp \ - src/residual.cpp \ - src/slice.cpp \ - src/vlc.cpp + src/avcdec_api.cpp \ + src/avc_bitstream.cpp \ + src/header.cpp \ + src/itrans.cpp \ + src/pred_inter.cpp \ + src/pred_intra.cpp \ + src/residual.cpp \ + src/slice.cpp \ + src/vlc.cpp LOCAL_MODULE := libstagefright_avcdec LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/src \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../common/include \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../common/include \ $(TOP)/frameworks/base/media/libstagefright/include \ - $(TOP)/frameworks/base/include/media/stagefright/openmax + frameworks/base/include/media/stagefright/openmax \ LOCAL_CFLAGS := -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF= include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftAVC.cpp + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../common/include \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_CFLAGS := -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_avcdec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright_avc_common \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_avcdec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.cpp b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp new file mode 100644 index 0000000..9f141ac --- /dev/null +++ b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftAVC" +#include + +#include "SoftAVC.h" + +#include +#include +#include + +#include "avcdec_api.h" +#include "avcdec_int.h" + +namespace android { + +static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 }; + +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; +} + +static int32_t Malloc(void *userData, int32_t size, int32_t attrs) { + return reinterpret_cast(malloc(size)); +} + +static void Free(void *userData, int32_t ptr) { + free(reinterpret_cast(ptr)); +} + +SoftAVC::SoftAVC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mHandle(new tagAVCHandle), + mInputBufferCount(0), + mWidth(160), + mHeight(120), + mCropLeft(0), + mCropTop(0), + mCropRight(mWidth - 1), + mCropBottom(mHeight - 1), + mSPSSeen(false), + mPPSSeen(false), + mCurrentTimeUs(-1), + mEOSStatus(INPUT_DATA_AVAILABLE), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAVC::~SoftAVC() { + PVAVCCleanUpDecoder(mHandle); + + delete mHandle; + mHandle = NULL; +} + +void SoftAVC::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 = 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 = 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 SoftAVC::initDecoder() { + memset(mHandle, 0, sizeof(tagAVCHandle)); + mHandle->AVCObject = NULL; + mHandle->userData = this; + mHandle->CBAVC_DPBAlloc = ActivateSPSWrapper; + mHandle->CBAVC_FrameBind = BindFrameWrapper; + mHandle->CBAVC_FrameUnbind = UnbindFrame; + mHandle->CBAVC_Malloc = Malloc; + mHandle->CBAVC_Free = Free; + + return OK; +} + +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 > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex != 0) { + return OMX_ErrorNoMore; + } + + if (formatParams->nPortIndex == 0) { + formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC; + 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 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 > 1) { + 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 = mCropRight - mCropLeft + 1; + rectParams->nHeight = mCropBottom - mCropTop + 1; + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +static void findNALFragment( + const OMX_BUFFERHEADERTYPE *inHeader, + const uint8_t **fragPtr, size_t *fragSize) { + const uint8_t *data = inHeader->pBuffer + inHeader->nOffset; + + size_t size = inHeader->nFilledLen; + + CHECK(size >= 4); + CHECK(!memcmp(kStartCode, data, 4)); + + size_t offset = 4; + while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) { + ++offset; + } + + *fragPtr = &data[4]; + if (offset + 3 >= size) { + *fragSize = size - 4; + } else { + *fragSize = offset - 4; + } +} + +void SoftAVC::onQueueFilled(OMX_U32 portIndex) { + if (mOutputPortSettingsChange != NONE) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) { + return; + } + + while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty()) + && outQueue.size() == kNumOutputBuffers) { + if (mEOSStatus == INPUT_EOS_SEEN) { + OMX_BUFFERHEADERTYPE *outHeader; + if (drainOutputBuffer(&outHeader)) { + List::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + + outHeader->nOffset = 0; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + outHeader->nTimeStamp = 0; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + mEOSStatus = OUTPUT_FRAMES_FLUSHED; + return; + } + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + mEOSStatus = INPUT_EOS_SEEN; + continue; + } + + mCurrentTimeUs = inHeader->nTimeStamp; + + const uint8_t *fragPtr; + size_t fragSize; + findNALFragment(inHeader, &fragPtr, &fragSize); + + bool releaseFragment; + OMX_BUFFERHEADERTYPE *outHeader; + status_t err = decodeFragment( + fragPtr, fragSize, + &releaseFragment, &outHeader); + + if (releaseFragment) { + CHECK_GE(inHeader->nFilledLen, fragSize + 4); + + inHeader->nOffset += fragSize + 4; + inHeader->nFilledLen -= fragSize + 4; + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + } + + if (outHeader != NULL) { + List::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + return; + } + + if (err == INFO_FORMAT_CHANGED) { + return; + } + + if (err != OK) { + notify(OMX_EventError, OMX_ErrorUndefined, err, NULL); + return; + } + } +} + +status_t SoftAVC::decodeFragment( + const uint8_t *fragPtr, size_t fragSize, + bool *releaseFragment, + OMX_BUFFERHEADERTYPE **outHeader) { + *releaseFragment = true; + *outHeader = NULL; + + int nalType; + int nalRefIdc; + AVCDec_Status res = + PVAVCDecGetNALType( + const_cast(fragPtr), fragSize, + &nalType, &nalRefIdc); + + if (res != AVCDEC_SUCCESS) { + LOGV("cannot determine nal type"); + return ERROR_MALFORMED; + } + + if (nalType != AVC_NALTYPE_SPS && nalType != AVC_NALTYPE_PPS + && (!mSPSSeen || !mPPSSeen)) { + // We haven't seen SPS or PPS yet. + return OK; + } + + switch (nalType) { + case AVC_NALTYPE_SPS: + { + mSPSSeen = true; + + res = PVAVCDecSeqParamSet( + mHandle, const_cast(fragPtr), + fragSize); + + if (res != AVCDEC_SUCCESS) { + return ERROR_MALFORMED; + } + + AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject; + + int32_t width = + (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16; + + int32_t height = + (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16; + + int32_t crop_left, crop_right, crop_top, crop_bottom; + if (pDecVid->seqParams[0]->frame_cropping_flag) + { + crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset; + crop_right = + width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1); + + if (pDecVid->seqParams[0]->frame_mbs_only_flag) + { + crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset; + crop_bottom = + height - + (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1); + } + else + { + crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset; + crop_bottom = + height - + (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1); + } + } else { + crop_bottom = height - 1; + crop_right = width - 1; + crop_top = crop_left = 0; + } + + status_t err = OK; + + if (mWidth != width || mHeight != height) { + mWidth = width; + mHeight = height; + + updatePortDefinitions(); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + + err = INFO_FORMAT_CHANGED; + } + + if (mCropLeft != crop_left + || mCropTop != crop_top + || mCropRight != crop_right + || mCropBottom != crop_bottom) { + mCropLeft = crop_left; + mCropTop = crop_top; + mCropRight = crop_right; + mCropBottom = crop_bottom; + + notify(OMX_EventPortSettingsChanged, + 1, + OMX_IndexConfigCommonOutputCrop, + NULL); + } + + return err; + } + + case AVC_NALTYPE_PPS: + { + mPPSSeen = true; + + res = PVAVCDecPicParamSet( + mHandle, const_cast(fragPtr), + fragSize); + + if (res != AVCDEC_SUCCESS) { + LOGV("PVAVCDecPicParamSet returned error %d", res); + return ERROR_MALFORMED; + } + + return OK; + } + + case AVC_NALTYPE_SLICE: + case AVC_NALTYPE_IDR: + { + res = PVAVCDecodeSlice( + mHandle, const_cast(fragPtr), + fragSize); + + if (res == AVCDEC_PICTURE_OUTPUT_READY) { + *releaseFragment = false; + + if (!drainOutputBuffer(outHeader)) { + return UNKNOWN_ERROR; + } + + return OK; + } + + if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) { + return OK; + } else { + LOGV("PVAVCDecodeSlice returned error %d", res); + return ERROR_MALFORMED; + } + } + + case AVC_NALTYPE_SEI: + { + res = PVAVCDecSEI( + mHandle, const_cast(fragPtr), + fragSize); + + if (res != AVCDEC_SUCCESS) { + return ERROR_MALFORMED; + } + + return OK; + } + + case AVC_NALTYPE_AUD: + case AVC_NALTYPE_FILL: + case AVC_NALTYPE_EOSEQ: + { + return OK; + } + + default: + { + LOGE("Should not be here, unknown nalType %d", nalType); + + return ERROR_MALFORMED; + } + } + + return OK; +} + +bool SoftAVC::drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader) { + int32_t index; + int32_t Release; + AVCFrameIO Output; + Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL; + AVCDec_Status status = + PVAVCDecGetOutput(mHandle, &index, &Release, &Output); + + if (status != AVCDEC_SUCCESS) { + return false; + } + + PortInfo *port = editPortInfo(1); + CHECK_GE(index, 0); + CHECK_LT((size_t)index, port->mBuffers.size()); + CHECK(port->mBuffers.editItemAt(index).mOwnedByUs); + + *outHeader = port->mBuffers.editItemAt(index).mHeader; + (*outHeader)->nOffset = 0; + (*outHeader)->nFilledLen = port->mDef.nBufferSize; + (*outHeader)->nFlags = 0; + + return true; +} + +void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + PVAVCDecReset(mHandle); + + mEOSStatus = INPUT_DATA_AVAILABLE; + } +} + +void SoftAVC::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 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; +} + +// static +int32_t SoftAVC::ActivateSPSWrapper( + void *userData, unsigned int sizeInMbs, unsigned int numBuffers) { + return static_cast(userData)->activateSPS(sizeInMbs, numBuffers); +} + +// static +int32_t SoftAVC::BindFrameWrapper( + void *userData, int32_t index, uint8_t **yuv) { + return static_cast(userData)->bindFrame(index, yuv); +} + +// static +void SoftAVC::UnbindFrame(void *userData, int32_t index) { +} + +int32_t SoftAVC::activateSPS( + unsigned int sizeInMbs, unsigned int numBuffers) { + PortInfo *port = editPortInfo(1); + CHECK_GE(port->mBuffers.size(), numBuffers); + CHECK_GE(port->mDef.nBufferSize, (sizeInMbs << 7) * 3); + + return 1; +} + +int32_t SoftAVC::bindFrame(int32_t index, uint8_t **yuv) { + PortInfo *port = editPortInfo(1); + + CHECK_GE(index, 0); + CHECK_LT((size_t)index, port->mBuffers.size()); + + BufferInfo *outBuffer = + &port->mBuffers.editItemAt(index); + + CHECK(outBuffer->mOwnedByUs); + + outBuffer->mHeader->nTimeStamp = mCurrentTimeUs; + *yuv = outBuffer->mHeader->pBuffer; + + return 1; +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAVC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.h b/media/libstagefright/codecs/avc/dec/SoftAVC.h new file mode 100644 index 0000000..1594b4d --- /dev/null +++ b/media/libstagefright/codecs/avc/dec/SoftAVC.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_AVC_H_ + +#define SOFT_AVC_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tagAVCHandle; + +namespace android { + +struct SoftAVC : public SimpleSoftOMXComponent { + SoftAVC(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +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); + +private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 18, + }; + + enum EOSStatus { + INPUT_DATA_AVAILABLE, + INPUT_EOS_SEEN, + OUTPUT_FRAMES_FLUSHED, + }; + + tagAVCHandle *mHandle; + + size_t mInputBufferCount; + + int32_t mWidth, mHeight; + int32_t mCropLeft, mCropTop, mCropRight, mCropBottom; + + bool mSPSSeen, mPPSSeen; + + int64_t mCurrentTimeUs; + + EOSStatus mEOSStatus; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + + status_t decodeFragment( + const uint8_t *fragPtr, size_t fragSize, + bool *releaseFrames, + OMX_BUFFERHEADERTYPE **outHeader); + + void updatePortDefinitions(); + bool drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader); + + static int32_t ActivateSPSWrapper( + void *userData, unsigned int sizeInMbs, unsigned int numBuffers); + + static int32_t BindFrameWrapper( + void *userData, int32_t index, uint8_t **yuv); + + static void UnbindFrame(void *userData, int32_t index); + + int32_t activateSPS( + unsigned int sizeInMbs, unsigned int numBuffers); + + int32_t bindFrame(int32_t index, uint8_t **yuv); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAVC); +}; + +} // namespace android + +#endif // SOFT_AVC_H_ + diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk index cfb9fe4..b6953bc 100644 --- a/media/libstagefright/codecs/g711/dec/Android.mk +++ b/media/libstagefright/codecs/g711/dec/Android.mk @@ -10,3 +10,22 @@ LOCAL_C_INCLUDES := \ LOCAL_MODULE := libstagefright_g711dec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftG711.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_g711dec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp new file mode 100644 index 0000000..15e2c26 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftG711" +#include + +#include "SoftG711.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; +} + +SoftG711::SoftG711( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mIsMLaw(true), + mNumChannels(1), + mSignalledError(false) { + if (!strcmp(name, "OMX.google.g711.alaw.decoder")) { + mIsMLaw = false; + } else { + CHECK(!strcmp(name, "OMX.google.g711.mlaw.decoder")); + } + + initPorts(); +} + +SoftG711::~SoftG711() { +} + +void SoftG711::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + 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( + mIsMLaw + ? MEDIA_MIMETYPE_AUDIO_G711_MLAW + : MEDIA_MIMETYPE_AUDIO_G711_ALAW); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingG711; + + 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 SoftG711::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 = mNumChannels; + pcmParams->nSamplingRate = 8000; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftG711::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) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + + return OMX_ErrorNone; + } + + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (mIsMLaw) { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.g711mlaw", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } else { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.g711alaw", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftG711::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) { + LOGE("input buffer too large (%ld).", inHeader->nFilledLen); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + } + + const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset; + + if (mIsMLaw) { + DecodeMLaw( + reinterpret_cast(outHeader->pBuffer), + inputptr, inHeader->nFilledLen); + } else { + DecodeALaw( + reinterpret_cast(outHeader->pBuffer), + inputptr, inHeader->nFilledLen); + } + + outHeader->nTimeStamp = inHeader->nTimeStamp; + outHeader->nOffset = 0; + outHeader->nFilledLen = inHeader->nFilledLen * 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 +void SoftG711::DecodeALaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t ix = x ^ 0x55; + ix &= 0x7f; + + int32_t iexp = ix >> 4; + int32_t mant = ix & 0x0f; + + if (iexp > 0) { + mant += 16; + } + + mant = (mant << 4) + 8; + + if (iexp > 1) { + mant = mant << (iexp - 1); + } + + *out++ = (x > 127) ? mant : -mant; + } +} + +// static +void SoftG711::DecodeMLaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t mantissa = ~x; + int32_t exponent = (mantissa >> 4) & 7; + int32_t segment = exponent + 1; + mantissa &= 0x0f; + + int32_t step = 4 << segment; + + int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33; + + *out++ = (x < 0x80) ? -abs : abs; + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftG711(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.h b/media/libstagefright/codecs/g711/dec/SoftG711.h new file mode 100644 index 0000000..bff0c68 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/SoftG711.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_G711_H_ + +#define SOFT_G711_H_ + +#include "SimpleSoftOMXComponent.h" + +namespace android { + +struct SoftG711 : public SimpleSoftOMXComponent { + SoftG711(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftG711(); + + 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 mIsMLaw; + OMX_U32 mNumChannels; + bool mSignalledError; + + void initPorts(); + + static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize); + static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize); + + DISALLOW_EVIL_CONSTRUCTORS(SoftG711); +}; + +} // namespace android + +#endif // SOFT_G711_H_ + diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk index 2d9bcc6..65b642c 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk @@ -48,3 +48,29 @@ LOCAL_C_INCLUDES := \ LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF= include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftMPEG4.cpp + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF= + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_m4vh263dec + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_mpeg4dec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp new file mode 100644 index 0000000..13e1662 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftMPEG4" +#include + +#include "SoftMPEG4.h" + +#include +#include +#include + +#include "mp4dec_api.h" + +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; +} + +SoftMPEG4::SoftMPEG4( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mMode(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), + mOutputPortSettingsChange(NONE) { + if (!strcmp(name, "OMX.google.h263.decoder")) { + mMode = MODE_H263; + } else { + CHECK(!strcmp(name, "OMX.google.mpeg4.decoder")); + } + + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftMPEG4::~SoftMPEG4() { + if (mInitialized) { + PVCleanUpVideoDecoder(mHandle); + } + + delete mHandle; + 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; + } + + 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; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + PortInfo *port = editPortInfo(1); + + OMX_BUFFERHEADERTYPE *outHeader = + port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + ++mInputBufferCount; + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + List::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + return; + } + + uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset; + + if (!mInitialized) { + uint8_t *vol_data[1]; + int32_t vol_size = 0; + + vol_data[0] = NULL; + + if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + vol_data[0] = bitstream; + vol_size = inHeader->nFilledLen; + } + + MP4DecodingMode mode = + (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE; + + Bool success = PVInitVideoDecoder( + mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode); + + if (!success) { + LOGW("PVInitVideoDecoder failed. Unsupported content?"); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle); + if (mode != actualMode) { + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + PVSetPostProcType((VideoDecControls *) mHandle, 0); + + if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + mInitialized = true; + + if (mode == MPEG4_MODE && portSettingsChanged()) { + return; + } + + continue; + } + + if (!mFramesConfigured) { + PortInfo *port = editPortInfo(1); + OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader; + + PVSetReferenceYUV(mHandle, outHeader->pBuffer); + + mFramesConfigured = true; + } + + uint32_t timestamp = 0xFFFFFFFF; + int32_t bufferSize = inHeader->nFilledLen; + + uint32_t useExtTimestamp = 0; + if (PVDecodeVideoFrame( + mHandle, &bitstream, ×tamp, &bufferSize, + &useExtTimestamp, + outHeader->pBuffer) != PV_TRUE) { + LOGE("failed to decode video frame."); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + if (portSettingsChanged()) { + return; + } + + outHeader->nTimeStamp = inHeader->nTimeStamp; + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + ++mInputBufferCount; + + outHeader->nOffset = 0; + outHeader->nFilledLen = (mWidth * mHeight * 3) / 2; + outHeader->nFlags = 0; + + List::iterator it = outQueue.begin(); + while ((*it)->mHeader != outHeader) { + ++it; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mNumSamplesOutput; + } +} + +bool SoftMPEG4::portSettingsChanged() { + int32_t disp_width, disp_height; + PVGetVideoDimensions(mHandle, &disp_width, &disp_height); + + int32_t buf_width, buf_height; + PVGetBufferDimensions(mHandle, &buf_width, &buf_height); + + CHECK_LE(disp_width, buf_width); + CHECK_LE(disp_height, buf_height); + + LOGV("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) { + mCropLeft = 0; + mCropTop = 0; + mCropRight = disp_width - 1; + mCropBottom = disp_height - 1; + + notify(OMX_EventPortSettingsChanged, + 1, + OMX_IndexConfigCommonOutputCrop, + NULL); + } + + if (buf_width != mWidth || buf_height != mHeight) { + mWidth = buf_width; + mHeight = buf_height; + + updatePortDefinitions(); + + if (mMode == MODE_H263) { + PVCleanUpVideoDecoder(mHandle); + + uint8_t *vol_data[1]; + int32_t vol_size = 0; + + vol_data[0] = NULL; + if (!PVInitVideoDecoder( + mHandle, vol_data, &vol_size, 1, mWidth, mHeight, + H263_MODE)) { + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return true; + } + } + + mFramesConfigured = false; + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return true; + } + + return false; +} + +void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0 && mInitialized) { + CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE); + } +} + +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::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 + 15) & -16) + * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2; +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftMPEG4(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h new file mode 100644 index 0000000..dff08a7 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_MPEG4_H_ + +#define SOFT_MPEG4_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tagvideoDecControls; + +namespace android { + +struct SoftMPEG4 : public SimpleSoftOMXComponent { + SoftMPEG4(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +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); + +private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 2, + }; + + 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; + + int32_t mNumSamplesOutput; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + + void updatePortDefinitions(); + bool portSettingsChanged(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4); +}; + +} // namespace android + +#endif // SOFT_MPEG4_H_ + + diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk index 753500e..2d35183 100644 --- a/media/libstagefright/codecs/mp3dec/Android.mk +++ b/media/libstagefright/codecs/mp3dec/Android.mk @@ -57,3 +57,26 @@ LOCAL_ARM_MODE := arm include $(BUILD_STATIC_LIBRARY) +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftMP3.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + $(LOCAL_PATH)/src \ + $(LOCAL_PATH)/include + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_mp3dec + +LOCAL_MODULE := libstagefright_soft_mp3dec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp new file mode 100644 index 0000000..f6770b0 --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftMP3" +#include + +#include "SoftMP3.h" + +#include +#include + +#include "include/pvmp3decoder_api.h" + +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; +} + +SoftMP3::SoftMP3( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mConfig(new tPVMP3DecoderExternal), + mDecoderBuf(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mNumChannels(2), + mSamplingRate(44100), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + initPorts(); + initDecoder(); +} + +SoftMP3::~SoftMP3() { + if (mDecoderBuf != NULL) { + free(mDecoderBuf); + mDecoderBuf = NULL; + } + + delete mConfig; + mConfig = NULL; +} + +void SoftMP3::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + 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_MPEG); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingMP3; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kOutputBufferSize; + 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); +} + +void SoftMP3::initDecoder() { + mConfig->equalizerType = flat; + mConfig->crcEnabled = false; + + uint32_t memRequirements = pvmp3_decoderMemRequirements(); + mDecoderBuf = malloc(memRequirements); + + pvmp3_InitDecoder(mConfig, mDecoderBuf); +} + +OMX_ERRORTYPE SoftMP3::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 = mNumChannels; + pcmParams->nSamplingRate = mSamplingRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftMP3::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, + "audio_decoder.mp3", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftMP3::onQueueFilled(OMX_U32 portIndex) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + 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->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } + + mConfig->pInputBuffer = + inHeader->pBuffer + inHeader->nOffset; + + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + mConfig->inputBufferMaxLength = 0; + mConfig->inputBufferUsedLength = 0; + + mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); + + mConfig->pOutputBuffer = + reinterpret_cast(outHeader->pBuffer); + + ERROR_CODE decoderErr; + if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf)) + != NO_DECODING_ERROR) { + LOGV("mp3 decoder returned error %d", decoderErr); + + if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR || + mConfig->outputFrameSize == 0) { + + if (mConfig->outputFrameSize == 0) { + LOGE("Output frame size is 0"); + } + + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + mSignalledError = true; + return; + } + + // 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; + mNumChannels = mConfig->num_channels; + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + outHeader->nOffset = 0; + outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate; + + outHeader->nFlags = 0; + + CHECK_GE(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; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } +} + +void SoftMP3::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. + pvmp3_InitDecoder(mConfig, mDecoderBuf); + } +} + +void SoftMP3::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; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftMP3(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h new file mode 100644 index 0000000..70d0682 --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_MP3_H_ + +#define SOFT_MP3_H_ + +#include "SimpleSoftOMXComponent.h" + +struct tPVMP3DecoderExternal; + +namespace android { + +struct SoftMP3 : public SimpleSoftOMXComponent { + SoftMP3(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftMP3(); + + 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); + +private: + enum { + kNumBuffers = 4, + kOutputBufferSize = 4608 * 2 + }; + + tPVMP3DecoderExternal *mConfig; + void *mDecoderBuf; + int64_t mAnchorTimeUs; + int64_t mNumFramesOutput; + + int32_t mNumChannels; + int32_t mSamplingRate; + + bool mConfigured; + + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + void initDecoder(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMP3); +}; + +} // namespace android + +#endif // SOFT_MP3_H_ + + diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk index b769f0d..1b3088f 100644 --- a/media/libstagefright/codecs/on2/dec/Android.mk +++ b/media/libstagefright/codecs/on2/dec/Android.mk @@ -2,15 +2,42 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - VPXDecoder.cpp + VPXDecoder.cpp \ LOCAL_MODULE := libstagefright_vpxdec LOCAL_C_INCLUDES := \ $(TOP)/frameworks/base/media/libstagefright/include \ - $(TOP)/frameworks/base/include/media/stagefright/openmax \ + frameworks/base/include/media/stagefright/openmax \ $(TOP)/external/libvpx \ $(TOP)/external/libvpx/vpx_codec \ $(TOP)/external/libvpx/vpx_ports include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftVPX.cpp + +LOCAL_C_INCLUDES := \ + $(TOP)/external/libvpx \ + $(TOP)/external/libvpx/vpx_codec \ + $(TOP)/external/libvpx/vpx_ports \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_vpxdec \ + libvpx + +LOCAL_SHARED_LIBRARIES := \ + libstagefright libstagefright_omx libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_vpxdec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp new file mode 100644 index 0000000..e9ce719 --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftVPX" +#include + +#include "SoftVPX.h" + +#include +#include + +#include "vpx/vpx_decoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8dx.h" + +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(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftVPX::~SoftVPX() { + vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); + delete (vpx_codec_ctx_t *)mCtx; + 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 = 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_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); +} + +status_t SoftVPX::initDecoder() { + mCtx = new vpx_codec_ctx_t; + vpx_codec_err_t vpx_err; + if ((vpx_err = vpx_codec_dec_init( + (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) { + LOGE("on2 decoder failed to initialize. (%d)", vpx_err); + return UNKNOWN_ERROR; + } + + 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; + } + + 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 (vpx_codec_decode( + (vpx_codec_ctx_t *)mCtx, + inHeader->pBuffer + inHeader->nOffset, + inHeader->nFilledLen, + NULL, + 0)) { + LOGE("on2 decoder failed to decode frame."); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + vpx_codec_iter_t iter = NULL; + vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); + + if (img != NULL) { + CHECK_EQ(img->fmt, IMG_FMT_I420); + + int32_t width = img->d_w; + int32_t height = img->d_h; + + if (width != mWidth || height != mHeight) { + mWidth = width; + mHeight = height; + + updatePortDefinitions(); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + outHeader->nOffset = 0; + outHeader->nFilledLen = (width * height * 3) / 2; + outHeader->nFlags = 0; + outHeader->nTimeStamp = inHeader->nTimeStamp; + + const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; + uint8_t *dst = outHeader->pBuffer; + for (size_t i = 0; i < img->d_h; ++i) { + memcpy(dst, srcLine, img->d_w); + + srcLine += img->stride[PLANE_Y]; + dst += img->d_w; + } + + srcLine = (const uint8_t *)img->planes[PLANE_U]; + for (size_t i = 0; i < img->d_h / 2; ++i) { + memcpy(dst, srcLine, img->d_w / 2); + + srcLine += img->stride[PLANE_U]; + dst += img->d_w / 2; + } + + srcLine = (const uint8_t *)img->planes[PLANE_V]; + for (size_t i = 0; i < img->d_h / 2; ++i) { + memcpy(dst, srcLine, img->d_w / 2); + + srcLine += img->stride[PLANE_V]; + dst += img->d_w / 2; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } +} + +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::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( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftVPX(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h new file mode 100644 index 0000000..3e814a2 --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_VPX_H_ + +#define SOFT_VPX_H_ + +#include "SimpleSoftOMXComponent.h" + +namespace android { + +struct SoftVPX : public SimpleSoftOMXComponent { + SoftVPX(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +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); + +private: + enum { + kNumBuffers = 4 + }; + + 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); +}; + +} // namespace android + +#endif // SOFT_VPX_H_ diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk index 5c768c8..06f0079 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.mk +++ b/media/libstagefright/codecs/vorbis/dec/Android.mk @@ -6,8 +6,33 @@ LOCAL_SRC_FILES := \ LOCAL_C_INCLUDES := \ frameworks/base/media/libstagefright/include \ - external/tremolo + external/tremolo \ LOCAL_MODULE := libstagefright_vorbisdec include $(BUILD_STATIC_LIBRARY) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + SoftVorbis.cpp + +LOCAL_C_INCLUDES := \ + external/tremolo \ + frameworks/base/media/libstagefright/include \ + frameworks/base/include/media/stagefright/openmax \ + +LOCAL_STATIC_LIBRARIES := \ + libstagefright_vorbisdec + +LOCAL_SHARED_LIBRARIES := \ + libvorbisidec libstagefright libstagefright_omx \ + libstagefright_foundation libutils + +LOCAL_MODULE := libstagefright_soft_vorbisdec +LOCAL_MODULE_TAGS := eng + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp new file mode 100644 index 0000000..4091111 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftVorbis" +#include + +#include "SoftVorbis.h" + +#include +#include + +extern "C" { + #include + + int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); +} + +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; +} + +SoftVorbis::SoftVorbis( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mInputBufferCount(0), + mState(NULL), + mVi(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mNumFramesLeftOnPage(-1), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftVorbis::~SoftVorbis() { + if (mState != NULL) { + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + } + + if (mVi != NULL) { + vorbis_info_clear(mVi); + delete mVi; + mVi = NULL; + } +} + +void SoftVorbis::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + 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_VORBIS); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerBuffer * 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); +} + +status_t SoftVorbis::initDecoder() { + return OK; +} + +OMX_ERRORTYPE SoftVorbis::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioVorbis: + { + OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams = + (OMX_AUDIO_PARAM_VORBISTYPE *)params; + + if (vorbisParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + vorbisParams->nBitRate = 0; + vorbisParams->nMinBitRate = 0; + vorbisParams->nMaxBitRate = 0; + vorbisParams->nAudioBandWidth = 0; + vorbisParams->nQuality = 3; + vorbisParams->bManaged = OMX_FALSE; + vorbisParams->bDownmix = OMX_FALSE; + + if (!isConfigured()) { + vorbisParams->nChannels = 1; + vorbisParams->nSampleRate = 44100; + } else { + vorbisParams->nChannels = mVi->channels; + vorbisParams->nSampleRate = mVi->rate; + vorbisParams->nBitRate = mVi->bitrate_nominal; + vorbisParams->nMinBitRate = mVi->bitrate_lower; + vorbisParams->nMaxBitRate = mVi->bitrate_upper; + } + + return OMX_ErrorNone; + } + + 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; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 44100; + } else { + pcmParams->nChannels = mVi->channels; + pcmParams->nSamplingRate = mVi->rate; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftVorbis::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, + "audio_decoder.vorbis", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioVorbis: + { + const OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams = + (const OMX_AUDIO_PARAM_VORBISTYPE *)params; + + if (vorbisParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftVorbis::isConfigured() const { + return mInputBufferCount >= 2; +} + +static void makeBitReader( + const void *data, size_t size, + ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) { + buf->data = (uint8_t *)data; + buf->size = size; + buf->refcount = 1; + buf->ptr.owner = NULL; + + ref->buffer = buf; + ref->begin = 0; + ref->length = size; + ref->next = NULL; + + oggpack_readinit(bits, ref); +} + +void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (mOutputPortSettingsChange != NONE) { + return; + } + + if (portIndex == 0 && mInputBufferCount < 2) { + BufferInfo *info = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *header = info->mHeader; + + const uint8_t *data = header->pBuffer + header->nOffset; + size_t size = header->nFilledLen; + + ogg_buffer buf; + ogg_reference ref; + oggpack_buffer bits; + + makeBitReader( + (const uint8_t *)data + 7, size - 7, + &buf, &ref, &bits); + + if (mInputBufferCount == 0) { + CHECK(mVi == NULL); + mVi = new vorbis_info; + vorbis_info_init(mVi); + + CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits)); + } else { + CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits)); + + CHECK(mState == NULL); + mState = new vorbis_dsp_state; + CHECK_EQ(0, vorbis_dsp_init(mState, mVi)); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + + inQueue.erase(inQueue.begin()); + info->mOwnedByUs = false; + notifyEmptyBufferDone(header); + + ++mInputBufferCount; + + return; + } + + 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; + } + + 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->pBuffer + inHeader->nOffset; + buf.size = inHeader->nFilledLen; + buf.refcount = 1; + buf.ptr.owner = NULL; + + ogg_reference ref; + ref.buffer = &buf; + ref.begin = 0; + ref.length = buf.size; + ref.next = NULL; + + ogg_packet pack; + pack.packet = &ref; + pack.bytes = ref.length; + pack.b_o_s = 0; + pack.e_o_s = 0; + pack.granulepos = 0; + pack.packetno = 0; + + int numFrames = 0; + + int err = vorbis_dsp_synthesis(mState, &pack, 1); + if (err != 0) { + LOGW("vorbis_dsp_synthesis returned %d", err); + } else { + numFrames = vorbis_dsp_pcmout( + mState, (int16_t *)outHeader->pBuffer, + kMaxNumSamplesPerBuffer); + + if (numFrames < 0) { + LOGE("vorbis_dsp_pcmout returned %d", numFrames); + numFrames = 0; + } + } + + if (mNumFramesLeftOnPage >= 0) { + if (numFrames > mNumFramesLeftOnPage) { + LOGV("discarding %d frames at end of page", + numFrames - mNumFramesLeftOnPage); + numFrames = mNumFramesLeftOnPage; + } + mNumFramesLeftOnPage -= numFrames; + } + + outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels; + outHeader->nOffset = 0; + outHeader->nFlags = 0; + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumFramesOutput * 1000000ll) / mVi->rate; + + mNumFramesOutput += numFrames; + + 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; + + ++mInputBufferCount; + } +} + +void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0 && mState != NULL) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + + mNumFramesOutput = 0; + vorbis_dsp_restart(mState); + } +} + +void SoftVorbis::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; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftVorbis(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h new file mode 100644 index 0000000..e252f55 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_VORBIS_H_ + +#define SOFT_VORBIS_H_ + +#include "SimpleSoftOMXComponent.h" + +struct vorbis_dsp_state; +struct vorbis_info; + +namespace android { + +struct SoftVorbis : public SimpleSoftOMXComponent { + SoftVorbis(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftVorbis(); + + 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); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerBuffer = 8192 * 2 + }; + + size_t mInputBufferCount; + + vorbis_dsp_state *mState; + vorbis_info *mVi; + + int64_t mAnchorTimeUs; + int64_t mNumFramesOutput; + int32_t mNumFramesLeftOnPage; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + + DISALLOW_EVIL_CONSTRUCTORS(SoftVorbis); +}; + +} // namespace android + +#endif // SOFT_VORBIS_H_ + diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h new file mode 100644 index 0000000..2a29a7d --- /dev/null +++ b/media/libstagefright/include/SimpleSoftOMXComponent.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SIMPLE_SOFT_OMX_COMPONENT_H_ + +#define SIMPLE_SOFT_OMX_COMPONENT_H_ + +#include "SoftOMXComponent.h" + +#include +#include +#include +#include + +namespace android { + +struct ALooper; + +struct SimpleSoftOMXComponent : public SoftOMXComponent { + SimpleSoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual ~SimpleSoftOMXComponent(); + + void onMessageReceived(const sp &msg); + +protected: + struct BufferInfo { + OMX_BUFFERHEADERTYPE *mHeader; + bool mOwnedByUs; + }; + + struct PortInfo { + OMX_PARAM_PORTDEFINITIONTYPE mDef; + Vector mBuffers; + List mQueue; + + enum { + NONE, + DISABLING, + ENABLING, + } mTransition; + }; + + void addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def); + + 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); + List &getPortQueue(OMX_U32 portIndex); + + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + + PortInfo *editPortInfo(OMX_U32 portIndex); + +private: + enum { + kWhatSendCommand, + kWhatEmptyThisBuffer, + kWhatFillThisBuffer, + }; + + Mutex mLock; + + sp mLooper; + sp > mHandler; + + OMX_STATETYPE mState; + OMX_STATETYPE mTargetState; + + Vector mPorts; + + bool isSetParameterAllowed( + OMX_INDEXTYPE index, const OMX_PTR params) const; + + virtual OMX_ERRORTYPE sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data); + + virtual OMX_ERRORTYPE getParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE setParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE useBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr); + + virtual OMX_ERRORTYPE allocateBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size); + + virtual OMX_ERRORTYPE freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state); + + void onSendCommand(OMX_COMMANDTYPE cmd, OMX_U32 param); + void onChangeState(OMX_STATETYPE state); + void onPortEnable(OMX_U32 portIndex, bool enable); + void onPortFlush(OMX_U32 portIndex, bool sendFlushComplete); + + void checkTransitions(); + + DISALLOW_EVIL_CONSTRUCTORS(SimpleSoftOMXComponent); +}; + +} // namespace android + +#endif // SIMPLE_SOFT_OMX_COMPONENT_H_ diff --git a/media/libstagefright/include/SoftOMXComponent.h b/media/libstagefright/include/SoftOMXComponent.h new file mode 100644 index 0000000..053bc22 --- /dev/null +++ b/media/libstagefright/include/SoftOMXComponent.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_OMX_COMPONENT_H_ + +#define SOFT_OMX_COMPONENT_H_ + +#include +#include +#include + +#include + +namespace android { + +struct SoftOMXComponent : public RefBase { + SoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual OMX_ERRORTYPE initCheck() const; + + void setLibHandle(void *libHandle); + void *libHandle() const; + +protected: + virtual ~SoftOMXComponent(); + + const char *name() const; + + void notify( + OMX_EVENTTYPE event, + OMX_U32 data1, OMX_U32 data2, OMX_PTR data); + + void notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header); + void notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header); + + virtual OMX_ERRORTYPE sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data); + + virtual OMX_ERRORTYPE getParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE setParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE getConfig( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE setConfig( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual OMX_ERRORTYPE getExtensionIndex( + const char *name, OMX_INDEXTYPE *index); + + virtual OMX_ERRORTYPE useBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr); + + virtual OMX_ERRORTYPE allocateBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size); + + virtual OMX_ERRORTYPE freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer); + + virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state); + +private: + AString mName; + const OMX_CALLBACKTYPE *mCallbacks; + OMX_COMPONENTTYPE *mComponent; + + void *mLibHandle; + + static OMX_ERRORTYPE SendCommandWrapper( + OMX_HANDLETYPE component, + OMX_COMMANDTYPE cmd, + OMX_U32 param, + OMX_PTR data); + + static OMX_ERRORTYPE GetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE SetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE GetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE SetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params); + + static OMX_ERRORTYPE GetExtensionIndexWrapper( + OMX_HANDLETYPE component, + OMX_STRING name, + OMX_INDEXTYPE *index); + + static OMX_ERRORTYPE UseBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr); + + static OMX_ERRORTYPE AllocateBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size); + + static OMX_ERRORTYPE FreeBufferWrapper( + OMX_HANDLETYPE component, + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer); + + static OMX_ERRORTYPE EmptyThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer); + + static OMX_ERRORTYPE FillThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer); + + static OMX_ERRORTYPE GetStateWrapper( + OMX_HANDLETYPE component, + OMX_STATETYPE *state); + + DISALLOW_EVIL_CONSTRUCTORS(SoftOMXComponent); +}; + +} // namespace android + +#endif // SOFT_OMX_COMPONENT_H_ diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 6e069c8..08ad6f3 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -1,41 +1,28 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifneq ($(BUILD_WITHOUT_PV),true) -# Set up the OpenCore variables. -include external/opencore/Config.mk -LOCAL_C_INCLUDES := $(PV_INCLUDES) -LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY) -endif - LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) LOCAL_SRC_FILES:= \ - OMX.cpp \ + OMX.cpp \ OMXComponentBase.cpp \ + OMXMaster.cpp \ OMXNodeInstance.cpp \ - OMXMaster.cpp - -ifneq ($(BUILD_WITHOUT_PV),true) -LOCAL_SRC_FILES += \ - OMXPVCodecsPlugin.cpp -else -LOCAL_CFLAGS += -DNO_OPENCORE -endif - -LOCAL_C_INCLUDES += $(TOP)/frameworks/base/include/media/stagefright/openmax - -LOCAL_SHARED_LIBRARIES := \ - libbinder \ - libmedia \ - libutils \ - libui \ - libcutils \ - -ifneq ($(BUILD_WITHOUT_PV),true) -LOCAL_SHARED_LIBRARIES += \ - libopencore_common -endif + SimpleSoftOMXComponent.cpp \ + SoftOMXComponent.cpp \ + SoftOMXPlugin.cpp \ + +LOCAL_C_INCLUDES += \ + frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/include/media/stagefright/openmax + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libmedia \ + libutils \ + libui \ + libcutils \ + libstagefright_foundation \ ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) LOCAL_LDLIBS += -lpthread -ldl @@ -49,5 +36,6 @@ LOCAL_MODULE:= libstagefright_omx include $(BUILD_SHARED_LIBRARY) -include $(call all-makefiles-under,$(LOCAL_PATH)) +################################################################################ +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp index 56b169a..545e6d4 100644 --- a/media/libstagefright/omx/OMXMaster.cpp +++ b/media/libstagefright/omx/OMXMaster.cpp @@ -20,23 +20,18 @@ #include "OMXMaster.h" +#include "SoftOMXPlugin.h" + #include #include -#ifndef NO_OPENCORE -#include "OMXPVCodecsPlugin.h" -#endif - namespace android { OMXMaster::OMXMaster() : mVendorLibHandle(NULL) { addVendorPlugin(); - -#ifndef NO_OPENCORE - addPlugin(new OMXPVCodecsPlugin); -#endif + addPlugin(new SoftOMXPlugin); } OMXMaster::~OMXMaster() { @@ -49,7 +44,11 @@ OMXMaster::~OMXMaster() { } void OMXMaster::addVendorPlugin() { - mVendorLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW); + addPlugin("libstagefrighthw.so"); +} + +void OMXMaster::addPlugin(const char *libname) { + mVendorLibHandle = dlopen(libname, RTLD_NOW); if (mVendorLibHandle == NULL) { return; diff --git a/media/libstagefright/omx/OMXMaster.h b/media/libstagefright/omx/OMXMaster.h index 7ba8d18..feee1f9 100644 --- a/media/libstagefright/omx/OMXMaster.h +++ b/media/libstagefright/omx/OMXMaster.h @@ -58,6 +58,7 @@ private: void *mVendorLibHandle; void addVendorPlugin(); + void addPlugin(const char *libname); void addPlugin(OMXPluginBase *plugin); void clearPlugins(); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index cdce772..8462988 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -234,6 +234,7 @@ status_t OMXNodeInstance::getParameter( Mutex::Autolock autoLock(mLock); OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params); + return StatusFromOMXError(err); } diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp b/media/libstagefright/omx/OMXPVCodecsPlugin.cpp deleted file mode 100644 index d1f5be3..0000000 --- a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "OMXPVCodecsPlugin.h" - -#include "pv_omxcore.h" - -#include - -namespace android { - -OMXPVCodecsPlugin::OMXPVCodecsPlugin() { - OMX_MasterInit(); -} - -OMXPVCodecsPlugin::~OMXPVCodecsPlugin() { - OMX_MasterDeinit(); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::makeComponentInstance( - const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component) { - return OMX_MasterGetHandle( - reinterpret_cast(component), - const_cast(name), - appData, - const_cast(callbacks)); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::destroyComponentInstance( - OMX_COMPONENTTYPE *component) { - return OMX_MasterFreeHandle(component); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::enumerateComponents( - OMX_STRING name, - size_t size, - OMX_U32 index) { - return OMX_MasterComponentNameEnum(name, size, index); -} - -OMX_ERRORTYPE OMXPVCodecsPlugin::getRolesOfComponent( - const char *name, - Vector *roles) { - roles->clear(); - - OMX_U32 numRoles; - OMX_ERRORTYPE err = - OMX_MasterGetRolesOfComponent( - const_cast(name), - &numRoles, - NULL); - - if (err != OMX_ErrorNone) { - return err; - } - - if (numRoles > 0) { - OMX_U8 **array = new OMX_U8 *[numRoles]; - for (OMX_U32 i = 0; i < numRoles; ++i) { - array[i] = new OMX_U8[OMX_MAX_STRINGNAME_SIZE]; - } - - OMX_U32 numRoles2; - err = OMX_MasterGetRolesOfComponent( - const_cast(name), &numRoles2, array); - - CHECK_EQ(err, OMX_ErrorNone); - CHECK_EQ(numRoles, numRoles2); - - for (OMX_U32 i = 0; i < numRoles; ++i) { - String8 s((const char *)array[i]); - roles->push(s); - - delete[] array[i]; - array[i] = NULL; - } - - delete[] array; - array = NULL; - } - - return OMX_ErrorNone; -} - -} // namespace android diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.h b/media/libstagefright/omx/OMXPVCodecsPlugin.h deleted file mode 100644 index c133232..0000000 --- a/media/libstagefright/omx/OMXPVCodecsPlugin.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OMX_PV_CODECS_PLUGIN_H_ - -#define OMX_PV_CODECS_PLUGIN_H_ - -#include - -namespace android { - -struct OMXPVCodecsPlugin : public OMXPluginBase { - OMXPVCodecsPlugin(); - virtual ~OMXPVCodecsPlugin(); - - virtual OMX_ERRORTYPE makeComponentInstance( - const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component); - - virtual OMX_ERRORTYPE destroyComponentInstance( - OMX_COMPONENTTYPE *component); - - virtual OMX_ERRORTYPE enumerateComponents( - OMX_STRING name, - size_t size, - OMX_U32 index); - - virtual OMX_ERRORTYPE getRolesOfComponent( - const char *name, - Vector *roles); - -private: - OMXPVCodecsPlugin(const OMXPVCodecsPlugin &); - OMXPVCodecsPlugin &operator=(const OMXPVCodecsPlugin &); -}; - -} // namespace android - -#endif // OMX_PV_CODECS_PLUGIN_H_ diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp new file mode 100644 index 0000000..179b2a0 --- /dev/null +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SimpleSoftOMXComponent" +#include + +#include "include/SimpleSoftOMXComponent.h" + +#include +#include +#include + +namespace android { + +SimpleSoftOMXComponent::SimpleSoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftOMXComponent(name, callbacks, appData, component), + mLooper(new ALooper), + mHandler(new AHandlerReflector(this)), + mState(OMX_StateLoaded), + mTargetState(OMX_StateLoaded) { + mLooper->setName(name); + mLooper->registerHandler(mHandler); + + mLooper->start( + false, // runOnCallingThread + false, // canCallJava + PRIORITY_AUDIO); +} + +SimpleSoftOMXComponent::~SimpleSoftOMXComponent() { + mLooper->unregisterHandler(mHandler->id()); + mLooper->stop(); +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { + CHECK(data == NULL); + + sp msg = new AMessage(kWhatSendCommand, mHandler->id()); + msg->setInt32("cmd", cmd); + msg->setInt32("param", param); + msg->post(); + + return OMX_ErrorNone; +} + +bool SimpleSoftOMXComponent::isSetParameterAllowed( + OMX_INDEXTYPE index, const OMX_PTR params) const { + if (mState == OMX_StateLoaded) { + return true; + } + + OMX_U32 portIndex; + + switch (index) { + case OMX_IndexParamPortDefinition: + { + portIndex = ((OMX_PARAM_PORTDEFINITIONTYPE *)params)->nPortIndex; + break; + } + + case OMX_IndexParamAudioPcm: + { + portIndex = ((OMX_AUDIO_PARAM_PCMMODETYPE *)params)->nPortIndex; + break; + } + + case OMX_IndexParamAudioAac: + { + portIndex = ((OMX_AUDIO_PARAM_AACPROFILETYPE *)params)->nPortIndex; + break; + } + + default: + return false; + } + + CHECK(portIndex < mPorts.size()); + + return !mPorts.itemAt(portIndex).mDef.bEnabled; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + Mutex::Autolock autoLock(mLock); + return internalGetParameter(index, params); +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + Mutex::Autolock autoLock(mLock); + + CHECK(isSetParameterAllowed(index, params)); + + return internalSetParameter(index, params); +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *defParams = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + + if (defParams->nPortIndex >= mPorts.size() + || defParams->nSize + != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { + return OMX_ErrorUndefined; + } + + const PortInfo *port = + &mPorts.itemAt(defParams->nPortIndex); + + memcpy(defParams, &port->mDef, sizeof(port->mDef)); + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *defParams = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + + if (defParams->nPortIndex >= mPorts.size() + || defParams->nSize + != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { + return OMX_ErrorUndefined; + } + + PortInfo *port = + &mPorts.editItemAt(defParams->nPortIndex); + + if (defParams->nBufferSize != port->mDef.nBufferSize) { + CHECK_GE(defParams->nBufferSize, port->mDef.nBufferSize); + port->mDef.nBufferSize = defParams->nBufferSize; + } + + if (defParams->nBufferCountActual + != port->mDef.nBufferCountActual) { + CHECK_GE(defParams->nBufferCountActual, + port->mDef.nBufferCountMin); + + port->mDef.nBufferCountActual = defParams->nBufferCountActual; + } + + return OMX_ErrorNone; + } + + default: + return OMX_ErrorUnsupportedIndex; + } +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer( + OMX_BUFFERHEADERTYPE **header, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr) { + Mutex::Autolock autoLock(mLock); + CHECK_LT(portIndex, mPorts.size()); + + *header = new OMX_BUFFERHEADERTYPE; + (*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE); + (*header)->nVersion.s.nVersionMajor = 1; + (*header)->nVersion.s.nVersionMinor = 0; + (*header)->nVersion.s.nRevision = 0; + (*header)->nVersion.s.nStep = 0; + (*header)->pBuffer = ptr; + (*header)->nAllocLen = size; + (*header)->nFilledLen = 0; + (*header)->nOffset = 0; + (*header)->pAppPrivate = appPrivate; + (*header)->pPlatformPrivate = NULL; + (*header)->pInputPortPrivate = NULL; + (*header)->pOutputPortPrivate = NULL; + (*header)->hMarkTargetComponent = NULL; + (*header)->pMarkData = NULL; + (*header)->nTickCount = 0; + (*header)->nTimeStamp = 0; + (*header)->nFlags = 0; + (*header)->nOutputPortIndex = portIndex; + (*header)->nInputPortIndex = portIndex; + + PortInfo *port = &mPorts.editItemAt(portIndex); + + CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE); + + CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual); + + port->mBuffers.push(); + + BufferInfo *buffer = + &port->mBuffers.editItemAt(port->mBuffers.size() - 1); + + buffer->mHeader = *header; + buffer->mOwnedByUs = false; + + if (port->mBuffers.size() == port->mDef.nBufferCountActual) { + port->mDef.bPopulated = OMX_TRUE; + checkTransitions(); + } + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer( + OMX_BUFFERHEADERTYPE **header, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size) { + OMX_U8 *ptr = new OMX_U8[size]; + + OMX_ERRORTYPE err = + useBuffer(header, portIndex, appPrivate, size, ptr); + + if (err != OMX_ErrorNone) { + delete[] ptr; + ptr = NULL; + + return err; + } + + CHECK((*header)->pPlatformPrivate == NULL); + (*header)->pPlatformPrivate = ptr; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *header) { + Mutex::Autolock autoLock(mLock); + + CHECK_LT(portIndex, mPorts.size()); + + PortInfo *port = &mPorts.editItemAt(portIndex); + +#if 0 // XXX + CHECK((mState == OMX_StateIdle && mTargetState == OMX_StateLoaded) + || port->mDef.bEnabled == OMX_FALSE); +#endif + + bool found = false; + for (size_t i = 0; i < port->mBuffers.size(); ++i) { + BufferInfo *buffer = &port->mBuffers.editItemAt(i); + + if (buffer->mHeader == header) { + CHECK(!buffer->mOwnedByUs); + + if (header->pPlatformPrivate != NULL) { + // This buffer's data was allocated by us. + CHECK(header->pPlatformPrivate == header->pBuffer); + + delete[] header->pBuffer; + header->pBuffer = NULL; + } + + delete header; + header = NULL; + + port->mBuffers.removeAt(i); + port->mDef.bPopulated = OMX_FALSE; + + checkTransitions(); + + found = true; + break; + } + } + + CHECK(found); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + sp msg = new AMessage(kWhatEmptyThisBuffer, mHandler->id()); + msg->setPointer("header", buffer); + msg->post(); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + sp msg = new AMessage(kWhatFillThisBuffer, mHandler->id()); + msg->setPointer("header", buffer); + msg->post(); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) { + Mutex::Autolock autoLock(mLock); + + *state = mState; + + return OMX_ErrorNone; +} + +void SimpleSoftOMXComponent::onMessageReceived(const sp &msg) { + Mutex::Autolock autoLock(mLock); + + switch (msg->what()) { + case kWhatSendCommand: + { + int32_t cmd, param; + CHECK(msg->findInt32("cmd", &cmd)); + CHECK(msg->findInt32("param", ¶m)); + + onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param); + break; + } + + case kWhatEmptyThisBuffer: + case kWhatFillThisBuffer: + { + OMX_BUFFERHEADERTYPE *header; + CHECK(msg->findPointer("header", (void **)&header)); + + CHECK(mState == OMX_StateExecuting && mTargetState == mState); + + bool found = false; + for (size_t i = 0; i < mPorts.size(); ++i) { + PortInfo *port = &mPorts.editItemAt(i); + + for (size_t j = 0; j < port->mBuffers.size(); ++j) { + BufferInfo *buffer = &port->mBuffers.editItemAt(j); + + if (buffer->mHeader == header) { + CHECK(!buffer->mOwnedByUs); + + buffer->mOwnedByUs = true; + + CHECK((msg->what() == kWhatEmptyThisBuffer + && port->mDef.eDir == OMX_DirInput) + || (port->mDef.eDir == OMX_DirOutput)); + + port->mQueue.push_back(buffer); + onQueueFilled(i); + + found = true; + break; + } + } + } + + CHECK(found); + break; + } + + default: + TRESPASS(); + break; + } +} + +void SimpleSoftOMXComponent::onSendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param) { + switch (cmd) { + case OMX_CommandStateSet: + { + onChangeState((OMX_STATETYPE)param); + break; + } + + case OMX_CommandPortEnable: + case OMX_CommandPortDisable: + { + onPortEnable(param, cmd == OMX_CommandPortEnable); + break; + } + + case OMX_CommandFlush: + { + onPortFlush(param, true /* sendFlushComplete */); + break; + } + + default: + TRESPASS(); + break; + } +} + +void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) { + // We shouldn't be in a state transition already. + CHECK_EQ((int)mState, (int)mTargetState); + + switch (mState) { + case OMX_StateLoaded: + CHECK_EQ((int)state, (int)OMX_StateIdle); + break; + case OMX_StateIdle: + CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting); + break; + case OMX_StateExecuting: + { + CHECK_EQ((int)state, (int)OMX_StateIdle); + + for (size_t i = 0; i < mPorts.size(); ++i) { + onPortFlush(i, false /* sendFlushComplete */); + } + + mState = OMX_StateIdle; + notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL); + break; + } + + default: + TRESPASS(); + } + + mTargetState = state; + + checkTransitions(); +} + +void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) { + CHECK_LT(portIndex, mPorts.size()); + + PortInfo *port = &mPorts.editItemAt(portIndex); + CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); + CHECK(port->mDef.bEnabled == !enable); + + if (!enable) { + port->mDef.bEnabled = OMX_FALSE; + port->mTransition = PortInfo::DISABLING; + + for (size_t i = 0; i < port->mBuffers.size(); ++i) { + BufferInfo *buffer = &port->mBuffers.editItemAt(i); + + if (buffer->mOwnedByUs) { + buffer->mOwnedByUs = false; + + if (port->mDef.eDir == OMX_DirInput) { + notifyEmptyBufferDone(buffer->mHeader); + } else { + CHECK_EQ(port->mDef.eDir, OMX_DirOutput); + notifyFillBufferDone(buffer->mHeader); + } + } + } + + port->mQueue.clear(); + } else { + port->mTransition = PortInfo::ENABLING; + } + + checkTransitions(); +} + +void SimpleSoftOMXComponent::onPortFlush( + OMX_U32 portIndex, bool sendFlushComplete) { + if (portIndex == OMX_ALL) { + for (size_t i = 0; i < mPorts.size(); ++i) { + onPortFlush(i, sendFlushComplete); + } + + if (sendFlushComplete) { + notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL); + } + + return; + } + + CHECK_LT(portIndex, mPorts.size()); + + PortInfo *port = &mPorts.editItemAt(portIndex); + CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); + + for (size_t i = 0; i < port->mBuffers.size(); ++i) { + BufferInfo *buffer = &port->mBuffers.editItemAt(i); + + if (!buffer->mOwnedByUs) { + continue; + } + + buffer->mHeader->nFilledLen = 0; + buffer->mHeader->nOffset = 0; + buffer->mHeader->nFlags = 0; + + buffer->mOwnedByUs = false; + + if (port->mDef.eDir == OMX_DirInput) { + notifyEmptyBufferDone(buffer->mHeader); + } else { + CHECK_EQ(port->mDef.eDir, OMX_DirOutput); + + notifyFillBufferDone(buffer->mHeader); + } + } + + port->mQueue.clear(); + + if (sendFlushComplete) { + notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL); + + onPortFlushCompleted(portIndex); + } +} + +void SimpleSoftOMXComponent::checkTransitions() { + if (mState != mTargetState) { + bool transitionComplete = true; + + if (mState == OMX_StateLoaded) { + CHECK_EQ((int)mTargetState, (int)OMX_StateIdle); + + for (size_t i = 0; i < mPorts.size(); ++i) { + const PortInfo &port = mPorts.itemAt(i); + if (port.mDef.bEnabled == OMX_FALSE) { + continue; + } + + if (port.mDef.bPopulated == OMX_FALSE) { + transitionComplete = false; + break; + } + } + } else if (mTargetState == OMX_StateLoaded) { + CHECK_EQ((int)mState, (int)OMX_StateIdle); + + for (size_t i = 0; i < mPorts.size(); ++i) { + const PortInfo &port = mPorts.itemAt(i); + if (port.mDef.bEnabled == OMX_FALSE) { + continue; + } + + size_t n = port.mBuffers.size(); + + if (n > 0) { + CHECK_LE(n, port.mDef.nBufferCountActual); + + if (n == port.mDef.nBufferCountActual) { + CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE); + } else { + CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE); + } + + transitionComplete = false; + break; + } + } + } + + if (transitionComplete) { + mState = mTargetState; + + notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL); + } + } + + for (size_t i = 0; i < mPorts.size(); ++i) { + PortInfo *port = &mPorts.editItemAt(i); + + if (port->mTransition == PortInfo::DISABLING) { + if (port->mBuffers.empty()) { + LOGV("Port %d now disabled.", i); + + port->mTransition = PortInfo::NONE; + notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL); + + onPortEnableCompleted(i, false /* enabled */); + } + } else if (port->mTransition == PortInfo::ENABLING) { + if (port->mDef.bPopulated == OMX_TRUE) { + LOGV("Port %d now enabled.", i); + + port->mTransition = PortInfo::NONE; + port->mDef.bEnabled = OMX_TRUE; + notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL); + + onPortEnableCompleted(i, true /* enabled */); + } + } + } +} + +void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) { + CHECK_EQ(def.nPortIndex, mPorts.size()); + + mPorts.push(); + PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1); + info->mDef = def; + info->mTransition = PortInfo::NONE; +} + +void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex) { +} + +void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex) { +} + +void SimpleSoftOMXComponent::onPortEnableCompleted( + OMX_U32 portIndex, bool enabled) { +} + +List & +SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) { + CHECK_LT(portIndex, mPorts.size()); + return mPorts.editItemAt(portIndex).mQueue; +} + +SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo( + OMX_U32 portIndex) { + CHECK_LT(portIndex, mPorts.size()); + return &mPorts.editItemAt(portIndex); +} + +} // namespace android diff --git a/media/libstagefright/omx/SoftOMXComponent.cpp b/media/libstagefright/omx/SoftOMXComponent.cpp new file mode 100644 index 0000000..b1c34dc --- /dev/null +++ b/media/libstagefright/omx/SoftOMXComponent.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftOMXComponent" +#include + +#include "include/SoftOMXComponent.h" + +#include + +namespace android { + +SoftOMXComponent::SoftOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : mName(name), + mCallbacks(callbacks), + mComponent(new OMX_COMPONENTTYPE), + mLibHandle(NULL) { + mComponent->nSize = sizeof(*mComponent); + mComponent->nVersion.s.nVersionMajor = 1; + mComponent->nVersion.s.nVersionMinor = 0; + mComponent->nVersion.s.nRevision = 0; + mComponent->nVersion.s.nStep = 0; + mComponent->pComponentPrivate = this; + mComponent->pApplicationPrivate = appData; + + mComponent->GetComponentVersion = NULL; + mComponent->SendCommand = SendCommandWrapper; + mComponent->GetParameter = GetParameterWrapper; + mComponent->SetParameter = SetParameterWrapper; + mComponent->GetConfig = GetConfigWrapper; + mComponent->SetConfig = SetConfigWrapper; + mComponent->GetExtensionIndex = GetExtensionIndexWrapper; + mComponent->GetState = GetStateWrapper; + mComponent->ComponentTunnelRequest = NULL; + mComponent->UseBuffer = UseBufferWrapper; + mComponent->AllocateBuffer = AllocateBufferWrapper; + mComponent->FreeBuffer = FreeBufferWrapper; + mComponent->EmptyThisBuffer = EmptyThisBufferWrapper; + mComponent->FillThisBuffer = FillThisBufferWrapper; + mComponent->SetCallbacks = NULL; + mComponent->ComponentDeInit = NULL; + mComponent->UseEGLImage = NULL; + mComponent->ComponentRoleEnum = NULL; + + *component = mComponent; +} + +SoftOMXComponent::~SoftOMXComponent() { + delete mComponent; + mComponent = NULL; +} + +void SoftOMXComponent::setLibHandle(void *libHandle) { + CHECK(libHandle != NULL); + mLibHandle = libHandle; +} + +void *SoftOMXComponent::libHandle() const { + return mLibHandle; +} + +OMX_ERRORTYPE SoftOMXComponent::initCheck() const { + return OMX_ErrorNone; +} + +const char *SoftOMXComponent::name() const { + return mName.c_str(); +} + +void SoftOMXComponent::notify( + OMX_EVENTTYPE event, + OMX_U32 data1, OMX_U32 data2, OMX_PTR data) { + (*mCallbacks->EventHandler)( + mComponent, + mComponent->pApplicationPrivate, + event, + data1, + data2, + data); +} + +void SoftOMXComponent::notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header) { + (*mCallbacks->EmptyBufferDone)( + mComponent, mComponent->pApplicationPrivate, header); +} + +void SoftOMXComponent::notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header) { + (*mCallbacks->FillBufferDone)( + mComponent, mComponent->pApplicationPrivate, header); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::SendCommandWrapper( + OMX_HANDLETYPE component, + OMX_COMMANDTYPE cmd, + OMX_U32 param, + OMX_PTR data) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->sendCommand(cmd, param, data); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getParameter(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::SetParameterWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->setParameter(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getConfig(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::SetConfigWrapper( + OMX_HANDLETYPE component, + OMX_INDEXTYPE index, + OMX_PTR params) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->setConfig(index, params); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetExtensionIndexWrapper( + OMX_HANDLETYPE component, + OMX_STRING name, + OMX_INDEXTYPE *index) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getExtensionIndex(name, index); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::UseBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->useBuffer(buffer, portIndex, appPrivate, size, ptr); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::AllocateBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->allocateBuffer(buffer, portIndex, appPrivate, size); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::FreeBufferWrapper( + OMX_HANDLETYPE component, + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->freeBuffer(portIndex, buffer); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::EmptyThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->emptyThisBuffer(buffer); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::FillThisBufferWrapper( + OMX_HANDLETYPE component, + OMX_BUFFERHEADERTYPE *buffer) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->fillThisBuffer(buffer); +} + +// static +OMX_ERRORTYPE SoftOMXComponent::GetStateWrapper( + OMX_HANDLETYPE component, + OMX_STATETYPE *state) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + return me->getState(state); +} + +//////////////////////////////////////////////////////////////////////////////// + +OMX_ERRORTYPE SoftOMXComponent::sendCommand( + OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::setParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getConfig( + OMX_INDEXTYPE index, OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::setConfig( + OMX_INDEXTYPE index, const OMX_PTR params) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getExtensionIndex( + const char *name, OMX_INDEXTYPE *index) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::useBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size, + OMX_U8 *ptr) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::allocateBuffer( + OMX_BUFFERHEADERTYPE **buffer, + OMX_U32 portIndex, + OMX_PTR appPrivate, + OMX_U32 size) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::freeBuffer( + OMX_U32 portIndex, + OMX_BUFFERHEADERTYPE *buffer) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::emptyThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::fillThisBuffer( + OMX_BUFFERHEADERTYPE *buffer) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SoftOMXComponent::getState(OMX_STATETYPE *state) { + return OMX_ErrorUndefined; +} + +} // namespace android diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp new file mode 100644 index 0000000..6bd6624 --- /dev/null +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftOMXPlugin" +#include + +#include "SoftOMXPlugin.h" +#include "include/SoftOMXComponent.h" + +#include + +#include + +namespace android { + +static const struct { + const char *mName; + const char *mLibNameSuffix; + const char *mRole; + +} kComponents[] = { + { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" }, + { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" }, + { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" }, + { "OMX.google.avc.decoder", "avcdec", "video_decoder.avc" }, + { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" }, + { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" }, + { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" }, + { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.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" }, +}; + +static const size_t kNumComponents = + sizeof(kComponents) / sizeof(kComponents[0]); + +SoftOMXPlugin::SoftOMXPlugin() { +} + +OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) { + LOGV("makeComponentInstance '%s'", name); + + for (size_t i = 0; i < kNumComponents; ++i) { + if (strcmp(name, kComponents[i].mName)) { + continue; + } + + AString libName = "libstagefright_soft_"; + libName.append(kComponents[i].mLibNameSuffix); + libName.append(".so"); + + void *libHandle = dlopen(libName.c_str(), RTLD_NOW); + + if (libHandle == NULL) { + LOGE("unable to dlopen %s", libName.c_str()); + + return OMX_ErrorComponentNotFound; + } + + typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)( + const char *, const OMX_CALLBACKTYPE *, + OMX_PTR, OMX_COMPONENTTYPE **); + + CreateSoftOMXComponentFunc createSoftOMXComponent = + (CreateSoftOMXComponentFunc)dlsym( + libHandle, + "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE" + "PvPP17OMX_COMPONENTTYPE"); + + if (createSoftOMXComponent == NULL) { + dlclose(libHandle); + libHandle = NULL; + + return OMX_ErrorComponentNotFound; + } + + sp codec = + (*createSoftOMXComponent)(name, callbacks, appData, component); + + if (codec == NULL) { + dlclose(libHandle); + libHandle = NULL; + + return OMX_ErrorInsufficientResources; + } + + OMX_ERRORTYPE err = codec->initCheck(); + if (err != OMX_ErrorNone) { + dlclose(libHandle); + libHandle = NULL; + + return err; + } + + codec->incStrong(this); + codec->setLibHandle(libHandle); + + return OMX_ErrorNone; + } + + return OMX_ErrorInvalidComponentName; +} + +OMX_ERRORTYPE SoftOMXPlugin::destroyComponentInstance( + OMX_COMPONENTTYPE *component) { + SoftOMXComponent *me = + (SoftOMXComponent *) + ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + + void *libHandle = me->libHandle(); + + me->decStrong(this); + me = NULL; + + dlclose(libHandle); + libHandle = NULL; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents( + OMX_STRING name, + size_t size, + OMX_U32 index) { + if (index >= kNumComponents) { + return OMX_ErrorNoMore; + } + + strcpy(name, kComponents[index].mName); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftOMXPlugin::getRolesOfComponent( + const char *name, + Vector *roles) { + for (size_t i = 0; i < kNumComponents; ++i) { + if (strcmp(name, kComponents[i].mName)) { + continue; + } + + roles->clear(); + roles->push(String8(kComponents[i].mRole)); + + return OMX_ErrorNone; + } + + return OMX_ErrorInvalidComponentName; +} + +} // namespace android diff --git a/media/libstagefright/omx/SoftOMXPlugin.h b/media/libstagefright/omx/SoftOMXPlugin.h new file mode 100644 index 0000000..f93c323 --- /dev/null +++ b/media/libstagefright/omx/SoftOMXPlugin.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_OMX_PLUGIN_H_ + +#define SOFT_OMX_PLUGIN_H_ + +#include +#include + +namespace android { + +struct SoftOMXPlugin : public OMXPluginBase { + SoftOMXPlugin(); + + virtual OMX_ERRORTYPE makeComponentInstance( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual OMX_ERRORTYPE destroyComponentInstance( + OMX_COMPONENTTYPE *component); + + virtual OMX_ERRORTYPE enumerateComponents( + OMX_STRING name, + size_t size, + OMX_U32 index); + + virtual OMX_ERRORTYPE getRolesOfComponent( + const char *name, + Vector *roles); + +private: + DISALLOW_EVIL_CONSTRUCTORS(SoftOMXPlugin); +}; + +} // namespace android + +#endif // SOFT_OMX_PLUGIN_H_ diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index 54c0d77..a404f1f 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -454,6 +455,7 @@ 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" }, // we appear to use this as a synonym to amrnb. { "audio_decoder.amr", "audio/3gpp" }, @@ -461,7 +463,10 @@ static const char *GetMimeFromComponentRole(const char *componentRole) { { "audio_decoder.amrnb", "audio/3gpp" }, { "audio_decoder.amrwb", "audio/amr-wb" }, { "audio_decoder.aac", "audio/mp4a-latm" }, - { "audio_decoder.mp3", "audio/mpeg" } + { "audio_decoder.mp3", "audio/mpeg" }, + { "audio_decoder.vorbis", "audio/vorbis" }, + { "audio_decoder.g711alaw", MEDIA_MIMETYPE_AUDIO_G711_ALAW }, + { "audio_decoder.g711mlaw", MEDIA_MIMETYPE_AUDIO_G711_MLAW }, }; for (size_t i = 0; i < sizeof(kRoleToMime) / sizeof(kRoleToMime[0]); ++i) { @@ -492,7 +497,15 @@ static const char *GetURLForMime(const char *mime) { { "audio/mp4a-latm", "file:///sdcard/media_api/video/H264_AAC.3gp" }, { "audio/mpeg", - "file:///sdcard/media_api/music/MP3CBR.mp3" } + "file:///sdcard/media_api/music/MP3CBR.mp3" }, + { "audio/vorbis", + "file:///sdcard/media_api/metaDataTestMedias/OGG/" + "When You Say Nothing At All.ogg" }, + { "video/x-vnd.on2.vp8", + "file:///sdcard/media_api/webm/big-buck-bunny_trailer.webm" }, + { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "file:///sdcard/M1F1-Alaw-AFsp.wav" }, + { MEDIA_MIMETYPE_AUDIO_G711_MLAW, + "file:///sdcard/M1F1-mulaw-AFsp.wav" }, }; for (size_t i = 0; i < sizeof(kMimeToURL) / sizeof(kMimeToURL[0]); ++i) { @@ -746,6 +759,10 @@ status_t Harness::testAll() { const IOMX::ComponentInfo &info = *it; const char *componentName = info.mName.string(); + if (strncmp(componentName, "OMX.google.", 11)) { + continue; + } + for (List::const_iterator role_it = info.mRoles.begin(); role_it != info.mRoles.end(); ++role_it) { const char *componentRole = (*role_it).string(); -- cgit v1.1