diff options
177 files changed, 6110 insertions, 1125 deletions
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp index 68969cf..42b0884 100644 --- a/camera/CameraParameters.cpp +++ b/camera/CameraParameters.cpp @@ -21,6 +21,7 @@ #include <string.h> #include <stdlib.h> #include <camera/CameraParameters.h> +#include <camera/CameraParametersExtra.h> #include <system/graphics.h> namespace android { @@ -106,6 +107,7 @@ const char CameraParameters::WHITE_BALANCE_DAYLIGHT[] = "daylight"; const char CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT[] = "cloudy-daylight"; const char CameraParameters::WHITE_BALANCE_TWILIGHT[] = "twilight"; const char CameraParameters::WHITE_BALANCE_SHADE[] = "shade"; +const char CameraParameters::WHITE_BALANCE_MANUAL_CCT[] = "manual-cct"; // Values for effect settings. const char CameraParameters::EFFECT_NONE[] = "none"; @@ -168,11 +170,16 @@ const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed"; const char CameraParameters::FOCUS_MODE_EDOF[] = "edof"; const char CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO[] = "continuous-video"; const char CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE[] = "continuous-picture"; +const char CameraParameters::FOCUS_MODE_MANUAL_POSITION[] = "manual"; // Values for light fx settings const char CameraParameters::LIGHTFX_LOWLIGHT[] = "low-light"; const char CameraParameters::LIGHTFX_HDR[] = "high-dynamic-range"; +#ifdef CAMERA_PARAMETERS_EXTRA_C +CAMERA_PARAMETERS_EXTRA_C +#endif + CameraParameters::CameraParameters() : mMap() { @@ -237,6 +244,9 @@ void CameraParameters::unflatten(const String8 ¶ms) void CameraParameters::set(const char *key, const char *value) { + if (key == NULL || value == NULL) + return; + // XXX i think i can do this with strspn() if (strchr(key, '=') || strchr(key, ';')) { //XXX ALOGE("Key \"%s\"contains invalid character (= or ;)", key); @@ -247,6 +257,14 @@ void CameraParameters::set(const char *key, const char *value) //XXX ALOGE("Value \"%s\"contains invalid character (= or ;)", value); return; } +#ifdef QCOM_HARDWARE + // qcom cameras default to delivering an extra zero-exposure frame on HDR. + // The android SDK only wants one frame, so disable this unless the app + // explicitly asks for it + if (!get("hdr-need-1x")) { + mMap.replaceValueFor(String8("hdr-need-1x"), String8("false")); + } +#endif mMap.replaceValueFor(String8(key), String8(value)); } diff --git a/camera/ICameraClient.cpp b/camera/ICameraClient.cpp index 179a341..4f43796 100644 --- a/camera/ICameraClient.cpp +++ b/camera/ICameraClient.cpp @@ -46,7 +46,12 @@ public: data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeInt32(msgType); data.writeInt32(ext1); - data.writeInt32(ext2); + if ((msgType == CAMERA_MSG_PREVIEW_FRAME) && (ext1 == CAMERA_FRAME_DATA_FD)) { + ALOGD("notifyCallback: CAMERA_MSG_PREVIEW_FRAME fd = %d", ext2); + data.writeFileDescriptor(ext2); + } else { + data.writeInt32(ext2); + } remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); } @@ -91,8 +96,14 @@ status_t BnCameraClient::onTransact( ALOGV("NOTIFY_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); int32_t msgType = data.readInt32(); - int32_t ext1 = data.readInt32(); - int32_t ext2 = data.readInt32(); + int32_t ext1 = data.readInt32(); + int32_t ext2 = 0; + if ((msgType == CAMERA_MSG_PREVIEW_FRAME) && (ext1 == CAMERA_FRAME_DATA_FD)) { + ext2 = data.readFileDescriptor(); + ALOGD("onTransact: CAMERA_MSG_PREVIEW_FRAME fd = %d", ext2); + } else { + ext2 = data.readInt32(); + } notifyCallback(msgType, ext1, ext2); return NO_ERROR; } break; diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk index 33f9d3b..b7eb70b 100644 --- a/drm/libdrmframework/Android.mk +++ b/drm/libdrmframework/Android.mk @@ -31,7 +31,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libdl -LOCAL_STATIC_LIBRARIES := \ +LOCAL_WHOLE_STATIC_LIBRARIES := \ libdrmframeworkcommon LOCAL_C_INCLUDES += \ diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index ba33ffe..d85050d 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -19,6 +19,7 @@ #include <utils/KeyedVector.h> #include <utils/String8.h> +#include <camera/CameraParametersExtra.h> namespace android { @@ -554,6 +555,7 @@ public: static const char WHITE_BALANCE_CLOUDY_DAYLIGHT[]; static const char WHITE_BALANCE_TWILIGHT[]; static const char WHITE_BALANCE_SHADE[]; + static const char WHITE_BALANCE_MANUAL_CCT[]; // Values for effect settings. static const char EFFECT_NONE[]; @@ -677,12 +679,18 @@ public: // other modes. static const char FOCUS_MODE_CONTINUOUS_PICTURE[]; + static const char FOCUS_MODE_MANUAL_POSITION[]; + // Values for light special effects // Low-light enhancement mode static const char LIGHTFX_LOWLIGHT[]; // High-dynamic range mode static const char LIGHTFX_HDR[]; +#ifdef CAMERA_PARAMETERS_EXTRA_H +CAMERA_PARAMETERS_EXTRA_H +#endif + /** * Returns the the supported preview formats as an enum given in graphics.h * corrsponding to the format given in the input string or -1 if no such diff --git a/include/camera/CameraParametersExtra.h b/include/camera/CameraParametersExtra.h new file mode 100644 index 0000000..80a67cc --- /dev/null +++ b/include/camera/CameraParametersExtra.h @@ -0,0 +1,35 @@ +// Overload this file in your device specific config if you need +// to add extra camera parameters. +// A typical file would look like this: +/* + * Copyright (C) 2014 The CyanogenMod 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 CAMERA_PARAMETERS_EXTRA_C \ +const char CameraParameters::KEY_SUPPORTED_BURST_NUM[] = "supported-burst-num"; \ +const char CameraParameters::KEY_BURST_NUM[] = "burst-num"; \ +const char CameraParameters::KEY_SUPPORTED_HDR_MODES[] = "supported-hdr-modes"; \ +const char CameraParameters::KEY_HDR_MODE[] = "hdr-mode"; \ +const char CameraParameters::HDR_MODE_OFF[] = "hdr-mode-off"; \ +const char CameraParameters::HDR_MODE_HDR[] = "hdr-mode-hdr"; + +#define CAMERA_PARAMETERS_EXTRA_H \ + static const char KEY_SUPPORTED_BURST_NUM[]; \ + static const char KEY_BURST_NUM[]; \ + static const char KEY_SUPPORTED_HDR_MODES[]; \ + static const char KEY_HDR_MODE[]; \ + static const char HDR_MODE_OFF[]; \ + static const char HDR_MODE_HDR[]; +*/ diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h index 891bc4b..c769c4b 100644 --- a/include/media/AudioParameter.h +++ b/include/media/AudioParameter.h @@ -48,6 +48,7 @@ public: static const char * const keyFrameCount; static const char * const keyInputSource; static const char * const keyScreenState; + static const char * const keyDevShutdown; String8 toString(); diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index e02f1b7..42fa3be 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -31,6 +31,7 @@ namespace android { struct audio_track_cblk_t; class AudioTrackClientProxy; class StaticAudioTrackClientProxy; +struct ExtendedMediaUtils; // ---------------------------------------------------------------------------- @@ -623,6 +624,7 @@ private: */ status_t obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, struct timespec *elapsed = NULL, size_t *nonContig = NULL); + friend struct ExtendedMediaUtils; public: /* Public API for TRANSFER_OBTAIN mode. @@ -940,6 +942,7 @@ protected: // For Device Selection API // a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing. audio_port_handle_t mSelectedDeviceId; + bool mPlaybackRateSet; private: class DeathNotifier : public IBinder::DeathRecipient { @@ -957,6 +960,7 @@ private: pid_t mClientPid; sp<AudioSystem::AudioDeviceCallback> mDeviceCallback; + friend struct ExtendedMediaUtils; }; class TimedAudioTrack : public AudioTrack diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index de82554..745151b 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -46,12 +46,12 @@ class IGraphicBufferProducer; template<typename T> class SortedVector; enum player_type { - STAGEFRIGHT_PLAYER = 3, NU_PLAYER = 4, // Test players are available only in the 'test' and 'eng' builds. // The shared library with the test player is passed passed as an // argument to the 'test:' url in the setDataSource call. TEST_PLAYER = 5, + DASH_PLAYER = 6, }; diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h index e02918f..c67bae9 100644..100755 --- a/include/media/MediaProfiles.h +++ b/include/media/MediaProfiles.h @@ -1,4 +1,6 @@ /* + ** Copyright (c) 2014, The Linux Foundation. All rights reserved. + ** Not a Contribution. ** ** Copyright 2010, The Android Open Source Project. ** @@ -56,6 +58,16 @@ enum camcorder_quality { CAMCORDER_QUALITY_HIGH_SPEED_1080P = 2004, CAMCORDER_QUALITY_HIGH_SPEED_2160P = 2005, CAMCORDER_QUALITY_HIGH_SPEED_LIST_END = 2005, + + CAMCORDER_QUALITY_VENDOR_START = 10000, + CAMCORDER_QUALITY_VGA = 10000, + CAMCORDER_QUALITY_4KDCI = 10001, + CAMCORDER_QUALITY_TIME_LAPSE_VGA = 10002, + CAMCORDER_QUALITY_TIME_LAPSE_4KDCI = 10003, + CAMCORDER_QUALITY_HIGH_SPEED_CIF = 10004, + CAMCORDER_QUALITY_HIGH_SPEED_VGA = 10005, + CAMCORDER_QUALITY_HIGH_SPEED_4KDCI = 10006, + CAMCORDER_QUALITY_VENDOR_END = 10006, }; enum video_decoder { @@ -126,6 +138,9 @@ public: * enc.vid.bps.max - max bit rate in bits per second * enc.vid.fps.min - min frame rate in frames per second * enc.vid.fps.max - max frame rate in frames per second + * enc.vid.hfr.width.max - max hfr video frame width + * enc.vid.hfr.height.max - max hfr video frame height + * enc.vid.hfr.mode.max - max hfr mode */ int getVideoEncoderParamByName(const char *name, video_encoder codec) const; @@ -264,12 +279,16 @@ private: int minBitRate, int maxBitRate, int minFrameWidth, int maxFrameWidth, int minFrameHeight, int maxFrameHeight, - int minFrameRate, int maxFrameRate) + int minFrameRate, int maxFrameRate, + int maxHFRFrameWidth, int maxHFRFrameHeight, + int maxHFRMode) : mCodec(codec), mMinBitRate(minBitRate), mMaxBitRate(maxBitRate), mMinFrameWidth(minFrameWidth), mMaxFrameWidth(maxFrameWidth), mMinFrameHeight(minFrameHeight), mMaxFrameHeight(maxFrameHeight), - mMinFrameRate(minFrameRate), mMaxFrameRate(maxFrameRate) {} + mMinFrameRate(minFrameRate), mMaxFrameRate(maxFrameRate), + mMaxHFRFrameWidth(maxHFRFrameWidth), mMaxHFRFrameHeight(maxHFRFrameHeight), + mMaxHFRMode(maxHFRMode) {} ~VideoEncoderCap() {} @@ -278,6 +297,8 @@ private: int mMinFrameWidth, mMaxFrameWidth; int mMinFrameHeight, mMaxFrameHeight; int mMinFrameRate, mMaxFrameRate; + int mMaxHFRFrameWidth, mMaxHFRFrameHeight; + int mMaxHFRMode; }; struct AudioEncoderCap { @@ -392,6 +413,8 @@ private: static VideoEncoderCap* createDefaultH263VideoEncoderCap(); static VideoEncoderCap* createDefaultM4vVideoEncoderCap(); static AudioEncoderCap* createDefaultAmrNBEncoderCap(); + static AudioEncoderCap* createDefaultAacEncoderCap(); + static AudioEncoderCap* createDefaultLpcmEncoderCap(); static int findTagForName(const NameToTagMap *map, size_t nMappings, const char *name); diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index 3fe749c..1f6ddad 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -53,7 +53,7 @@ enum media_event_type { MEDIA_ERROR = 100, MEDIA_INFO = 200, MEDIA_SUBTITLE_DATA = 201, - MEDIA_META_DATA = 202, + MEDIA_META_DATA = 202 }; // Generic error codes for the media player framework. Errors are fatal, the @@ -209,7 +209,7 @@ public: void died(); void disconnect(); - status_t setDataSource( + virtual status_t setDataSource( const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers); @@ -224,7 +224,7 @@ public: status_t prepareAsync(); status_t start(); status_t stop(); - status_t pause(); + virtual status_t pause(); bool isPlaying(); status_t setPlaybackSettings(const AudioPlaybackRate& rate); status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */); @@ -234,7 +234,7 @@ public: float* videoFps /* nonnull */); status_t getVideoWidth(int *w); status_t getVideoHeight(int *h); - status_t seekTo(int msec); + virtual status_t seekTo(int msec); status_t getCurrentPosition(int *msec); status_t getDuration(int *msec); status_t reset(); @@ -243,7 +243,7 @@ public: status_t setLooping(int loop); bool isLooping(); status_t setVolume(float leftVolume, float rightVolume); - void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL); + virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL); status_t invoke(const Parcel& request, Parcel *reply); status_t setMetadataFilter(const Parcel& filter); status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata); @@ -289,6 +289,7 @@ private: float mSendLevel; struct sockaddr_in mRetransmitEndpoint; bool mRetransmitEndpointValid; + friend class QCMediaPlayer; }; }; // namespace android diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index 15ff82d..93b5d67 100644..100755 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -1,4 +1,7 @@ /* + ** Copyright (c) 2014, The Linux Foundation. All rights reserved. + ** Not a Contribution. + ** ** Copyright (C) 2008 The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -74,6 +77,9 @@ enum output_format { /* VP8/VORBIS data in a WEBM container */ OUTPUT_FORMAT_WEBM = 9, + OUTPUT_FORMAT_QCP = 20, + OUTPUT_FORMAT_WAVE = 21, + OUTPUT_FORMAT_LIST_END // must be last - used to validate format type }; @@ -86,6 +92,10 @@ enum audio_encoder { AUDIO_ENCODER_AAC_ELD = 5, AUDIO_ENCODER_VORBIS = 6, + AUDIO_ENCODER_EVRC = 10, + AUDIO_ENCODER_QCELP = 11, + AUDIO_ENCODER_LPCM = 12, + AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type }; @@ -96,7 +106,11 @@ enum video_encoder { VIDEO_ENCODER_MPEG_4_SP = 3, VIDEO_ENCODER_VP8 = 4, - VIDEO_ENCODER_LIST_END // must be the last - used to validate the video encoder type + VIDEO_ENCODER_LIST_END, // must be the last - used to validate the video encoder type + + VIDEO_ENCODER_LIST_VENDOR_START = 1000, + VIDEO_ENCODER_H265 = 1001, + VIDEO_ENCODER_LIST_VENDOR_END, }; /* @@ -225,13 +239,13 @@ public: status_t setOutputFile(int fd, int64_t offset, int64_t length); status_t setVideoSize(int width, int height); status_t setVideoFrameRate(int frames_per_second); - status_t setParameters(const String8& params); + virtual status_t setParameters(const String8& params); status_t setListener(const sp<MediaRecorderListener>& listener); status_t setClientName(const String16& clientName); status_t prepare(); status_t getMaxAmplitude(int* max); - status_t start(); - status_t stop(); + virtual status_t start(); + virtual status_t stop(); status_t reset(); status_t init(); status_t close(); @@ -240,7 +254,7 @@ public: status_t setInputSurface(const sp<PersistentSurface>& surface); sp<IGraphicBufferProducer> querySurfaceMediaSourceFromMediaServer(); -private: +protected: void doCleanUp(); status_t doReset(); diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index 8b5b862..2e621fe 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -28,6 +28,8 @@ #include <media/stagefright/SkipCutBuffer.h> #include <OMX_Audio.h> +#include <system/audio.h> + #define TRACK_BUFFER_TIMING 0 namespace android { @@ -93,8 +95,11 @@ struct ACodec : public AHierarchicalStateMachine, public CodecBase { protected: virtual ~ACodec(); + virtual status_t setupCustomCodec( + status_t err, const char *mime, const sp<AMessage> &msg); + virtual status_t GetVideoCodingTypeFromMime( + const char *mime, OMX_VIDEO_CODINGTYPE *codingType); -private: struct BaseState; struct UninitializedState; struct LoadedState; @@ -152,6 +157,7 @@ private: }; struct BufferInfo { + BufferInfo() : mCustomData(-1) {} enum Status { OWNED_BY_US, OWNED_BY_COMPONENT, @@ -173,6 +179,7 @@ private: sp<GraphicBuffer> mGraphicBuffer; int mFenceFd; FrameRenderTracker::Info *mRenderInfo; + int mCustomData; // The following field and 4 methods are used for debugging only bool mIsReadFence; @@ -271,7 +278,7 @@ private: status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode); status_t allocateBuffersOnPort(OMX_U32 portIndex); status_t freeBuffersOnPort(OMX_U32 portIndex); - status_t freeBuffer(OMX_U32 portIndex, size_t i); + virtual status_t freeBuffer(OMX_U32 portIndex, size_t i); status_t handleSetSurface(const sp<Surface> &surface); status_t setupNativeWindowSizeFormatAndUsage( @@ -300,8 +307,8 @@ private: uint32_t portIndex, IOMX::buffer_id bufferID, ssize_t *index = NULL); - status_t setComponentRole(bool isEncoder, const char *mime); - status_t configureCodec(const char *mime, const sp<AMessage> &msg); + virtual status_t setComponentRole(bool isEncoder, const char *mime); + virtual status_t configureCodec(const char *mime, const sp<AMessage> &msg); status_t configureTunneledVideoPlayback(int32_t audioHwSync, const sp<ANativeWindow> &nativeWindow); @@ -314,10 +321,10 @@ private: status_t setSupportedOutputFormat(bool getLegacyFlexibleFormat); - status_t setupVideoDecoder( + virtual status_t setupVideoDecoder( const char *mime, const sp<AMessage> &msg, bool usingNativeBuffers); - status_t setupVideoEncoder( + virtual status_t setupVideoEncoder( const char *mime, const sp<AMessage> &msg); status_t setVideoFormatOnPort( @@ -340,9 +347,11 @@ private: int32_t maxOutputChannelCount, const drcParams_t& drc, int32_t pcmLimiterEnable); - status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate); + status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate, + int32_t bitsPerSample = 16); - status_t setupEAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate); + status_t setupEAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate, + int32_t bitsPerSample = 16); status_t selectAudioPortFormat( OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat); @@ -354,7 +363,8 @@ private: bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel); status_t setupRawAudioFormat( - OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels); + OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels, + int32_t bitsPerSample = 16); status_t setPriority(int32_t priority); status_t setOperatingRate(float rateFloat, bool isVideo); @@ -372,7 +382,7 @@ private: status_t configureBitrate( int32_t bitrate, OMX_VIDEO_CONTROLRATETYPE bitrateMode); - status_t setupErrorCorrectionParameters(); + virtual status_t setupErrorCorrectionParameters(); status_t initNativeWindow(); @@ -408,7 +418,7 @@ private: bool dropIncomplete = false, FrameRenderTracker::Info *until = NULL); void sendFormatChange(const sp<AMessage> &reply); - status_t getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify); + virtual status_t getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify); void signalError( OMX_ERRORTYPE error = OMX_ErrorUndefined, @@ -420,11 +430,36 @@ private: DescribeColorFormatParams &describeParams); status_t requestIDRFrame(); - status_t setParameters(const sp<AMessage> ¶ms); + virtual status_t setParameters(const sp<AMessage> ¶ms); // Send EOS on input stream. void onSignalEndOfInputStream(); + virtual void setBFrames(OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type) {} + virtual void setBFrames(OMX_VIDEO_PARAM_AVCTYPE *h264type, + const int32_t iFramesInterval, const int32_t frameRate) {} + + virtual status_t getVQZIPInfo(const sp<AMessage> &msg) { + return OK; + } + virtual bool canAllocateBuffer(OMX_U32 /* portIndex */) { + return false; + } + virtual void enableCustomAllocationMode(const sp<AMessage> &/* msg */) {} + virtual status_t allocateBuffer( + OMX_U32 portIndex, size_t bufSize, BufferInfo &info); + + virtual status_t setDSModeHint(sp<AMessage>& msg, + OMX_U32 flags, int64_t timeUs) { + return UNKNOWN_ERROR; + } + + virtual bool getDSModeHint(const sp<AMessage>& msg) { + return false; + } + + sp<IOMXObserver> createObserver(); + DISALLOW_EVIL_CONSTRUCTORS(ACodec); }; diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h index 3074910..9750bcd 100644 --- a/include/media/stagefright/AudioSource.h +++ b/include/media/stagefright/AudioSource.h @@ -46,6 +46,8 @@ struct AudioSource : public MediaSource, public MediaBufferObserver { virtual status_t stop() { return reset(); } virtual sp<MetaData> getFormat(); + virtual status_t pause() { return ERROR_UNSUPPORTED; } + // Returns the maximum amplitude since last call. int16_t getMaxAmplitude(); @@ -58,9 +60,11 @@ struct AudioSource : public MediaSource, public MediaBufferObserver { protected: virtual ~AudioSource(); -private: +protected: enum { - kMaxBufferSize = 2048, + //calculated for max duration 80 msec with 48K sampling rate. + kMaxBufferSize = 30720, + // After the initial mute, we raise the volume linearly // over kAutoRampDurationUs. @@ -68,7 +72,7 @@ private: // This is the initial mute duration to suppress // the video recording signal tone - kAutoRampStartUs = 0, + kAutoRampStartUs = 500000, }; Mutex mLock; @@ -100,10 +104,10 @@ private: int32_t startFrame, int32_t rampDurationFrames, uint8_t *data, size_t bytes); - void queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs); + virtual void queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs); void releaseQueuedFrames_l(); void waitOutstandingEncodingFrames_l(); - status_t reset(); + virtual status_t reset(); AudioSource(const AudioSource &); AudioSource &operator=(const AudioSource &); diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h index 069e897..527ee15 100644 --- a/include/media/stagefright/CameraSource.h +++ b/include/media/stagefright/CameraSource.h @@ -92,6 +92,8 @@ public: virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); + virtual status_t pause() { return ERROR_UNSUPPORTED; } + /** * Check whether a CameraSource object is properly initialized. * Must call this method before stop(). @@ -189,7 +191,7 @@ protected: void releaseCamera(); -private: +protected: friend struct CameraSourceListener; Mutex mLock; diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h index 34213be..f264d98 100644 --- a/include/media/stagefright/CameraSourceTimeLapse.h +++ b/include/media/stagefright/CameraSourceTimeLapse.h @@ -20,6 +20,7 @@ #include <pthread.h> +#include <media/stagefright/CameraSource.h> #include <utils/RefBase.h> #include <utils/threads.h> #include <utils/String16.h> @@ -56,7 +57,7 @@ public: // returning quickly. void startQuickReadReturns(); -private: +protected: // size of the encoded video. int32_t mVideoWidth; int32_t mVideoHeight; @@ -152,7 +153,7 @@ private: // the frame needs to be encoded, it returns false and also modifies // the time stamp to be one frame time ahead of the last encoded // frame's time stamp. - bool skipFrameAndModifyTimeStamp(int64_t *timestampUs); + virtual bool skipFrameAndModifyTimeStamp(int64_t *timestampUs); // Wrapper to enter threadTimeLapseEntry() static void *ThreadTimeLapseWrapper(void *me); diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index dcde36f..56abe71 100644 --- a/include/media/stagefright/DataSource.h +++ b/include/media/stagefright/DataSource.h @@ -36,6 +36,40 @@ class IDataSource; struct IMediaHTTPService; class String8; struct HTTPBase; +class DataSource; + +class Sniffer : public RefBase { +public: + Sniffer(); + + //////////////////////////////////////////////////////////////////////////// + + bool sniff(DataSource *source, String8 *mimeType, float *confidence, sp<AMessage> *meta); + + // The sniffer can optionally fill in "meta" with an AMessage containing + // a dictionary of values that helps the corresponding extractor initialize + // its state without duplicating effort already exerted by the sniffer. + typedef bool (*SnifferFunc)( + const sp<DataSource> &source, String8 *mimeType, + float *confidence, sp<AMessage> *meta); + + //if isExtendedExtractor = true, store the location of the sniffer to register + void registerSniffer_l(SnifferFunc func); + void registerDefaultSniffers(); + + virtual ~Sniffer() {} + +private: + Mutex mSnifferMutex; + List<SnifferFunc> mSniffers; + List<SnifferFunc> mExtraSniffers; + List<SnifferFunc>::iterator extendedSnifferPosition; + + void registerSnifferPlugin(); + + Sniffer(const Sniffer &); + Sniffer &operator=(const Sniffer &); +}; class DataSource : public RefBase { public: @@ -51,12 +85,13 @@ public: const char *uri, const KeyedVector<String8, String8> *headers = NULL, String8 *contentType = NULL, - HTTPBase *httpSource = NULL); + HTTPBase *httpSource = NULL, + bool useExtendedCache = false); static sp<DataSource> CreateMediaHTTP(const sp<IMediaHTTPService> &httpService); static sp<DataSource> CreateFromIDataSource(const sp<IDataSource> &source); - DataSource() {} + DataSource() : mSniffer(new Sniffer()) {} virtual status_t initCheck() const = 0; @@ -110,12 +145,10 @@ public: protected: virtual ~DataSource() {} -private: - static Mutex gSnifferMutex; - static List<SnifferFunc> gSniffers; - static bool gSniffersRegistered; + sp<Sniffer> mSniffer; static void RegisterSniffer_l(SnifferFunc func); + static void RegisterSnifferPlugin(); DataSource(const DataSource &); DataSource &operator=(const DataSource &); diff --git a/include/media/stagefright/ExtendedMediaDefs.h b/include/media/stagefright/ExtendedMediaDefs.h new file mode 100644 index 0000000..18b5e9a --- /dev/null +++ b/include/media/stagefright/ExtendedMediaDefs.h @@ -0,0 +1,69 @@ +/*Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _EXTENDED_MEDIA_DEFS_H_ +#define _EXTENDED_MEDIA_DEFS_H_ + +namespace android { + +extern const char *MEDIA_MIMETYPE_AUDIO_EVRC; +extern const char *MEDIA_MIMETYPE_VIDEO_WMV; +extern const char *MEDIA_MIMETYPE_VIDEO_WMV_VC1; +extern const char *MEDIA_MIMETYPE_AUDIO_WMA; +extern const char *MEDIA_MIMETYPE_AUDIO_WMA_PRO; +extern const char *MEDIA_MIMETYPE_AUDIO_WMA_LOSSLESS; +extern const char *MEDIA_MIMETYPE_CONTAINER_ASF; +extern const char *MEDIA_MIMETYPE_VIDEO_DIVX; +extern const char *MEDIA_MIMETYPE_CONTAINER_AAC; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCP; +extern const char *MEDIA_MIMETYPE_VIDEO_DIVX311; +extern const char *MEDIA_MIMETYPE_VIDEO_DIVX4; +extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2; +extern const char *MEDIA_MIMETYPE_CONTAINER_3G2; +extern const char *MEDIA_MIMETYPE_AUDIO_DTS; +extern const char *MEDIA_MIMETYPE_AUDIO_DTS_LBR; +extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS; +extern const char *MEDIA_MIMETYPE_AUDIO_AIFF; +extern const char *MEDIA_MIMETYPE_AUDIO_ALAC; +extern const char *MEDIA_MIMETYPE_AUDIO_APE; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCAMR_NB; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCAMR_WB; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCWAV; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG2TS; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG2PS; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG4; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCMATROSKA; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCOGG; +extern const char *MEDIA_MIMETYPE_CONTAINER_QCFLV; +extern const char *MEDIA_MIMETYPE_VIDEO_VPX; +extern const char *MEDIA_MIMETYPE_CONTAINER_QTIFLAC; +extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4_DP; + +} // namespace android + +#endif // _EXTENDED_MEDIA_DEFS_H_ diff --git a/include/media/stagefright/FFMPEGSoftCodec.h b/include/media/stagefright/FFMPEGSoftCodec.h new file mode 100644 index 0000000..83373d0 --- /dev/null +++ b/include/media/stagefright/FFMPEGSoftCodec.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 FFMPEG_SOFT_CODEC_H_ +#define FFMPEG_SOFT_CODEC_H_ + +#include <media/IOMX.h> +#include <media/MediaCodecInfo.h> + +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/AString.h> + +#include <media/stagefright/MetaData.h> + +#include <OMX_Audio.h> +#include <OMX_Video.h> + +namespace android { + +struct FFMPEGSoftCodec { + + enum { + kPortIndexInput = 0, + kPortIndexOutput = 1 + }; + + static void convertMessageToMetaDataFF( + const sp<AMessage> &msg, sp<MetaData> &meta); + + static void convertMetaDataToMessageFF( + const sp<MetaData> &meta, sp<AMessage> *format); + + static const char* overrideComponentName( + uint32_t quirks, const sp<MetaData> &meta, + const char *mime, bool isEncoder); + + static void overrideComponentName( + uint32_t quirks, const sp<AMessage> &msg, + AString* componentName, AString* mime, + int32_t isEncoder); + + static status_t setSupportedRole( + const sp<IOMX> &omx, IOMX::node_id node, + bool isEncoder, const char *mime); + + static status_t setAudioFormat( + const sp<AMessage> &msg, const char* mime, + sp<IOMX> OMXhandle, IOMX::node_id nodeID); + + static status_t setVideoFormat( + const sp<AMessage> &msg, const char* mime, + sp<IOMX> OMXhandle,IOMX::node_id nodeID, + bool isEncoder, OMX_VIDEO_CODINGTYPE *compressionFormat); + + static status_t getAudioPortFormat( + OMX_U32 portIndex, int coding, + sp<AMessage> ¬ify, sp<IOMX> OMXhandle, IOMX::node_id nodeID); + + static status_t getVideoPortFormat( + OMX_U32 portIndex, int coding, + sp<AMessage> ¬ify, sp<IOMX> OMXhandle, IOMX::node_id nodeID); + +private: + static const char* getMsgKey(int key); + + static status_t setWMVFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setRVFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setFFmpegVideoFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setRawAudioFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setWMAFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setVORBISFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setRAFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setFLACFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setMP2Format( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setAC3Format( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setAPEFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setDTSFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + + static status_t setFFmpegAudioFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + +}; + +} +#endif diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h index a981d1c..21844ca 100644 --- a/include/media/stagefright/FileSource.h +++ b/include/media/stagefright/FileSource.h @@ -39,6 +39,10 @@ public: virtual status_t getSize(off64_t *size); + virtual String8 getUri() { + return mUri; + } + virtual sp<DecryptHandle> DrmInitialization(const char *mime); virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client); @@ -48,6 +52,7 @@ protected: private: int mFd; + String8 mUri; int64_t mOffset; int64_t mLength; Mutex mLock; @@ -60,6 +65,7 @@ private: unsigned char *mDrmBuf; ssize_t readAtDRM(off64_t offset, void *data, size_t size); + void fetchUriFromFd(int fd); FileSource(const FileSource &); FileSource &operator=(const FileSource &); diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h index a195fe8..055c079 100644 --- a/include/media/stagefright/MPEG4Writer.h +++ b/include/media/stagefright/MPEG4Writer.h @@ -108,6 +108,8 @@ private: sp<AMessage> mMetaKeys; + bool mIsVideoHEVC; + void setStartTimestampUs(int64_t timeUs); int64_t getStartTimestampUs(); // Not const status_t startTracks(MetaData *params); @@ -181,6 +183,9 @@ private: // By default, real time recording is on. bool isRealTimeRecording() const; + // To use 3gp4 box for clips with AMR audio + bool mIsAudioAMR; + void lock(); void unlock(); diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h index c10963d..b0ae83b 100644 --- a/include/media/stagefright/MediaCodec.h +++ b/include/media/stagefright/MediaCodec.h @@ -51,6 +51,7 @@ struct MediaCodec : public AHandler { BUFFER_FLAG_SYNCFRAME = 1, BUFFER_FLAG_CODECCONFIG = 2, BUFFER_FLAG_EOS = 4, + BUFFER_FLAG_EXTRADATA = 0x1000, }; enum { diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h index 71f58a9..d41d87b 100644 --- a/include/media/stagefright/MediaCodecSource.h +++ b/include/media/stagefright/MediaCodecSource.h @@ -28,7 +28,7 @@ class AMessage; struct AReplyToken; class IGraphicBufferProducer; class IGraphicBufferConsumer; -class MediaCodec; +struct MediaCodec; class MetaData; struct MediaCodecSource : public MediaSource, diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h index 21eb04a..ffbd20c 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -66,6 +66,43 @@ extern const char *MEDIA_MIMETYPE_TEXT_VTT; extern const char *MEDIA_MIMETYPE_TEXT_CEA_608; extern const char *MEDIA_MIMETYPE_DATA_TIMED_ID3; +extern const char *MEDIA_MIMETYPE_AUDIO_EAC3_JOC; +extern const char *MEDIA_MIMETYPE_AUDIO_EAC3; + +extern const char *MEDIA_MIMETYPE_VIDEO_FLV1; +extern const char *MEDIA_MIMETYPE_VIDEO_MJPEG; +extern const char *MEDIA_MIMETYPE_VIDEO_RV; +extern const char *MEDIA_MIMETYPE_VIDEO_VC1; +extern const char *MEDIA_MIMETYPE_VIDEO_WMV; +extern const char *MEDIA_MIMETYPE_VIDEO_HEVC; +extern const char *MEDIA_MIMETYPE_VIDEO_FFMPEG; + +extern const char *MEDIA_MIMETYPE_AUDIO_AC3; +extern const char *MEDIA_MIMETYPE_AUDIO_PCM; +extern const char *MEDIA_MIMETYPE_AUDIO_RA; +extern const char *MEDIA_MIMETYPE_AUDIO_WMA; +extern const char *MEDIA_MIMETYPE_AUDIO_FFMPEG; + +extern const char *MEDIA_MIMETYPE_CONTAINER_APE; +extern const char *MEDIA_MIMETYPE_CONTAINER_DIVX; +extern const char *MEDIA_MIMETYPE_CONTAINER_DTS; +extern const char *MEDIA_MIMETYPE_CONTAINER_FLAC; +extern const char *MEDIA_MIMETYPE_CONTAINER_FLV; +extern const char *MEDIA_MIMETYPE_CONTAINER_MOV; +extern const char *MEDIA_MIMETYPE_CONTAINER_MP2; +extern const char *MEDIA_MIMETYPE_CONTAINER_MPG; +extern const char *MEDIA_MIMETYPE_CONTAINER_RA; +extern const char *MEDIA_MIMETYPE_CONTAINER_RM; +extern const char *MEDIA_MIMETYPE_CONTAINER_TS; +extern const char *MEDIA_MIMETYPE_CONTAINER_WEBM; +extern const char *MEDIA_MIMETYPE_CONTAINER_VC1; +extern const char *MEDIA_MIMETYPE_CONTAINER_HEVC; +extern const char *MEDIA_MIMETYPE_CONTAINER_WMA; +extern const char *MEDIA_MIMETYPE_CONTAINER_WMV; +extern const char *MEDIA_MIMETYPE_CONTAINER_FFMPEG; + } // namespace android +#include <media/stagefright/ExtendedMediaDefs.h> + #endif // MEDIA_DEFS_H_ diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h index 183933a..2f2057f 100644 --- a/include/media/stagefright/MediaExtractor.h +++ b/include/media/stagefright/MediaExtractor.h @@ -19,17 +19,30 @@ #define MEDIA_EXTRACTOR_H_ #include <utils/RefBase.h> +#include <media/stagefright/DataSource.h> namespace android { -class DataSource; class MediaSource; class MetaData; class MediaExtractor : public RefBase { public: + typedef MediaExtractor *(*CreateFunc)(const sp<DataSource> &source, + const char *mime, const sp<AMessage> &meta); + + struct Plugin { + DataSource::SnifferFunc sniff; + CreateFunc create; + }; + + static Plugin *getPlugin() { + return &sPlugin; + } + static sp<MediaExtractor> Create( - const sp<DataSource> &source, const char *mime = NULL); + const sp<DataSource> &source, const char *mime = NULL, + const uint32_t flags = 0); virtual size_t countTracks() = 0; virtual sp<MediaSource> getTrack(size_t index) = 0; @@ -67,6 +80,7 @@ public: } virtual void setUID(uid_t uid) { } + virtual void setExtraFlags(uint32_t flag) {} protected: MediaExtractor() : mIsDrm(false) {} @@ -74,6 +88,7 @@ protected: private: bool mIsDrm; + static Plugin sPlugin; MediaExtractor(const MediaExtractor &); MediaExtractor &operator=(const MediaExtractor &); diff --git a/include/media/stagefright/MediaHTTP.h b/include/media/stagefright/MediaHTTP.h index 006d8d8..88683bd 100644 --- a/include/media/stagefright/MediaHTTP.h +++ b/include/media/stagefright/MediaHTTP.h @@ -54,7 +54,7 @@ protected: virtual String8 getUri(); virtual String8 getMIMEType() const; -private: +protected: status_t mInitCheck; sp<IMediaHTTPConnection> mHTTPConnection; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 8d4e15a..0dd5995 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -50,6 +50,10 @@ enum { kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz) kKeyFrameRate = 'frmR', // int32_t (video frame rate fps) kKeyBitRate = 'brte', // int32_t (bps) + kKeyCodecId = 'cdid', // int32_t + kKeyBitsPerSample = 'sbit', // int32_t (DUPE of kKeySampleBits) + kKeyCodedSampleBits = 'cosb', // int32_t + kKeySampleFormat = 'sfmt', // int32_t kKeyESDS = 'esds', // raw data kKeyAACProfile = 'aacp', // int32_t kKeyAVCC = 'avcc', // raw data @@ -131,6 +135,23 @@ enum { kKeyIsUnreadable = 'unre', // bool (int32_t) + kKeyRawCodecSpecificData = 'rcsd', // raw data - added to support mmParser + kKeyDivXVersion = 'DivX', // int32_t + kKeyDivXDrm = 'QDrm', // void * + kKeyWMAEncodeOpt = 'eopt', // int32_t + kKeyWMABlockAlign = 'blka', // int32_t + kKeyWMAVersion = 'wmav', // int32_t + kKeyWMAAdvEncOpt1 = 'ade1', // int16_t + kKeyWMAAdvEncOpt2 = 'ade2', // int32_t + kKeyWMAFormatTag = 'fmtt', // int64_t + kKeyWMABitspersample = 'bsps', // int64_t + kKeyWMAVirPktSize = 'vpks', // int64_t + kKeyWMVProfile = 'wmvp', // int32_t + + kKeyWMVVersion = 'wmvv', // int32_t + kKeyRVVersion = '#rvv', // int32_t + kKeyBlockAlign = 'ablk', // int32_t , should be different from kKeyWMABlockAlign + // An indication that a video buffer has been rendered. kKeyRendered = 'rend', // bool (int32_t) @@ -181,6 +202,9 @@ enum { // H264 supplemental enhancement information offsets/sizes kKeySEI = 'sei ', // raw data + + kKeyPCMFormat = 'pfmt', + kKeyArbitraryMode = 'ArbM', }; enum { @@ -190,6 +214,32 @@ enum { kTypeD263 = 'd263', }; +enum { + kTypeDivXVer_3_11, + kTypeDivXVer_4, + kTypeDivXVer_5, + kTypeDivXVer_6, +}; + +enum { + kTypeWMA, + kTypeWMAPro, + kTypeWMALossLess, +}; + +enum { + kTypeWMVVer_7, // WMV1 + kTypeWMVVer_8, // WMV2 + kTypeWMVVer_9, // WMV3 +}; + +// http://en.wikipedia.org/wiki/RealVideo +enum { + kTypeRVVer_G2, // rv20: RealVideo G2 + kTypeRVVer_8, // rv30: RealVideo 8 + kTypeRVVer_9, // rv40: RealVideo 9 +}; + class MetaData : public RefBase { public: MetaData(); diff --git a/media/libavextensions/Android.mk b/media/libavextensions/Android.mk new file mode 100644 index 0000000..3918857 --- /dev/null +++ b/media/libavextensions/Android.mk @@ -0,0 +1,92 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + stagefright/ExtendedMediaDefs.cpp \ + stagefright/AVUtils.cpp \ + stagefright/AVFactory.cpp \ + +LOCAL_C_INCLUDES:= \ + $(TOP)/frameworks/av/include/media/ \ + $(TOP)/frameworks/av/media/libavextensions \ + $(TOP)/frameworks/native/include/media/hardware \ + $(TOP)/frameworks/native/include/media/openmax \ + $(TOP)/external/flac/include \ + $(TOP)/$(call project-path-for,qcom-media)/mm-core/inc \ + $(TOP)/frameworks/av/media/libstagefright \ + +LOCAL_CFLAGS += -Wno-multichar -Werror + +ifeq ($(TARGET_ENABLE_QC_AV_ENHANCEMENTS),true) + LOCAL_CFLAGS += -DENABLE_AV_ENHANCEMENTS +endif + +LOCAL_MODULE:= libavextensions +LOCAL_CLANG := false + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) + +######################################################## + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + media/AVMediaUtils.cpp \ + +LOCAL_C_INCLUDES:= \ + $(TOP)/frameworks/av/include/media/ \ + $(TOP)/frameworks/av/media/libavextensions \ + $(TOP)/frameworks/native/include/media/hardware \ + $(TOP)/frameworks/native/include/media/openmax \ + $(TOP)/external/flac/include \ + $(TOP)/$(call project-path-for,qcom-media)/mm-core/inc + +LOCAL_CFLAGS += -Wno-multichar -Werror + +ifeq ($(TARGET_ENABLE_QC_AV_ENHANCEMENTS),true) + LOCAL_CFLAGS += -DENABLE_AV_ENHANCEMENTS +endif + +LOCAL_MODULE:= libavmediaextentions +LOCAL_CLANG := false + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) + +######################################################## + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + mediaplayerservice/AVMediaServiceFactory.cpp \ + mediaplayerservice/AVMediaServiceUtils.cpp \ + mediaplayerservice/AVNuFactory.cpp \ + mediaplayerservice/AVNuUtils.cpp \ + +LOCAL_C_INCLUDES:= \ + $(TOP)/frameworks/av/include/media/ \ + $(TOP)/frameworks/av/media/libmediaplayerservice \ + $(TOP)/frameworks/av/media/libavextensions \ + $(TOP)/frameworks/av/media/libstagefright/include \ + $(TOP)/frameworks/av/media/libstagefright/rtsp \ + $(TOP)/frameworks/native/include/media/hardware \ + $(TOP)/frameworks/native/include/media/openmax \ + $(TOP)/external/flac/include \ + $(TOP)/$(call project-path-for,qcom-media)/mm-core/inc + +LOCAL_CFLAGS += -Wno-multichar -Werror + +ifeq ($(TARGET_ENABLE_QC_AV_ENHANCEMENTS),true) + LOCAL_CFLAGS += -DENABLE_AV_ENHANCEMENTS +endif + +LOCAL_MODULE:= libavmediaserviceextensions +LOCAL_CLANG := false + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) + diff --git a/media/libavextensions/common/AVExtensionsCommon.h b/media/libavextensions/common/AVExtensionsCommon.h new file mode 100644 index 0000000..c39872e --- /dev/null +++ b/media/libavextensions/common/AVExtensionsCommon.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _AV_EXTENSIONS_COMMON_H_ +#define _AV_EXTENSIONS_COMMON_H_ + +namespace android { + +typedef void *(*createFunction_t)(void); + +template <typename T> +struct ExtensionsLoader { + + static T *createInstance(const char *createFunctionName); + +private: + static void loadLib(); + static createFunction_t loadCreateFunction(const char *createFunctionName); + static void *mLibHandle; +}; + +/* + * Boiler-plate to declare the class as a singleton (with a static getter) + * which can be loaded (dlopen'd) via ExtensionsLoader + */ +#define DECLARE_LOADABLE_SINGLETON(className) \ +protected: \ + className(); \ + virtual ~className(); \ + static className *sInst; \ +private: \ + className(const className&); \ + className &operator=(className &); \ +public: \ + static className *get() { \ + return sInst; \ + } \ + friend struct ExtensionsLoader<className>; + +} //namespace android + +#endif // _AV_EXTENSIONS_COMMON_H_ diff --git a/media/libavextensions/common/ExtensionsLoader.hpp b/media/libavextensions/common/ExtensionsLoader.hpp new file mode 100644 index 0000000..e03979c --- /dev/null +++ b/media/libavextensions/common/ExtensionsLoader.hpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <dlfcn.h> +#include <common/AVExtensionsCommon.h> + +namespace android { + +static const char * CUSTOMIZATION_LIB_NAME = "libavenhancements.so"; + +/* + * Create strongly-typed objects of type T + * If the customization library exists and does contain a "named" constructor, + * invoke and create an instance + * Else create the object of type T itself + * + * Contains a static instance to dlopen'd library, But may end up + * opening the library mutiple times. Following snip from dlopen man page is + * reassuring "...Only a single copy of an object file is brought into the + * address space, even if dlopen() is invoked multiple times in reference to + * the file, and even if different pathnames are used to reference the file.." + */ + +template <typename T> +T *ExtensionsLoader<T>::createInstance(const char *createFunctionName) { + ALOGV("createInstance(%dbit) : %s", sizeof(intptr_t)*8, createFunctionName); + // create extended object if extensions-lib is available and + // AV_ENHANCEMENTS is enabled +#if ENABLE_AV_ENHANCEMENTS + createFunction_t createFunc = loadCreateFunction(createFunctionName); + if (createFunc) { + return reinterpret_cast<T *>((*createFunc)()); + } +#endif + // Else, create the default object + return new T; +} + +template <typename T> +void ExtensionsLoader<T>::loadLib() { + if (!mLibHandle) { + mLibHandle = ::dlopen(CUSTOMIZATION_LIB_NAME, RTLD_LAZY); + if (!mLibHandle) { + ALOGV("%s", dlerror()); + return; + } + ALOGV("Opened %s", CUSTOMIZATION_LIB_NAME); + } +} + +template <typename T> +createFunction_t ExtensionsLoader<T>::loadCreateFunction(const char *createFunctionName) { + loadLib(); + if (!mLibHandle) { + return NULL; + } + createFunction_t func = (createFunction_t)dlsym(mLibHandle, createFunctionName); + if (!func) { + ALOGW("symbol %s not found: %s",createFunctionName, dlerror()); + } + return func; +} + +//static +template <typename T> +void *ExtensionsLoader<T>::mLibHandle = NULL; + +} //namespace android diff --git a/media/libavextensions/media/AVMediaExtensions.h b/media/libavextensions/media/AVMediaExtensions.h new file mode 100644 index 0000000..ae26143 --- /dev/null +++ b/media/libavextensions/media/AVMediaExtensions.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _AV_MEDIA_EXTENSIONS_H_ +#define _AV_MEDIA_EXTENSIONS_H_ + +#include <common/AVExtensionsCommon.h> +#include <hardware/audio.h> +#include <media/AudioTrack.h> + +namespace android { + +class MediaRecorder; +class Parcel; +/* + * Common delegate to the classes in libstagefright + */ +struct AVMediaUtils { + + virtual bool AudioTrackIsPcmOffloaded(const audio_format_t /*format*/) { + return false; + } + virtual status_t AudioTrackGetPosition(AudioTrack* /*track*/, + uint32_t* /*position*/) { + return NO_INIT; + } + + virtual status_t AudioTrackGetTimestamp(AudioTrack* /*track*/, + AudioTimestamp* /*timestamp*/) { + return NO_INIT; + } + + virtual size_t AudioTrackGetOffloadFrameCount(size_t frameCount) { + return frameCount; + } + + virtual sp<MediaRecorder> createMediaRecorder(const String16& opPackageName); + virtual void writeCustomData( + Parcel * /* reply */, void * /* buffer_data */) {} + virtual void readCustomData( + const Parcel * /* reply */, void ** /*buffer_data */ ) {} + virtual void closeFileDescriptor(void * /* buffer_ptr */) {} + // ----- NO TRESSPASSING BEYOND THIS LINE ------ + DECLARE_LOADABLE_SINGLETON(AVMediaUtils); +}; + +} //namespace android + +#endif //_AV_MEDIA_EXTENSIONS_H_ + diff --git a/media/libavextensions/media/AVMediaUtils.cpp b/media/libavextensions/media/AVMediaUtils.cpp new file mode 100644 index 0000000..0f9e9eb --- /dev/null +++ b/media/libavextensions/media/AVMediaUtils.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AVMediaUtils" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaCodecList.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/ACodec.h> + +#include <media/AudioTrack.h> +#include <media/mediarecorder.h> + +#include "common/ExtensionsLoader.hpp" +#include "media/AVMediaExtensions.h" + +namespace android { + +// ----- NO TRESSPASSING BEYOND THIS LINE ------ +AVMediaUtils::AVMediaUtils() { +} + +AVMediaUtils::~AVMediaUtils() { +} + +sp<MediaRecorder> AVMediaUtils::createMediaRecorder(const String16& opPackageName) { + return new MediaRecorder(opPackageName); +} + +//static +AVMediaUtils *AVMediaUtils::sInst = + ExtensionsLoader<AVMediaUtils>::createInstance("createExtendedMediaUtils"); + +} //namespace android + diff --git a/media/libavextensions/mediaplayerservice/AVMediaServiceExtensions.h b/media/libavextensions/mediaplayerservice/AVMediaServiceExtensions.h new file mode 100644 index 0000000..f2c789e --- /dev/null +++ b/media/libavextensions/mediaplayerservice/AVMediaServiceExtensions.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _AV_MEDIA_SERVICE_EXTENSIONS_H_ +#define _AV_MEDIA_SERVICE_EXTENSIONS_H_ + +#include <common/AVExtensionsCommon.h> +#include <MediaPlayerFactory.h> + +#include <utils/List.h> +#include <utils/RefBase.h> +#include <utils/String16.h> + +#include <media/Metadata.h> + +namespace android { + +struct StagefrightRecorder; +struct ARTSPConnection; +struct ARTPConnection; +struct AString; +struct MyHandler; +struct ABuffer; + +/* + * Factory to create objects of base-classes in libmediaplayerservice + */ +struct AVMediaServiceFactory { + virtual StagefrightRecorder *createStagefrightRecorder(const String16 &); + + // RTSP extensions + virtual sp<ARTSPConnection> createARTSPConnection(bool uidValid, uid_t uid); + virtual sp<ARTPConnection> createARTPConnection(); + + // ----- NO TRESSPASSING BEYOND THIS LINE ------ + DECLARE_LOADABLE_SINGLETON(AVMediaServiceFactory); +}; + +/* + * Common delegate to the classes in libmediaplayerservice + */ +struct AVMediaServiceUtils { + virtual void getDashPlayerFactory(MediaPlayerFactory::IFactory *&, player_type ) {} + // RTSP IPV6 utils + virtual bool pokeAHole(sp<MyHandler> handler, int rtpSocket, int rtcpSocket, + const AString &transport, const AString &sessionHost); + virtual void makePortPair(int *rtpSocket, int *rtcpSocket, unsigned *rtpPort, + bool isIPV6); + virtual const char* parseURL(AString *host); + // RTSP customization utils + virtual bool parseTrackURL(AString url, AString val); + virtual void appendRange(AString *request); + virtual void setServerTimeoutUs(int64_t timeout); + virtual void appendMeta(media::Metadata *meta); + virtual bool checkNPTMapping(uint32_t *rtpInfoTime, int64_t *playTimeUs, + bool *nptValid, uint32_t rtpTime); + virtual void addH263AdvancedPacket(const sp<ABuffer> &buffer, + List<sp<ABuffer>> *packets, uint32_t rtpTime); + virtual bool parseNTPRange(const char *s, float *npt1, float *npt2); + + // ----- NO TRESSPASSING BEYOND THIS LINE ------ + DECLARE_LOADABLE_SINGLETON(AVMediaServiceUtils); +}; + +} + +#endif // _AV_EXTENSIONS__H_ diff --git a/media/libavextensions/mediaplayerservice/AVMediaServiceFactory.cpp b/media/libavextensions/mediaplayerservice/AVMediaServiceFactory.cpp new file mode 100644 index 0000000..10b66f5 --- /dev/null +++ b/media/libavextensions/mediaplayerservice/AVMediaServiceFactory.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "AVMediaServiceFactory" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include "ARTPConnection.h" +#include "ARTSPConnection.h" + +#include "MediaRecorderClient.h" +#include "MediaPlayerService.h" +#include "StagefrightRecorder.h" + +#include "common/ExtensionsLoader.hpp" +#include "mediaplayerservice/AVMediaServiceExtensions.h" + +namespace android { +StagefrightRecorder *AVMediaServiceFactory::createStagefrightRecorder( + const String16 &opPackageName) { + return new StagefrightRecorder(opPackageName); +} + +sp<ARTSPConnection> AVMediaServiceFactory::createARTSPConnection( + bool uidValid, uid_t uid) { + return new ARTSPConnection(uidValid, uid); +} + +sp<ARTPConnection> AVMediaServiceFactory::createARTPConnection() { + return new ARTPConnection(); +} + +// ----- NO TRESSPASSING BEYOND THIS LINE ------ +AVMediaServiceFactory::AVMediaServiceFactory() { +} + +AVMediaServiceFactory::~AVMediaServiceFactory() { +} + +//static +AVMediaServiceFactory *AVMediaServiceFactory::sInst = + ExtensionsLoader<AVMediaServiceFactory>::createInstance("createExtendedMediaServiceFactory"); + +} //namespace android + diff --git a/media/libavextensions/mediaplayerservice/AVMediaServiceUtils.cpp b/media/libavextensions/mediaplayerservice/AVMediaServiceUtils.cpp new file mode 100644 index 0000000..0e42367 --- /dev/null +++ b/media/libavextensions/mediaplayerservice/AVMediaServiceUtils.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "AVMediaServiceUtils" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include "ARTPConnection.h" +#include "ASessionDescription.h" +#include "MyHandler.h" + +#include "common/ExtensionsLoader.hpp" +#include "mediaplayerservice/AVMediaServiceExtensions.h" + +namespace android { + +bool AVMediaServiceUtils::pokeAHole(sp<MyHandler> handler, int rtpSocket, int rtcpSocket, + const AString &transport, const AString &/*sessionHost*/) { + if (handler == NULL) { + ALOGW("MyHandler is NULL"); + return false; + } + return handler->pokeAHole(rtpSocket, rtcpSocket, transport); +} + +void AVMediaServiceUtils::makePortPair(int *rtpSocket, int *rtcpSocket, unsigned *rtpPort, + bool /*isIPV6*/) { + return ARTPConnection::MakePortPair(rtpSocket, rtcpSocket, rtpPort); +} + +const char* AVMediaServiceUtils::parseURL(AString *host) { + return strchr(host->c_str(), ':'); +} + +bool AVMediaServiceUtils::parseTrackURL(AString /*url*/, AString /*val*/) { + return false; +} + +void AVMediaServiceUtils::appendRange(AString * /*request*/) { + return; +} + +void AVMediaServiceUtils::setServerTimeoutUs(int64_t /*timeout*/) { + return; +} + +void AVMediaServiceUtils::addH263AdvancedPacket(const sp<ABuffer> &/*buffer*/, + List<sp<ABuffer>> * /*packets*/, uint32_t /*rtpTime*/) { + return; +} + +void AVMediaServiceUtils::appendMeta(media::Metadata * /*meta*/) { + return; +} + +bool AVMediaServiceUtils::checkNPTMapping(uint32_t * /*rtpInfoTime*/, int64_t * /*playTimeUs*/, + bool * /*nptValid*/, uint32_t /*rtpTime*/) { + return false; +} + +bool AVMediaServiceUtils::parseNTPRange(const char *s, float *npt1, float *npt2) { + return ASessionDescription::parseNTPRange(s, npt1, npt2); +} + +// ----- NO TRESSPASSING BEYOND THIS LINE ------ +AVMediaServiceUtils::AVMediaServiceUtils() { +} + +AVMediaServiceUtils::~AVMediaServiceUtils() { +} + +//static +AVMediaServiceUtils *AVMediaServiceUtils::sInst = + ExtensionsLoader<AVMediaServiceUtils>::createInstance("createExtendedMediaServiceUtils"); + +} //namespace android + diff --git a/media/libavextensions/mediaplayerservice/AVNuExtensions.h b/media/libavextensions/mediaplayerservice/AVNuExtensions.h new file mode 100644 index 0000000..5a7a0cb --- /dev/null +++ b/media/libavextensions/mediaplayerservice/AVNuExtensions.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _AV_NU_EXTENSIONS_H_ +#define _AV_NU_EXTENSIONS_H_ + +#include <common/AVExtensionsCommon.h> + +namespace android { + +struct NuPlayer; +/* + * Factory to create extended NuPlayer objects + */ +struct AVNuFactory { + virtual sp<NuPlayer> createNuPlayer(pid_t pid); + + virtual sp<NuPlayer::DecoderBase> createPassThruDecoder( + const sp<AMessage> ¬ify, + const sp<NuPlayer::Source> &source, + const sp<NuPlayer::Renderer> &renderer); + + virtual sp<NuPlayer::DecoderBase> createDecoder( + const sp<AMessage> ¬ify, + const sp<NuPlayer::Source> &source, + pid_t pid, + const sp<NuPlayer::Renderer> &renderer); + + virtual sp<NuPlayer::Renderer> createRenderer( + const sp<MediaPlayerBase::AudioSink> &sink, + const sp<AMessage> ¬ify, + uint32_t flags); + + // ----- NO TRESSPASSING BEYOND THIS LINE ------ + DECLARE_LOADABLE_SINGLETON(AVNuFactory); +}; + +/* + * Common delegate to the classes in NuPlayer + */ +struct AVNuUtils { + + virtual sp<MetaData> createPCMMetaFromSource(const sp<MetaData> &); + virtual bool pcmOffloadException(const sp<MetaData> &); + virtual bool isRAWFormat(const sp<MetaData> &); + virtual bool isRAWFormat(const sp<AMessage> &); + virtual bool isVorbisFormat(const sp<MetaData> &); + virtual int updateAudioBitWidth(audio_format_t audioFormat, + const sp<AMessage> &); + virtual audio_format_t getKeyPCMFormat(const sp<MetaData> &); + virtual void setKeyPCMFormat(const sp<MetaData> &, audio_format_t audioFormat); + virtual audio_format_t getPCMFormat(const sp<AMessage> &); + virtual void setPCMFormat(const sp<AMessage> &, audio_format_t audioFormat); + virtual void setSourcePCMFormat(const sp<MetaData> &); + virtual void setDecodedPCMFormat(const sp<AMessage> &); + virtual status_t convertToSinkFormatIfNeeded(const sp<ABuffer> &, sp<ABuffer> &, + audio_format_t sinkFormat, bool isOffload); + virtual uint32_t getUseSetBuffersFlag(); + virtual bool canUseSetBuffers(const sp<MetaData> &Meta); + + virtual void printFileName(int fd); + virtual void checkFormatChange(bool *formatChange, const sp<ABuffer> &accessUnit); + virtual void addFlagsInMeta(const sp<ABuffer> &buffer, int32_t flags, bool isAudio); + + // ----- NO TRESSPASSING BEYOND THIS LINE ------ + DECLARE_LOADABLE_SINGLETON(AVNuUtils); +}; + +} + +#endif // _AV_EXTENSIONS__H_ diff --git a/media/libavextensions/mediaplayerservice/AVNuFactory.cpp b/media/libavextensions/mediaplayerservice/AVNuFactory.cpp new file mode 100644 index 0000000..ff7c074 --- /dev/null +++ b/media/libavextensions/mediaplayerservice/AVNuFactory.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AVNuFactory" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +#include <nuplayer/NuPlayer.h> +#include <nuplayer/NuPlayerDecoderBase.h> +#include <nuplayer/NuPlayerDecoderPassThrough.h> +#include <nuplayer/NuPlayerDecoder.h> +#include <nuplayer/NuPlayerCCDecoder.h> +#include <gui/Surface.h> +#include <nuplayer/NuPlayerSource.h> +#include <nuplayer/NuPlayerRenderer.h> + +#include "common/ExtensionsLoader.hpp" +#include "mediaplayerservice/AVNuExtensions.h" + +namespace android { + +sp<NuPlayer> AVNuFactory::createNuPlayer(pid_t pid) { + return new NuPlayer(pid); +} + +sp<NuPlayer::DecoderBase> AVNuFactory::createPassThruDecoder( + const sp<AMessage> ¬ify, + const sp<NuPlayer::Source> &source, + const sp<NuPlayer::Renderer> &renderer) { + return new NuPlayer::DecoderPassThrough(notify, source, renderer); +} + +sp<NuPlayer::DecoderBase> AVNuFactory::createDecoder( + const sp<AMessage> ¬ify, + const sp<NuPlayer::Source> &source, + pid_t pid, + const sp<NuPlayer::Renderer> &renderer) { + return new NuPlayer::Decoder(notify, source, pid, renderer); +} + +sp<NuPlayer::Renderer> AVNuFactory::createRenderer( + const sp<MediaPlayerBase::AudioSink> &sink, + const sp<AMessage> ¬ify, + uint32_t flags) { + return new NuPlayer::Renderer(sink, notify, flags); +} + +// ----- NO TRESSPASSING BEYOND THIS LINE ------ +AVNuFactory::AVNuFactory() { +} + +AVNuFactory::~AVNuFactory() { +} + +//static +AVNuFactory *AVNuFactory::sInst = + ExtensionsLoader<AVNuFactory>::createInstance("createExtendedNuFactory"); + +} //namespace android + diff --git a/media/libavextensions/mediaplayerservice/AVNuUtils.cpp b/media/libavextensions/mediaplayerservice/AVNuUtils.cpp new file mode 100644 index 0000000..ba837e4 --- /dev/null +++ b/media/libavextensions/mediaplayerservice/AVNuUtils.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "AVNuUtils" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +#include <media/stagefright/MediaDefs.h> + +#include <nuplayer/NuPlayer.h> +#include <nuplayer/NuPlayerDecoderBase.h> +#include <nuplayer/NuPlayerDecoderPassThrough.h> +#include <nuplayer/NuPlayerSource.h> +#include <nuplayer/NuPlayerRenderer.h> + +#include "common/ExtensionsLoader.hpp" +#include "mediaplayerservice/AVNuExtensions.h" + +namespace android { + +sp<MetaData> AVNuUtils::createPCMMetaFromSource(const sp<MetaData> &sMeta) { + return sMeta; +} + +bool AVNuUtils::pcmOffloadException(const sp<MetaData> &) { + return true; +} + +bool AVNuUtils::isRAWFormat(const sp<MetaData> &meta) { + const char *mime = {0}; + if (meta == NULL) { + return false; + } + CHECK(meta->findCString(kKeyMIMEType, &mime)); + if (!strncasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW, 9)) + return true; + else + return false; +} + +bool AVNuUtils::isRAWFormat(const sp<AMessage> &format) { + AString mime; + if (format == NULL) { + return false; + } + CHECK(format->findString("mime", &mime)); + if (!strncasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_RAW, 9)) + return true; + else + return false; + +} + +bool AVNuUtils::isVorbisFormat(const sp<MetaData> &) { + return false; +} + +int AVNuUtils::updateAudioBitWidth(audio_format_t /*audioFormat*/, + const sp<AMessage> &){ + return 16; +} + +audio_format_t AVNuUtils::getKeyPCMFormat(const sp<MetaData> &meta) { + int32_t pcmFormat = 0; + if (meta->findInt32('pfmt', &pcmFormat)) + return (audio_format_t)pcmFormat; + + return AUDIO_FORMAT_PCM_16_BIT; +} + +void AVNuUtils::setKeyPCMFormat(const sp<MetaData> &meta, audio_format_t audioFormat) { + if (audio_is_linear_pcm(audioFormat)) + meta->setInt32('pfmt', audioFormat); +} + +audio_format_t AVNuUtils::getPCMFormat(const sp<AMessage> &format) { + int32_t pcmFormat = 0; + if (format->findInt32("pcm-format", &pcmFormat)) + return (audio_format_t)pcmFormat; + + int32_t bits = 16; + if (format->findInt32("bit-width", &bits)) { + if (bits == 8) + return AUDIO_FORMAT_PCM_8_BIT; + if (bits == 24) + return AUDIO_FORMAT_PCM_32_BIT; + if (bits == 32) + return AUDIO_FORMAT_PCM_FLOAT; + } + return AUDIO_FORMAT_PCM_16_BIT; +} + +void AVNuUtils::setPCMFormat(const sp<AMessage> &format, audio_format_t audioFormat) { + if (audio_is_linear_pcm(audioFormat)) + format->setInt32("pcm-format", audioFormat); +} + +void AVNuUtils::setSourcePCMFormat(const sp<MetaData> &) { + +} + +void AVNuUtils::setDecodedPCMFormat(const sp<AMessage> &) { + +} + +status_t AVNuUtils::convertToSinkFormatIfNeeded(const sp<ABuffer> &, sp<ABuffer> &, + audio_format_t /*sinkFormat*/, bool /*isOffload*/) { + return INVALID_OPERATION; +} + +void AVNuUtils::printFileName(int) {} + +void AVNuUtils::checkFormatChange(bool * /*formatChange*/, + const sp<ABuffer> & /*accessUnit*/) { +} + +void AVNuUtils::addFlagsInMeta(const sp<ABuffer> & /*buffer*/, + int32_t /*flags*/, bool /*isAudio*/) { +} + +uint32_t AVNuUtils::getUseSetBuffersFlag() { + return 0; +} + +bool AVNuUtils::canUseSetBuffers(const sp<MetaData> &/*Meta*/) { + return false; +} + +// ----- NO TRESSPASSING BEYOND THIS LINE ------ +AVNuUtils::AVNuUtils() {} + +AVNuUtils::~AVNuUtils() {} + +//static +AVNuUtils *AVNuUtils::sInst = + ExtensionsLoader<AVNuUtils>::createInstance("createExtendedNuUtils"); + +} //namespace android + diff --git a/media/libavextensions/stagefright/AVExtensions.h b/media/libavextensions/stagefright/AVExtensions.h new file mode 100644 index 0000000..937180f --- /dev/null +++ b/media/libavextensions/stagefright/AVExtensions.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _AV_EXTENSIONS_H_ +#define _AV_EXTENSIONS_H_ + +#include <media/stagefright/DataSource.h> +#include <common/AVExtensionsCommon.h> +#include <system/audio.h> +#include <camera/ICamera.h> +#include <media/mediarecorder.h> +#include <media/IOMX.h> + +namespace android { + +class AudioParameter; +class MetaData; +class MediaExtractor; +class MPEG4Writer; +struct ABuffer; +struct ACodec; +struct ALooper; +struct IMediaHTTPConnection; +struct MediaCodec; +struct MediaSource; +struct MediaHTTP; +struct NuCachedSource2; +class CameraParameters; +class MediaBuffer; +struct AudioSource; +class CameraSource; +class CameraSourceTimeLapse; +class ICamera; +class ICameraRecordingProxy; +class String16; +class IGraphicBufferProducer; +struct Size; + +/* + * Factory to create objects of base-classes in libstagefright + */ +struct AVFactory { + virtual sp<ACodec> createACodec(); + virtual MediaExtractor* createExtendedExtractor( + const sp<DataSource> &source, const char *mime, + const sp<AMessage> &meta, const uint32_t flags); + virtual sp<MediaExtractor> updateExtractor( + sp<MediaExtractor> ext, const sp<DataSource> &source, + const char *mime, const sp<AMessage> &meta, const uint32_t flags); + virtual sp<NuCachedSource2> createCachedSource( + const sp<DataSource> &source, + const char *cacheConfig = NULL, + bool disconnectAtHighwatermark = false); + virtual MediaHTTP* createMediaHTTP( + const sp<IMediaHTTPConnection> &conn); + + virtual AudioSource* createAudioSource( + audio_source_t inputSource, + const String16 &opPackageName, + uint32_t sampleRate, + uint32_t channels, + uint32_t outSampleRate = 0); + + virtual CameraSource *CreateCameraSourceFromCamera( + const sp<ICamera> &camera, + const sp<ICameraRecordingProxy> &proxy, + int32_t cameraId, + const String16& clientName, + uid_t clientUid, + Size videoSize, + int32_t frameRate, + const sp<IGraphicBufferProducer>& surface, + bool storeMetaDataInVideoBuffers = true); + + virtual CameraSourceTimeLapse *CreateCameraSourceTimeLapseFromCamera( + const sp<ICamera> &camera, + const sp<ICameraRecordingProxy> &proxy, + int32_t cameraId, + const String16& clientName, + uid_t clientUid, + Size videoSize, + int32_t videoFrameRate, + const sp<IGraphicBufferProducer>& surface, + int64_t timeBetweenFrameCaptureUs, + bool storeMetaDataInVideoBuffers = true); + // ----- NO TRESSPASSING BEYOND THIS LINE ------ + DECLARE_LOADABLE_SINGLETON(AVFactory); +}; + +/* + * Common delegate to the classes in libstagefright + */ +struct AVUtils { + + virtual status_t convertMetaDataToMessage( + const sp<MetaData> &meta, sp<AMessage> *format); + virtual DataSource::SnifferFunc getExtendedSniffer(); + virtual status_t mapMimeToAudioFormat( audio_format_t& format, const char* mime); + virtual status_t sendMetaDataToHal(const sp<MetaData>& meta, AudioParameter *param); + + virtual sp<MediaCodec> createCustomComponentByName(const sp<ALooper> &looper, + const char* mime, bool encoder, const sp<AMessage> &format); + virtual bool isEnhancedExtension(const char *extension); + + virtual bool is24bitPCMOffloadEnabled(); + virtual bool is16bitPCMOffloadEnabled(); + virtual int getAudioSampleBits(const sp<MetaData> &); + virtual int getAudioSampleBits(const sp<AMessage> &); + virtual void setPcmSampleBits(const sp<MetaData> &, int32_t /*bitWidth*/); + virtual void setPcmSampleBits(const sp<AMessage> &, int32_t /*bitWidth*/); + + virtual audio_format_t updateAudioFormat(audio_format_t audioFormat, + const sp<MetaData> &); + + virtual audio_format_t updateAudioFormat(audio_format_t audioFormat, + const sp<AMessage> &); + + virtual bool canOffloadAPE(const sp<MetaData> &meta); + + virtual int32_t getAudioMaxInputBufferSize(audio_format_t audioFormat, + const sp<AMessage> &); + + virtual bool mapAACProfileToAudioFormat(const sp<MetaData> &, + audio_format_t &, + uint64_t /*eAacProfile*/); + + virtual bool mapAACProfileToAudioFormat(const sp<AMessage> &, + audio_format_t &, + uint64_t /*eAacProfile*/); + + virtual void extractCustomCameraKeys( + const CameraParameters& /*params*/, sp<MetaData> &/*meta*/) {} + virtual void printFileName(int /*fd*/) {} + virtual void addDecodingTimesFromBatch(MediaBuffer * /*buf*/, + List<int64_t> &/*decodeTimeQueue*/) {} + + virtual bool useQCHWEncoder(const sp<AMessage> &, AString &) { return false; } + + virtual bool canDeferRelease(const sp<MetaData> &/*meta*/) { return false; } + virtual void setDeferRelease(sp<MetaData> &/*meta*/) {} + + struct HEVCMuxer { + + virtual bool reassembleHEVCCSD(const AString &mime, sp<ABuffer> csd0, sp<MetaData> &meta); + + virtual void writeHEVCFtypBox(MPEG4Writer *writer); + + virtual status_t makeHEVCCodecSpecificData(const uint8_t *data, + size_t size, void** codecSpecificData, + size_t *codecSpecificDataSize); + + virtual const char *getFourCCForMime(const char *mime); + + virtual void writeHvccBox(MPEG4Writer *writer, + void* codecSpecificData, size_t codecSpecificDataSize, + bool useNalLengthFour); + + virtual bool isVideoHEVC(const char* mime); + + virtual void getHEVCCodecSpecificDataFromInputFormatIfPossible( + sp<MetaData> meta, void **codecSpecificData, + size_t *codecSpecificDataSize, bool *gotAllCodecSpecificData); + + protected: + HEVCMuxer() {}; + virtual ~HEVCMuxer() {}; + friend struct AVUtils; + }; + + virtual inline HEVCMuxer& HEVCMuxerUtils() { + return mHEVCMuxer; + } + + virtual bool isAudioMuxFormatSupported(const char *mime); + virtual void cacheCaptureBuffers(sp<ICamera> camera, video_encoder encoder); + virtual const char *getCustomCodecsLocation(); + + virtual void setIntraPeriod( + int nPFrames, int nBFrames, const sp<IOMX> OMXhandle, + IOMX::node_id nodeID); + +private: + HEVCMuxer mHEVCMuxer; + // ----- NO TRESSPASSING BEYOND THIS LINE ------ + DECLARE_LOADABLE_SINGLETON(AVUtils); +}; + +} + +#endif // _AV_EXTENSIONS__H_ diff --git a/media/libavextensions/stagefright/AVFactory.cpp b/media/libavextensions/stagefright/AVFactory.cpp new file mode 100644 index 0000000..f6d5f53 --- /dev/null +++ b/media/libavextensions/stagefright/AVFactory.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "AVFactory" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaCodecList.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/ACodec.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaHTTP.h> +#include <media/stagefright/AudioSource.h> +#include <media/stagefright/CameraSource.h> +#include <media/stagefright/CameraSourceTimeLapse.h> +#include <camera/CameraParameters.h> + +#include "common/ExtensionsLoader.hpp" +#include "stagefright/AVExtensions.h" +#include "include/NuCachedSource2.h" + +namespace android { + +sp<ACodec> AVFactory::createACodec() { + return new ACodec; +} + +MediaExtractor* AVFactory::createExtendedExtractor( + const sp<DataSource> &, const char *, const sp<AMessage> &, + const uint32_t) { + return NULL; +} + +sp<MediaExtractor> AVFactory::updateExtractor( + sp<MediaExtractor> ext, const sp<DataSource> &, + const char *, const sp<AMessage> &, const uint32_t) { + return ext; +} + +sp<NuCachedSource2> AVFactory::createCachedSource( + const sp<DataSource> &source, + const char *cacheConfig, + bool disconnectAtHighwatermark) { + return NuCachedSource2::Create(source, cacheConfig, disconnectAtHighwatermark); +} + +MediaHTTP* AVFactory::createMediaHTTP( + const sp<IMediaHTTPConnection> &conn) { + return new MediaHTTP(conn); +} + +AudioSource* AVFactory::createAudioSource( + audio_source_t inputSource, + const String16 &opPackageName, + uint32_t sampleRate, + uint32_t channels, + uint32_t outSampleRate) { + return new AudioSource(inputSource, opPackageName, sampleRate, + channels, outSampleRate); +} + +CameraSource* AVFactory::CreateCameraSourceFromCamera( + const sp<ICamera> &camera, + const sp<ICameraRecordingProxy> &proxy, + int32_t cameraId, + const String16& clientName, + uid_t clientUid, + Size videoSize, + int32_t frameRate, + const sp<IGraphicBufferProducer>& surface, + bool storeMetaDataInVideoBuffers) { + return CameraSource::CreateFromCamera(camera, proxy, cameraId, + clientName, clientUid, videoSize, frameRate, surface, + storeMetaDataInVideoBuffers); +} + +CameraSourceTimeLapse* AVFactory::CreateCameraSourceTimeLapseFromCamera( + const sp<ICamera> &camera, + const sp<ICameraRecordingProxy> &proxy, + int32_t cameraId, + const String16& clientName, + uid_t clientUid, + Size videoSize, + int32_t videoFrameRate, + const sp<IGraphicBufferProducer>& surface, + int64_t timeBetweenFrameCaptureUs, + bool storeMetaDataInVideoBuffers) { + return CameraSourceTimeLapse::CreateFromCamera(camera, proxy, cameraId, + clientName, clientUid, videoSize, videoFrameRate, surface, + timeBetweenFrameCaptureUs, storeMetaDataInVideoBuffers); +} +// ----- NO TRESSPASSING BEYOND THIS LINE ------ +AVFactory::AVFactory() { +} + +AVFactory::~AVFactory() { +} + +//static +AVFactory *AVFactory::sInst = + ExtensionsLoader<AVFactory>::createInstance("createExtendedFactory"); + +} //namespace android + diff --git a/media/libavextensions/stagefright/AVUtils.cpp b/media/libavextensions/stagefright/AVUtils.cpp new file mode 100644 index 0000000..50c0f89 --- /dev/null +++ b/media/libavextensions/stagefright/AVUtils.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "AVUtils" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaCodecList.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/ACodec.h> +#include <media/stagefright/MediaCodec.h> + +#include "common/ExtensionsLoader.hpp" +#include "stagefright/AVExtensions.h" + +namespace android { + +status_t AVUtils::convertMetaDataToMessage( + const sp<MetaData> &, sp<AMessage> *) { + return OK; +} + +status_t AVUtils::mapMimeToAudioFormat( + audio_format_t&, const char* ) { + return OK; +} + +status_t AVUtils::sendMetaDataToHal( + const sp<MetaData>&, AudioParameter *){ + return OK; +} + +bool AVUtils::is24bitPCMOffloadEnabled() {return false;} +bool AVUtils::is16bitPCMOffloadEnabled() {return false;} + +int AVUtils::getAudioSampleBits(const sp<MetaData> &) { + return 16; +} + +int AVUtils::getAudioSampleBits(const sp<AMessage> &format) { + int32_t bits = 16; + format->findInt32("bit-width", &bits); + return bits; +} + +void AVUtils::setPcmSampleBits(const sp<AMessage> &, int32_t /*bitWidth*/) { +} + +void AVUtils::setPcmSampleBits(const sp<MetaData> &, int32_t /*bitWidth*/) { +} + +audio_format_t AVUtils::updateAudioFormat(audio_format_t audioFormat, + const sp<MetaData> &){ + return audioFormat; +} + +audio_format_t AVUtils::updateAudioFormat(audio_format_t audioFormat, + const sp<AMessage> &){ + return audioFormat; +} + +static bool dumbSniffer( + const sp<DataSource> &, String8 *, + float *, sp<AMessage> *) { + return false; +} + +DataSource::SnifferFunc AVUtils::getExtendedSniffer() { + return dumbSniffer; +} + +sp<MediaCodec> AVUtils::createCustomComponentByName( + const sp<ALooper> &, const char* , bool, const sp<AMessage> &) { + return NULL; +} + +bool AVUtils::canOffloadAPE(const sp<MetaData> &) { + return true; +} + +int32_t AVUtils::getAudioMaxInputBufferSize(audio_format_t, const sp<AMessage> &) { + return 0; +} + +bool AVUtils::mapAACProfileToAudioFormat(const sp<MetaData> &, audio_format_t &, + uint64_t /*eAacProfile*/) { + return false ; +} + +bool AVUtils::mapAACProfileToAudioFormat(const sp<AMessage> &, audio_format_t &, + uint64_t /*eAacProfile*/) { + return false ; +} + +bool AVUtils::isEnhancedExtension(const char *) { + return false; +} + +bool AVUtils::HEVCMuxer::reassembleHEVCCSD(const AString &/*mime*/, sp<ABuffer> /*csd0*/, sp<MetaData> &/*meta*/) { + return false; +} + +void AVUtils::HEVCMuxer::writeHEVCFtypBox(MPEG4Writer * /*writer*/) { + return; +} + +status_t AVUtils::HEVCMuxer::makeHEVCCodecSpecificData(const uint8_t * /*data*/, + size_t /*size*/, void ** /*codecSpecificData*/, + size_t * /*codecSpecificDataSize*/) { + return UNKNOWN_ERROR; +} + +const char *AVUtils::HEVCMuxer::getFourCCForMime(const char * /*mime*/) { + return NULL; +} + +void AVUtils::HEVCMuxer::writeHvccBox(MPEG4Writer * /*writer*/, + void * /*codecSpecificData*/, size_t /*codecSpecificDataSize*/, + bool /*useNalLengthFour*/) { + return; +} + +bool AVUtils::HEVCMuxer::isVideoHEVC(const char * /*mime*/) { + return false; +} + +void AVUtils::HEVCMuxer::getHEVCCodecSpecificDataFromInputFormatIfPossible( + sp<MetaData> /*meta*/, void ** /*codecSpecificData*/, + size_t * /*codecSpecificDataSize*/, bool * /*gotAllCodecSpecificData*/) { + return; +} + +bool AVUtils::isAudioMuxFormatSupported(const char *) { + return true; +} + +void AVUtils::cacheCaptureBuffers(sp<ICamera>, video_encoder) { + return; +} + +const char *AVUtils::getCustomCodecsLocation() { + return "/etc/media_codecs.xml"; +} + +void AVUtils::setIntraPeriod( + int, int, const sp<IOMX>, + IOMX::node_id) { + return; +} + +// ----- NO TRESSPASSING BEYOND THIS LINE ------ +AVUtils::AVUtils() {} + +AVUtils::~AVUtils() {} + +//static +AVUtils *AVUtils::sInst = + ExtensionsLoader<AVUtils>::createInstance("createExtendedUtils"); + +} //namespace android + diff --git a/media/libavextensions/stagefright/ExtendedMediaDefs.cpp b/media/libavextensions/stagefright/ExtendedMediaDefs.cpp new file mode 100644 index 0000000..cf2683c --- /dev/null +++ b/media/libavextensions/stagefright/ExtendedMediaDefs.cpp @@ -0,0 +1,68 @@ +/*Copyright (c) 2012 - 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <stagefright/ExtendedMediaDefs.h> + +namespace android { + +const char *MEDIA_MIMETYPE_AUDIO_EVRC = "audio/evrc"; +const char *MEDIA_MIMETYPE_VIDEO_WMV = "video/x-ms-wmv"; +const char *MEDIA_MIMETYPE_VIDEO_WMV_VC1 = "video/wvc1"; +const char *MEDIA_MIMETYPE_AUDIO_WMA = "audio/x-ms-wma"; +const char *MEDIA_MIMETYPE_AUDIO_WMA_PRO = "audio/x-ms-wma-pro"; +const char *MEDIA_MIMETYPE_AUDIO_WMA_LOSSLESS = "audio/x-ms-wma-lossless"; +const char *MEDIA_MIMETYPE_CONTAINER_ASF = "video/x-ms-asf"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX = "video/divx"; +const char *MEDIA_MIMETYPE_CONTAINER_AAC = "audio/aac"; +const char *MEDIA_MIMETYPE_CONTAINER_QCP = "audio/vnd.qcelp"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX311 = "video/divx311"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX4 = "video/divx4"; +const char *MEDIA_MIMETYPE_CONTAINER_MPEG2 = "video/mp2"; +const char *MEDIA_MIMETYPE_CONTAINER_3G2 = "video/3g2"; +const char *MEDIA_MIMETYPE_AUDIO_DTS = "audio/dts"; +const char *MEDIA_MIMETYPE_AUDIO_DTS_LBR = "audio/dts-lbr"; +const char *MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS = "audio/amr-wb-plus"; +const char *MEDIA_MIMETYPE_AUDIO_AIFF = "audio/x-aiff"; +const char *MEDIA_MIMETYPE_AUDIO_ALAC = "audio/alac"; +const char *MEDIA_MIMETYPE_AUDIO_APE = "audio/x-ape"; +const char *MEDIA_MIMETYPE_CONTAINER_QCAMR_NB = "audio/qc-amr"; +const char *MEDIA_MIMETYPE_CONTAINER_QCAMR_WB = "audio/qc-amr-wb"; +const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG = "audio/qc-mpeg"; +const char *MEDIA_MIMETYPE_CONTAINER_QCWAV = "audio/qc-wav"; +const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG2TS = "video/qc-mp2ts"; +const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG2PS = "video/qc-mp2ps"; +const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG4 = "video/qc-mp4"; +const char *MEDIA_MIMETYPE_CONTAINER_QCMATROSKA = "video/qc-matroska"; +const char *MEDIA_MIMETYPE_CONTAINER_QCOGG = "video/qc-ogg"; +const char *MEDIA_MIMETYPE_CONTAINER_QCFLV = "video/qc-flv"; +const char *MEDIA_MIMETYPE_VIDEO_VPX = "video/x-vnd.on2.vp8"; //backward compatibility +const char *MEDIA_MIMETYPE_CONTAINER_QTIFLAC = "audio/qti-flac"; +const char *MEDIA_MIMETYPE_VIDEO_MPEG4_DP = "video/mp4v-esdp"; + +} // namespace android diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c index 4a41037..18059b2 100644 --- a/media/libeffects/downmix/EffectDownmix.c +++ b/media/libeffects/downmix/EffectDownmix.c @@ -624,9 +624,12 @@ int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bo pDownmixer->apply_volume_correction = false; pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1 } else { - // when configuring the effect, do not allow a blank channel mask - if (pConfig->inputCfg.channels == 0) { - ALOGE("Downmix_Configure error: input channel mask can't be 0"); + // when configuring the effect, do not allow a blank or unsupported channel mask + if ((pConfig->inputCfg.channels == 0) || + (Downmix_foldGeneric(pConfig->inputCfg.channels, + NULL, NULL, 0, false) == false)) { + ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported", + pConfig->inputCfg.channels); return -EINVAL; } pDownmixer->input_channel_count = diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index af904a6..e01c414 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -29,6 +29,7 @@ #include "EffectBundle.h" #include "math.h" +#include <media/stagefright/foundation/ADebug.h> // effect_handle_t interface implementation for bass boost extern "C" const struct effect_interface_s gLvmEffectInterface; @@ -226,7 +227,7 @@ extern "C" int EffectCreate(const effect_uuid_t *uuid, pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE; pContext->pBundledContext->nOutputDevice = AUDIO_DEVICE_NONE; pContext->pBundledContext->nVirtualizerForcedDevice = AUDIO_DEVICE_NONE; - pContext->pBundledContext->NumberEffectsEnabled = 0; + pContext->pBundledContext->EffectsBitMap = 0; pContext->pBundledContext->NumberEffectsCalled = 0; pContext->pBundledContext->firstVolume = LVM_TRUE; pContext->pBundledContext->volume = 0; @@ -366,28 +367,28 @@ extern "C" int EffectRelease(effect_handle_t handle){ ALOGV("\tEffectRelease LVM_BASS_BOOST Clearing global intstantiated flag"); pSessionContext->bBassInstantiated = LVM_FALSE; if(pContext->pBundledContext->SamplesToExitCountBb > 0){ - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_BASS_BOOST); } pContext->pBundledContext->SamplesToExitCountBb = 0; } else if(pContext->EffectType == LVM_VIRTUALIZER) { ALOGV("\tEffectRelease LVM_VIRTUALIZER Clearing global intstantiated flag"); pSessionContext->bVirtualizerInstantiated = LVM_FALSE; if(pContext->pBundledContext->SamplesToExitCountVirt > 0){ - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_VIRTUALIZER); } pContext->pBundledContext->SamplesToExitCountVirt = 0; } else if(pContext->EffectType == LVM_EQUALIZER) { ALOGV("\tEffectRelease LVM_EQUALIZER Clearing global intstantiated flag"); pSessionContext->bEqualizerInstantiated =LVM_FALSE; if(pContext->pBundledContext->SamplesToExitCountEq > 0){ - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_EQUALIZER); } pContext->pBundledContext->SamplesToExitCountEq = 0; } else if(pContext->EffectType == LVM_VOLUME) { ALOGV("\tEffectRelease LVM_VOLUME Clearing global intstantiated flag"); pSessionContext->bVolumeInstantiated = LVM_FALSE; if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE){ - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_VOLUME); } } else { ALOGV("\tLVM_ERROR : EffectRelease : Unsupported effect\n\n\n\n\n\n\n"); @@ -563,6 +564,7 @@ int LvmBundle_init(EffectContext *pContext){ for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){ if (MemTab.Region[i].Size != 0){ MemTab.Region[i].pBaseAddress = malloc(MemTab.Region[i].Size); + CHECK(MemTab.Region[i].pBaseAddress != NULL); if (MemTab.Region[i].pBaseAddress == LVM_NULL){ ALOGV("\tLVM_ERROR :LvmBundle_init CreateInstance Failed to allocate %" PRIu32 @@ -729,6 +731,7 @@ int LvmBundle_process(LVM_INT16 *pIn, } pContext->pBundledContext->workBuffer = (LVM_INT16 *)malloc(frameCount * sizeof(LVM_INT16) * 2); + CHECK(pContext->pBundledContext->workBuffer != NULL); pContext->pBundledContext->frameCount = frameCount; } pOutTmp = pContext->pBundledContext->workBuffer; @@ -2753,7 +2756,7 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) return -EINVAL; } if(pContext->pBundledContext->SamplesToExitCountBb <= 0){ - pContext->pBundledContext->NumberEffectsEnabled++; + pContext->pBundledContext->EffectsBitMap |= (1 << LVM_BASS_BOOST); } pContext->pBundledContext->SamplesToExitCountBb = (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); @@ -2766,7 +2769,7 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) return -EINVAL; } if(pContext->pBundledContext->SamplesToExitCountEq <= 0){ - pContext->pBundledContext->NumberEffectsEnabled++; + pContext->pBundledContext->EffectsBitMap |= (1 << LVM_EQUALIZER); } pContext->pBundledContext->SamplesToExitCountEq = (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); @@ -2778,7 +2781,7 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) return -EINVAL; } if(pContext->pBundledContext->SamplesToExitCountVirt <= 0){ - pContext->pBundledContext->NumberEffectsEnabled++; + pContext->pBundledContext->EffectsBitMap |= (1 << LVM_VIRTUALIZER); } pContext->pBundledContext->SamplesToExitCountVirt = (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); @@ -2790,7 +2793,7 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) ALOGV("\tEffect_setEnabled() LVM_VOLUME is already enabled"); return -EINVAL; } - pContext->pBundledContext->NumberEffectsEnabled++; + pContext->pBundledContext->EffectsBitMap |= (1 << LVM_VOLUME); pContext->pBundledContext->bVolumeEnabled = LVM_TRUE; break; default: @@ -2807,6 +2810,9 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) ALOGV("\tEffect_setEnabled() LVM_BASS_BOOST is already disabled"); return -EINVAL; } + if(pContext->pBundledContext->SamplesToExitCountBb <= 0) { + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_BASS_BOOST); + } pContext->pBundledContext->bBassEnabled = LVM_FALSE; break; case LVM_EQUALIZER: @@ -2814,6 +2820,9 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) ALOGV("\tEffect_setEnabled() LVM_EQUALIZER is already disabled"); return -EINVAL; } + if(pContext->pBundledContext->SamplesToExitCountEq <= 0) { + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_EQUALIZER); + } pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE; break; case LVM_VIRTUALIZER: @@ -2821,6 +2830,9 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) ALOGV("\tEffect_setEnabled() LVM_VIRTUALIZER is already disabled"); return -EINVAL; } + if(pContext->pBundledContext->SamplesToExitCountVirt <= 0) { + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_VIRTUALIZER); + } pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE; break; case LVM_VOLUME: @@ -2828,6 +2840,7 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) ALOGV("\tEffect_setEnabled() LVM_VOLUME is already disabled"); return -EINVAL; } + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_VOLUME); pContext->pBundledContext->bVolumeEnabled = LVM_FALSE; break; default: @@ -2877,7 +2890,7 @@ int Effect_process(effect_handle_t self, LVM_INT16 *out = (LVM_INT16 *)outBuffer->raw; //ALOGV("\tEffect_process Start : Enabled = %d Called = %d (%8d %8d %8d)", -//pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled, +//popcount(pContext->pBundledContext->EffectsBitMap), pContext->pBundledContext->NumberEffectsCalled, // pContext->pBundledContext->SamplesToExitCountBb, // pContext->pBundledContext->SamplesToExitCountVirt, // pContext->pBundledContext->SamplesToExitCountEq); @@ -2911,7 +2924,7 @@ int Effect_process(effect_handle_t self, } if(pContext->pBundledContext->SamplesToExitCountBb <= 0) { status = -ENODATA; - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_BASS_BOOST); ALOGV("\tEffect_process() this is the last frame for LVM_BASS_BOOST"); } } @@ -2919,7 +2932,7 @@ int Effect_process(effect_handle_t self, (pContext->EffectType == LVM_VOLUME)){ //ALOGV("\tEffect_process() LVM_VOLUME Effect is not enabled"); status = -ENODATA; - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_VOLUME); } if ((pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE)&& (pContext->EffectType == LVM_EQUALIZER)){ @@ -2931,7 +2944,7 @@ int Effect_process(effect_handle_t self, } if(pContext->pBundledContext->SamplesToExitCountEq <= 0) { status = -ENODATA; - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_EQUALIZER); ALOGV("\tEffect_process() this is the last frame for LVM_EQUALIZER"); } } @@ -2945,7 +2958,7 @@ int Effect_process(effect_handle_t self, } if(pContext->pBundledContext->SamplesToExitCountVirt <= 0) { status = -ENODATA; - pContext->pBundledContext->NumberEffectsEnabled--; + pContext->pBundledContext->EffectsBitMap &= ~(1 << LVM_VIRTUALIZER); ALOGV("\tEffect_process() this is the last frame for LVM_VIRTUALIZER"); } } @@ -2955,9 +2968,9 @@ int Effect_process(effect_handle_t self, } if(pContext->pBundledContext->NumberEffectsCalled == - pContext->pBundledContext->NumberEffectsEnabled){ + popcount(pContext->pBundledContext->EffectsBitMap)){ //ALOGV("\tEffect_process Calling process with %d effects enabled, %d called: Effect %d", - //pContext->pBundledContext->NumberEffectsEnabled, + //popcount(pContext->pBundledContext->EffectsBitMap), //pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType); if(status == -ENODATA){ @@ -2976,7 +2989,7 @@ int Effect_process(effect_handle_t self, } } else { //ALOGV("\tEffect_process Not Calling process with %d effects enabled, %d called: Effect %d", - //pContext->pBundledContext->NumberEffectsEnabled, + //popcount(pContext->pBundledContext->EffectsBitMap), //pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType); // 2 is for stereo input if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { @@ -3028,9 +3041,9 @@ int Effect_command(effect_handle_t self, // called the number of effect called could be greater // pContext->pBundledContext->NumberEffectsCalled = 0; - //ALOGV("\tEffect_command NumberEffectsCalled = %d, NumberEffectsEnabled = %d", - // pContext->pBundledContext->NumberEffectsCalled, - // pContext->pBundledContext->NumberEffectsEnabled); + //ALOGV("\tEffect_command: Enabled = %d Called = %d", + // popcount(pContext->pBundledContext->EffectsBitMap), + // pContext->pBundledContext->NumberEffectsCalled); switch (cmdCode){ case EFFECT_CMD_INIT: @@ -3311,7 +3324,7 @@ int Effect_command(effect_handle_t self, (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){ ALOGV("\tEFFECT_CMD_SET_DEVICE device is invalid for LVM_BASS_BOOST %d", *(int32_t *)pCmdData); - ALOGV("\tEFFECT_CMD_SET_DEVICE temporary disable LVM_BAS_BOOST"); + ALOGV("\tEFFECT_CMD_SET_DEVICE temporary disable LVM_BASS_BOOST"); // If a device doesnt support bassboost the effect must be temporarily disabled // the effect must still report its original state as this can only be changed diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h index 9459b87..5fa5668 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h @@ -75,8 +75,8 @@ struct BundledEffectContext{ bool bVirtualizerTempDisabled; /* Flag for effect to be re-enabled */ audio_devices_t nOutputDevice; /* Output device for the effect */ audio_devices_t nVirtualizerForcedDevice; /* Forced device virtualization mode*/ - int NumberEffectsEnabled; /* Effects in this session */ int NumberEffectsCalled; /* Effects called so far */ + uint32_t EffectsBitMap; /* Effects enable bit mask */ bool firstVolume; /* No smoothing on first Vol change */ // Saved parameters for each effect */ // Bass Boost diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index a3c3d3c..74e4eb1 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -69,12 +69,21 @@ LOCAL_SRC_FILES:= \ StringArray.cpp \ AudioPolicy.cpp + +#QTI Resampler +ifeq ($(call is-vendor-board-platform,QCOM), true) +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_RESAMPLER)), true) +LOCAL_CFLAGS += -DQTI_RESAMPLER +endif +endif +#QTI Resampler + LOCAL_SHARED_LIBRARIES := \ libui liblog libcutils libutils libbinder libsonivox libicuuc libicui18n libexpat \ libcamera_client libstagefright_foundation \ libgui libdl libaudioutils libnbaio -LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper +LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper libavmediaextentions LOCAL_MODULE:= libmedia @@ -84,6 +93,7 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/frameworks/av/include/media/ \ $(TOP)/frameworks/av/media/libstagefright \ + $(TOP)/frameworks/av/media/libavextensions \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp index 8c8cf45..535f459 100644 --- a/media/libmedia/AudioParameter.cpp +++ b/media/libmedia/AudioParameter.cpp @@ -32,6 +32,7 @@ const char * const AudioParameter::keyChannels = AUDIO_PARAMETER_STREAM_CHANNELS const char * const AudioParameter::keyFrameCount = AUDIO_PARAMETER_STREAM_FRAME_COUNT; const char * const AudioParameter::keyInputSource = AUDIO_PARAMETER_STREAM_INPUT_SOURCE; const char * const AudioParameter::keyScreenState = AUDIO_PARAMETER_KEY_SCREEN_STATE; +const char * const AudioParameter::keyDevShutdown = AUDIO_PARAMETER_KEY_DEV_SHUTDOWN; AudioParameter::AudioParameter(const String8& keyValuePairs) { diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 011b31f..6dc52cd 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -26,6 +26,7 @@ #include <utils/Log.h> #include <private/media/AudioTrackShared.h> #include <media/IAudioFlinger.h> +#include "SeempLog.h" #define WAIT_PERIOD_MS 10 @@ -293,6 +294,7 @@ status_t AudioRecord::set( status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) { ALOGV("start, sync event %d trigger session %d", event, triggerSession); + SEEMPLOG_RECORD(89,""); AutoMutex lock(mLock); if (mActive) { @@ -340,6 +342,7 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) void AudioRecord::stop() { + SEEMPLOG_RECORD(90,""); AutoMutex lock(mLock); if (!mActive) { return; diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ab3d66a..946c118 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -30,6 +30,8 @@ #include <media/IAudioFlinger.h> #include <media/AudioPolicyHelper.h> #include <media/AudioResamplerPublic.h> +#include "media/AVMediaExtensions.h" +#include <cutils/properties.h> #define WAIT_PERIOD_MS 10 #define WAIT_STREAM_END_TIMEOUT_SEC 120 @@ -167,7 +169,8 @@ AudioTrack::AudioTrack() mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mPausedPosition(0), - mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), + mPlaybackRateSet(false) { mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN; mAttributes.usage = AUDIO_USAGE_UNKNOWN; @@ -197,7 +200,8 @@ AudioTrack::AudioTrack( mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mPausedPosition(0), - mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), + mPlaybackRateSet(false) { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, @@ -227,7 +231,8 @@ AudioTrack::AudioTrack( mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mPausedPosition(0), - mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), + mPlaybackRateSet(false) { mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, @@ -541,6 +546,12 @@ status_t AudioTrack::start() // force refresh of remaining frames by processAudioBuffer() as last // write before stop could be partial. mRefreshRemaining = true; + + // for static track, clear the old flags when start from stopped state + if (mSharedBuffer != 0) + android_atomic_and( + ~(CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), + &mCblk->mFlags); } mNewPosition = mPosition + mUpdatePeriod; int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags); @@ -706,7 +717,7 @@ status_t AudioTrack::setVolume(float left, float right) mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right))); - if (isOffloaded_l()) { + if (isOffloaded_l() && mAudioTrack != NULL) { mAudioTrack->signal(); } return NO_ERROR; @@ -846,6 +857,15 @@ status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) //set effective rates mProxy->setPlaybackRate(playbackRateTemp); mProxy->setSampleRate(effectiveRate); // FIXME: not quite "atomic" with setPlaybackRate + + // fallback out of Direct PCM if setPlaybackRate is called on PCM track + if (property_get_bool("audio.offload.track.enable", false) && + (mFormat == AUDIO_FORMAT_PCM_16_BIT) && (mOffloadInfo == NULL) && + (mFlags == AUDIO_OUTPUT_FLAG_NONE)) { + mPlaybackRateSet = true; + android_atomic_or(CBLK_INVALID, &mCblk->mFlags); + } + return NO_ERROR; } @@ -1001,10 +1021,18 @@ status_t AudioTrack::getPosition(uint32_t *position) return NO_ERROR; } + if (AVMediaUtils::get()->AudioTrackIsPcmOffloaded(mFormat) && + AVMediaUtils::get()->AudioTrackGetPosition(this, position) == NO_ERROR) { + return NO_ERROR; + } + if (mOutput != AUDIO_IO_HANDLE_NONE) { uint32_t halFrames; // actually unused - (void) AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames); - // FIXME: on getRenderPosition() error, we return OK with frame position 0. + status_t status = AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames); + if (status != NO_ERROR) { + ALOGW("failed to getRenderPosition for offload session status %d", status); + return INVALID_OPERATION; + } } // FIXME: dspFrames may not be zero in (mState == STATE_STOPPED || mState == STATE_FLUSHED) // due to hardware latency. We leave this behavior for now. @@ -1129,11 +1157,16 @@ status_t AudioTrack::createTrack_l() audio_stream_type_t streamType = mStreamType; audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL; - status_t status; - status = AudioSystem::getOutputForAttr(attr, &output, + audio_offload_info_t tOffloadInfo = AUDIO_INFO_INITIALIZER; + if (mPlaybackRateSet == true && mOffloadInfo == NULL && mFormat == AUDIO_FORMAT_PCM_16_BIT) { + mOffloadInfo = &tOffloadInfo; + } + status_t status = AudioSystem::getOutputForAttr(attr, &output, (audio_session_t)mSessionId, &streamType, mClientUid, mSampleRate, mFormat, mChannelMask, mFlags, mSelectedDeviceId, mOffloadInfo); + //reset offload info if forced + mOffloadInfo = (mOffloadInfo == &tOffloadInfo) ? NULL : mOffloadInfo; if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) { ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u, format %#x," @@ -1203,6 +1236,7 @@ status_t AudioTrack::createTrack_l() frameCount = mSharedBuffer->size(); } else if (frameCount == 0) { frameCount = mAfFrameCount; + frameCount = AVMediaUtils::get()->AudioTrackGetOffloadFrameCount(frameCount); } if (mNotificationFramesAct != frameCount) { mNotificationFramesAct = frameCount; @@ -1838,6 +1872,36 @@ nsecs_t AudioTrack::processAudioBuffer() // get anchor time to account for callbacks. const nsecs_t timeBeforeCallbacks = systemTime(); + // perform callbacks while unlocked + if (newUnderrun) { + mCbf(EVENT_UNDERRUN, mUserData, NULL); + } + while (loopCountNotifications > 0) { + mCbf(EVENT_LOOP_END, mUserData, NULL); + --loopCountNotifications; + } + if (flags & CBLK_BUFFER_END) { + mCbf(EVENT_BUFFER_END, mUserData, NULL); + } + if (markerReached) { + mCbf(EVENT_MARKER, mUserData, &markerPosition); + } + while (newPosCount > 0) { + size_t temp = newPosition; + mCbf(EVENT_NEW_POS, mUserData, &temp); + newPosition += updatePeriod; + newPosCount--; + } + + if (mObservedSequence != sequence) { + mObservedSequence = sequence; + mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL); + // for offloaded tracks, just wait for the upper layers to recreate the track + if (isOffloadedOrDirect()) { + return NS_INACTIVE; + } + } + if (waitStreamEnd) { // FIXME: Instead of blocking in proxy->waitStreamEndDone(), Callback thread // should wait on proxy futex and handle CBLK_STREAM_END_DONE within this function @@ -1852,6 +1916,12 @@ nsecs_t AudioTrack::processAudioBuffer() case NO_ERROR: case DEAD_OBJECT: case TIMED_OUT: + if (isOffloaded_l()) { + if (mCblk->mFlags & (CBLK_INVALID)){ + // will trigger EVENT_STREAM_END in next iteration + return 0; + } + } mCbf(EVENT_STREAM_END, mUserData, NULL); { AutoMutex lock(mLock); @@ -1872,36 +1942,6 @@ nsecs_t AudioTrack::processAudioBuffer() return 0; } - // perform callbacks while unlocked - if (newUnderrun) { - mCbf(EVENT_UNDERRUN, mUserData, NULL); - } - while (loopCountNotifications > 0) { - mCbf(EVENT_LOOP_END, mUserData, NULL); - --loopCountNotifications; - } - if (flags & CBLK_BUFFER_END) { - mCbf(EVENT_BUFFER_END, mUserData, NULL); - } - if (markerReached) { - mCbf(EVENT_MARKER, mUserData, &markerPosition); - } - while (newPosCount > 0) { - size_t temp = newPosition; - mCbf(EVENT_NEW_POS, mUserData, &temp); - newPosition += updatePeriod; - newPosCount--; - } - - if (mObservedSequence != sequence) { - mObservedSequence = sequence; - mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL); - // for offloaded tracks, just wait for the upper layers to recreate the track - if (isOffloadedOrDirect()) { - return NS_INACTIVE; - } - } - // if inactive, then don't run me again until re-started if (!active) { return NS_INACTIVE; @@ -2232,14 +2272,21 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) } } - // The presented frame count must always lag behind the consumed frame count. - // To avoid a race, read the presented frames first. This ensures that presented <= consumed. - status_t status = mAudioTrack->getTimestamp(timestamp); - if (status != NO_ERROR) { - ALOGV_IF(status != WOULD_BLOCK, "getTimestamp error:%#x", status); - return status; + status_t status = UNKNOWN_ERROR; + //do not call Timestamp if its PCM offloaded + if (!AVMediaUtils::get()->AudioTrackIsPcmOffloaded(mFormat)) { + // The presented frame count must always lag behind the consumed frame count. + // To avoid a race, read the presented frames first. This ensures that presented <= consumed. + + status = mAudioTrack->getTimestamp(timestamp); + if (status != NO_ERROR) { + ALOGV_IF(status != WOULD_BLOCK, "getTimestamp error:%#x", status); + return status; + } + } - if (isOffloadedOrDirect_l()) { + + if (isOffloadedOrDirect_l() && !AVMediaUtils::get()->AudioTrackIsPcmOffloaded(mFormat)) { if (isOffloaded_l() && (mState == STATE_PAUSED || mState == STATE_PAUSED_STOPPING)) { // use cached paused position in case another offloaded track is running. timestamp.mPosition = mPausedPosition; @@ -2297,6 +2344,11 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) } } else { // Update the mapping between local consumed (mPosition) and server consumed (mServer) + + if (AVMediaUtils::get()->AudioTrackGetTimestamp(this, ×tamp) == NO_ERROR) { + return NO_ERROR; + } + (void) updateAndGetPosition_l(); // Server consumed (mServer) and presented both use the same server time base, // and server consumed is always >= presented. diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp index a398ff7..5d822cf 100644 --- a/media/libmedia/ICrypto.cpp +++ b/media/libmedia/ICrypto.cpp @@ -24,6 +24,7 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> +#include <media/AVMediaExtensions.h> namespace android { @@ -136,6 +137,7 @@ struct BpCrypto : public BpInterface<ICrypto> { if (secure) { data.writeInt64(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(dstPtr))); + AVMediaUtils::get()->writeCustomData(&data, dstPtr); } remote()->transact(DECRYPT, data, &reply); @@ -235,6 +237,7 @@ status_t BnCrypto::onTransact( if (opaqueSize > 0) { opaqueData = malloc(opaqueSize); + CHECK(opaqueData != NULL); data.read(opaqueData, opaqueSize); } @@ -296,6 +299,7 @@ status_t BnCrypto::onTransact( void *secureBufferId, *dstPtr; if (secure) { secureBufferId = reinterpret_cast<void *>(static_cast<uintptr_t>(data.readInt64())); + AVMediaUtils::get()->readCustomData(&data, &secureBufferId); } else { dstPtr = calloc(1, totalSize); } @@ -348,6 +352,8 @@ status_t BnCrypto::onTransact( } free(dstPtr); dstPtr = NULL; + } else { + AVMediaUtils::get()->closeFileDescriptor(secureBufferId); } delete[] subSamples; diff --git a/media/libmedia/IEffectClient.cpp b/media/libmedia/IEffectClient.cpp index 1322e72..531f767 100644 --- a/media/libmedia/IEffectClient.cpp +++ b/media/libmedia/IEffectClient.cpp @@ -22,6 +22,8 @@ #include <sys/types.h> #include <media/IEffectClient.h> +#include <media/stagefright/foundation/ADebug.h> + namespace android { enum { @@ -117,12 +119,14 @@ status_t BnEffectClient::onTransact( char *cmd = NULL; if (cmdSize) { cmd = (char *)malloc(cmdSize); + CHECK(cmd != NULL); data.read(cmd, cmdSize); } uint32_t replySize = data.readInt32(); char *resp = NULL; if (replySize) { resp = (char *)malloc(replySize); + CHECK(resp != NULL); data.read(resp, replySize); } commandExecuted(cmdCode, cmdSize, cmd, replySize, resp); diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 5423c2a..5356494 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -22,6 +22,7 @@ #include <binder/Parcel.h> #include <media/IOMX.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/AVMediaExtensions.h> namespace android { @@ -472,6 +473,7 @@ public: *buffer = (buffer_id)reply.readInt32(); *buffer_data = (void *)reply.readInt64(); + AVMediaUtils::get()->readCustomData(&reply, buffer_data); return err; } @@ -980,6 +982,7 @@ status_t BnOMX::onTransact( if (err == OK) { reply->writeInt32((int32_t)buffer); reply->writeInt64((uintptr_t)buffer_data); + AVMediaUtils::get()->writeCustomData(reply, buffer_data); } return NO_ERROR; diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index c5790fb..87ec309 100644..100755 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -1,4 +1,6 @@ /* +** Copyright (c) 2014, The Linux Foundation. All rights reserved. +** Not a Contribution. ** ** Copyright 2010, The Android Open Source Project ** @@ -37,7 +39,8 @@ MediaProfiles *MediaProfiles::sInstance = NULL; const MediaProfiles::NameToTagMap MediaProfiles::sVideoEncoderNameMap[] = { {"h263", VIDEO_ENCODER_H263}, {"h264", VIDEO_ENCODER_H264}, - {"m4v", VIDEO_ENCODER_MPEG_4_SP} + {"m4v", VIDEO_ENCODER_MPEG_4_SP}, + {"h265", VIDEO_ENCODER_H265} }; const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = { @@ -45,7 +48,8 @@ const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = { {"amrwb", AUDIO_ENCODER_AMR_WB}, {"aac", AUDIO_ENCODER_AAC}, {"heaac", AUDIO_ENCODER_HE_AAC}, - {"aaceld", AUDIO_ENCODER_AAC_ELD} + {"aaceld", AUDIO_ENCODER_AAC_ELD}, + {"lpcm", AUDIO_ENCODER_LPCM}, }; const MediaProfiles::NameToTagMap MediaProfiles::sFileFormatMap[] = { @@ -88,6 +92,15 @@ const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = { {"highspeed720p", CAMCORDER_QUALITY_HIGH_SPEED_720P}, {"highspeed1080p", CAMCORDER_QUALITY_HIGH_SPEED_1080P}, {"highspeed2160p", CAMCORDER_QUALITY_HIGH_SPEED_2160P}, + + // Vendor-specific profiles + {"vga", CAMCORDER_QUALITY_VGA}, + {"4kdci", CAMCORDER_QUALITY_4KDCI}, + {"timelapsevga", CAMCORDER_QUALITY_TIME_LAPSE_VGA}, + {"timelapse4kdci", CAMCORDER_QUALITY_TIME_LAPSE_4KDCI}, + {"highspeedcif", CAMCORDER_QUALITY_HIGH_SPEED_CIF}, + {"highspeedvga", CAMCORDER_QUALITY_HIGH_SPEED_VGA}, + {"highspeed4kdci", CAMCORDER_QUALITY_HIGH_SPEED_4KDCI}, }; #if LOG_NDEBUG @@ -126,6 +139,8 @@ MediaProfiles::logVideoEncoderCap(const MediaProfiles::VideoEncoderCap& cap UNUS ALOGV("frame width: min = %d and max = %d", cap.mMinFrameWidth, cap.mMaxFrameWidth); ALOGV("frame height: min = %d and max = %d", cap.mMinFrameHeight, cap.mMaxFrameHeight); ALOGV("frame rate: min = %d and max = %d", cap.mMinFrameRate, cap.mMaxFrameRate); + ALOGV("max HFR width: = %d max HFR height: = %d", cap.mMaxHFRFrameWidth, cap.mMaxHFRFrameHeight); + ALOGV("max HFR mode: = %d", cap.mMaxHFRMode); } /*static*/ void @@ -261,10 +276,23 @@ MediaProfiles::createVideoEncoderCap(const char **atts) const int codec = findTagForName(sVideoEncoderNameMap, nMappings, atts[1]); CHECK(codec != -1); + int maxHFRWidth = 0, maxHFRHeight = 0, maxHFRMode = 0; + // Check if there are enough (start through end) attributes in the + // 0-terminated list, to include our additional HFR params. Then check + // if each of those match the expected names. + if (atts[20] && atts[21] && !strcmp("maxHFRFrameWidth", atts[20]) && + atts[22] && atts[23] && !strcmp("maxHFRFrameHeight", atts[22]) && + atts[24] && atts[25] && !strcmp("maxHFRMode", atts[24])) { + maxHFRWidth = atoi(atts[21]); + maxHFRHeight = atoi(atts[23]); + maxHFRMode = atoi(atts[25]); + } + MediaProfiles::VideoEncoderCap *cap = new MediaProfiles::VideoEncoderCap(static_cast<video_encoder>(codec), atoi(atts[5]), atoi(atts[7]), atoi(atts[9]), atoi(atts[11]), atoi(atts[13]), - atoi(atts[15]), atoi(atts[17]), atoi(atts[19])); + atoi(atts[15]), atoi(atts[17]), atoi(atts[19]), + maxHFRWidth, maxHFRHeight, maxHFRMode); logVideoEncoderCap(*cap); return cap; } @@ -423,8 +451,10 @@ MediaProfiles::startElementHandler(void *userData, const char *name, const char } static bool isCamcorderProfile(camcorder_quality quality) { - return quality >= CAMCORDER_QUALITY_LIST_START && - quality <= CAMCORDER_QUALITY_LIST_END; + return (quality >= CAMCORDER_QUALITY_LIST_START && + quality <= CAMCORDER_QUALITY_LIST_END) || + (quality >= CAMCORDER_QUALITY_VENDOR_START && + quality <= CAMCORDER_QUALITY_VENDOR_END); } static bool isTimelapseProfile(camcorder_quality quality) { @@ -616,14 +646,14 @@ MediaProfiles::getInstance() MediaProfiles::createDefaultH263VideoEncoderCap() { return new MediaProfiles::VideoEncoderCap( - VIDEO_ENCODER_H263, 192000, 420000, 176, 352, 144, 288, 1, 20); + VIDEO_ENCODER_H263, 192000, 420000, 176, 352, 144, 288, 1, 20, 0, 0, 0); } /*static*/ MediaProfiles::VideoEncoderCap* MediaProfiles::createDefaultM4vVideoEncoderCap() { return new MediaProfiles::VideoEncoderCap( - VIDEO_ENCODER_MPEG_4_SP, 192000, 420000, 176, 352, 144, 288, 1, 20); + VIDEO_ENCODER_MPEG_4_SP, 192000, 420000, 176, 352, 144, 288, 1, 20, 0, 0, 0); } @@ -778,6 +808,8 @@ MediaProfiles::createDefaultCamcorderProfiles(MediaProfiles *profiles) MediaProfiles::createDefaultAudioEncoders(MediaProfiles *profiles) { profiles->mAudioEncoders.add(createDefaultAmrNBEncoderCap()); + profiles->mAudioEncoders.add(createDefaultAacEncoderCap()); + profiles->mAudioEncoders.add(createDefaultLpcmEncoderCap()); } /*static*/ void @@ -812,6 +844,20 @@ MediaProfiles::createDefaultAmrNBEncoderCap() AUDIO_ENCODER_AMR_NB, 5525, 12200, 8000, 8000, 1, 1); } +/*static*/ MediaProfiles::AudioEncoderCap* +MediaProfiles::createDefaultAacEncoderCap() +{ + return new MediaProfiles::AudioEncoderCap( + AUDIO_ENCODER_AAC, 64000, 156000, 8000, 48000, 1, 2); +} + +/*static*/ MediaProfiles::AudioEncoderCap* +MediaProfiles::createDefaultLpcmEncoderCap() +{ + return new MediaProfiles::AudioEncoderCap( + AUDIO_ENCODER_LPCM, 768000, 4608000, 8000, 48000, 1, 6); +} + /*static*/ void MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles) { @@ -927,6 +973,9 @@ int MediaProfiles::getVideoEncoderParamByName(const char *name, video_encoder co if (!strcmp("enc.vid.bps.max", name)) return mVideoEncoders[index]->mMaxBitRate; if (!strcmp("enc.vid.fps.min", name)) return mVideoEncoders[index]->mMinFrameRate; if (!strcmp("enc.vid.fps.max", name)) return mVideoEncoders[index]->mMaxFrameRate; + if (!strcmp("enc.vid.hfr.width.max", name)) return mVideoEncoders[index]->mMaxHFRFrameWidth; + if (!strcmp("enc.vid.hfr.height.max", name)) return mVideoEncoders[index]->mMaxHFRFrameHeight; + if (!strcmp("enc.vid.hfr.mode.max", name)) return mVideoEncoders[index]->mMaxHFRMode; ALOGE("The given video encoder param name %s is not found", name); return -1; diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp index dcbb769..dac0a9e 100644 --- a/media/libmedia/MediaScanner.cpp +++ b/media/libmedia/MediaScanner.cpp @@ -24,6 +24,8 @@ #include <sys/stat.h> #include <dirent.h> +#include <media/stagefright/foundation/ADebug.h> + namespace android { MediaScanner::MediaScanner() @@ -240,6 +242,7 @@ MediaScanResult MediaScanner::doProcessDirectoryEntry( MediaAlbumArt *MediaAlbumArt::clone() { size_t byte_size = this->size() + sizeof(MediaAlbumArt); MediaAlbumArt *result = reinterpret_cast<MediaAlbumArt *>(malloc(byte_size)); + CHECK(result != NULL); result->mSize = this->size(); memcpy(&result->mData[0], &this->mData[0], this->size()); return result; @@ -253,6 +256,7 @@ void MediaAlbumArt::init(MediaAlbumArt *instance, int32_t dataSize, const void * MediaAlbumArt *MediaAlbumArt::fromData(int32_t dataSize, const void* data) { size_t byte_size = sizeof(MediaAlbumArt) + dataSize; MediaAlbumArt *result = reinterpret_cast<MediaAlbumArt *>(malloc(byte_size)); + CHECK(result != NULL); init(result, dataSize, data); return result; } diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 6da5348..53b229e 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -804,6 +804,12 @@ ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool ALOGE("Unable to marshal AudioFlinger"); return; } + + if (mSamplingRate > 48000) { + ALOGW("mSamplingRate %d . limit to 48k", mSamplingRate); + mSamplingRate = 48000; + } + mThreadCanCallJava = threadCanCallJava; mStreamType = streamType; mVolume = volume; @@ -1046,7 +1052,7 @@ bool ToneGenerator::initAudioTrack() { ALOGV("Create Track: %p", mpAudioTrack.get()); mpAudioTrack->set(mStreamType, - 0, // sampleRate + mSamplingRate, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO, 0, // frameCount diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 502ab2d..6c0606c 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -395,6 +395,10 @@ bool MediaPlayer::isPlaying() ALOGE("internal/external state mismatch corrected"); mCurrentState = MEDIA_PLAYER_STARTED; } + if ((mCurrentState & MEDIA_PLAYER_PLAYBACK_COMPLETE) && temp) { + ALOGE("internal/external state mismatch corrected"); + mCurrentState = MEDIA_PLAYER_STARTED; + } return temp; } ALOGV("isPlaying: no active player"); diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 4d1b587..c4c5b47 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -18,7 +18,6 @@ LOCAL_SRC_FILES:= \ MetadataRetrieverClient.cpp \ RemoteDisplay.cpp \ SharedLibrary.cpp \ - StagefrightPlayer.cpp \ StagefrightRecorder.cpp \ TestPlayerStub.cpp \ @@ -46,6 +45,9 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_nuplayer \ libstagefright_rtsp \ +LOCAL_WHOLE_STATIC_LIBRARIES := \ + libavmediaserviceextensions \ + LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/media/libstagefright/include \ $(TOP)/frameworks/av/media/libstagefright/rtsp \ @@ -53,13 +55,14 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/media/libstagefright/webm \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/external/tremolo/Tremolo \ + $(TOP)/frameworks/av/media/libavextensions \ LOCAL_CFLAGS += -Werror -Wno-error=deprecated-declarations -Wall LOCAL_CLANG := true LOCAL_MODULE:= libmediaplayerservice -LOCAL_32_BIT_ONLY := true +#LOCAL_32_BIT_ONLY := true include $(BUILD_SHARED_LIBRARY) diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index d5d12f7..f0afc5a 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -31,8 +31,8 @@ #include "MediaPlayerFactory.h" #include "TestPlayerStub.h" -#include "StagefrightPlayer.h" #include "nuplayer/NuPlayerDriver.h" +#include <mediaplayerservice/AVMediaServiceExtensions.h> namespace android { @@ -64,12 +64,6 @@ status_t MediaPlayerFactory::registerFactory_l(IFactory* factory, } static player_type getDefaultPlayerType() { - char value[PROPERTY_VALUE_MAX]; - if (property_get("media.stagefright.use-awesome", value, NULL) - && (!strcmp("1", value) || !strcasecmp("true", value))) { - return STAGEFRIGHT_PLAYER; - } - return NU_PLAYER; } @@ -87,7 +81,7 @@ void MediaPlayerFactory::unregisterFactory(player_type type) { #define GET_PLAYER_TYPE_IMPL(a...) \ Mutex::Autolock lock_(&sLock); \ \ - player_type ret = STAGEFRIGHT_PLAYER; \ + player_type ret = NU_PLAYER; \ float bestScore = 0.0; \ \ for (size_t i = 0; i < sFactoryMap.size(); ++i) { \ @@ -176,63 +170,6 @@ sp<MediaPlayerBase> MediaPlayerFactory::createPlayer( * * *****************************************************************************/ -class StagefrightPlayerFactory : - public MediaPlayerFactory::IFactory { - public: - virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, - int fd, - int64_t offset, - int64_t length, - float /*curScore*/) { - if (legacyDrm()) { - sp<DataSource> source = new FileSource(dup(fd), offset, length); - String8 mimeType; - float confidence; - if (SniffWVM(source, &mimeType, &confidence, NULL /* format */)) { - return 1.0; - } - } - - if (getDefaultPlayerType() == STAGEFRIGHT_PLAYER) { - char buf[20]; - lseek(fd, offset, SEEK_SET); - read(fd, buf, sizeof(buf)); - lseek(fd, offset, SEEK_SET); - - uint32_t ident = *((uint32_t*)buf); - - // Ogg vorbis? - if (ident == 0x5367674f) // 'OggS' - return 1.0; - } - - return 0.0; - } - - virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, - const char* url, - float /*curScore*/) { - if (legacyDrm() && !strncasecmp("widevine://", url, 11)) { - return 1.0; - } - return 0.0; - } - - virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) { - ALOGV(" create StagefrightPlayer"); - return new StagefrightPlayer(); - } - private: - bool legacyDrm() { - char value[PROPERTY_VALUE_MAX]; - if (property_get("persist.sys.media.legacy-drm", value, NULL) - && (!strcmp("1", value) || !strcasecmp("true", value))) { - return true; - } - return false; - } -}; - class NuPlayerFactory : public MediaPlayerFactory::IFactory { public: virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, @@ -305,14 +242,20 @@ class TestPlayerFactory : public MediaPlayerFactory::IFactory { }; void MediaPlayerFactory::registerBuiltinFactories() { + + MediaPlayerFactory::IFactory* pCustomFactory = NULL; Mutex::Autolock lock_(&sLock); if (sInitComplete) return; - registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER); registerFactory_l(new NuPlayerFactory(), NU_PLAYER); registerFactory_l(new TestPlayerFactory(), TEST_PLAYER); + AVMediaServiceUtils::get()->getDashPlayerFactory(pCustomFactory, DASH_PLAYER); + if(pCustomFactory != NULL) { + ALOGV("Registering DASH_PLAYER"); + registerFactory_l(pCustomFactory, DASH_PLAYER); + } sInitComplete = true; } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index bcfd83a..6e104a4 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -21,6 +21,7 @@ #define LOG_TAG "MediaPlayerService" #include <utils/Log.h> +#include <inttypes.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> @@ -73,7 +74,6 @@ #include "MediaPlayerFactory.h" #include "TestPlayerStub.h" -#include "StagefrightPlayer.h" #include "nuplayer/NuPlayerDriver.h" #include <OMX.h> @@ -148,7 +148,7 @@ bool unmarshallFilter(const Parcel& p, if (p.dataAvail() < size) { - ALOGE("Filter too short expected %d but got %d", size, p.dataAvail()); + ALOGE("Filter too short expected %zu but got %zu", size, p.dataAvail()); *status = NOT_ENOUGH_DATA; return false; } @@ -733,7 +733,7 @@ status_t MediaPlayerService::Client::setDataSource( status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length) { - ALOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length); + ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, length); struct stat sb; int ret = fstat(fd, &sb); if (ret != 0) { @@ -741,11 +741,11 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64 return UNKNOWN_ERROR; } - ALOGV("st_dev = %llu", static_cast<uint64_t>(sb.st_dev)); + ALOGV("st_dev = %" PRIu64 "", static_cast<uint64_t>(sb.st_dev)); ALOGV("st_mode = %u", sb.st_mode); ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid)); ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid)); - ALOGV("st_size = %llu", sb.st_size); + ALOGV("st_size = %" PRId64 "", sb.st_size); if (offset >= sb.st_size) { ALOGE("offset error"); @@ -754,7 +754,7 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64 } if (offset + length > sb.st_size) { length = sb.st_size - offset; - ALOGV("calculated length = %lld", length); + ALOGV("calculated length = %" PRId64 "", length); } player_type playerType = MediaPlayerFactory::getPlayerType(this, @@ -1249,8 +1249,17 @@ void MediaPlayerService::Client::notify( if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) { if (client->mAudioOutput != NULL) client->mAudioOutput->switchToNextOutput(); - client->mNextClient->start(); - client->mNextClient->mClient->notify(MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj); + ALOGD("gapless:current track played back"); + ALOGD("gapless:try to do a gapless switch to next track"); + status_t ret; + ret = client->mNextClient->start(); + if (ret == NO_ERROR) { + client->mNextClient->mClient->notify(MEDIA_INFO, + MEDIA_INFO_STARTED_AS_NEXT, 0, obj); + } else { + client->mClient->notify(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN , 0, obj); + ALOGW("gapless:start playback for next track failed"); + } } } @@ -1366,6 +1375,7 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid, int pid, } setMinBufferCount(); + mBitWidth = 16; } MediaPlayerService::AudioOutput::~AudioOutput() @@ -1622,6 +1632,15 @@ status_t MediaPlayerService::AudioOutput::open( } else if (mRecycledTrack->format() != format) { reuse = false; } + + if (bothOffloaded) { + if (mBitWidth != offloadInfo->bit_width) { + ALOGV("output bit width differs %d v/s %d", + mBitWidth, offloadInfo->bit_width); + reuse = false; + } + } + } else { ALOGV("no track available to recycle"); } @@ -1702,7 +1721,7 @@ status_t MediaPlayerService::AudioOutput::open( if (!bothOffloaded) { if (mRecycledTrack->frameCount() != t->frameCount()) { - ALOGV("framecount differs: %u/%u frames", + ALOGV("framecount differs: %zu/%zu frames", mRecycledTrack->frameCount(), t->frameCount()); reuse = false; } @@ -1737,6 +1756,13 @@ status_t MediaPlayerService::AudioOutput::open( mFlags = flags; mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate); mFrameSize = t->frameSize(); + + if (offloadInfo) { + mBitWidth = offloadInfo->bit_width; + } else { + mBitWidth = 16; + } + uint32_t pos; if (t->getPosition(&pos) == OK) { mBytesWritten = uint64_t(pos) * mFrameSize; @@ -1837,6 +1863,7 @@ void MediaPlayerService::AudioOutput::switchToNextOutput() { mNextOutput->mBytesWritten = mBytesWritten; mNextOutput->mFlags = mFlags; mNextOutput->mFrameSize = mFrameSize; + mNextOutput->mBitWidth = mBitWidth; close_l(); mCallbackData = NULL; // destruction handled by mNextOutput } else { @@ -2101,6 +2128,7 @@ bool CallbackThread::threadLoop() { if (mBuffer == NULL) { mBufferSize = sink->bufferSize(); mBuffer = malloc(mBufferSize); + CHECK(mBuffer != NULL); } size_t actualSize = diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 60d4617..748b25f 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -160,6 +160,7 @@ class MediaPlayerService : public BnMediaPlayerService // static variables below not protected by mutex static bool mIsOnEmulator; static int mMinBufferCount; // 12 for emulator; otherwise 4 + uint16_t mBitWidth; // CallbackData is what is passed to the AudioTrack as the "user" data. // We need to be able to target this to a different Output on the fly, diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index f761dec..6f242e5 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "MediaRecorderService" #include <utils/Log.h> +#include <inttypes.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> @@ -39,6 +40,7 @@ #include "StagefrightRecorder.h" #include <gui/IGraphicBufferProducer.h> +#include "mediaplayerservice/AVMediaServiceExtensions.h" namespace android { @@ -166,7 +168,7 @@ status_t MediaRecorderClient::setAudioEncoder(int ae) status_t MediaRecorderClient::setOutputFile(int fd, int64_t offset, int64_t length) { - ALOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length); + ALOGV("setOutputFile(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); Mutex::Autolock lock(mLock); if (mRecorder == NULL) { ALOGE("recorder is not initialized"); @@ -305,7 +307,7 @@ MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, { ALOGV("Client constructor"); mPid = pid; - mRecorder = new StagefrightRecorder(opPackageName); + mRecorder = AVMediaServiceFactory::get()->createStagefrightRecorder(opPackageName); mMediaPlayerService = service; } diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index a5a1fa5..894a855 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "MetadataRetrieverClient" #include <utils/Log.h> +#include <inttypes.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> @@ -84,7 +85,6 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) { sp<MediaMetadataRetrieverBase> p; switch (playerType) { - case STAGEFRIGHT_PLAYER: case NU_PLAYER: { p = new StagefrightMetadataRetriever; @@ -133,7 +133,7 @@ status_t MetadataRetrieverClient::setDataSource( status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t length) { - ALOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length); + ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, length); Mutex::Autolock lock(mLock); struct stat sb; int ret = fstat(fd, &sb); @@ -141,20 +141,20 @@ status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno)); return BAD_VALUE; } - ALOGV("st_dev = %llu", static_cast<uint64_t>(sb.st_dev)); + ALOGV("st_dev = %" PRIu64 "", static_cast<uint64_t>(sb.st_dev)); ALOGV("st_mode = %u", sb.st_mode); ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid)); ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid)); - ALOGV("st_size = %llu", sb.st_size); + ALOGV("st_size = %" PRIu64 "", sb.st_size); if (offset >= sb.st_size) { - ALOGE("offset (%lld) bigger than file size (%llu)", offset, sb.st_size); + ALOGE("offset (%" PRId64 ") bigger than file size (%" PRIu64 ")", offset, sb.st_size); ::close(fd); return BAD_VALUE; } if (offset + length > sb.st_size) { length = sb.st_size - offset; - ALOGV("calculated length = %lld", length); + ALOGV("calculated length = %" PRId64 "", length); } player_type playerType = @@ -195,7 +195,7 @@ Mutex MetadataRetrieverClient::sLock; sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option) { - ALOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option); + ALOGV("getFrameAtTime: time(%" PRId64 " us) option(%d)", timeUs, option); Mutex::Autolock lock(mLock); Mutex::Autolock glock(sLock); mThumbnail.clear(); @@ -217,7 +217,7 @@ sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option) } mThumbnail = new MemoryBase(heap, 0, size); if (mThumbnail == NULL) { - ALOGE("not enough memory for VideoFrame size=%u", size); + ALOGE("not enough memory for VideoFrame size=%zu", size); delete frame; return NULL; } @@ -258,7 +258,7 @@ sp<IMemory> MetadataRetrieverClient::extractAlbumArt() } mAlbumArt = new MemoryBase(heap, 0, size); if (mAlbumArt == NULL) { - ALOGE("not enough memory for MediaAlbumArt size=%u", size); + ALOGE("not enough memory for MediaAlbumArt size=%zu", size); delete albumArt; return NULL; } diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp deleted file mode 100644 index 3fedd9b..0000000 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ /dev/null @@ -1,230 +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. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "StagefrightPlayer" -#include <utils/Log.h> - -#include "StagefrightPlayer.h" - -#include "AwesomePlayer.h" - -#include <media/Metadata.h> -#include <media/stagefright/MediaExtractor.h> - -namespace android { - -StagefrightPlayer::StagefrightPlayer() - : mPlayer(new AwesomePlayer) { - ALOGV("StagefrightPlayer"); - - mPlayer->setListener(this); -} - -StagefrightPlayer::~StagefrightPlayer() { - ALOGV("~StagefrightPlayer"); - reset(); - - delete mPlayer; - mPlayer = NULL; -} - -status_t StagefrightPlayer::initCheck() { - ALOGV("initCheck"); - return OK; -} - -status_t StagefrightPlayer::setUID(uid_t uid) { - mPlayer->setUID(uid); - - return OK; -} - -status_t StagefrightPlayer::setDataSource( - const sp<IMediaHTTPService> &httpService, - const char *url, - const KeyedVector<String8, String8> *headers) { - return mPlayer->setDataSource(httpService, url, headers); -} - -// Warning: The filedescriptor passed into this method will only be valid until -// the method returns, if you want to keep it, dup it! -status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) { - ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); - return mPlayer->setDataSource(dup(fd), offset, length); -} - -status_t StagefrightPlayer::setDataSource(const sp<IStreamSource> &source) { - return mPlayer->setDataSource(source); -} - -status_t StagefrightPlayer::setVideoSurfaceTexture( - const sp<IGraphicBufferProducer> &bufferProducer) { - ALOGV("setVideoSurfaceTexture"); - - return mPlayer->setSurfaceTexture(bufferProducer); -} - -status_t StagefrightPlayer::prepare() { - return mPlayer->prepare(); -} - -status_t StagefrightPlayer::prepareAsync() { - return mPlayer->prepareAsync(); -} - -status_t StagefrightPlayer::start() { - ALOGV("start"); - - return mPlayer->play(); -} - -status_t StagefrightPlayer::stop() { - ALOGV("stop"); - - return pause(); // what's the difference? -} - -status_t StagefrightPlayer::pause() { - ALOGV("pause"); - - return mPlayer->pause(); -} - -bool StagefrightPlayer::isPlaying() { - ALOGV("isPlaying"); - return mPlayer->isPlaying(); -} - -status_t StagefrightPlayer::seekTo(int msec) { - ALOGV("seekTo %.2f secs", msec / 1E3); - - status_t err = mPlayer->seekTo((int64_t)msec * 1000); - - return err; -} - -status_t StagefrightPlayer::getCurrentPosition(int *msec) { - ALOGV("getCurrentPosition"); - - int64_t positionUs; - status_t err = mPlayer->getPosition(&positionUs); - - if (err != OK) { - return err; - } - - *msec = (positionUs + 500) / 1000; - - return OK; -} - -status_t StagefrightPlayer::getDuration(int *msec) { - ALOGV("getDuration"); - - int64_t durationUs; - status_t err = mPlayer->getDuration(&durationUs); - - if (err != OK) { - *msec = 0; - return OK; - } - - *msec = (durationUs + 500) / 1000; - - return OK; -} - -status_t StagefrightPlayer::reset() { - ALOGV("reset"); - - mPlayer->reset(); - - return OK; -} - -status_t StagefrightPlayer::setLooping(int loop) { - ALOGV("setLooping"); - - return mPlayer->setLooping(loop); -} - -player_type StagefrightPlayer::playerType() { - ALOGV("playerType"); - return STAGEFRIGHT_PLAYER; -} - -status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) { - ALOGV("invoke()"); - return mPlayer->invoke(request, reply); -} - -void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) { - MediaPlayerInterface::setAudioSink(audioSink); - - mPlayer->setAudioSink(audioSink); -} - -status_t StagefrightPlayer::setParameter(int key, const Parcel &request) { - ALOGV("setParameter(key=%d)", key); - return mPlayer->setParameter(key, request); -} - -status_t StagefrightPlayer::getParameter(int key, Parcel *reply) { - ALOGV("getParameter"); - return mPlayer->getParameter(key, reply); -} - -status_t StagefrightPlayer::setPlaybackSettings(const AudioPlaybackRate &rate) { - return mPlayer->setPlaybackSettings(rate); -} - -status_t StagefrightPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) { - return mPlayer->getPlaybackSettings(rate); -} - -status_t StagefrightPlayer::getMetadata( - const media::Metadata::Filter& /* ids */, Parcel *records) { - using media::Metadata; - - uint32_t flags = mPlayer->flags(); - - Metadata metadata(records); - - metadata.appendBool( - Metadata::kPauseAvailable, - flags & MediaExtractor::CAN_PAUSE); - - metadata.appendBool( - Metadata::kSeekBackwardAvailable, - flags & MediaExtractor::CAN_SEEK_BACKWARD); - - metadata.appendBool( - Metadata::kSeekForwardAvailable, - flags & MediaExtractor::CAN_SEEK_FORWARD); - - metadata.appendBool( - Metadata::kSeekAvailable, - flags & MediaExtractor::CAN_SEEK); - - return OK; -} - -status_t StagefrightPlayer::dump(int fd, const Vector<String16> &args) const { - return mPlayer->dump(fd, args); -} - -} // namespace android diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h deleted file mode 100644 index 96013df..0000000 --- a/media/libmediaplayerservice/StagefrightPlayer.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -** -** Copyright 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 ANDROID_STAGEFRIGHTPLAYER_H -#define ANDROID_STAGEFRIGHTPLAYER_H - -#include <media/MediaPlayerInterface.h> - -namespace android { - -struct AwesomePlayer; - -class StagefrightPlayer : public MediaPlayerInterface { -public: - StagefrightPlayer(); - virtual ~StagefrightPlayer(); - - virtual status_t initCheck(); - - virtual status_t setUID(uid_t uid); - - virtual status_t setDataSource( - const sp<IMediaHTTPService> &httpService, - const char *url, - const KeyedVector<String8, String8> *headers); - - virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - - virtual status_t setDataSource(const sp<IStreamSource> &source); - - virtual status_t setVideoSurfaceTexture( - const sp<IGraphicBufferProducer> &bufferProducer); - virtual status_t prepare(); - virtual status_t prepareAsync(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t pause(); - virtual bool isPlaying(); - virtual status_t seekTo(int msec); - virtual status_t getCurrentPosition(int *msec); - virtual status_t getDuration(int *msec); - virtual status_t reset(); - virtual status_t setLooping(int loop); - virtual player_type playerType(); - virtual status_t invoke(const Parcel &request, Parcel *reply); - virtual void setAudioSink(const sp<AudioSink> &audioSink); - virtual status_t setParameter(int key, const Parcel &request); - virtual status_t getParameter(int key, Parcel *reply); - virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate); - virtual status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */); - - virtual status_t getMetadata( - const media::Metadata::Filter& ids, Parcel *records); - - virtual status_t dump(int fd, const Vector<String16> &args) const; - -private: - AwesomePlayer *mPlayer; - - StagefrightPlayer(const StagefrightPlayer &); - StagefrightPlayer &operator=(const StagefrightPlayer &); -}; - -} // namespace android - -#endif // ANDROID_STAGEFRIGHTPLAYER_H diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index e521fae..80d5ac2 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -19,6 +19,7 @@ #include <inttypes.h> #include <utils/Log.h> +#include <inttypes.h> #include "WebmWriter.h" #include "StagefrightRecorder.h" @@ -55,9 +56,12 @@ #include <system/audio.h> #include "ARTPWriter.h" +#include <stagefright/AVExtensions.h> namespace android { +static const int64_t kMax32BitFileSize = 0x00ffffffffLL; // 4GB + // To collect the encoder usage for the battery app static void addBatteryData(uint32_t params) { sp<IBinder> binder = @@ -249,7 +253,7 @@ status_t StagefrightRecorder::setInputSurface( } status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { - ALOGV("setOutputFile: %d, %lld, %lld", fd, offset, length); + ALOGV("setOutputFile: %d, %" PRId64 ", %" PRId64 "", fd, offset, length); // These don't make any sense, do they? CHECK_EQ(offset, 0ll); CHECK_EQ(length, 0ll); @@ -364,7 +368,7 @@ status_t StagefrightRecorder::setParamAudioSamplingRate(int32_t sampleRate) { status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) { ALOGV("setParamAudioNumberOfChannels: %d", channels); - if (channels <= 0 || channels >= 3) { + if (channels <= 0 || channels >= 7) { ALOGE("Invalid number of audio channels: %d", channels); return BAD_VALUE; } @@ -416,42 +420,46 @@ status_t StagefrightRecorder::setParamVideoRotation(int32_t degrees) { } status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { - ALOGV("setParamMaxFileDurationUs: %lld us", timeUs); + ALOGV("setParamMaxFileDurationUs: %" PRId64 " us", timeUs); // This is meant for backward compatibility for MediaRecorder.java if (timeUs <= 0) { - ALOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs); + ALOGW("Max file duration is not positive: %" PRId64 " us. Disabling duration limit.", timeUs); timeUs = 0; // Disable the duration limit for zero or negative values. } else if (timeUs <= 100000LL) { // XXX: 100 milli-seconds - ALOGE("Max file duration is too short: %lld us", timeUs); + ALOGE("Max file duration is too short: %" PRId64 " us", timeUs); return BAD_VALUE; } if (timeUs <= 15 * 1000000LL) { - ALOGW("Target duration (%lld us) too short to be respected", timeUs); + ALOGW("Target duration (%" PRId64 " us) too short to be respected", timeUs); } mMaxFileDurationUs = timeUs; return OK; } status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) { - ALOGV("setParamMaxFileSizeBytes: %lld bytes", bytes); + ALOGV("setParamMaxFileSizeBytes: %" PRId64 " bytes", bytes); // This is meant for backward compatibility for MediaRecorder.java if (bytes <= 0) { - ALOGW("Max file size is not positive: %lld bytes. " + ALOGW("Max file size is not positive: %" PRId64 " bytes. " "Disabling file size limit.", bytes); bytes = 0; // Disable the file size limit for zero or negative values. } else if (bytes <= 1024) { // XXX: 1 kB - ALOGE("Max file size is too small: %lld bytes", bytes); + ALOGE("Max file size is too small: %" PRId64 " bytes", bytes); return BAD_VALUE; } if (bytes <= 100 * 1024) { - ALOGW("Target file size (%lld bytes) is too small to be respected", bytes); + ALOGW("Target file size (%" PRId64 " bytes) is too small to be respected", bytes); } mMaxFileSizeBytes = bytes; + + // If requested size is >4GB, force 64-bit offsets + mUse64BitFileOffset |= (bytes >= kMax32BitFileSize); + return OK; } @@ -500,9 +508,9 @@ status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) { } status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) { - ALOGV("setParamTrackTimeStatus: %lld", timeDurationUs); + ALOGV("setParamTrackTimeStatus: %" PRId64 "", timeDurationUs); if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms? - ALOGE("Tracking time duration too short: %lld us", timeDurationUs); + ALOGE("Tracking time duration too short: %" PRId64 " us", timeDurationUs); return BAD_VALUE; } mTrackEveryTimeDurationUs = timeDurationUs; @@ -581,11 +589,11 @@ status_t StagefrightRecorder::setParamCaptureFpsEnable(int32_t captureFpsEnable) status_t StagefrightRecorder::setParamCaptureFps(float fps) { ALOGV("setParamCaptureFps: %.2f", fps); - int64_t timeUs = (int64_t) (1000000.0 / fps + 0.5f); + int64_t timeUs = (int64_t) (1000000.0f / fps + 0.5f); // Not allowing time more than a day if (timeUs <= 0 || timeUs > 86400*1E6) { - ALOGE("Time between frame capture (%lld) is out of range [0, 1 Day]", timeUs); + ALOGE("Time between frame capture (%" PRId64 ") is out of range [0, 1 Day]", timeUs); return BAD_VALUE; } @@ -814,8 +822,10 @@ status_t StagefrightRecorder::prepareInternal() { break; default: - ALOGE("Unsupported output file format: %d", mOutputFormat); - status = UNKNOWN_ERROR; + if (handleCustomRecording() != OK) { + ALOGE("Unsupported output file format: %d", mOutputFormat); + status = UNKNOWN_ERROR; + } break; } @@ -879,8 +889,10 @@ status_t StagefrightRecorder::start() { default: { - ALOGE("Unsupported output file format: %d", mOutputFormat); - status = UNKNOWN_ERROR; + if (handleCustomOutputFormats() != OK) { + ALOGE("Unsupported output file format: %d", mOutputFormat); + status = UNKNOWN_ERROR; + } break; } } @@ -927,13 +939,12 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { } } - sp<AudioSource> audioSource = - new AudioSource( - mAudioSource, - mOpPackageName, - sourceSampleRate, - mAudioChannels, - mSampleRate); + sp<AudioSource> audioSource = AVFactory::get()->createAudioSource( + mAudioSource, + mOpPackageName, + sourceSampleRate, + mAudioChannels, + mSampleRate); status_t err = audioSource->initCheck(); @@ -965,8 +976,10 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { break; default: - ALOGE("Unknown audio encoder: %d", mAudioEncoder); - return NULL; + if (handleCustomAudioSource(format) != OK) { + ALOGE("Unknown audio encoder: %d", mAudioEncoder); + return NULL; + } } int32_t maxInputSize; @@ -984,6 +997,13 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { sp<MediaSource> audioEncoder = MediaCodecSource::Create(mLooper, format, audioSource); + // If encoder could not be created (as in LPCM), then + // use the AudioSource directly as the MediaSource. + if (audioEncoder == NULL && + mAudioEncoder == AUDIO_ENCODER_LPCM) { + ALOGD("No encoder is needed for linear PCM format"); + audioEncoder = audioSource; + } mAudioSourceNode = audioSource; if (audioEncoder == NULL) { @@ -1440,22 +1460,23 @@ status_t StagefrightRecorder::setupCameraSource( videoSize.height = mVideoHeight; if (mCaptureFpsEnable) { if (mTimeBetweenCaptureUs < 0) { - ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld", + ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %" PRId64 "", mTimeBetweenCaptureUs); return BAD_VALUE; } - mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera( + mCameraSourceTimeLapse = AVFactory::get()->CreateCameraSourceTimeLapseFromCamera( mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, videoSize, mFrameRate, mPreviewSurface, mTimeBetweenCaptureUs); *cameraSource = mCameraSourceTimeLapse; } else { - *cameraSource = CameraSource::CreateFromCamera( + *cameraSource = AVFactory::get()->CreateCameraSourceFromCamera( mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, videoSize, mFrameRate, mPreviewSurface); } + AVUtils::get()->cacheCaptureBuffers(mCamera, mVideoEncoder); mCamera.clear(); mCameraProxy.clear(); if (*cameraSource == NULL) { @@ -1487,6 +1508,11 @@ status_t StagefrightRecorder::setupCameraSource( return OK; } +bool StagefrightRecorder::setCustomVideoEncoderMime(const video_encoder /*videoEncoder*/, + sp<AMessage> /*format*/) { + return false; +} + status_t StagefrightRecorder::setupVideoEncoder( sp<MediaSource> cameraSource, sp<MediaSource> *source) { @@ -1512,6 +1538,9 @@ status_t StagefrightRecorder::setupVideoEncoder( break; default: + if (setCustomVideoEncoderMime(mVideoEncoder, format)) { + break; + } CHECK(!"Should not be here, unsupported video encoding."); break; } @@ -1541,7 +1570,7 @@ status_t StagefrightRecorder::setupVideoEncoder( // set up time lapse/slow motion for surface source if (mCaptureFpsEnable) { if (mTimeBetweenCaptureUs <= 0) { - ALOGE("Invalid mTimeBetweenCaptureUs value: %lld", + ALOGE("Invalid mTimeBetweenCaptureUs value: %" PRId64 "", mTimeBetweenCaptureUs); return BAD_VALUE; } @@ -1549,6 +1578,8 @@ status_t StagefrightRecorder::setupVideoEncoder( } } + setupCustomVideoEncoderParams(cameraSource, format); + format->setInt32("bitrate", mVideoBitRate); format->setInt32("frame-rate", mFrameRate); format->setInt32("i-frame-interval", mIFramesIntervalSec); @@ -1614,8 +1645,10 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { break; default: - ALOGE("Unsupported audio encoder: %d", mAudioEncoder); - return UNKNOWN_ERROR; + if (handleCustomAudioEncoder() != OK) { + ALOGE("Unsupported audio encoder: %d", mAudioEncoder); + return UNKNOWN_ERROR; + } } sp<MediaSource> audioEncoder = createAudioSource(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index da00bc7..d2ff62d 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -39,6 +39,7 @@ class IGraphicBufferConsumer; class IGraphicBufferProducer; class SurfaceMediaSource; struct ALooper; +struct AMessage; struct StagefrightRecorder : public MediaRecorderBase { StagefrightRecorder(const String16 &opPackageName); @@ -70,7 +71,7 @@ struct StagefrightRecorder : public MediaRecorderBase { // Querying a SurfaceMediaSourcer virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() const; -private: +protected: sp<ICamera> mCamera; sp<ICameraRecordingProxy> mCameraProxy; sp<IGraphicBufferProducer> mPreviewSurface; @@ -131,7 +132,7 @@ private: static const int kMaxHighSpeedFps = 1000; - status_t prepareInternal(); + virtual status_t prepareInternal(); status_t setupMPEG4orWEBMRecording(); void setupMPEG4orWEBMMetaData(sp<MetaData> *meta); status_t setupAMRRecording(); @@ -139,8 +140,8 @@ private: status_t setupRawAudioRecording(); status_t setupRTPRecording(); status_t setupMPEG2TSRecording(); - sp<MediaSource> createAudioSource(); - status_t checkVideoEncoderCapabilities(); + virtual sp<MediaSource> createAudioSource(); + virtual status_t checkVideoEncoderCapabilities(); status_t checkAudioEncoderCapabilities(); // Generic MediaSource set-up. Returns the appropriate // source (CameraSource or SurfaceMediaSource) @@ -148,10 +149,13 @@ private: status_t setupMediaSource(sp<MediaSource> *mediaSource); status_t setupCameraSource(sp<CameraSource> *cameraSource); status_t setupAudioEncoder(const sp<MediaWriter>& writer); - status_t setupVideoEncoder(sp<MediaSource> cameraSource, sp<MediaSource> *source); + virtual status_t setupVideoEncoder(sp<MediaSource> cameraSource, sp<MediaSource> *source); + virtual void setupCustomVideoEncoderParams(sp<MediaSource> /*cameraSource*/, + sp<AMessage> &/*format*/) {} + virtual bool setCustomVideoEncoderMime(const video_encoder videoEncoder, sp<AMessage> format); // Encoding parameter handling utilities - status_t setParameter(const String8 &key, const String8 &value); + virtual status_t setParameter(const String8 &key, const String8 &value); status_t setParamAudioEncodingBitRate(int32_t bitRate); status_t setParamAudioNumberOfChannels(int32_t channles); status_t setParamAudioSamplingRate(int32_t sampleRate); @@ -181,7 +185,11 @@ private: void clipAudioSampleRate(); void clipNumberOfAudioChannels(); void setDefaultProfileIfNecessary(); - void setDefaultVideoEncoderIfNecessary(); + virtual void setDefaultVideoEncoderIfNecessary(); + virtual status_t handleCustomOutputFormats() {return UNKNOWN_ERROR;} + virtual status_t handleCustomRecording() {return UNKNOWN_ERROR;} + virtual status_t handleCustomAudioSource(sp<AMessage> /*format*/) {return UNKNOWN_ERROR;} + virtual status_t handleCustomAudioEncoder() {return UNKNOWN_ERROR;} StagefrightRecorder(const StagefrightRecorder &); diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk index cd20837..6729cd5 100644 --- a/media/libmediaplayerservice/nuplayer/Android.mk +++ b/media/libmediaplayerservice/nuplayer/Android.mk @@ -23,7 +23,9 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/media/libstagefright/rtsp \ $(TOP)/frameworks/av/media/libstagefright/timedtext \ $(TOP)/frameworks/av/media/libmediaplayerservice \ - $(TOP)/frameworks/native/include/media/openmax + $(TOP)/frameworks/native/include/media/openmax \ + $(TOP)/frameworks/av/media/libavextensions \ + $(TOP)/frameworks/av/include/media \ LOCAL_CFLAGS += -Werror -Wall diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index b3eb5fd..7be4d23 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -37,6 +37,7 @@ #include "../../libstagefright/include/NuCachedSource2.h" #include "../../libstagefright/include/WVMExtractor.h" #include "../../libstagefright/include/HTTPBase.h" +#include "mediaplayerservice/AVNuExtensions.h" namespace android { @@ -60,6 +61,7 @@ NuPlayer::GenericSource::GenericSource( mAudioIsVorbis(false), mIsWidevine(false), mIsSecure(false), + mUseSetBuffers(false), mIsStreaming(false), mUIDValid(uidValid), mUID(uid), @@ -172,7 +174,8 @@ status_t NuPlayer::GenericSource::initFromDataSource() { extractor = mWVMExtractor; } else { extractor = MediaExtractor::Create(mDataSource, - mimeType.isEmpty() ? NULL : mimeType.string()); + mimeType.isEmpty() ? NULL : mimeType.string(), + mIsStreaming ? 0 : AVNuUtils::get()->getUseSetBuffersFlag()); } if (extractor == NULL) { @@ -202,6 +205,11 @@ status_t NuPlayer::GenericSource::initFromDataSource() { } } + if (AVNuUtils::get()->canUseSetBuffers(mFileMeta)) { + mUseSetBuffers = true; + ALOGI("setBuffers mode enabled"); + } + int32_t totalBitrate = 0; size_t numtracks = extractor->countTracks(); @@ -318,7 +326,7 @@ int64_t NuPlayer::GenericSource::getLastReadPosition() { status_t NuPlayer::GenericSource::setBuffers( bool audio, Vector<MediaBuffer *> &buffers) { - if (mIsSecure && !audio) { + if ((mIsSecure || mUseSetBuffers) && !audio) { return mVideoTrack.mSource->setBuffers(buffers); } return INVALID_OPERATION; @@ -374,7 +382,8 @@ void NuPlayer::GenericSource::onPrepareAsync() { mDataSource = DataSource::CreateFromURI( mHTTPService, uri, &mUriHeaders, &contentType, - static_cast<HTTPBase *>(mHttpSource.get())); + static_cast<HTTPBase *>(mHttpSource.get()), + true /*use extended cache*/); } else { mIsWidevine = false; @@ -427,7 +436,8 @@ void NuPlayer::GenericSource::onPrepareAsync() { | FLAG_CAN_PAUSE | FLAG_CAN_SEEK_BACKWARD | FLAG_CAN_SEEK_FORWARD - | FLAG_CAN_SEEK); + | FLAG_CAN_SEEK + | (mUseSetBuffers ? FLAG_USE_SET_BUFFERS : 0)); if (mIsSecure) { // secure decoders must be instantiated before starting widevine source @@ -1022,7 +1032,8 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( // start pulling in more buffers if we only have one (or no) buffer left // so that decoder has less chance of being starved - if (track->mPackets->getAvailableBufferCount(&finalResult) < 2) { + if ((track->mPackets->getAvailableBufferCount(&finalResult) < 2) + && !mUseSetBuffers) { postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO); } @@ -1366,7 +1377,7 @@ sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer( } sp<ABuffer> ab; - if (mIsSecure && !audio) { + if ((mIsSecure || mUseSetBuffers) && !audio) { // data is already provided in the buffer ab = new ABuffer(NULL, mb->range_length()); mb->add_ref(); @@ -1481,7 +1492,9 @@ void NuPlayer::GenericSource::readBuffer( break; case MEDIA_TRACK_TYPE_AUDIO: track = &mAudioTrack; - if (mIsWidevine) { + if (mHttpSource != NULL && getTrackCount() == 1) { + maxBuffers = 16; + } else if (mIsWidevine || (mHttpSource != NULL)) { maxBuffers = 8; } else { maxBuffers = 64; @@ -1512,9 +1525,10 @@ void NuPlayer::GenericSource::readBuffer( if (seekTimeUs >= 0) { options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); seeking = true; + track->mPackets->clear(); } - if (mIsWidevine) { + if (mIsWidevine || mUseSetBuffers) { options.setNonBlocking(); } @@ -1536,7 +1550,8 @@ void NuPlayer::GenericSource::readBuffer( queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track); sp<ABuffer> buffer = mediaBufferToABuffer( - mbuf, trackType, seekTimeUs, actualTimeUs); + mbuf, trackType, seekTimeUs, + numBuffers == 0 ? actualTimeUs : NULL); track->mPackets->queueAccessUnit(buffer); formatChange = false; seeking = false; diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index ac980ef..bdcf706 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -42,7 +42,7 @@ class WVMExtractor; struct NuPlayer::GenericSource : public NuPlayer::Source { GenericSource(const sp<AMessage> ¬ify, bool uidValid, uid_t uid); - status_t setDataSource( + virtual status_t setDataSource( const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers); @@ -84,7 +84,7 @@ protected: virtual sp<MetaData> getFormatMeta(bool audio); -private: +protected: enum { kWhatPrepareAsync, kWhatFetchSubtitleData, @@ -126,6 +126,7 @@ private: bool mAudioIsVorbis; bool mIsWidevine; bool mIsSecure; + bool mUseSetBuffers; bool mIsStreaming; bool mUIDValid; uid_t mUID; @@ -164,7 +165,7 @@ private: int64_t getLastReadPosition(); void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position); - void notifyPreparedAndCleanup(status_t err); + virtual void notifyPreparedAndCleanup(status_t err); void onSecureDecodersInstantiated(status_t err); void finishPrepareAsync(); status_t startSources(); @@ -181,7 +182,7 @@ private: void onSeek(sp<AMessage> msg); status_t doSeek(int64_t seekTimeUs); - void onPrepareAsync(); + virtual void onPrepareAsync(); void fetchTextData( uint32_t what, media_track_type type, diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index 126625a..a57fdc1 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -30,6 +30,8 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/MediaDefs.h> +#include <media/stagefright/Utils.h> + namespace android { @@ -118,6 +120,19 @@ sp<AMessage> NuPlayer::HTTPLiveSource::getFormat(bool audio) { return format; } +sp<MetaData> NuPlayer::HTTPLiveSource::getFormatMeta(bool audio) { + sp<AMessage> format = getFormat(audio); + + if (format == NULL) { + return NULL; + } + + sp<MetaData> meta = new MetaData; + convertMessageToMetaData(format, meta); + return meta; +} + + status_t NuPlayer::HTTPLiveSource::feedMoreTSData() { return OK; } @@ -197,7 +212,11 @@ status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select, i } status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) { - return mLiveSession->seekTo(seekTimeUs); + if (mLiveSession->isSeekable()) { + return mLiveSession->seekTo(seekTimeUs); + } else { + return INVALID_OPERATION; + } } void NuPlayer::HTTPLiveSource::pollForRawData( diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 9e0ec2f..388156c 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -39,6 +39,7 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit); virtual sp<AMessage> getFormat(bool audio); + virtual sp<MetaData> getFormatMeta(bool audio); virtual status_t feedMoreTSData(); virtual status_t getDuration(int64_t *durationUs); @@ -53,7 +54,6 @@ protected: virtual void onMessageReceived(const sp<AMessage> &msg); -private: enum Flags { // Don't log any URLs. kFlagIncognito = 1, @@ -78,7 +78,7 @@ private: bool mHasMetadata; bool mMetadataSelected; - void onSessionNotify(const sp<AMessage> &msg); + virtual void onSessionNotify(const sp<AMessage> &msg); void pollForRawData( const sp<AMessage> &msg, int32_t currentGeneration, LiveSession::StreamType fetchType, int32_t pushWhat); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index c0146d5..ec1ab79 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -56,6 +56,7 @@ #include "ESDS.h" #include <media/stagefright/Utils.h> +#include "mediaplayerservice/AVNuExtensions.h" namespace android { @@ -130,6 +131,23 @@ private: DISALLOW_EVIL_CONSTRUCTORS(FlushDecoderAction); }; +struct NuPlayer::InstantiateDecoderAction : public Action { + InstantiateDecoderAction(bool audio, sp<DecoderBase> *decoder) + : mAudio(audio), + mdecoder(decoder) { + } + + virtual void execute(NuPlayer *player) { + player->instantiateDecoder(mAudio, mdecoder); + } + +private: + bool mAudio; + sp<DecoderBase> *mdecoder; + + DISALLOW_EVIL_CONSTRUCTORS(InstantiateDecoderAction); +}; + struct NuPlayer::PostMessageAction : public Action { PostMessageAction(const sp<AMessage> &msg) : mMessage(msg) { @@ -216,7 +234,7 @@ void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) { msg->post(); } -static bool IsHTTPLiveURL(const char *url) { +bool NuPlayer::IsHTTPLiveURL(const char *url) { if (!strncasecmp("http://", url, 7) || !strncasecmp("https://", url, 8) || !strncasecmp("file://", url, 7)) { @@ -1092,6 +1110,12 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { int32_t reason; CHECK(msg->findInt32("reason", &reason)); ALOGV("Tear down audio with reason %d.", reason); + + if (ifDecodedPCMOffload()) { + tearDownPCMOffload(msg); + break; + } + mAudioDecoder.clear(); ++mAudioDecoderGeneration; bool needsToCreateAudioDecoder = true; @@ -1145,6 +1169,9 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { FLUSH_CMD_SHUTDOWN /* video */)); mDeferredActions.push_back( + new SimpleAction(&NuPlayer::closeAudioSink)); + + mDeferredActions.push_back( new SimpleAction(&NuPlayer::performReset)); processDeferredActions(); @@ -1295,6 +1322,7 @@ void NuPlayer::onStart(int64_t startPositionUs) { } sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); + AVNuUtils::get()->setSourcePCMFormat(audioMeta); audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; if (mAudioSink != NULL) { streamType = mAudioSink->getAudioStreamType(); @@ -1304,6 +1332,10 @@ void NuPlayer::onStart(int64_t startPositionUs) { mOffloadAudio = canOffloadStream(audioMeta, (videoFormat != NULL), mSource->isStreaming(), streamType); + if (!mOffloadAudio) { + mOffloadAudio = canOffloadDecodedPCMStream(audioMeta, (videoFormat != NULL), mSource->isStreaming(), streamType); + } + if (mOffloadAudio) { flags |= Renderer::FLAG_OFFLOAD_AUDIO; } @@ -1311,7 +1343,7 @@ void NuPlayer::onStart(int64_t startPositionUs) { sp<AMessage> notify = new AMessage(kWhatRendererNotify, this); ++mRendererGeneration; notify->setInt32("generation", mRendererGeneration); - mRenderer = new Renderer(mAudioSink, notify, flags); + mRenderer = AVNuFactory::get()->createRenderer(mAudioSink, notify, flags); mRendererLooper = new ALooper; mRendererLooper->setName("NuPlayerRenderer"); mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO); @@ -1440,7 +1472,7 @@ void NuPlayer::tryOpenAudioSinkForOffload(const sp<AMessage> &format, bool hasVi // is possible; otherwise the decoders call the renderer openAudioSink directly. status_t err = mRenderer->openAudioSink( - format, true /* offloadOnly */, hasVideo, AUDIO_OUTPUT_FLAG_NONE, &mOffloadAudio); + format, true /* offloadOnly */, hasVideo, AUDIO_OUTPUT_FLAG_NONE, &mOffloadAudio, mSource->isStreaming()); if (err != OK) { // Any failure we turn off mOffloadAudio. mOffloadAudio = false; @@ -1470,8 +1502,11 @@ void NuPlayer::determineAudioModeChange() { sp<AMessage> videoFormat = mSource->getFormat(false /* audio */); audio_stream_type_t streamType = mAudioSink->getAudioStreamType(); const bool hasVideo = (videoFormat != NULL); - const bool canOffload = canOffloadStream( + bool canOffload = canOffloadStream( audioMeta, hasVideo, mSource->isStreaming(), streamType); + if (!canOffload) { + canOffload = canOffloadDecodedPCMStream(audioMeta, (videoFormat != NULL), mSource->isStreaming(), streamType); + } if (canOffload) { if (!mOffloadAudio) { mRenderer->signalEnableOffloadAudio(); @@ -1483,6 +1518,7 @@ void NuPlayer::determineAudioModeChange() { if (mOffloadAudio) { mRenderer->signalDisableOffloadAudio(); mOffloadAudio = false; + setDecodedPcmOffload(false); } } } @@ -1493,7 +1529,10 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) { if (*decoder != NULL || (audio && mFlushingAudio == SHUT_DOWN)) { return OK; } - + if (mSource == NULL) { + ALOGD("%s Ignore instantiate decoder after clearing source", __func__); + return INVALID_OPERATION; + } sp<AMessage> format = mSource->getFormat(audio); if (format == NULL) { @@ -1531,12 +1570,17 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) { notify->setInt32("generation", mAudioDecoderGeneration); determineAudioModeChange(); - if (mOffloadAudio) { + + if (AVNuUtils::get()->isRAWFormat(format)) { + AVNuUtils::get()->setPCMFormat(format, + AVNuUtils::get()->getKeyPCMFormat(mSource->getFormatMeta(true /* audio */))); + } + if (mOffloadAudio && !ifDecodedPCMOffload()) { const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL); format->setInt32("has-video", hasVideo); - *decoder = new DecoderPassThrough(notify, mSource, mRenderer); + *decoder = AVNuFactory::get()->createPassThruDecoder(notify, mSource, mRenderer); } else { - *decoder = new Decoder(notify, mSource, mPID, mRenderer); + *decoder = AVNuFactory::get()->createDecoder(notify, mSource, mPID, mRenderer); } } else { sp<AMessage> notify = new AMessage(kWhatVideoNotify, this); @@ -1561,7 +1605,8 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) { (*decoder)->configure(format); // allocate buffers to decrypt widevine source buffers - if (!audio && (mSourceFlags & Source::FLAG_SECURE)) { + if (!audio && ((mSourceFlags & Source::FLAG_SECURE) || + (mSourceFlags & Source::FLAG_USE_SET_BUFFERS))) { Vector<sp<ABuffer> > inputBufs; CHECK_EQ((*decoder)->getInputBuffers(&inputBufs), (status_t)OK); @@ -2217,6 +2262,13 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { break; } + case Source::kWhatRTCPByeReceived: + { + ALOGV("notify the client that Bye message is received"); + notifyListener(MEDIA_INFO, 2000, 0); + break; + } + default: TRESPASS(); } @@ -2362,4 +2414,45 @@ void NuPlayer::Source::onMessageReceived(const sp<AMessage> & /* msg */) { TRESPASS(); } +void NuPlayer::tearDownPCMOffload(const sp<AMessage> &msg) { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + if (mAudioDecoder != NULL) { + switch (mFlushingAudio) { + case NONE: + case FLUSHING_DECODER: + mDeferredActions.push_back( + new FlushDecoderAction(FLUSH_CMD_SHUTDOWN /* audio */, + FLUSH_CMD_NONE /* video */)); + + if (reason == Renderer::kDueToError) { + mDeferredActions.push_back( + new InstantiateDecoderAction(true /* audio */, &mAudioDecoder)); + } + + int64_t positionUs; + if (!msg->findInt64("positionUs", &positionUs)) { + positionUs = mPreviousSeekTimeUs; + } + mDeferredActions.push_back(new SeekAction(positionUs)); + break; + default: + ALOGW("tearDownPCMOffload while flushing audio in %d", mFlushingAudio); + break; + } + } + + if (mRenderer != NULL) { + closeAudioSink(); + mRenderer->flush( + true /* audio */, false /* notifyComplete */); + if (mVideoDecoder != NULL) { + mRenderer->flush( + false /* audio */, false /* notifyComplete */); + } + } + processDeferredActions(); +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index c9f0bbd..73e196e 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -41,7 +41,7 @@ struct NuPlayer : public AHandler { void setDataSourceAsync(const sp<IStreamSource> &source); - void setDataSourceAsync( + virtual void setDataSourceAsync( const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers); @@ -86,12 +86,15 @@ protected: virtual ~NuPlayer(); virtual void onMessageReceived(const sp<AMessage> &msg); - + virtual bool ifDecodedPCMOffload() {return false;} + virtual void setDecodedPcmOffload(bool /*decodePcmOffload*/) {} + virtual bool canOffloadDecodedPCMStream(const sp<MetaData> /*meta*/, + bool /*hasVideo*/, bool /*isStreaming*/, audio_stream_type_t /*streamType*/) {return false;} + static bool IsHTTPLiveURL(const char *url); public: struct NuPlayerStreamListener; struct Source; -private: struct Decoder; struct DecoderBase; struct DecoderPassThrough; @@ -106,9 +109,11 @@ private: struct SetSurfaceAction; struct ResumeDecoderAction; struct FlushDecoderAction; + struct InstantiateDecoderAction; struct PostMessageAction; struct SimpleAction; +protected: enum { kWhatSetDataSource = '=DaS', kWhatPrepare = 'prep', @@ -221,11 +226,11 @@ private: mFlushComplete[1][1] = false; } - void tryOpenAudioSinkForOffload(const sp<AMessage> &format, bool hasVideo); + virtual void tryOpenAudioSinkForOffload(const sp<AMessage> &format, bool hasVideo); void closeAudioSink(); void determineAudioModeChange(); - status_t instantiateDecoder(bool audio, sp<DecoderBase> *decoder); + virtual status_t instantiateDecoder(bool audio, sp<DecoderBase> *decoder); status_t onInstantiateSecureDecoders(); @@ -233,7 +238,7 @@ private: const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat = NULL); - void notifyListener(int msg, int ext1, int ext2, const Parcel *in = NULL); + virtual void notifyListener(int msg, int ext1, int ext2, const Parcel *in = NULL); void handleFlushComplete(bool audio, bool isDecoder); void finishFlushIfPossible(); @@ -256,14 +261,14 @@ private: void processDeferredActions(); - void performSeek(int64_t seekTimeUs); + virtual void performSeek(int64_t seekTimeUs); void performDecoderFlush(FlushCommand audio, FlushCommand video); void performReset(); void performScanSources(); void performSetSurface(const sp<Surface> &wrapper); void performResumeDecoders(bool needNotify); - void onSourceNotify(const sp<AMessage> &msg); + virtual void onSourceNotify(const sp<AMessage> &msg); void onClosedCaptionNotify(const sp<AMessage> &msg); void queueDecoderShutdown( @@ -275,6 +280,8 @@ private: void writeTrackInfo(Parcel* reply, const sp<AMessage> format) const; + void tearDownPCMOffload(const sp<AMessage> &msg); + DISALLOW_EVIL_CONSTRUCTORS(NuPlayer); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 3646828..9ca8c9f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -34,10 +34,14 @@ #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include <stagefright/AVExtensions.h> +#include <stagefright/FFMPEGSoftCodec.h> #include <gui/Surface.h> #include "avc_utils.h" #include "ATSParser.h" +#include "mediaplayerservice/AVNuExtensions.h" + namespace android { @@ -78,7 +82,9 @@ NuPlayer::Decoder::Decoder( } NuPlayer::Decoder::~Decoder() { - mCodec->release(); + if (mCodec != NULL) { + mCodec->release(); + } releaseAndResetMediaBuffers(); } @@ -251,8 +257,17 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { mComponentName.append(" decoder"); ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get()); - mCodec = MediaCodec::CreateByType( - mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid); + mCodec = AVUtils::get()->createCustomComponentByName(mCodecLooper, mime.c_str(), false /* encoder */, format); + FFMPEGSoftCodec::overrideComponentName(0, format, &mComponentName, &mime, false); + + if (mCodec == NULL) { + if (!mComponentName.startsWith(mime.c_str())) { + mCodec = MediaCodec::CreateByComponentName(mCodecLooper, mComponentName.c_str()); + } else { + mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */); + } + } + int32_t secure = 0; if (format->findInt32("secure", &secure) && secure != 0) { if (mCodec != NULL) { @@ -357,7 +372,14 @@ void NuPlayer::Decoder::onResume(bool notifyComplete) { if (notifyComplete) { mResumePending = true; } - mCodec->start(); + + if (mCodec != NULL) { + mCodec->start(); + } else { + ALOGW("Decoder %s onResume without a valid codec object", + mComponentName.c_str()); + handleError(NO_INIT); + } } void NuPlayer::Decoder::doFlush(bool notifyComplete) { @@ -558,6 +580,11 @@ bool NuPlayer::Decoder::handleAnOutputBuffer( sp<ABuffer> buffer; mCodec->getOutputBuffer(index, &buffer); + if (buffer == NULL) { + handleError(UNKNOWN_ERROR); + return false; + } + if (index >= mOutputBuffers.size()) { for (size_t i = mOutputBuffers.size(); i <= index; ++i) { mOutputBuffers.add(); @@ -569,6 +596,8 @@ bool NuPlayer::Decoder::handleAnOutputBuffer( buffer->setRange(offset, size); buffer->meta()->clear(); buffer->meta()->setInt64("timeUs", timeUs); + setPcmFormat(buffer->meta()); + AVNuUtils::get()->addFlagsInMeta(buffer, flags, mIsAudio); bool eos = flags & MediaCodec::BUFFER_FLAG_EOS; // we do not expect CODECCONFIG or SYNCFRAME for decoder @@ -636,7 +665,7 @@ void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) { } mRenderer->openAudioSink( - format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */); + format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */, mSource->isStreaming()); } } @@ -708,6 +737,7 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) { // treat seamless format change separately formatChange = !seamlessFormatChange; } + AVNuUtils::get()->checkFormatChange(&formatChange, accessUnit); // For format or time change, return EOS to queue EOS input, // then wait for EOS on output. diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index eeb4af4..5f84a06 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -49,8 +49,8 @@ protected: virtual void onFlush(); virtual void onShutdown(bool notifyComplete); virtual bool doRequestBuffers(); + virtual void setPcmFormat(const sp<AMessage> & /*format*/) {} -private: enum { kWhatCodecNotify = 'cdcN', kWhatRenderBuffer = 'rndr', @@ -103,7 +103,7 @@ private: size_t size, int64_t timeUs, int32_t flags); - void handleOutputFormatChange(const sp<AMessage> &format); + virtual void handleOutputFormatChange(const sp<AMessage> &format); void releaseAndResetMediaBuffers(); void requestCodecNotification(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp index 30146c4..937936d 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp @@ -29,14 +29,12 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> +#include "mediaplayerservice/AVNuExtensions.h" #include "ATSParser.h" namespace android { -// TODO optimize buffer size for power consumption -// The offload read buffer size is 32 KB but 24 KB uses less power. -static const size_t kAggregateBufferSizeBytes = 24 * 1024; static const size_t kMaxCachedBytes = 200000; NuPlayer::DecoderPassThrough::DecoderPassThrough( @@ -46,6 +44,9 @@ NuPlayer::DecoderPassThrough::DecoderPassThrough( : DecoderBase(notify), mSource(source), mRenderer(renderer), + // TODO optimize buffer size for power consumption + // The offload read buffer size is 32 KB but 24 KB uses less power. + mAggregateBufferSizeBytes(24 * 1024), mSkipRenderingUntilMediaTimeUs(-1ll), mPaused(false), mReachedEOS(true), @@ -76,7 +77,7 @@ void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) { // format is different. status_t err = mRenderer->openAudioSink( format, true /* offloadOnly */, hasVideo, - AUDIO_OUTPUT_FLAG_NONE /* flags */, NULL /* isOffloaded */); + AUDIO_OUTPUT_FLAG_NONE /* flags */, NULL /* isOffloaded */, mSource->isStreaming()); if (err != OK) { handleError(err); } @@ -173,9 +174,9 @@ sp<ABuffer> NuPlayer::DecoderPassThrough::aggregateBuffer( size_t smallSize = accessUnit->size(); if ((mAggregateBuffer == NULL) // Don't bother if only room for a few small buffers. - && (smallSize < (kAggregateBufferSizeBytes / 3))) { + && (smallSize < (mAggregateBufferSizeBytes / 3))) { // Create a larger buffer for combining smaller buffers from the extractor. - mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes); + mAggregateBuffer = new ABuffer(mAggregateBufferSizeBytes); mAggregateBuffer->setRange(0, 0); // start empty } @@ -201,6 +202,7 @@ sp<ABuffer> NuPlayer::DecoderPassThrough::aggregateBuffer( if ((bigSize == 0) && smallTimestampValid) { mAggregateBuffer->meta()->setInt64("timeUs", timeUs); } + setPcmFormat(mAggregateBuffer->meta()); // Append small buffer to the bigger buffer. memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize); bigSize += smallSize; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h index db33e87..629e266 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h @@ -43,25 +43,27 @@ protected: virtual void onFlush(); virtual void onShutdown(bool notifyComplete); virtual bool doRequestBuffers(); + virtual void setPcmFormat(const sp<AMessage> & /*format*/) {} + virtual sp<ABuffer> aggregateBuffer(const sp<ABuffer> &accessUnit); -private: enum { kWhatBufferConsumed = 'bufC', }; sp<Source> mSource; sp<Renderer> mRenderer; + size_t mAggregateBufferSizeBytes; int64_t mSkipRenderingUntilMediaTimeUs; bool mPaused; - - bool mReachedEOS; + bool mReachedEOS; // Used by feedDecoderInputData to aggregate small buffers into // one large buffer. + status_t mPendingAudioErr; sp<ABuffer> mPendingAudioAccessUnit; - status_t mPendingAudioErr; sp<ABuffer> mAggregateBuffer; +private: // mPendingBuffersToDrain are only for debugging. It can be removed // when the power investigation is done. size_t mPendingBuffersToDrain; @@ -72,7 +74,6 @@ private: bool isDoneFetching() const; status_t dequeueAccessUnit(sp<ABuffer> *accessUnit); - sp<ABuffer> aggregateBuffer(const sp<ABuffer> &accessUnit); status_t fetchInputData(sp<AMessage> &reply); void doFlush(bool notifyComplete); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 7370224..a294d36 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -31,6 +31,9 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/Utils.h> +#include "mediaplayerservice/AVNuExtensions.h" +#include "mediaplayerservice/AVMediaServiceExtensions.h" + namespace android { NuPlayerDriver::NuPlayerDriver(pid_t pid) @@ -55,7 +58,7 @@ NuPlayerDriver::NuPlayerDriver(pid_t pid) true, /* canCallJava */ PRIORITY_AUDIO); - mPlayer = new NuPlayer(pid); + mPlayer = AVNuFactory::get()->createNuPlayer(pid); mLooper->registerHandler(mPlayer); mPlayer->setDriver(this); @@ -114,6 +117,7 @@ status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) { mCondition.wait(mLock); } + AVNuUtils::get()->printFileName(fd); return mAsyncResult; } @@ -607,6 +611,8 @@ status_t NuPlayerDriver::getMetadata( Metadata::kSeekAvailable, mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK); + AVMediaServiceUtils::get()->appendMeta(&meta); + return OK; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 776dba8..490a0d2 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -26,12 +26,15 @@ #include <media/stagefright/foundation/AUtils.h> #include <media/stagefright/foundation/AWakeLock.h> #include <media/stagefright/MediaClock.h> +#include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/Utils.h> #include <media/stagefright/VideoFrameScheduler.h> #include <inttypes.h> +#include "mediaplayerservice/AVNuExtensions.h" +#include "stagefright/AVExtensions.h" namespace android { @@ -81,6 +84,16 @@ const NuPlayer::Renderer::PcmInfo NuPlayer::Renderer::AUDIO_PCMINFO_INITIALIZER // static const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; +static bool sFrameAccurateAVsync = false; + +static void readProperties() { + char value[PROPERTY_VALUE_MAX]; + if (property_get("persist.sys.media.avsync", value, NULL)) { + sFrameAccurateAVsync = + !strcmp("1", value) || !strcasecmp("true", value); + } +} + NuPlayer::Renderer::Renderer( const sp<MediaPlayerBase::AudioSink> &sink, const sp<AMessage> ¬ify, @@ -102,6 +115,7 @@ NuPlayer::Renderer::Renderer( mVideoLateByUs(0ll), mHasAudio(false), mHasVideo(false), + mFoundAudioEOS(false), mNotifyCompleteAudio(false), mNotifyCompleteVideo(false), mSyncQueues(false), @@ -122,6 +136,7 @@ NuPlayer::Renderer::Renderer( mMediaClock = new MediaClock; mPlaybackRate = mPlaybackSettings.mSpeed; mMediaClock->setPlaybackRate(mPlaybackRate); + readProperties(); } NuPlayer::Renderer::~Renderer() { @@ -312,7 +327,8 @@ void NuPlayer::Renderer::setVideoFrameRate(float fps) { // Called on any threads. status_t NuPlayer::Renderer::getCurrentPosition(int64_t *mediaUs) { - return mMediaClock->getMediaTime(ALooper::GetNowUs(), mediaUs); + return mMediaClock->getMediaTime( + ALooper::GetNowUs(), mediaUs, (mHasAudio && mFoundAudioEOS)); } void NuPlayer::Renderer::clearAudioFirstAnchorTime_l() { @@ -348,18 +364,20 @@ status_t NuPlayer::Renderer::openAudioSink( bool offloadOnly, bool hasVideo, uint32_t flags, - bool *isOffloaded) { + bool *isOffloaded, + bool isStreaming) { sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, this); msg->setMessage("format", format); msg->setInt32("offload-only", offloadOnly); msg->setInt32("has-video", hasVideo); msg->setInt32("flags", flags); + msg->setInt32("isStreaming", isStreaming); sp<AMessage> response; - msg->postAndAwaitResponse(&response); + status_t postStatus = msg->postAndAwaitResponse(&response); int32_t err; - if (!response->findInt32("err", &err)) { + if (postStatus != OK || !response->findInt32("err", &err)) { err = INVALID_OPERATION; } else if (err == OK && isOffloaded != NULL) { int32_t offload; @@ -392,7 +410,10 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { uint32_t flags; CHECK(msg->findInt32("flags", (int32_t *)&flags)); - status_t err = onOpenAudioSink(format, offloadOnly, hasVideo, flags); + uint32_t isStreaming; + CHECK(msg->findInt32("isStreaming", (int32_t *)&isStreaming)); + + status_t err = onOpenAudioSink(format, offloadOnly, hasVideo, flags, isStreaming); sp<AMessage> response = new AMessage; response->setInt32("err", err); @@ -435,9 +456,10 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) { if (onDrainAudioQueue()) { uint32_t numFramesPlayed; - CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), - (status_t)OK); - + if (mAudioSink->getPosition(&numFramesPlayed) != OK) { + ALOGW("mAudioSink->getPosition failed"); + break; + } uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed; @@ -1074,6 +1096,11 @@ void NuPlayer::Renderer::postDrainVideoQueue() { ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs); // post 2 display refreshes before rendering is due + // FIXME currently this increases power consumption, so unless frame-accurate + // AV sync is requested, post closer to required render time (at 0.63 vsyncs) + if (!sFrameAccurateAVsync) { + twoVsyncsUs >>= 4; + } msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0); mDrainVideoQueuePending = true; @@ -1164,6 +1191,9 @@ void NuPlayer::Renderer::notifyVideoRenderingStart() { } void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult, int64_t delayUs) { + if (audio) { + mFoundAudioEOS = true; + } sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatEOS); notify->setInt32("audio", static_cast<int32_t>(audio)); @@ -1479,6 +1509,7 @@ void NuPlayer::Renderer::onPause() { mDrainAudioQueuePending = false; mDrainVideoQueuePending = false; + mVideoRenderingStarted = false; // force-notify NOTE_INFO MEDIA_INFO_RENDERING_START after resume if (mHasAudio) { mAudioSink->pause(); @@ -1490,15 +1521,18 @@ void NuPlayer::Renderer::onPause() { } void NuPlayer::Renderer::onResume() { + readProperties(); + if (!mPaused) { return; } if (mHasAudio) { + status_t status = NO_ERROR; cancelAudioOffloadPauseTimeout(); - status_t err = mAudioSink->start(); - if (err != OK) { - ALOGE("cannot start AudioSink err %d", err); + status = mAudioSink->start(); + if (offloadingAudio() && status != NO_ERROR && status != INVALID_OPERATION) { + ALOGD("received error :%d on resume for offload track posting TEAR_DOWN event",status); notifyAudioTearDown(); } } @@ -1559,6 +1593,7 @@ int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { int64_t numFramesPlayedAt; AudioTimestamp ts; static const int64_t kStaleTimestamp100ms = 100000; + int64_t durationUs; status_t res = mAudioSink->getTimestamp(ts); if (res == OK) { // case 1: mixing audio tracks and offloaded tracks. @@ -1585,14 +1620,20 @@ int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) { // numFramesPlayed, (long long)numFramesPlayedAt); } else { // case 3: transitory at new track or audio fast tracks. res = mAudioSink->getPosition(&numFramesPlayed); - CHECK_EQ(res, (status_t)OK); - numFramesPlayedAt = nowUs; - numFramesPlayedAt += 1000LL * mAudioSink->latency() / 2; /* XXX */ + if (res != OK) { + //query to getPosition fails, use media clock to simulate render position + getCurrentPosition(&durationUs); + durationUs = durationUs - mAnchorTimeMediaUs; + return durationUs; + } else { + numFramesPlayedAt = nowUs; + numFramesPlayedAt += 1000LL * mAudioSink->latency() / 2; /* XXX */ + } //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAt); } //CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test - int64_t durationUs = getDurationUsIfPlayedAtSampleRate(numFramesPlayed) + durationUs = getDurationUsIfPlayedAtSampleRate(numFramesPlayed) + nowUs - numFramesPlayedAt; if (durationUs < 0) { // Occurs when numFramesPlayed position is very small and the following: @@ -1650,7 +1691,8 @@ status_t NuPlayer::Renderer::onOpenAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, - uint32_t flags) { + uint32_t flags, + bool isStreaming) { ALOGV("openAudioSink: offloadOnly(%d) offloadingAudio(%d)", offloadOnly, offloadingAudio()); bool audioSinkChanged = false; @@ -1664,13 +1706,17 @@ status_t NuPlayer::Renderer::onOpenAudioSink( channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; } + int32_t bitWidth = 16; + format->findInt32("bit-width", &bitWidth); + int32_t sampleRate; CHECK(format->findInt32("sample-rate", &sampleRate)); + AString mime; + CHECK(format->findString("mime", &mime)); + if (offloadingAudio()) { audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; - AString mime; - CHECK(format->findString("mime", &mime)); status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str()); if (err != OK) { @@ -1678,22 +1724,34 @@ status_t NuPlayer::Renderer::onOpenAudioSink( "audio_format", mime.c_str()); onDisableOffloadAudio(); } else { - ALOGV("Mime \"%s\" mapped to audio_format 0x%x", - mime.c_str(), audioFormat); + audioFormat = AVUtils::get()->updateAudioFormat(audioFormat, format); + bitWidth = AVUtils::get()->getAudioSampleBits(format); int avgBitRate = -1; - format->findInt32("bit-rate", &avgBitRate); + format->findInt32("bitrate", &avgBitRate); int32_t aacProfile = -1; if (audioFormat == AUDIO_FORMAT_AAC && format->findInt32("aac-profile", &aacProfile)) { // Redefine AAC format as per aac profile - mapAACProfileToAudioFormat( - audioFormat, - aacProfile); + int32_t isADTSSupported; + isADTSSupported = AVUtils::get()->mapAACProfileToAudioFormat(format, + audioFormat, + aacProfile); + if (!isADTSSupported) { + mapAACProfileToAudioFormat(audioFormat, + aacProfile); + } else { + ALOGV("Format is AAC ADTS\n"); + } } + int32_t offloadBufferSize = + AVUtils::get()->getAudioMaxInputBufferSize( + audioFormat, + format); audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + offloadInfo.duration_us = -1; format->findInt64( "durationUs", &offloadInfo.duration_us); @@ -1703,7 +1761,9 @@ status_t NuPlayer::Renderer::onOpenAudioSink( offloadInfo.stream_type = AUDIO_STREAM_MUSIC; offloadInfo.bit_rate = avgBitRate; offloadInfo.has_video = hasVideo; - offloadInfo.is_streaming = true; + offloadInfo.is_streaming = isStreaming; + offloadInfo.bit_width = bitWidth; + offloadInfo.offload_buffer_size = offloadBufferSize; if (memcmp(&mCurrentOffloadInfo, &offloadInfo, sizeof(offloadInfo)) == 0) { ALOGV("openAudioSink: no change in offload mode"); @@ -1767,7 +1827,7 @@ status_t NuPlayer::Renderer::onOpenAudioSink( const PcmInfo info = { (audio_channel_mask_t)channelMask, (audio_output_flags_t)pcmFlags, - AUDIO_FORMAT_PCM_16_BIT, // TODO: change to audioFormat + AVNuUtils::get()->getPCMFormat(format), numChannels, sampleRate }; @@ -1802,7 +1862,7 @@ status_t NuPlayer::Renderer::onOpenAudioSink( sampleRate, numChannels, (audio_channel_mask_t)channelMask, - AUDIO_FORMAT_PCM_16_BIT, + AVNuUtils::get()->getPCMFormat(format), 0 /* bufferCount - unused */, mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL, mUseAudioCallback ? this : NULL, diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 87bcbf9..50bd0a9 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -73,12 +73,15 @@ struct NuPlayer::Renderer : public AHandler { status_t getCurrentPosition(int64_t *mediaUs); int64_t getVideoLateByUs(); + virtual audio_stream_type_t getAudioStreamType(){return AUDIO_STREAM_DEFAULT;} + status_t openAudioSink( const sp<AMessage> &format, bool offloadOnly, bool hasVideo, uint32_t flags, - bool *isOffloaded); + bool *isOffloaded, + bool isStreaming); void closeAudioSink(); enum { @@ -101,7 +104,6 @@ protected: virtual void onMessageReceived(const sp<AMessage> &msg); -private: enum { kWhatDrainAudioQueue = 'draA', kWhatDrainVideoQueue = 'draV', @@ -162,6 +164,7 @@ private: int64_t mVideoLateByUs; bool mHasAudio; bool mHasVideo; + bool mFoundAudioEOS; bool mNotifyCompleteAudio; bool mNotifyCompleteVideo; @@ -228,7 +231,7 @@ private: void prepareForMediaRenderingStart_l(); void notifyIfMediaRenderingStarted_l(); - void onQueueBuffer(const sp<AMessage> &msg); + virtual void onQueueBuffer(const sp<AMessage> &msg); void onQueueEOS(const sp<AMessage> &msg); void onFlush(const sp<AMessage> &msg); void onAudioSinkChanged(); @@ -250,7 +253,8 @@ private: const sp<AMessage> &format, bool offloadOnly, bool hasVideo, - uint32_t flags); + uint32_t flags, + bool isStreaming); void onCloseAudioSink(); void notifyEOS(bool audio, status_t finalResult, int64_t delayUs = 0); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 11a6a9f..b248316 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -39,6 +39,7 @@ struct NuPlayer::Source : public AHandler { FLAG_DYNAMIC_DURATION = 16, FLAG_SECURE = 32, FLAG_PROTECTED = 64, + FLAG_USE_SET_BUFFERS = 128, }; enum { @@ -57,6 +58,7 @@ struct NuPlayer::Source : public AHandler { kWhatQueueDecoderShutdown, kWhatDrmNoLicense, kWhatInstantiateSecureDecoders, + kWhatRTCPByeReceived, }; // The provides message is used to notify the player about various @@ -132,10 +134,10 @@ protected: void notifyFlagsChanged(uint32_t flags); void notifyVideoSizeChanged(const sp<AMessage> &format = NULL); void notifyInstantiateSecureDecoders(const sp<AMessage> &reply); - void notifyPrepared(status_t err = OK); + virtual void notifyPrepared(status_t err = OK); -private: sp<AMessage> mNotify; +private: DISALLOW_EVIL_CONSTRUCTORS(Source); }; diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index af0351e..35567a5 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -27,6 +27,7 @@ #include <media/IMediaHTTPService.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaData.h> +#include <mediaplayerservice/AVMediaServiceExtensions.h> namespace android { @@ -131,6 +132,10 @@ void NuPlayer::RTSPSource::pause() { // Check if EOS or ERROR is received if (source != NULL && source->isFinished(mediaDurationUs)) { + if (mHandler != NULL) { + ALOGI("Nearing EOS...No Pause is issued"); + mHandler->cancelTimeoutCheck(); + } return; } } @@ -476,8 +481,11 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { if (!info->mNPTMappingValid) { // This is a live stream, we didn't receive any normal // playtime mapping. We won't map to npt time. - source->queueAccessUnit(accessUnit); - break; + if (!AVMediaServiceUtils::get()->checkNPTMapping(&info->mRTPTime, + &info->mNormalPlaytimeUs, &info->mNPTMappingValid, rtpTime)) { + source->queueAccessUnit(accessUnit); + break; + } } int64_t nptUs = @@ -563,6 +571,14 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { break; } + case MyHandler::kWhatByeReceived: + { + sp<AMessage> msg = dupNotify(); + msg->setInt32("what", kWhatRTCPByeReceived); + msg->post(); + break; + } + case SDPLoader::kWhatSDPLoaded: { onSDPLoaded(msg); diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp index 45e8a30..1353e3f 100644 --- a/media/libstagefright/AACExtractor.cpp +++ b/media/libstagefright/AACExtractor.cpp @@ -136,7 +136,8 @@ AACExtractor::AACExtractor( const sp<DataSource> &source, const sp<AMessage> &_meta) : mDataSource(source), mInitCheck(NO_INIT), - mFrameDurationUs(0) { + mFrameDurationUs(0), + mApeMeta(new MetaData) { sp<AMessage> meta = _meta; if (meta == NULL) { @@ -170,11 +171,25 @@ AACExtractor::AACExtractor( off64_t streamSize, numFrames = 0; size_t frameSize = 0; int64_t duration = 0; + uint8_t apeTag[8]; if (mDataSource->getSize(&streamSize) == OK) { while (offset < streamSize) { + mDataSource->readAt(offset, &apeTag, 8); + if (ape.isAPE(apeTag)) { + size_t apeSize = 0; + mDataSource->readAt(offset + 8 + 4, &apeSize, 1); + + if (ape.parseAPE(source, offset, mApeMeta) == false) { + break; + } + + mOffsetVector.push(offset); + offset += apeSize; + continue; + } if ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) { - return; + break; } mOffsetVector.push(offset); @@ -196,15 +211,13 @@ AACExtractor::~AACExtractor() { } sp<MetaData> AACExtractor::getMetaData() { - sp<MetaData> meta = new MetaData; if (mInitCheck != OK) { - return meta; + return mApeMeta; } + mApeMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS); - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS); - - return meta; + return mApeMeta; } size_t AACExtractor::countTracks() { diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 8d9bd21..a81bca5 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -44,6 +44,8 @@ #include <media/stagefright/OMXCodec.h> #include <media/stagefright/PersistentSurface.h> #include <media/stagefright/SurfaceUtils.h> +#include <media/stagefright/FFMPEGSoftCodec.h> + #include <media/hardware/HardwareAPI.h> #include <OMX_AudioExt.h> @@ -54,6 +56,8 @@ #include "include/avc_utils.h" +#include <stagefright/AVExtensions.h> + namespace android { // OMX errors are directly mapped into status_t range if @@ -539,6 +543,15 @@ ACodec::ACodec() ACodec::~ACodec() { } +status_t ACodec::setupCustomCodec(status_t err, const char *mime, const sp<AMessage> &msg) { + if (!strncmp(mComponentName.c_str(), "OMX.ffmpeg.", 11) && !mIsEncoder) { + return FFMPEGSoftCodec::setAudioFormat( + msg, mime, mOMX, mNode); + } + + return err; +} + void ACodec::setNotificationMessage(const sp<AMessage> &msg) { mNotify = msg; } @@ -613,8 +626,8 @@ void ACodec::initiateShutdown(bool keepComponentAllocated) { msg->setInt32("keepComponentAllocated", keepComponentAllocated); msg->post(); if (!keepComponentAllocated) { - // ensure shutdown completes in 3 seconds - (new AMessage(kWhatReleaseCodecInstance, this))->post(3000000); + // ensure shutdown completes in 30 seconds + (new AMessage(kWhatReleaseCodecInstance, this))->post(30000000); } } @@ -828,15 +841,11 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { : OMXCodec::kRequiresAllocateBufferOnOutputPorts; if ((portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) - || (portIndex == kPortIndexOutput && usingMetadataOnEncoderOutput())) { + || (portIndex == kPortIndexOutput && usingMetadataOnEncoderOutput()) + || canAllocateBuffer(portIndex)) { mem.clear(); - void *ptr; - err = mOMX->allocateBuffer( - mNode, portIndex, bufSize, &info.mBufferID, - &ptr); - - info.mData = new ABuffer(ptr, bufSize); + err = allocateBuffer(portIndex, bufSize, info); } else if (mQuirks & requiresAllocateBufferBit) { err = mOMX->allocateBufferWithBackup( mNode, portIndex, mem, &info.mBufferID, allottedSize); @@ -1587,7 +1596,11 @@ status_t ACodec::setComponentRole( } if (i == kNumMimeToRole) { - return ERROR_UNSUPPORTED; + status_t err = ERROR_UNSUPPORTED; + if (!strncmp(mComponentName.c_str(), "OMX.ffmpeg.", 11)) { + err = FFMPEGSoftCodec::setSupportedRole(mOMX, mNode, isEncoder, mime); + } + return err; } const char *role = @@ -1639,6 +1652,8 @@ status_t ACodec::configureCodec( return err; } + enableCustomAllocationMode(msg); + int32_t bitRate = 0; // FLAC encoder doesn't need a bitrate, other encoders do if (encoder && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC) @@ -1906,7 +1921,8 @@ status_t ACodec::configureCodec( if (video) { // determine need for software renderer bool usingSwRenderer = false; - if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) { + if (haveNativeWindow && (mComponentName.startsWith("OMX.google.") || + mComponentName.startsWith("OMX.ffmpeg."))) { usingSwRenderer = true; haveNativeWindow = false; } @@ -1991,10 +2007,12 @@ status_t ACodec::configureCodec( // and have the decoder figure it all out. err = OK; } else { + int32_t bitsPerSample = 16; + msg->findInt32("bit-width", &bitsPerSample); err = setupRawAudioFormat( encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, - numChannels); + numChannels, bitsPerSample); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { int32_t numChannels, sampleRate; @@ -2069,7 +2087,7 @@ status_t ACodec::configureCodec( } err = setupG711Codec(encoder, sampleRate, numChannels); } - } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC) && encoder) { int32_t numChannels = 0, sampleRate = 0, compressionLevel = -1; if (encoder && (!msg->findInt32("channel-count", &numChannels) @@ -2105,16 +2123,21 @@ status_t ACodec::configureCodec( || !msg->findInt32("sample-rate", &sampleRate)) { err = INVALID_OPERATION; } else { - err = setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels); + int32_t bitsPerSample = 16; + msg->findInt32("bit-width", &bitsPerSample); + err = setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels, bitsPerSample); } - } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC3)) { + } else if (!strncmp(mComponentName.c_str(), "OMX.google.", 11) + && !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC3)) { int32_t numChannels; int32_t sampleRate; if (!msg->findInt32("channel-count", &numChannels) || !msg->findInt32("sample-rate", &sampleRate)) { err = INVALID_OPERATION; } else { - err = setupAC3Codec(encoder, numChannels, sampleRate); + int32_t bitsPerSample = 16; + msg->findInt32("bit-width", &bitsPerSample); + err = setupAC3Codec(encoder, numChannels, sampleRate, bitsPerSample); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_EAC3)) { int32_t numChannels; @@ -2123,8 +2146,12 @@ status_t ACodec::configureCodec( || !msg->findInt32("sample-rate", &sampleRate)) { err = INVALID_OPERATION; } else { - err = setupEAC3Codec(encoder, numChannels, sampleRate); + int32_t bitsPerSample = 16; + msg->findInt32("bit-width", &bitsPerSample); + err = setupEAC3Codec(encoder, numChannels, sampleRate, bitsPerSample); } + } else { + err = setupCustomCodec(err, mime, msg); } if (err != OK) { @@ -2438,9 +2465,9 @@ status_t ACodec::setupAACCodec( } status_t ACodec::setupAC3Codec( - bool encoder, int32_t numChannels, int32_t sampleRate) { + bool encoder, int32_t numChannels, int32_t sampleRate, int32_t bitsPerSample) { status_t err = setupRawAudioFormat( - encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels); + encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels, bitsPerSample); if (err != OK) { return err; @@ -2476,9 +2503,9 @@ status_t ACodec::setupAC3Codec( } status_t ACodec::setupEAC3Codec( - bool encoder, int32_t numChannels, int32_t sampleRate) { + bool encoder, int32_t numChannels, int32_t sampleRate, int32_t bitsPerSample) { status_t err = setupRawAudioFormat( - encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels); + encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels, bitsPerSample); if (err != OK) { return err; @@ -2624,7 +2651,7 @@ status_t ACodec::setupFlacCodec( } status_t ACodec::setupRawAudioFormat( - OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) { + OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels, int32_t bitsPerSample) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); def.nPortIndex = portIndex; @@ -2659,7 +2686,7 @@ status_t ACodec::setupRawAudioFormat( pcmParams.nChannels = numChannels; pcmParams.eNumData = OMX_NumericalDataSigned; pcmParams.bInterleaved = OMX_TRUE; - pcmParams.nBitPerSample = 16; + pcmParams.nBitPerSample = bitsPerSample; pcmParams.nSamplingRate = sampleRate; pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear; @@ -2835,13 +2862,14 @@ static const struct VideoCodingMapEntry { { MEDIA_MIMETYPE_VIDEO_AVC, OMX_VIDEO_CodingAVC }, { MEDIA_MIMETYPE_VIDEO_HEVC, OMX_VIDEO_CodingHEVC }, { MEDIA_MIMETYPE_VIDEO_MPEG4, OMX_VIDEO_CodingMPEG4 }, + { MEDIA_MIMETYPE_VIDEO_MPEG4_DP, OMX_VIDEO_CodingMPEG4 }, { MEDIA_MIMETYPE_VIDEO_H263, OMX_VIDEO_CodingH263 }, { MEDIA_MIMETYPE_VIDEO_MPEG2, OMX_VIDEO_CodingMPEG2 }, { MEDIA_MIMETYPE_VIDEO_VP8, OMX_VIDEO_CodingVP8 }, { MEDIA_MIMETYPE_VIDEO_VP9, OMX_VIDEO_CodingVP9 }, }; -static status_t GetVideoCodingTypeFromMime( +status_t ACodec::GetVideoCodingTypeFromMime( const char *mime, OMX_VIDEO_CODINGTYPE *codingType) { for (size_t i = 0; i < sizeof(kVideoCodingMapEntry) / sizeof(kVideoCodingMapEntry[0]); @@ -2885,7 +2913,13 @@ status_t ACodec::setupVideoDecoder( status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat); if (err != OK) { - return err; + if (!strncmp(mComponentName.c_str(), "OMX.ffmpeg.", 11)) { + err = FFMPEGSoftCodec::setVideoFormat( + msg, mime, mOMX, mNode, mIsEncoder, &compressionFormat); + } + if (err != OK) { + return err; + } } err = setVideoPortFormatType( @@ -3035,7 +3069,14 @@ status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) { err = GetVideoCodingTypeFromMime(mime, &compressionFormat); if (err != OK) { - return err; + if (!strncmp(mComponentName.c_str(), "OMX.ffmpeg.", 11)) { + err = FFMPEGSoftCodec::setVideoFormat( + msg, mime, mOMX, mNode, mIsEncoder, &compressionFormat); + } + if (err != OK) { + ALOGE("Not a supported video mime type: %s", mime); + return err; + } } err = setVideoPortFormatType( @@ -3228,6 +3269,7 @@ status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) { mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(level); } + setBFrames(&mpeg4type); err = mOMX->setParameter( mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); @@ -3425,6 +3467,8 @@ status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) { err = verifySupportForProfileAndLevel(profile, level); if (err != OK) { + ALOGE("%s does not support profile %x @ level %x", + mComponentName.c_str(), profile, level); return err; } @@ -3433,11 +3477,14 @@ status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) { } // XXX + // Allow higher profiles to be set since the encoder seems to support +#if 0 if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) { ALOGW("Use baseline profile instead of %d for AVC recording", h264type.eProfile); h264type.eProfile = OMX_VIDEO_AVCProfileBaseline; } +#endif if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) { h264type.nSliceHeaderSpacing = 0; @@ -3458,6 +3505,7 @@ status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) { h264type.nCabacInitIdc = 0; } + setBFrames(&h264type, iFrameInterval, frameRate); if (h264type.nBFrames != 0) { h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB; } @@ -3498,6 +3546,8 @@ status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) { frameRate = (float)tmp; } + AVUtils::get()->setIntraPeriod(setPFramesSpacing(iFrameInterval, frameRate), 0, mOMX, mNode); + OMX_VIDEO_PARAM_HEVCTYPE hevcType; InitOMXParams(&hevcType); hevcType.nPortIndex = kPortIndexOutput; @@ -4134,6 +4184,14 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { default: { + if (!strncmp(mComponentName.c_str(), "OMX.ffmpeg.", 11)) { + err = FFMPEGSoftCodec::getVideoPortFormat(portIndex, + (int)videoDef->eCompressionFormat, notify, mOMX, mNode); + if (err == OK) { + break; + } + } + if (mIsEncoder ^ (portIndex == kPortIndexOutput)) { // should be CodingUnused ALOGE("Raw port video compression format is %s(%d)", @@ -4179,7 +4237,10 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { if (params.nChannels <= 0 || (params.nChannels != 1 && !params.bInterleaved) - || params.nBitPerSample != 16u + || (params.nBitPerSample != 16u + && params.nBitPerSample != 24u + && params.nBitPerSample != 32u + && params.nBitPerSample != 8u)// we support 8/16/24/32 bit s/w decoding || params.eNumData != OMX_NumericalDataSigned || params.ePCMMode != OMX_AUDIO_PCMModeLinear) { ALOGE("unsupported PCM port: %u channels%s, %u-bit, %s(%d), %s(%d) mode ", @@ -4194,6 +4255,7 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW); notify->setInt32("channel-count", params.nChannels); notify->setInt32("sample-rate", params.nSamplingRate); + notify->setInt32("bit-width", params.nBitPerSample); if (mChannelMaskPresent) { notify->setInt32("channel-mask", mChannelMask); @@ -4244,6 +4306,7 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { case OMX_AUDIO_CodingFLAC: { + if (portIndex == kPortIndexInput) { OMX_AUDIO_PARAM_FLACTYPE params; InitOMXParams(¶ms); params.nPortIndex = portIndex; @@ -4258,6 +4321,7 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { notify->setInt32("channel-count", params.nChannels); notify->setInt32("sample-rate", params.nSampleRate); break; + } } case OMX_AUDIO_CodingMP3: @@ -4398,6 +4462,14 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) { } default: + if (!strncmp(mComponentName.c_str(), "OMX.ffmpeg.", 11)) { + err = FFMPEGSoftCodec::getAudioPortFormat(portIndex, + (int)audioDef->eEncoding, notify, mOMX, mNode); + } + if (err == OK) { + break; + } + ALOGE("Unsupported audio coding: %s(%d)\n", asString(audioDef->eEncoding), audioDef->eEncoding); return BAD_TYPE; @@ -4448,6 +4520,8 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { mEncoderPadding * frameSize); } + getVQZIPInfo(notify); + notify->post(); mSentFormat = true; @@ -4596,6 +4670,7 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { ALOGI("[%s] forcing the release of codec", mCodec->mComponentName.c_str()); status_t err = mCodec->mOMX->freeNode(mCodec->mNode); + mCodec->changeState(mCodec->mUninitializedState); ALOGE_IF("[%s] failed to release codec instance: err=%d", mCodec->mComponentName.c_str(), err); sp<AMessage> notify = mCodec->mNotify->dup(); @@ -5180,6 +5255,7 @@ bool ACodec::BaseState::onOMXFillBufferDone( mCodec->mSkipCutBuffer->submit(info->mData); } info->mData->meta()->setInt64("timeUs", timeUs); + info->mData->meta()->setObject("graphic-buffer", info->mGraphicBuffer); sp<AMessage> notify = mCodec->mNotify->dup(); notify->setInt32("what", CodecBase::kWhatDrainThisBuffer); @@ -5189,6 +5265,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( reply->setInt32("buffer-id", info->mBufferID); + (void)mCodec->setDSModeHint(reply, flags, timeUs); + notify->setMessage("reply", reply); notify->post(); @@ -5243,8 +5321,9 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err); } + bool skip = mCodec->getDSModeHint(msg); int32_t render; - if (mCodec->mNativeWindow != NULL + if (!skip && mCodec->mNativeWindow != NULL && msg->findInt32("render", &render) && render != 0 && info->mData != NULL && info->mData->size() != 0) { ATRACE_NAME("render"); @@ -5690,13 +5769,85 @@ bool ACodec::LoadedState::onConfigureComponent( ALOGE("[%s] configureCodec returning error %d", mCodec->mComponentName.c_str(), err); - mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err)); - return false; + int32_t encoder; + if (!msg->findInt32("encoder", &encoder)) { + encoder = false; + } + + if (!encoder && !strncmp(mime.c_str(), "video/", strlen("video/"))) { + Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs; + + OMXCodec::findMatchingCodecs( + mime.c_str(), + encoder, // createEncoder + NULL, // matchComponentName + 0, // flags + &matchingCodecs); + + status_t err = mCodec->mOMX->freeNode(mCodec->mNode); + + if (err != OK) { + ALOGE("Failed to freeNode"); + mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err)); + return false; + } + + mCodec->mNode = 0; + AString componentName; + sp<CodecObserver> observer = new CodecObserver; + + err = NAME_NOT_FOUND; + for (size_t matchIndex = 0; matchIndex < matchingCodecs.size(); + ++matchIndex) { + componentName = matchingCodecs.itemAt(matchIndex).mName.string(); + if (!strcmp(mCodec->mComponentName.c_str(), componentName.c_str())) { + continue; + } + + pid_t tid = gettid(); + int prevPriority = androidGetThreadPriority(tid); + androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND); + err = mCodec->mOMX->allocateNode(componentName.c_str(), observer, &mCodec->mNode); + androidSetThreadPriority(tid, prevPriority); + + if (err == OK) { + break; + } else { + ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str()); + } + + mCodec->mNode = 0; + } + + if (mCodec->mNode == 0) { + if (!mime.empty()) { + ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.", + encoder ? "en" : "de", mime.c_str(), err); + } else { + ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err); + } + + mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err)); + return false; + } + + sp<AMessage> notify = new AMessage(kWhatOMXMessageList, mCodec); + observer->setNotificationMessage(notify); + mCodec->mComponentName = componentName; + + err = mCodec->configureCodec(mime.c_str(), msg); + + if (err != OK) { + mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err)); + return false; + } + } } { sp<AMessage> notify = mCodec->mNotify->dup(); notify->setInt32("what", CodecBase::kWhatComponentConfigured); + notify->setString("componentName", mCodec->mComponentName.c_str()); notify->setMessage("input-format", mCodec->mInputFormat); notify->setMessage("output-format", mCodec->mOutputFormat); notify->post(); @@ -6359,6 +6510,23 @@ void ACodec::onSignalEndOfInputStream() { notify->post(); } +sp<IOMXObserver> ACodec::createObserver() { + sp<CodecObserver> observer = new CodecObserver; + sp<AMessage> notify = new AMessage(kWhatOMXMessageList, this); + observer->setNotificationMessage(notify); + return observer; +} + +status_t ACodec::allocateBuffer( + OMX_U32 portIndex, size_t bufSize, BufferInfo &info) { + void *ptr; + status_t err = mOMX->allocateBuffer( + mNode, portIndex, bufSize, &info.mBufferID, &ptr); + + info.mData = new ABuffer(ptr, bufSize); + return err; +} + bool ACodec::ExecutingState::onOMXFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) { mCodec->onFrameRendered(mediaTimeUs, systemNano); return true; diff --git a/media/libstagefright/APE.cpp b/media/libstagefright/APE.cpp new file mode 100644 index 0000000..74ca7dc --- /dev/null +++ b/media/libstagefright/APE.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "APE_TAG" +#include <utils/Log.h> + +#include "include/APE.h" + +namespace android { + +APE::APE(){ + +} + +APE::~APE(){ + +} + +bool APE::isAPE(uint8_t *apeTag) const { + if(apeTag[0] == 'A' && apeTag[1] == 'P' && apeTag[2] == 'E' && + apeTag[3] == 'T' && apeTag[4] == 'A' && apeTag[5] == 'G' && + apeTag[6] == 'E' && apeTag[7] == 'X'){ + return true; + } + return false; +} + +size_t sizeItemKey(const sp<DataSource> &source, off64_t offset){ + off64_t ItemKeyOffset = offset; + uint8_t keyTerminator = 0; + size_t keySize = 0; + while (keyTerminator != 0){ + source->readAt(ItemKeyOffset, &keyTerminator, 1); + ItemKeyOffset++; + keySize++; + } + return keySize - 1; +} + +bool APE::parseAPE(const sp<DataSource> &source, off64_t offset, + sp<MetaData> &meta){ + + struct Map { + int key; + const char *tag; + } const kMap[] = { + { kKeyAlbum, "Album" }, + { kKeyArtist, "Artist" }, + { kKeyAlbumArtist, "Album" }, + { kKeyComposer, "Composer" }, + { kKeyGenre, "Genre" }, + { kKeyTitle, "Title" }, + { kKeyYear, "Year" }, + { kKeyCDTrackNumber, "Track" }, + { kKeyDate, "Record Date"}, + }; + + static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); + + off64_t headerOffset = offset; + headerOffset += 16; + itemNumber = 0; + if (source->readAt(headerOffset, &itemNumber, 1) == 0) + return false; + + headerOffset += 16; + + for(uint32_t it = 0; it < itemNumber; it++){ + lenValue = 0; + if (source->readAt(headerOffset, &lenValue, 1) == 0) + return false; + + headerOffset += 4; + + itemFlags = 0; + if (source->readAt(headerOffset, &itemFlags, 1) == 0) + return false; + + headerOffset += 4; + + size_t sizeKey = sizeItemKey(source, headerOffset); + + char *key = new char[sizeKey]; + + if (source->readAt(headerOffset, key, sizeKey) == 0) + return false; + + key[sizeKey] = '\0'; + headerOffset += sizeKey + 1; + + char *val = new char[lenValue + 1]; + + if (source->readAt(headerOffset, val, lenValue) == 0) + return false; + + val[lenValue] = '\0'; + + for (size_t i = 0; i < kNumMapEntries; i++){ + if (!strcmp(key, kMap[i].tag)){ + if (itemFlags == 0) + meta->setCString(kMap[i].key, (const char *)val); + break; + } + } + headerOffset += lenValue; + delete[] key; + delete[] val; + } + + return true; +} +} //namespace android diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 2529aa7..725789a 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -69,9 +69,12 @@ LOCAL_SRC_FILES:= \ WVMExtractor.cpp \ XINGSeeker.cpp \ avc_utils.cpp \ + APE.cpp \ + FFMPEGSoftCodec.cpp \ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/include/media/ \ + $(TOP)/frameworks/av/media/libavextensions \ $(TOP)/frameworks/av/include/media/stagefright/timedtext \ $(TOP)/frameworks/native/include/media/hardware \ $(TOP)/frameworks/native/include/media/openmax \ @@ -104,7 +107,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libvorbisidec \ libz \ - libpowermanager + libpowermanager \ LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ @@ -120,6 +123,30 @@ LOCAL_STATIC_LIBRARIES := \ libFLAC \ libmedia_helper \ +LOCAL_WHOLE_STATIC_LIBRARIES := libavextensions + +ifeq ($(BOARD_USE_S3D_SUPPORT), true) +ifeq ($(BOARD_USES_HWC_SERVICES), true) +LOCAL_CFLAGS += -DUSE_S3D_SUPPORT -DHWC_SERVICES +LOCAL_C_INCLUDES += \ + $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include \ + $(TOP)/hardware/samsung_slsi/openmax/include/exynos \ + $(TOP)/hardware/samsung_slsi/$(TARGET_BOARD_PLATFORM)-insignal/libhwcService \ + $(TOP)/hardware/samsung_slsi/$(TARGET_BOARD_PLATFORM)-insignal/libhwc \ + $(TOP)/hardware/samsung_slsi/$(TARGET_BOARD_PLATFORM)-insignal/include \ + $(TOP)/hardware/samsung_slsi/$(TARGET_SOC)/libhwcmodule \ + $(TOP)/hardware/samsung_slsi/$(TARGET_SOC)/include \ + $(TOP)/hardware/samsung_slsi/exynos/libexynosutils \ + $(TOP)/hardware/samsung_slsi/exynos/include + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + +LOCAL_SHARED_LIBRARIES += \ + libExynosHWCService +endif +endif + LOCAL_SHARED_LIBRARIES += \ libstagefright_enc_common \ libstagefright_avc_common \ @@ -136,6 +163,17 @@ endif LOCAL_CLANG := true +ifeq ($(BOARD_USE_SAMSUNG_CAMERAFORMAT_NV21), true) +# This needs flag requires the following string constant in +# CameraParametersExtra.h: +# +# const char CameraParameters::PIXEL_FORMAT_YUV420SP_NV21[] = "nv21"; +LOCAL_CFLAGS += -DUSE_SAMSUNG_CAMERAFORMAT_NV21 +endif + +# FFMPEG plugin +LOCAL_C_INCLUDES += $(TOP)/external/stagefright-plugins/include + LOCAL_MODULE:= libstagefright LOCAL_MODULE_TAGS := optional diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 6e4a1dd..dc9c37b 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -61,7 +61,7 @@ AudioSource::AudioSource( mNumClientOwnedBuffers(0) { ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u", sampleRate, outSampleRate, channelCount); - CHECK(channelCount == 1 || channelCount == 2); + CHECK(channelCount == 1 || channelCount == 2 || channelCount == 6); CHECK(sampleRate > 0); size_t minFrameCount; @@ -124,6 +124,8 @@ status_t AudioSource::start(MetaData *params) { int64_t startTimeUs; if (params && params->findInt64(kKeyTime, &startTimeUs)) { mStartTimeUs = startTimeUs; + } else { + mStartTimeUs = systemTime() / 1000ll; } status_t err = mRecord->start(); if (err == OK) { diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 66280da..aea0f13 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -33,6 +33,8 @@ #include <utils/String8.h> #include <cutils/properties.h> +#include <stagefright/AVExtensions.h> + #if LOG_NDEBUG #define UNUSED_UNLESS_VERBOSE(x) (void)(x) #else @@ -98,6 +100,11 @@ void CameraSourceListener::postDataTimestamp( } static int32_t getColorFormat(const char* colorFormat) { + if (!colorFormat) { + ALOGE("Invalid color format"); + return -1; + } + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) { return OMX_COLOR_FormatYUV420Planar; } @@ -110,6 +117,13 @@ static int32_t getColorFormat(const char* colorFormat) { return OMX_COLOR_FormatYUV420SemiPlanar; } +#ifdef USE_SAMSUNG_CAMERAFORMAT_NV21 + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP_NV21)) { + static const int OMX_SEC_COLOR_FormatNV21Linear = 0x7F000011; + return OMX_SEC_COLOR_FormatNV21Linear; + } +#endif /* USE_SAMSUNG_CAMERAFORMAT_NV21 */ + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I)) { return OMX_COLOR_FormatYCbYCr; } @@ -298,6 +312,12 @@ status_t CameraSource::isCameraColorFormatSupported( return OK; } +static int32_t getHighSpeedFrameRate(const CameraParameters& params) { + const char* hsr = params.get("video-hsr"); + int32_t rate = (hsr != NULL && strncmp(hsr, "off", 3)) ? atoi(hsr) : 0; + return rate > 240 ? 240 : rate; +} + /* * Configure the camera to use the requested video size * (width and height) and/or frame rate. If both width and @@ -350,6 +370,10 @@ status_t CameraSource::configureCamera( params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES); CHECK(supportedFrameRates != NULL); ALOGV("Supported frame rates: %s", supportedFrameRates); + if (getHighSpeedFrameRate(*params)) { + ALOGI("Use default 30fps for HighSpeed %dfps", frameRate); + frameRate = 30; + } char buf[4]; snprintf(buf, 4, "%d", frameRate); if (strstr(supportedFrameRates, buf) == NULL) { @@ -451,6 +475,8 @@ status_t CameraSource::checkFrameRate( ALOGE("Failed to retrieve preview frame rate (%d)", frameRateActual); return UNKNOWN_ERROR; } + int32_t highSpeedRate = getHighSpeedFrameRate(params); + frameRateActual = highSpeedRate ? highSpeedRate : frameRateActual; // Check the actual video frame rate against the target/requested // video frame rate. @@ -575,6 +601,8 @@ status_t CameraSource::initWithCameraAccess( mMeta->setInt32(kKeyStride, mVideoSize.width); mMeta->setInt32(kKeySliceHeight, mVideoSize.height); mMeta->setInt32(kKeyFrameRate, mVideoFrameRate); + AVUtils::get()->extractCustomCameraKeys(params, mMeta); + return OK; } diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 0acd9d0..926e95c 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -279,7 +279,8 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { // The first 2 output frames from the encoder are: decoder specific info and // the compressed video frame data for the first input video frame. if (mNumFramesEncoded >= 1 && *timestampUs < - (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenFrameCaptureUs)) { + (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenFrameCaptureUs) && + (mTimeBetweenFrameCaptureUs > mTimeBetweenTimeLapseVideoFramesUs + 1)) { // Skip all frames from last encoded frame until // sufficient time (mTimeBetweenFrameCaptureUs) has passed. // Tell the camera to release its recording frame and return. @@ -294,6 +295,12 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { mLastTimeLapseFrameRealTimestampUs = *timestampUs; *timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs; + // Update start-time once the captured-time reaches the expected start-time. + // Not doing so will result in CameraSource always dropping frames since + // updated-timestamp will never intersect start-timestamp + if ((mNumFramesReceived == 0 && mLastTimeLapseFrameRealTimestampUs >= mStartTimeUs)) { + mStartTimeUs = *timestampUs; + } return false; } return false; diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 5020c6c..2df045f 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -47,9 +47,28 @@ #include <utils/String8.h> #include <cutils/properties.h> +#include <cutils/log.h> + +#include <dlfcn.h> + +#include <stagefright/AVExtensions.h> namespace android { +static void *loadExtractorPlugin() { + void *ret = NULL; + char lib[PROPERTY_VALUE_MAX]; + if (property_get("media.sf.extractor-plugin", lib, NULL)) { + if (void *extractorLib = ::dlopen(lib, RTLD_LAZY)) { + ret = ::dlsym(extractorLib, "getExtractorPlugin"); + ALOGW_IF(!ret, "Failed to find symbol, dlerror: %s", ::dlerror()); + } else { + ALOGV("Failed to load %s, dlerror: %s", lib, ::dlerror()); + } + } + return ret; +} + bool DataSource::getUInt16(off64_t offset, uint16_t *x) { *x = 0; @@ -110,29 +129,49 @@ status_t DataSource::getSize(off64_t *size) { //////////////////////////////////////////////////////////////////////////////// -Mutex DataSource::gSnifferMutex; -List<DataSource::SnifferFunc> DataSource::gSniffers; -bool DataSource::gSniffersRegistered = false; - bool DataSource::sniff( String8 *mimeType, float *confidence, sp<AMessage> *meta) { + + return mSniffer->sniff(this, mimeType, confidence, meta); +} + +// static +void DataSource::RegisterSniffer_l(SnifferFunc /* func */) { + return; +} + +// static +void DataSource::RegisterDefaultSniffers() { + return; +} + +//////////////////////////////////////////////////////////////////////////////// + +Sniffer::Sniffer() { + registerDefaultSniffers(); +} + +bool Sniffer::sniff( + DataSource *source, String8 *mimeType, float *confidence, sp<AMessage> *meta) { + + bool forceExtraSniffers = false; + + if (*confidence == 3.14f) { + // Magic value, as set by MediaExtractor when a video container looks incomplete + forceExtraSniffers = true; + } + *mimeType = ""; *confidence = 0.0f; meta->clear(); - { - Mutex::Autolock autoLock(gSnifferMutex); - if (!gSniffersRegistered) { - return false; - } - } - - for (List<SnifferFunc>::iterator it = gSniffers.begin(); - it != gSniffers.end(); ++it) { + Mutex::Autolock autoLock(mSnifferMutex); + for (List<SnifferFunc>::iterator it = mSniffers.begin(); + it != mSniffers.end(); ++it) { String8 newMimeType; float newConfidence; sp<AMessage> newMeta; - if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) { + if ((*it)(source, &newMimeType, &newConfidence, &newMeta)) { if (newConfidence > *confidence) { *mimeType = newMimeType; *confidence = newConfidence; @@ -141,47 +180,81 @@ bool DataSource::sniff( } } + /* Only do the deeper sniffers if the results are null or in doubt */ + if (mimeType->length() == 0 || *confidence < 0.21f || forceExtraSniffers) { + for (List<SnifferFunc>::iterator it = mExtraSniffers.begin(); + it != mExtraSniffers.end(); ++it) { + String8 newMimeType; + float newConfidence; + sp<AMessage> newMeta; + if ((*it)(source, &newMimeType, &newConfidence, &newMeta)) { + if (newConfidence > *confidence) { + *mimeType = newMimeType; + *confidence = newConfidence; + *meta = newMeta; + } + } + } + } + return *confidence > 0.0; } -// static -void DataSource::RegisterSniffer_l(SnifferFunc func) { - for (List<SnifferFunc>::iterator it = gSniffers.begin(); - it != gSniffers.end(); ++it) { +void Sniffer::registerSniffer_l(SnifferFunc func) { + + for (List<SnifferFunc>::iterator it = mSniffers.begin(); + it != mSniffers.end(); ++it) { if (*it == func) { return; } } - gSniffers.push_back(func); + mSniffers.push_back(func); } -// static -void DataSource::RegisterDefaultSniffers() { - Mutex::Autolock autoLock(gSnifferMutex); - if (gSniffersRegistered) { - return; +void Sniffer::registerSnifferPlugin() { + static void (*getExtractorPlugin)(MediaExtractor::Plugin *) = + (void (*)(MediaExtractor::Plugin *))loadExtractorPlugin(); + + MediaExtractor::Plugin *plugin = MediaExtractor::getPlugin(); + if (!plugin->sniff && getExtractorPlugin) { + getExtractorPlugin(plugin); } + if (plugin->sniff) { + for (List<SnifferFunc>::iterator it = mExtraSniffers.begin(); + it != mExtraSniffers.end(); ++it) { + if (*it == plugin->sniff) { + return; + } + } - RegisterSniffer_l(SniffMPEG4); - RegisterSniffer_l(SniffMatroska); - RegisterSniffer_l(SniffOgg); - RegisterSniffer_l(SniffWAV); - RegisterSniffer_l(SniffFLAC); - RegisterSniffer_l(SniffAMR); - RegisterSniffer_l(SniffMPEG2TS); - RegisterSniffer_l(SniffMP3); - RegisterSniffer_l(SniffAAC); - RegisterSniffer_l(SniffMPEG2PS); - RegisterSniffer_l(SniffWVM); - RegisterSniffer_l(SniffMidi); + mExtraSniffers.push_back(plugin->sniff); + } +} + +void Sniffer::registerDefaultSniffers() { + Mutex::Autolock autoLock(mSnifferMutex); + + registerSniffer_l(SniffMPEG4); + registerSniffer_l(SniffMatroska); + registerSniffer_l(SniffOgg); + registerSniffer_l(SniffWAV); + registerSniffer_l(SniffFLAC); + registerSniffer_l(SniffAMR); + registerSniffer_l(SniffMPEG2TS); + registerSniffer_l(SniffMP3); + registerSniffer_l(SniffAAC); + registerSniffer_l(SniffMPEG2PS); + registerSniffer_l(SniffWVM); + registerSniffer_l(SniffMidi); + registerSniffer_l(AVUtils::get()->getExtendedSniffer()); + registerSnifferPlugin(); char value[PROPERTY_VALUE_MAX]; if (property_get("drm.service.enabled", value, NULL) && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { - RegisterSniffer_l(SniffDRM); + registerSniffer_l(SniffDRM); } - gSniffersRegistered = true; } // static @@ -190,7 +263,8 @@ sp<DataSource> DataSource::CreateFromURI( const char *uri, const KeyedVector<String8, String8> *headers, String8 *contentType, - HTTPBase *httpSource) { + HTTPBase *httpSource, + bool useExtendedCache) { if (contentType != NULL) { *contentType = ""; } @@ -214,7 +288,7 @@ sp<DataSource> DataSource::CreateFromURI( ALOGE("Failed to make http connection from http service!"); return NULL; } - httpSource = new MediaHTTP(conn); + httpSource = AVFactory::get()->createMediaHTTP(conn); } String8 tmp; @@ -246,10 +320,17 @@ sp<DataSource> DataSource::CreateFromURI( *contentType = httpSource->getMIMEType(); } - source = NuCachedSource2::Create( - httpSource, - cacheConfig.isEmpty() ? NULL : cacheConfig.string(), - disconnectAtHighwatermark); + if (useExtendedCache) { + source = AVFactory::get()->createCachedSource( + httpSource, + cacheConfig.isEmpty() ? NULL : cacheConfig.string(), + disconnectAtHighwatermark); + } else { + source = NuCachedSource2::Create( + httpSource, + cacheConfig.isEmpty() ? NULL : cacheConfig.string(), + disconnectAtHighwatermark); + } } else { // We do not want that prefetching, caching, datasource wrapper // in the widevine:// case. @@ -278,7 +359,7 @@ sp<DataSource> DataSource::CreateMediaHTTP(const sp<IMediaHTTPService> &httpServ if (conn == NULL) { return NULL; } else { - return new MediaHTTP(conn); + return AVFactory::get()->createMediaHTTP(conn); } } diff --git a/media/libstagefright/FFMPEGSoftCodec.cpp b/media/libstagefright/FFMPEGSoftCodec.cpp new file mode 100644 index 0000000..3e6692b --- /dev/null +++ b/media/libstagefright/FFMPEGSoftCodec.cpp @@ -0,0 +1,1149 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 "FFMPEGSoftCodec" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ABitReader.h> + +#include <media/stagefright/FFMPEGSoftCodec.h> + +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaCodecList.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXCodec.h> +#include <media/stagefright/Utils.h> + +#include <OMX_Component.h> +#include <OMX_AudioExt.h> +#include <OMX_IndexExt.h> + +#include <OMX_FFMPEG_Extn.h> + +namespace android { + +enum MetaKeyType{ + INT32, INT64, STRING, DATA, CSD +}; + +struct MetaKeyEntry{ + int MetaKey; + const char* MsgKey; + MetaKeyType KeyType; +}; + +static const MetaKeyEntry MetaKeyTable[] { + {kKeyAACAOT , "aac-profile" , INT32}, + {kKeyArbitraryMode , "use-arbitrary-mode" , INT32}, + {kKeyBitRate , "bitrate" , INT32}, + {kKeyBitsPerSample , "bit-width" , INT32}, + {kKeyBlockAlign , "block-align" , INT32}, + {kKeyChannelCount , "channel-count" , INT32}, + {kKeyCodecId , "codec-id" , INT32}, + {kKeyCodedSampleBits , "coded-sample-bits" , INT32}, + {kKeyRawCodecSpecificData , "raw-codec-specific-data", CSD}, + {kKeyRVVersion , "rv-version" , INT32}, + {kKeySampleFormat , "sample-format" , INT32}, + {kKeySampleRate , "sample-rate" , INT32}, + {kKeyWMAVersion , "wma-version" , INT32}, // int32_t + {kKeyWMVVersion , "wmv-version" , INT32}, + {kKeyPCMFormat , "pcm-format" , INT32}, +}; + +const char* FFMPEGSoftCodec::getMsgKey(int key) { + static const size_t numMetaKeys = + sizeof(MetaKeyTable) / sizeof(MetaKeyTable[0]); + size_t i; + for (i = 0; i < numMetaKeys; ++i) { + if (key == MetaKeyTable[i].MetaKey) { + return MetaKeyTable[i].MsgKey; + } + } + return "unknown"; +} + +void FFMPEGSoftCodec::convertMetaDataToMessageFF( + const sp<MetaData> &meta, sp<AMessage> *format) { + const char * str_val; + int32_t int32_val; + int64_t int64_val; + uint32_t data_type; + const void * data; + size_t size; + static const size_t numMetaKeys = + sizeof(MetaKeyTable) / sizeof(MetaKeyTable[0]); + size_t i; + for (i = 0; i < numMetaKeys; ++i) { + if (MetaKeyTable[i].KeyType == INT32 && + meta->findInt32(MetaKeyTable[i].MetaKey, &int32_val)) { + ALOGV("found metakey %s of type int32", MetaKeyTable[i].MsgKey); + format->get()->setInt32(MetaKeyTable[i].MsgKey, int32_val); + } else if (MetaKeyTable[i].KeyType == INT64 && + meta->findInt64(MetaKeyTable[i].MetaKey, &int64_val)) { + ALOGV("found metakey %s of type int64", MetaKeyTable[i].MsgKey); + format->get()->setInt64(MetaKeyTable[i].MsgKey, int64_val); + } else if (MetaKeyTable[i].KeyType == STRING && + meta->findCString(MetaKeyTable[i].MetaKey, &str_val)) { + ALOGV("found metakey %s of type string", MetaKeyTable[i].MsgKey); + format->get()->setString(MetaKeyTable[i].MsgKey, str_val); + } else if ( (MetaKeyTable[i].KeyType == DATA || + MetaKeyTable[i].KeyType == CSD) && + meta->findData(MetaKeyTable[i].MetaKey, &data_type, &data, &size)) { + ALOGV("found metakey %s of type data", MetaKeyTable[i].MsgKey); + if (MetaKeyTable[i].KeyType == CSD) { + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + sp<ABuffer> buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + format->get()->setBuffer("csd-0", buffer); + } else { + const uint8_t *ptr = (const uint8_t *)data; + CHECK(size >= 8); + int seqLength = 0, picLength = 0; + for (size_t i = 4; i < (size - 4); i++) + { + if ((*(ptr + i) == 0) && (*(ptr + i + 1) == 0) && + (*(ptr + i + 2) == 0) && (*(ptr + i + 3) == 1)) + seqLength = i; + } + sp<ABuffer> buffer = new ABuffer(seqLength); + memcpy(buffer->data(), data, seqLength); + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + format->get()->setBuffer("csd-0", buffer); + picLength=size-seqLength; + sp<ABuffer> buffer1 = new ABuffer(picLength); + memcpy(buffer1->data(), (const uint8_t *)data + seqLength, picLength); + buffer1->meta()->setInt32("csd", true); + buffer1->meta()->setInt64("timeUs", 0); + format->get()->setBuffer("csd-1", buffer1); + } + } else { + sp<ABuffer> buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + format->get()->setBuffer(MetaKeyTable[i].MsgKey, buffer); + } + } + + } +} + +void FFMPEGSoftCodec::convertMessageToMetaDataFF( + const sp<AMessage> &msg, sp<MetaData> &meta) { + AString str_val; + int32_t int32_val; + int64_t int64_val; + static const size_t numMetaKeys = + sizeof(MetaKeyTable) / sizeof(MetaKeyTable[0]); + size_t i; + for (i = 0; i < numMetaKeys; ++i) { + if (MetaKeyTable[i].KeyType == INT32 && + msg->findInt32(MetaKeyTable[i].MsgKey, &int32_val)) { + ALOGV("found metakey %s of type int32", MetaKeyTable[i].MsgKey); + meta->setInt32(MetaKeyTable[i].MetaKey, int32_val); + } else if (MetaKeyTable[i].KeyType == INT64 && + msg->findInt64(MetaKeyTable[i].MsgKey, &int64_val)) { + ALOGV("found metakey %s of type int64", MetaKeyTable[i].MsgKey); + meta->setInt64(MetaKeyTable[i].MetaKey, int64_val); + } else if (MetaKeyTable[i].KeyType == STRING && + msg->findString(MetaKeyTable[i].MsgKey, &str_val)) { + ALOGV("found metakey %s of type string", MetaKeyTable[i].MsgKey); + meta->setCString(MetaKeyTable[i].MetaKey, str_val.c_str()); + } + } +} + + +template<class T> +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; +} + +void FFMPEGSoftCodec::overrideComponentName( + uint32_t /*quirks*/, const sp<AMessage> &msg, AString* componentName, AString* mime, int32_t isEncoder) { + + int32_t wmvVersion = 0; + if (!strncasecmp(mime->c_str(), MEDIA_MIMETYPE_VIDEO_WMV, strlen(MEDIA_MIMETYPE_VIDEO_WMV)) && + msg->findInt32(getMsgKey(kKeyWMVVersion), &wmvVersion)) { + ALOGD("Found WMV version key %d", wmvVersion); + if (wmvVersion == 1) { + ALOGD("Use FFMPEG for unsupported WMV track"); + componentName->setTo("OMX.ffmpeg.wmv.decoder"); + } + } + + int32_t encodeOptions = 0; + if (!isEncoder && !strncasecmp(mime->c_str(), MEDIA_MIMETYPE_AUDIO_WMA, strlen(MEDIA_MIMETYPE_AUDIO_WMA)) && + !msg->findInt32(getMsgKey(kKeyWMAEncodeOpt), &encodeOptions)) { + ALOGD("Use FFMPEG for unsupported WMA track"); + componentName->setTo("OMX.ffmpeg.wma.decoder"); + } + + // Google's decoder doesn't support MAIN profile + int32_t aacProfile = 0; + if (!isEncoder && !strncasecmp(mime->c_str(), MEDIA_MIMETYPE_AUDIO_AAC, strlen(MEDIA_MIMETYPE_AUDIO_AAC)) && + msg->findInt32(getMsgKey(kKeyAACAOT), &aacProfile)) { + if (aacProfile == OMX_AUDIO_AACObjectMain) { + ALOGD("Use FFMPEG for AAC MAIN profile"); + componentName->setTo("OMX.ffmpeg.aac.decoder"); + } + } +} + +status_t FFMPEGSoftCodec::setVideoFormat( + const sp<AMessage> &msg, const char* mime, sp<IOMX> OMXhandle, + IOMX::node_id nodeID, bool isEncoder, + OMX_VIDEO_CODINGTYPE *compressionFormat) { + status_t err = OK; + + if (isEncoder) { + ALOGE("Encoding not supported"); + err = BAD_VALUE; + + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_WMV, mime)) { + err = setWMVFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setWMVFormat() failed (err = %d)", err); + } else { + *compressionFormat = OMX_VIDEO_CodingWMV; + } + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_RV, mime)) { + err = setRVFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setRVFormat() failed (err = %d)", err); + } else { + *compressionFormat = OMX_VIDEO_CodingRV; + } + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VC1, mime)) { + *compressionFormat = (OMX_VIDEO_CODINGTYPE)OMX_VIDEO_CodingVC1; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_FLV1, mime)) { + *compressionFormat = (OMX_VIDEO_CODINGTYPE)OMX_VIDEO_CodingFLV1; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX, mime)) { + *compressionFormat = (OMX_VIDEO_CODINGTYPE)OMX_VIDEO_CodingDIVX; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { + *compressionFormat = (OMX_VIDEO_CODINGTYPE)OMX_VIDEO_CodingHEVC; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_FFMPEG, mime)) { + ALOGV("Setting the OMX_VIDEO_PARAM_FFMPEGTYPE params"); + err = setFFmpegVideoFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setFFmpegVideoFormat() failed (err = %d)", err); + } else { + *compressionFormat = OMX_VIDEO_CodingAutoDetect; + } + } else { + err = BAD_TYPE; + } + + return err; +} + +status_t FFMPEGSoftCodec::getVideoPortFormat(OMX_U32 portIndex, int coding, + sp<AMessage> ¬ify, sp<IOMX> OMXHandle, IOMX::node_id nodeId) { + + status_t err = BAD_TYPE; + switch (coding) { + case OMX_VIDEO_CodingWMV: + { + OMX_VIDEO_PARAM_WMVTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, OMX_IndexParamVideoWmv, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + int32_t version; + if (params.eFormat == OMX_VIDEO_WMVFormat7) { + version = kTypeWMVVer_7; + } else if (params.eFormat == OMX_VIDEO_WMVFormat8) { + version = kTypeWMVVer_8; + } else { + version = kTypeWMVVer_9; + } + notify->setString("mime", MEDIA_MIMETYPE_VIDEO_WMV); + notify->setInt32("wmv-version", version); + break; + } + case OMX_VIDEO_CodingAutoDetect: + { + OMX_VIDEO_PARAM_FFMPEGTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamVideoFFmpeg, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_VIDEO_FFMPEG); + notify->setInt32("codec-id", params.eCodecId); + break; + } + case OMX_VIDEO_CodingRV: + { + OMX_VIDEO_PARAM_RVTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamVideoRv, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + int32_t version; + if (params.eFormat == OMX_VIDEO_RVFormatG2) { + version = kTypeRVVer_G2; + } else if (params.eFormat == OMX_VIDEO_RVFormat8) { + version = kTypeRVVer_8; + } else { + version = kTypeRVVer_9; + } + notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RV); + break; + } + } + return err; +} + +status_t FFMPEGSoftCodec::getAudioPortFormat(OMX_U32 portIndex, int coding, + sp<AMessage> ¬ify, sp<IOMX> OMXHandle, IOMX::node_id nodeId) { + + status_t err = BAD_TYPE; + switch (coding) { + case OMX_AUDIO_CodingRA: + { + OMX_AUDIO_PARAM_RATYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, OMX_IndexParamAudioRa, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RA); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSamplingRate); + break; + } + case OMX_AUDIO_CodingMP2: + { + OMX_AUDIO_PARAM_MP2TYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamAudioMp2, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + case OMX_AUDIO_CodingWMA: + { + OMX_AUDIO_PARAM_WMATYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, OMX_IndexParamAudioWma, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_WMA); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSamplingRate); + break; + } + case OMX_AUDIO_CodingAPE: + { + OMX_AUDIO_PARAM_APETYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamAudioApe, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_APE); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSamplingRate); + notify->setInt32("bit-width", params.nBitsPerSample); + break; + } + case OMX_AUDIO_CodingFLAC: + { + OMX_AUDIO_PARAM_FLACTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamAudioFlac, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_FLAC); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + notify->setInt32("bit-width", params.nCompressionLevel); // piggyback + break; + } + + case OMX_AUDIO_CodingDTS: + { + OMX_AUDIO_PARAM_DTSTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamAudioDts, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_DTS); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSamplingRate); + break; + } + case OMX_AUDIO_CodingAC3: + { + OMX_AUDIO_PARAM_ANDROID_AC3TYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AC3); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + + case OMX_AUDIO_CodingAutoDetect: + { + OMX_AUDIO_PARAM_FFMPEGTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = OMXHandle->getParameter( + nodeId, (OMX_INDEXTYPE)OMX_IndexParamAudioFFmpeg, ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_FFMPEG); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + } + return err; +} + +status_t FFMPEGSoftCodec::setAudioFormat( + const sp<AMessage> &msg, const char* mime, sp<IOMX> OMXhandle, + IOMX::node_id nodeID) { + ALOGV("setAudioFormat called"); + status_t err = OK; + + ALOGV("setAudioFormat: %s", msg->debugString(0).c_str()); + + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_WMA, mime)) { + err = setWMAFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setWMAFormat() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_VORBIS, mime)) { + err = setVORBISFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setVORBISFormat() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RA, mime)) { + err = setRAFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setRAFormat() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_FLAC, mime)) { + err = setFLACFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setFLACFormat() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II, mime)) { + err = setMP2Format(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setMP2Format() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mime)) { + err = setAC3Format(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setAC3Format() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_APE, mime)) { + err = setAPEFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setAPEFormat() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_DTS, mime)) { + err = setDTSFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setDTSFormat() failed (err = %d)", err); + } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_FFMPEG, mime)) { + err = setFFmpegAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) { + ALOGE("setFFmpegAudioFormat() failed (err = %d)", err); + } + } + + return err; +} + +status_t FFMPEGSoftCodec::setSupportedRole( + const sp<IOMX> &omx, IOMX::node_id node, + bool isEncoder, const char *mime) { + + ALOGV("setSupportedRole Called %s", mime); + + struct MimeToRole { + const char *mime; + const char *decoderRole; + const char *encoderRole; + }; + + static const MimeToRole kFFMPEGMimeToRole[] = { + { MEDIA_MIMETYPE_AUDIO_AAC, + "audio_decoder.aac", NULL }, + { MEDIA_MIMETYPE_AUDIO_MPEG, + "audio_decoder.mp3", NULL }, + { MEDIA_MIMETYPE_AUDIO_VORBIS, + "audio_decoder.vorbis", NULL }, + { MEDIA_MIMETYPE_AUDIO_WMA, + "audio_decoder.wma", NULL }, + { MEDIA_MIMETYPE_AUDIO_RA, + "audio_decoder.ra" , NULL }, + { MEDIA_MIMETYPE_AUDIO_FLAC, + "audio_decoder.flac", NULL }, + { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II, + "audio_decoder.mp2", NULL }, + { MEDIA_MIMETYPE_AUDIO_AC3, + "audio_decoder.ac3", NULL }, + { MEDIA_MIMETYPE_AUDIO_APE, + "audio_decoder.ape", NULL }, + { MEDIA_MIMETYPE_AUDIO_DTS, + "audio_decoder.dts", NULL }, + { MEDIA_MIMETYPE_VIDEO_MPEG2, + "video_decoder.mpeg2", NULL }, + { MEDIA_MIMETYPE_VIDEO_DIVX, + "video_decoder.divx", NULL }, + { MEDIA_MIMETYPE_VIDEO_DIVX4, + "video_decoder.divx", NULL }, + { MEDIA_MIMETYPE_VIDEO_DIVX311, + "video_decoder.divx", NULL }, + { MEDIA_MIMETYPE_VIDEO_WMV, + "video_decoder.wmv", NULL }, + { MEDIA_MIMETYPE_VIDEO_VC1, + "video_decoder.vc1", NULL }, + { MEDIA_MIMETYPE_VIDEO_RV, + "video_decoder.rv", NULL }, + { MEDIA_MIMETYPE_VIDEO_FLV1, + "video_decoder.flv1", NULL }, + { MEDIA_MIMETYPE_VIDEO_HEVC, + "video_decoder.hevc", NULL }, + { MEDIA_MIMETYPE_AUDIO_FFMPEG, + "audio_decoder.trial", NULL }, + { MEDIA_MIMETYPE_VIDEO_FFMPEG, + "video_decoder.trial", NULL }, + }; + static const size_t kNumMimeToRole = + sizeof(kFFMPEGMimeToRole) / sizeof(kFFMPEGMimeToRole[0]); + + size_t i; + for (i = 0; i < kNumMimeToRole; ++i) { + if (!strcasecmp(mime, kFFMPEGMimeToRole[i].mime)) { + break; + } + } + + if (i == kNumMimeToRole) { + return ERROR_UNSUPPORTED; + } + + const char *role = + isEncoder ? kFFMPEGMimeToRole[i].encoderRole + : kFFMPEGMimeToRole[i].decoderRole; + + if (role != NULL) { + OMX_PARAM_COMPONENTROLETYPE roleParams; + InitOMXParams(&roleParams); + + strncpy((char *)roleParams.cRole, + role, OMX_MAX_STRINGNAME_SIZE - 1); + + roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; + + status_t err = omx->setParameter( + node, OMX_IndexParamStandardComponentRole, + &roleParams, sizeof(roleParams)); + + if (err != OK) { + ALOGW("Failed to set standard component role '%s'.", role); + return err; + } + } + return OK; +} + +//video +status_t FFMPEGSoftCodec::setWMVFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t version = -1; + OMX_VIDEO_PARAM_WMVTYPE paramWMV; + + if (!msg->findInt32(getMsgKey(kKeyWMVVersion), &version)) { + ALOGE("WMV version not detected"); + } + + InitOMXParams(¶mWMV); + paramWMV.nPortIndex = kPortIndexInput; + + status_t err = OMXhandle->getParameter( + nodeID, OMX_IndexParamVideoWmv, ¶mWMV, sizeof(paramWMV)); + if (err != OK) { + return err; + } + + if (version == kTypeWMVVer_7) { + paramWMV.eFormat = OMX_VIDEO_WMVFormat7; + } else if (version == kTypeWMVVer_8) { + paramWMV.eFormat = OMX_VIDEO_WMVFormat8; + } else if (version == kTypeWMVVer_9) { + paramWMV.eFormat = OMX_VIDEO_WMVFormat9; + } + + err = OMXhandle->setParameter( + nodeID, OMX_IndexParamVideoWmv, ¶mWMV, sizeof(paramWMV)); + return err; +} + +status_t FFMPEGSoftCodec::setRVFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t version = kTypeRVVer_G2; + OMX_VIDEO_PARAM_RVTYPE paramRV; + + if (!msg->findInt32(getMsgKey(kKeyRVVersion), &version)) { + ALOGE("RV version not detected"); + } + + InitOMXParams(¶mRV); + paramRV.nPortIndex = kPortIndexInput; + + status_t err = OMXhandle->getParameter( + nodeID, OMX_IndexParamVideoRv, ¶mRV, sizeof(paramRV)); + if (err != OK) + return err; + + if (version == kTypeRVVer_G2) { + paramRV.eFormat = OMX_VIDEO_RVFormatG2; + } else if (version == kTypeRVVer_8) { + paramRV.eFormat = OMX_VIDEO_RVFormat8; + } else if (version == kTypeRVVer_9) { + paramRV.eFormat = OMX_VIDEO_RVFormat9; + } + + err = OMXhandle->setParameter( + nodeID, OMX_IndexParamVideoRv, ¶mRV, sizeof(paramRV)); + return err; +} + +status_t FFMPEGSoftCodec::setFFmpegVideoFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t codec_id = 0; + int32_t width = 0; + int32_t height = 0; + OMX_VIDEO_PARAM_FFMPEGTYPE param; + + ALOGD("setFFmpegVideoFormat"); + + if (msg->findInt32(getMsgKey(kKeyWidth), &width)) { + ALOGE("No video width specified"); + } + if (msg->findInt32(getMsgKey(kKeyHeight), &height)) { + ALOGE("No video height specified"); + } + if (!msg->findInt32(getMsgKey(kKeyCodecId), &codec_id)) { + ALOGE("No codec id sent for FFMPEG catch-all codec!"); + } + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + status_t err = OMXhandle->getParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamVideoFFmpeg, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.eCodecId = codec_id; + param.nWidth = width; + param.nHeight = height; + + err = OMXhandle->setParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamVideoFFmpeg, ¶m, sizeof(param)); + return err; +} + +//audio +status_t FFMPEGSoftCodec::setRawAudioFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t sampleRate = 0; + int32_t bitsPerSample = 16; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + if (!msg->findInt32(getMsgKey(kKeyBitsPerSample), &bitsPerSample)) { + ALOGD("No PCM format specified, using 16 bit"); + } + + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexOutput; + + status_t err = OMXhandle->getParameter( + nodeID, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + return err; + } + + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + err = OMXhandle->setParameter( + nodeID, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + return err; + } + + OMX_AUDIO_PARAM_PCMMODETYPE pcmParams; + InitOMXParams(&pcmParams); + pcmParams.nPortIndex = kPortIndexOutput; + + err = OMXhandle->getParameter( + nodeID, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); + + if (err != OK) { + return err; + } + + pcmParams.nChannels = numChannels; + pcmParams.eNumData = OMX_NumericalDataSigned; + pcmParams.bInterleaved = OMX_TRUE; + pcmParams.nBitPerSample = bitsPerSample; + pcmParams.nSamplingRate = sampleRate; + pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear; + + if (getOMXChannelMapping(numChannels, pcmParams.eChannelMapping) != OK) { + return OMX_ErrorNone; + } + + return OMXhandle->setParameter( + nodeID, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); +} + +status_t FFMPEGSoftCodec::setWMAFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t version = 0; + int32_t numChannels = 0; + int32_t bitRate = 0; + int32_t sampleRate = 0; + int32_t blockAlign = 0; + int32_t bitsPerSample = 0; + + OMX_AUDIO_PARAM_WMATYPE paramWMA; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + CHECK(msg->findInt32(getMsgKey(kKeyBitRate), &bitRate)); + if (!msg->findInt32(getMsgKey(kKeyBlockAlign), &blockAlign)) { + // we should be last on the codec list, but another sniffer may + // have handled it and there is no hardware codec. + if (!msg->findInt32(getMsgKey(kKeyWMABlockAlign), &blockAlign)) { + return ERROR_UNSUPPORTED; + } + } + + // mm-parser may want a different bit depth + if (msg->findInt32(getMsgKey(kKeyWMABitspersample), &bitsPerSample)) { + msg->setInt32("bit-width", bitsPerSample); + } + + ALOGV("Channels: %d, SampleRate: %d, BitRate: %d, blockAlign: %d", + numChannels, sampleRate, bitRate, blockAlign); + + CHECK(msg->findInt32(getMsgKey(kKeyWMAVersion), &version)); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶mWMA); + paramWMA.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA)); + if (err != OK) + return err; + + paramWMA.nChannels = numChannels; + paramWMA.nSamplingRate = sampleRate; + paramWMA.nBitRate = bitRate; + paramWMA.nBlockAlign = blockAlign; + + // http://msdn.microsoft.com/en-us/library/ff819498(v=vs.85).aspx + if (version == kTypeWMA) { + paramWMA.eFormat = OMX_AUDIO_WMAFormat7; + } else if (version == kTypeWMAPro) { + paramWMA.eFormat = OMX_AUDIO_WMAFormat8; + } else if (version == kTypeWMALossLess) { + paramWMA.eFormat = OMX_AUDIO_WMAFormat9; + } + + return OMXhandle->setParameter( + nodeID, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA)); +} + +status_t FFMPEGSoftCodec::setVORBISFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t sampleRate = 0; + OMX_AUDIO_PARAM_VORBISTYPE param; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + + ALOGV("Channels: %d, SampleRate: %d", + numChannels, sampleRate); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, OMX_IndexParamAudioVorbis, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.nChannels = numChannels; + param.nSampleRate = sampleRate; + + return OMXhandle->setParameter( + nodeID, OMX_IndexParamAudioVorbis, ¶m, sizeof(param)); +} + +status_t FFMPEGSoftCodec::setRAFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t bitRate = 0; + int32_t sampleRate = 0; + int32_t blockAlign = 0; + OMX_AUDIO_PARAM_RATYPE paramRA; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + msg->findInt32(getMsgKey(kKeyBitRate), &bitRate); + CHECK(msg->findInt32(getMsgKey(kKeyBlockAlign), &blockAlign)); + + ALOGV("Channels: %d, SampleRate: %d, BitRate: %d, blockAlign: %d", + numChannels, sampleRate, bitRate, blockAlign); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶mRA); + paramRA.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, OMX_IndexParamAudioRa, ¶mRA, sizeof(paramRA)); + if (err != OK) + return err; + + paramRA.eFormat = OMX_AUDIO_RAFormatUnused; // FIXME, cook only??? + paramRA.nChannels = numChannels; + paramRA.nSamplingRate = sampleRate; + // FIXME, HACK!!!, I use the nNumRegions parameter pass blockAlign!!! + // the cook audio codec need blockAlign! + paramRA.nNumRegions = blockAlign; + + return OMXhandle->setParameter( + nodeID, OMX_IndexParamAudioRa, ¶mRA, sizeof(paramRA)); +} + +status_t FFMPEGSoftCodec::setFLACFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t sampleRate = 0; + int32_t bitsPerSample = 16; + OMX_AUDIO_PARAM_FLACTYPE param; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + CHECK(msg->findInt32(getMsgKey(kKeyBitsPerSample), &bitsPerSample)); + + ALOGV("Channels: %d, SampleRate: %d BitsPerSample: %d", + numChannels, sampleRate, bitsPerSample); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, OMX_IndexParamAudioFlac, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.nChannels = numChannels; + param.nSampleRate = sampleRate; + param.nCompressionLevel = bitsPerSample; // piggyback hax! + + return OMXhandle->setParameter( + nodeID, OMX_IndexParamAudioFlac, ¶m, sizeof(param)); +} + +status_t FFMPEGSoftCodec::setMP2Format( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t sampleRate = 0; + OMX_AUDIO_PARAM_MP2TYPE param; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + + ALOGV("Channels: %d, SampleRate: %d", + numChannels, sampleRate); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioMp2, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.nChannels = numChannels; + param.nSampleRate = sampleRate; + + return OMXhandle->setParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioMp2, ¶m, sizeof(param)); +} + +status_t FFMPEGSoftCodec::setAC3Format( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t sampleRate = 0; + OMX_AUDIO_PARAM_ANDROID_AC3TYPE param; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + + ALOGV("Channels: %d, SampleRate: %d", + numChannels, sampleRate); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.nChannels = numChannels; + param.nSampleRate = sampleRate; + + return OMXhandle->setParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, ¶m, sizeof(param)); +} + +status_t FFMPEGSoftCodec::setAPEFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t sampleRate = 0; + int32_t bitsPerSample = 0; + OMX_AUDIO_PARAM_APETYPE param; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + CHECK(msg->findInt32(getMsgKey(kKeyBitsPerSample), &bitsPerSample)); + + ALOGV("Channels:%d, SampleRate:%d, bitsPerSample:%d", + numChannels, sampleRate, bitsPerSample); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioApe, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.nChannels = numChannels; + param.nSamplingRate = sampleRate; + param.nBitsPerSample = bitsPerSample; + + return OMXhandle->setParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioApe, ¶m, sizeof(param)); +} + +status_t FFMPEGSoftCodec::setDTSFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t numChannels = 0; + int32_t sampleRate = 0; + OMX_AUDIO_PARAM_DTSTYPE param; + + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate)); + + ALOGV("Channels: %d, SampleRate: %d", + numChannels, sampleRate); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioDts, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.nChannels = numChannels; + param.nSamplingRate = sampleRate; + + return OMXhandle->setParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioDts, ¶m, sizeof(param)); +} + +status_t FFMPEGSoftCodec::setFFmpegAudioFormat( + const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID) +{ + int32_t codec_id = 0; + int32_t numChannels = 0; + int32_t bitRate = 0; + int32_t bitsPerSample = 16; + int32_t sampleRate = 0; + int32_t blockAlign = 0; + int32_t sampleFormat = 0; + int32_t codedSampleBits = 0; + OMX_AUDIO_PARAM_FFMPEGTYPE param; + + ALOGD("setFFmpegAudioFormat"); + + CHECK(msg->findInt32(getMsgKey(kKeyCodecId), &codec_id)); + CHECK(msg->findInt32(getMsgKey(kKeyChannelCount), &numChannels)); + CHECK(msg->findInt32(getMsgKey(kKeySampleFormat), &sampleFormat)); + msg->findInt32(getMsgKey(kKeyBitRate), &bitRate); + msg->findInt32(getMsgKey(kKeyBitsPerSample), &bitsPerSample); + msg->findInt32(getMsgKey(kKeySampleRate), &sampleRate); + msg->findInt32(getMsgKey(kKeyBlockAlign), &blockAlign); + msg->findInt32(getMsgKey(kKeyBitsPerSample), &bitsPerSample); + msg->findInt32(getMsgKey(kKeyCodedSampleBits), &codedSampleBits); + + status_t err = setRawAudioFormat(msg, OMXhandle, nodeID); + if (err != OK) + return err; + + InitOMXParams(¶m); + param.nPortIndex = kPortIndexInput; + + err = OMXhandle->getParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioFFmpeg, ¶m, sizeof(param)); + if (err != OK) + return err; + + param.eCodecId = codec_id; + param.nChannels = numChannels; + param.nBitRate = bitRate; + param.nBitsPerSample = codedSampleBits; + param.nSampleRate = sampleRate; + param.nBlockAlign = blockAlign; + param.eSampleFormat = sampleFormat; + + return OMXhandle->setParameter( + nodeID, (OMX_INDEXTYPE)OMX_IndexParamAudioFFmpeg, ¶m, sizeof(param)); +} + +} diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp index 89a91f7..87345e1 100644 --- a/media/libstagefright/FLACExtractor.cpp +++ b/media/libstagefright/FLACExtractor.cpp @@ -32,6 +32,13 @@ #include <media/stagefright/MediaSource.h> #include <media/stagefright/MediaBuffer.h> +#ifdef ENABLE_AV_ENHANCEMENTS +#include "QCMediaDefs.h" +#include "QCMetaData.h" +#endif + +#include <system/audio.h> + namespace android { class FLACParser; @@ -72,6 +79,8 @@ private: class FLACParser : public RefBase { +friend class FLACSource; + public: FLACParser( const sp<DataSource> &dataSource, @@ -103,6 +112,8 @@ public: // media buffers void allocateBuffers(); void releaseBuffers(); + void copyBuffer(short *dst, const int *const *src, unsigned nSamples); + MediaBuffer *readBuffer() { return readBuffer(false, 0LL); } @@ -113,6 +124,7 @@ public: protected: virtual ~FLACParser(); + private: sp<DataSource> mDataSource; sp<MetaData> mFileMetadata; @@ -122,7 +134,6 @@ private: // media buffers size_t mMaxBufferSize; MediaBufferGroup *mGroup; - void (*mCopy)(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels); // handle to underlying libFLAC parser FLAC__StreamDecoder *mDecoder; @@ -377,109 +388,41 @@ void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) mErrorStatus = status; } -// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved. -// These are candidates for optimization if needed. - -static void copyMono8( - short *dst, - const int *const *src, - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] << 8; - } -} - -static void copyStereo8( - short *dst, - const int *const *src, - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] << 8; - *dst++ = src[1][i] << 8; - } -} - -static void copyMultiCh8(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels) +void FLACParser::copyBuffer(short *dst, const int *const *src, unsigned nSamples) { - for (unsigned i = 0; i < nSamples; ++i) { - for (unsigned c = 0; c < nChannels; ++c) { - *dst++ = src[c][i] << 8; - } - } -} - -static void copyMono16( - short *dst, - const int *const *src, - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i]; - } -} - -static void copyStereo16( - short *dst, - const int *const *src, - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i]; - *dst++ = src[1][i]; - } -} - -static void copyMultiCh16(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels) -{ - for (unsigned i = 0; i < nSamples; ++i) { - for (unsigned c = 0; c < nChannels; ++c) { - *dst++ = src[c][i]; - } - } -} - -// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger - -static void copyMono24( - short *dst, - const int *const *src, - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] >> 8; - } -} - -static void copyStereo24( - short *dst, - const int *const *src, - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] >> 8; - *dst++ = src[1][i] >> 8; - } -} - -static void copyMultiCh24(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels) -{ - for (unsigned i = 0; i < nSamples; ++i) { - for (unsigned c = 0; c < nChannels; ++c) { - *dst++ = src[c][i] >> 8; + unsigned int nChannels = getChannels(); + unsigned int nBits = getBitsPerSample(); + switch (nBits) { + case 8: + for (unsigned i = 0; i < nSamples; ++i) { + for (unsigned c = 0; c < nChannels; ++c) { + *dst++ = src[c][i] << 8; + } + } + break; + case 16: + for (unsigned i = 0; i < nSamples; ++i) { + for (unsigned c = 0; c < nChannels; ++c) { + *dst++ = src[c][i]; + } + } + break; + case 24: + case 32: + { + int32_t *out = (int32_t *)dst; + for (unsigned i = 0; i < nSamples; ++i) { + for (unsigned c = 0; c < nChannels; ++c) { + *out++ = src[c][i] << 8; + } + } + break; } + default: + TRESPASS(); } } -static void copyTrespass( - short * /* dst */, - const int *const * /* src */, - unsigned /* nSamples */, - unsigned /* nChannels */) { - TRESPASS(); -} - // FLACParser FLACParser::FLACParser( @@ -492,7 +435,6 @@ FLACParser::FLACParser( mInitCheck(false), mMaxBufferSize(0), mGroup(NULL), - mCopy(copyTrespass), mDecoder(NULL), mCurrentPos(0LL), mEOF(false), @@ -571,6 +513,8 @@ status_t FLACParser::init() } // check sample rate switch (getSampleRate()) { + case 100: + case 1000: case 8000: case 11025: case 12000: @@ -578,38 +522,18 @@ status_t FLACParser::init() case 22050: case 24000: case 32000: + case 42000: case 44100: + case 46000: case 48000: case 88200: case 96000: + case 192000: break; default: ALOGE("unsupported sample rate %u", getSampleRate()); return NO_INIT; } - // configure the appropriate copy function, defaulting to trespass - static const struct { - unsigned mChannels; - unsigned mBitsPerSample; - void (*mCopy)(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels); - } table[] = { - { 1, 8, copyMono8 }, - { 2, 8, copyStereo8 }, - { 8, 8, copyMultiCh8 }, - { 1, 16, copyMono16 }, - { 2, 16, copyStereo16 }, - { 8, 16, copyMultiCh16 }, - { 1, 24, copyMono24 }, - { 2, 24, copyStereo24 }, - { 8, 24, copyMultiCh24 }, - }; - for (unsigned i = 0; i < sizeof(table)/sizeof(table[0]); ++i) { - if (table[i].mChannels >= getChannels() && - table[i].mBitsPerSample == getBitsPerSample()) { - mCopy = table[i].mCopy; - break; - } - } // populate track metadata if (mTrackMetadata != 0) { mTrackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); @@ -618,6 +542,7 @@ status_t FLACParser::init() // sample rate is non-zero, so division by zero not possible mTrackMetadata->setInt64(kKeyDuration, (getTotalSamples() * 1000000LL) / getSampleRate()); + mTrackMetadata->setInt32(kKeyBitsPerSample, getBitsPerSample()); } } else { ALOGE("missing STREAMINFO"); @@ -633,7 +558,9 @@ void FLACParser::allocateBuffers() { CHECK(mGroup == NULL); mGroup = new MediaBufferGroup; - mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(short); + // allocate enough to hold 24-bits (packed in 32 bits) + unsigned int bytesPerSample = getBitsPerSample() > 16 ? 4 : 2; + mMaxBufferSize = getMaxBlockSize() * getChannels() * bytesPerSample; mGroup->add_buffer(new MediaBuffer(mMaxBufferSize)); } @@ -686,12 +613,12 @@ MediaBuffer *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) if (err != OK) { return NULL; } - size_t bufferSize = blocksize * getChannels() * sizeof(short); + size_t bufferSize = blocksize * getChannels() * (getBitsPerSample() > 16 ? 4 : 2); CHECK(bufferSize <= mMaxBufferSize); short *data = (short *) buffer->data(); buffer->set_range(0, bufferSize); // copy PCM from FLAC write buffer to our media buffer, with interleaving - (*mCopy)(data, mWriteBuffer, blocksize, getChannels()); + copyBuffer(data, mWriteBuffer, blocksize); // fill in buffer metadata CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number; @@ -726,9 +653,10 @@ FLACSource::~FLACSource() status_t FLACSource::start(MetaData * /* params */) { + CHECK(!mStarted); + ALOGV("FLACSource::start"); - CHECK(!mStarted); mParser->allocateBuffers(); mStarted = true; @@ -845,12 +773,14 @@ bool SniffFLAC( { // first 4 is the signature word // second 4 is the sizeof STREAMINFO + // 1st bit of 2nd 4 bytes represent whether last block of metadata or not // 042 is the mandatory STREAMINFO // no need to read rest of the header, as a premature EOF will be caught later uint8_t header[4+4]; if (source->readAt(0, header, sizeof(header)) != sizeof(header) - || memcmp("fLaC\0\0\0\042", header, 4+4)) - { + || memcmp("fLaC", header, 4) + || !(header[4] == 0x80 || header[4] == 0x00) + || memcmp("\0\0\042", header + 5, 3)) { return false; } diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index 565f156..f7b1a02 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -30,6 +30,7 @@ namespace android { FileSource::FileSource(const char *filename) : mFd(-1), + mUri(filename), mOffset(0), mLength(-1), mDecryptHandle(NULL), @@ -58,6 +59,7 @@ FileSource::FileSource(int fd, int64_t offset, int64_t length) mDrmBuf(NULL){ CHECK(offset >= 0); CHECK(length >= 0); + fetchUriFromFd(fd); } FileSource::~FileSource() { @@ -188,4 +190,18 @@ ssize_t FileSource::readAtDRM(off64_t offset, void *data, size_t size) { return mDrmManagerClient->pread(mDecryptHandle, data, size, offset + mOffset); } } + +void FileSource::fetchUriFromFd(int fd) { + ssize_t len = 0; + char path[PATH_MAX] = {0}; + char link[PATH_MAX] = {0}; + + mUri.clear(); + + snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), fd); + if ((len = readlink(path, link, sizeof(link)-1)) != -1) { + link[len] = '\0'; + mUri.setTo(link); + } +} } // namespace android diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index a1af3aa..eee13c7 100755 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -315,6 +315,9 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC('m', 'p', '4', 'a'): return MEDIA_MIMETYPE_AUDIO_AAC; + case FOURCC('.', 'm', 'p', '3'): + return MEDIA_MIMETYPE_AUDIO_MPEG; + case FOURCC('s', 'a', 'm', 'r'): return MEDIA_MIMETYPE_AUDIO_AMR_NB; @@ -535,6 +538,7 @@ status_t MPEG4Extractor::readMetaData() { } if (psshsize > 0 && psshsize <= UINT32_MAX) { char *buf = (char*)malloc(psshsize); + CHECK(buf != NULL); char *ptr = buf; for (size_t i = 0; i < mPssh.size(); i++) { memcpy(ptr, mPssh[i].uuid, 20); // uuid + length @@ -837,7 +841,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { && chunk_type != FOURCC('c', 'o', 'v', 'r') && mPath.size() == 5 && underMetaDataPath(mPath)) { off64_t stop_offset = *offset + chunk_size; - *offset = data_offset; + *offset = stop_offset; while (*offset < stop_offset) { status_t err = parseChunk(offset, depth + 1); if (err != OK) { @@ -1351,7 +1355,14 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); off64_t stop_offset = *offset + chunk_size; - *offset = data_offset + sizeof(buffer); + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG, FourCC2MIME(chunk_type)) || + !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, FourCC2MIME(chunk_type))) { + // ESD is not required in mp3 + // amr wb with damr atom corrupted can cause the clip to not play + *offset = stop_offset; + } else { + *offset = data_offset + sizeof(buffer); + } while (*offset < stop_offset) { status_t err = parseChunk(offset, depth + 1); if (err != OK) { @@ -1590,13 +1601,21 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_MALFORMED; *offset += chunk_size; + // Ignore stss block for audio even if its present + // All audio sample are sync samples itself, + // self decodeable and playable. + // Parsing this block for audio restricts audio seek to few entries + // available in this block, sometimes 0, which is undesired. + const char *mime; + CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); + if (strncasecmp("audio/", mime, 6)) { + status_t err = + mLastTrack->sampleTable->setSyncSampleParams( + data_offset, chunk_data_size); - status_t err = - mLastTrack->sampleTable->setSyncSampleParams( - data_offset, chunk_data_size); - - if (err != OK) { - return err; + if (err != OK) { + return err; + } } break; @@ -2971,12 +2990,12 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return OK; } - if (objectTypeIndication == 0x6b) { - // The media subtype is MP3 audio - // Our software MP3 audio decoder may not be able to handle - // packetized MP3 audio; for now, lets just return ERROR_UNSUPPORTED - ALOGE("MP3 track in MP4/3GPP file is not supported"); - return ERROR_UNSUPPORTED; + if (objectTypeIndication == 0x6b + || objectTypeIndication == 0x69) { + // This is mpeg1/2 audio content, set mimetype to mpeg + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); + ALOGD("objectTypeIndication:0x%x, set mimetype to mpeg ",objectTypeIndication); + return OK; } const uint8_t *csd; @@ -3129,7 +3148,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( extensionFlag, objectType); } - if (numChannels == 0) { + if (numChannels == 0 && (br.numBitsLeft() > 0)) { int32_t channelsEffectiveNum = 0; int32_t channelsNum = 0; if (br.numBitsLeft() < 32) { @@ -4606,7 +4625,9 @@ static bool LegacySniffMPEG4( return false; } - if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8) + if (!memcmp(header, "ftyp3g2a", 8) || !memcmp(header, "ftyp3g2b", 8) + || !memcmp(header, "ftyp3g2c", 8) + || !memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8) || !memcmp(header, "ftyp3gr6", 8) || !memcmp(header, "ftyp3gs6", 8) || !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8) || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8) diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 47f114a..cb9df29 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -38,10 +38,11 @@ #include <media/stagefright/MediaSource.h> #include <media/stagefright/Utils.h> #include <media/mediarecorder.h> +#include <stagefright/AVExtensions.h> #include <cutils/properties.h> #include "include/ESDS.h" - +#include <stagefright/AVExtensions.h> #ifndef __predict_false #define __predict_false(exp) __builtin_expect((exp) != 0, 0) @@ -91,6 +92,7 @@ public: bool isAvc() const { return mIsAvc; } bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } + bool isHEVC() const { return mIsHEVC; } void addChunkOffset(off64_t offset); int32_t getTrackId() const { return mTrackId; } status_t dump(int fd, const Vector<String16>& args) const; @@ -236,6 +238,7 @@ private: bool mIsAvc; bool mIsAudio; bool mIsMPEG4; + bool mIsHEVC; int32_t mTrackId; int64_t mTrackDurationUs; int64_t mMaxChunkDurationUs; @@ -385,7 +388,9 @@ MPEG4Writer::MPEG4Writer(int fd) mLongitudex10000(0), mAreGeoTagsAvailable(false), mStartTimeOffsetMs(-1), - mMetaKeys(new AMessage()) { + mMetaKeys(new AMessage()), + mIsVideoHEVC(false), + mIsAudioAMR(false) { addDeviceMeta(); // Verify mFd is seekable @@ -463,6 +468,8 @@ const char *MPEG4Writer::Track::getFourCCForMime(const char *mime) { return "s263"; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { return "avc1"; + } else { + return AVUtils::get()->HEVCMuxerUtils().getFourCCForMime(mime); } } else { ALOGE("Track (%s) other than video or audio is not supported", mime); @@ -488,10 +495,23 @@ status_t MPEG4Writer::addSource(const sp<MediaSource> &source) { const char *mime; source->getFormat()->findCString(kKeyMIMEType, &mime); bool isAudio = !strncasecmp(mime, "audio/", 6); + bool isVideo = !strncasecmp(mime, "video/", 6); if (Track::getFourCCForMime(mime) == NULL) { ALOGE("Unsupported mime '%s'", mime); return ERROR_UNSUPPORTED; } + mIsAudioAMR = isAudio && (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) || + !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)); + + + if (isVideo) { + mIsVideoHEVC = AVUtils::get()->HEVCMuxerUtils().isVideoHEVC(mime); + } + + if (isAudio && !AVUtils::get()->isAudioMuxFormatSupported(mime)) { + ALOGE("Muxing is not supported for %s", mime); + return ERROR_UNSUPPORTED; + } // At this point, we know the track to be added is either // video or audio. Thus, we only need to check whether it @@ -580,7 +600,7 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { // If the estimation is wrong, we will pay the price of wasting // some reserved space. This should not happen so often statistically. - static const int32_t factor = mUse32BitOffset? 1: 2; + int32_t factor = mUse32BitOffset? 1: 2; static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000); int64_t size = MIN_MOOV_BOX_SIZE; @@ -677,8 +697,6 @@ status_t MPEG4Writer::start(MetaData *param) { mIsRealTimeRecording = isRealTimeRecording; } - mStartTimestampUs = -1; - if (mStarted) { if (mPaused) { mPaused = false; @@ -687,6 +705,8 @@ status_t MPEG4Writer::start(MetaData *param) { return OK; } + mStartTimestampUs = -1; + if (!param || !param->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; @@ -1047,8 +1067,10 @@ void MPEG4Writer::writeFtypBox(MetaData *param) { beginBox("ftyp"); int32_t fileType; - if (param && param->findInt32(kKeyFileType, &fileType) && - fileType != OUTPUT_FORMAT_MPEG_4) { + if (mIsVideoHEVC) { + AVUtils::get()->HEVCMuxerUtils().writeHEVCFtypBox(this); + } else if (mIsAudioAMR || (param && param->findInt32(kKeyFileType, &fileType) && + fileType != OUTPUT_FORMAT_MPEG_4)) { writeFourcc("3gp4"); writeInt32(0); writeFourcc("isom"); @@ -1464,6 +1486,12 @@ MPEG4Writer::Track::Track( mIsAudio = !strncasecmp(mime, "audio/", 6); mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); + mIsHEVC = AVUtils::get()->HEVCMuxerUtils().isVideoHEVC(mime); + + if (mIsHEVC) { + AVUtils::get()->HEVCMuxerUtils().getHEVCCodecSpecificDataFromInputFormatIfPossible( + mMeta, &mCodecSpecificData, &mCodecSpecificDataSize, &mGotAllCodecSpecificData); + } setTimeScale(); } @@ -1562,6 +1590,7 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { size_t size; if (mMeta->findData(kKeyAVCC, &type, &data, &size)) { mCodecSpecificData = malloc(size); + CHECK(mCodecSpecificData != NULL); mCodecSpecificDataSize = size; memcpy(mCodecSpecificData, data, size); mGotAllCodecSpecificData = true; @@ -1575,6 +1604,7 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { ESDS esds(data, size); if (esds.getCodecSpecificInfo(&data, &size) == OK) { mCodecSpecificData = malloc(size); + CHECK(mCodecSpecificData != NULL); mCodecSpecificDataSize = size; memcpy(mCodecSpecificData, data, size); mGotAllCodecSpecificData = true; @@ -1657,7 +1687,7 @@ void MPEG4Writer::writeChunkToFile(Chunk* chunk) { while (!chunk->mSamples.empty()) { List<MediaBuffer *>::iterator it = chunk->mSamples.begin(); - off64_t offset = chunk->mTrack->isAvc() + off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHEVC()) ? addLengthPrefixedSample_l(*it) : addSample_l(*it); @@ -1881,6 +1911,10 @@ status_t MPEG4Writer::Track::stop() { status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); ALOGD("%s track stopped", mIsAudio? "Audio": "Video"); + if (mOwner->exceedsFileSizeLimit() && mStszTableEntries->count() == 0) { + ALOGE(" Filesize limit exceeded and zero samples written "); + return ERROR_END_OF_STREAM; + } return err; } @@ -1971,6 +2005,7 @@ status_t MPEG4Writer::Track::copyAVCCodecSpecificData( mCodecSpecificDataSize = size; mCodecSpecificData = malloc(size); + CHECK(mCodecSpecificData != NULL); memcpy(mCodecSpecificData, data, size); return OK; } @@ -2093,6 +2128,7 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData( // ISO 14496-15: AVC file format mCodecSpecificDataSize += 7; // 7 more bytes in the header mCodecSpecificData = malloc(mCodecSpecificDataSize); + CHECK(mCodecSpecificData != NULL); uint8_t *header = (uint8_t *)mCodecSpecificData; header[0] = 1; // version header[1] = mProfileIdc; // profile indication @@ -2227,10 +2263,18 @@ status_t MPEG4Writer::Track::threadEntry() { } else if (mIsMPEG4) { mCodecSpecificDataSize = buffer->range_length(); mCodecSpecificData = malloc(mCodecSpecificDataSize); + CHECK(mCodecSpecificData != NULL); memcpy(mCodecSpecificData, (const uint8_t *)buffer->data() + buffer->range_offset(), buffer->range_length()); + } else if (mIsHEVC) { + status_t err = AVUtils::get()->HEVCMuxerUtils().makeHEVCCodecSpecificData( + (const uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length(), &mCodecSpecificData, &mCodecSpecificDataSize); + if ((status_t)OK != err) { + return err; + } } buffer->release(); @@ -2240,20 +2284,28 @@ status_t MPEG4Writer::Track::threadEntry() { continue; } - // Make a deep copy of the MediaBuffer and Metadata and release - // the original as soon as we can - MediaBuffer *copy = new MediaBuffer(buffer->range_length()); - memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(), - buffer->range_length()); - copy->set_range(0, buffer->range_length()); - meta_data = new MetaData(*buffer->meta_data().get()); - buffer->release(); - buffer = NULL; + MediaBuffer *copy = NULL; + // Check if the upstream source hints it is OK to hold on to the + // buffer without releasing immediately and avoid cloning the buffer + if (AVUtils::get()->canDeferRelease(buffer->meta_data())) { + copy = buffer; + meta_data = new MetaData(*buffer->meta_data().get()); + } else { + // Make a deep copy of the MediaBuffer and Metadata and release + // the original as soon as we can + copy = new MediaBuffer(buffer->range_length()); + memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length()); + copy->set_range(0, buffer->range_length()); + meta_data = new MetaData(*buffer->meta_data().get()); + buffer->release(); + buffer = NULL; + } - if (mIsAvc) StripStartcode(copy); + if (mIsAvc || mIsHEVC) StripStartcode(copy); size_t sampleSize = copy->range_length(); - if (mIsAvc) { + if (mIsAvc || mIsHEVC) { if (mOwner->useNalLengthFour()) { sampleSize += 4; } else { @@ -2287,6 +2339,7 @@ status_t MPEG4Writer::Track::threadEntry() { previousPausedDurationUs = mStartTimestampUs; } +#if 0 if (mResumed) { int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs; if (WARN_UNLESS(durExcludingEarlierPausesUs >= 0ll, "for %s track", trackName)) { @@ -2303,6 +2356,7 @@ status_t MPEG4Writer::Track::threadEntry() { previousPausedDurationUs += pausedDurationUs - lastDurationUs; mResumed = false; } +#endif timestampUs -= previousPausedDurationUs; if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) { @@ -2320,8 +2374,8 @@ status_t MPEG4Writer::Track::threadEntry() { CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs)); decodingTimeUs -= previousPausedDurationUs; cttsOffsetTimeUs = - timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs; - if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) { + timestampUs - decodingTimeUs; + if (WARN_UNLESS(kMaxCttsOffsetTimeUs >= decodingTimeUs - timestampUs, "for %s track", trackName)) { copy->release(); return ERROR_MALFORMED; } @@ -2453,7 +2507,7 @@ status_t MPEG4Writer::Track::threadEntry() { trackProgressStatus(timestampUs); } if (!hasMultipleTracks) { - off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) + off64_t offset = (mIsAvc || mIsHEVC)? mOwner->addLengthPrefixedSample_l(copy) : mOwner->addSample_l(copy); uint32_t count = (mOwner->use32BitFileOffset() @@ -2546,8 +2600,13 @@ status_t MPEG4Writer::Track::threadEntry() { if (mIsAudio) { ALOGI("Audio track drift time: %" PRId64 " us", mOwner->getDriftTimeUs()); } - - if (err == ERROR_END_OF_STREAM) { + // if err is ERROR_IO (ex: during SSR), return OK to save the + // recorded file successfully. Session tear down will happen as part of + // client callback + if (mIsAudio && (err == ERROR_IO)) { + return OK; + } + else if (err == ERROR_END_OF_STREAM) { return OK; } return err; @@ -2705,7 +2764,8 @@ status_t MPEG4Writer::Track::checkCodecSpecificData() const { CHECK(mMeta->findCString(kKeyMIMEType, &mime)); if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) || !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || - !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { + !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) || + mIsHEVC) { if (!mCodecSpecificData || mCodecSpecificDataSize <= 0) { ALOGE("Missing codec specific data"); @@ -2726,6 +2786,11 @@ void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) { ALOGV("%s track time scale: %d", mIsAudio? "Audio": "Video", mTimeScale); + if (mMdatSizeBytes == 0) { + ALOGW("Track data is not available."); + return; + } + uint32_t now = getMpeg4Time(); mOwner->beginBox("trak"); writeTkhdBox(now); @@ -2803,17 +2868,22 @@ void MPEG4Writer::Track::writeVideoFourCCBox() { mOwner->writeInt16(0x18); // depth mOwner->writeInt16(-1); // predefined - CHECK_LT(23 + mCodecSpecificDataSize, 128); - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { writeMp4vEsdsBox(); } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { writeD263Box(); } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { writeAvccBox(); + } else if (mIsHEVC) { + AVUtils::get()->HEVCMuxerUtils().writeHvccBox(mOwner, mCodecSpecificData, + mCodecSpecificDataSize, + mOwner->useNalLengthFour()); + } + + if (!mIsHEVC) { + writePaspBox(); } - writePaspBox(); mOwner->endBox(); // mp4v, s263 or avc1 } @@ -2898,6 +2968,9 @@ void MPEG4Writer::Track::writeMp4vEsdsBox() { CHECK_GT(mCodecSpecificDataSize, 0); mOwner->beginBox("esds"); + // Make sure all sizes encode to a single byte. + CHECK_LT(mCodecSpecificDataSize + 23, 128); + mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt8(0x03); // ES_DescrTag @@ -3086,7 +3159,7 @@ void MPEG4Writer::Track::writePaspBox() { int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const { int64_t trackStartTimeOffsetUs = 0; int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); - if (mStartTimestampUs != moovStartTimeUs) { + if (mStartTimestampUs != moovStartTimeUs && mStszTableEntries->count() != 0) { CHECK_GT(mStartTimestampUs, moovStartTimeUs); trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs; } diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp index 1f80a47..525a156 100644 --- a/media/libstagefright/MediaBuffer.cpp +++ b/media/libstagefright/MediaBuffer.cpp @@ -54,6 +54,7 @@ MediaBuffer::MediaBuffer(size_t size) mOwnsData(true), mMetaData(new MetaData), mOriginal(NULL) { + CHECK(mData != NULL); } MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 7019537..45e9a79 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -51,6 +51,7 @@ #include <private/android_filesystem_config.h> #include <utils/Log.h> #include <utils/Singleton.h> +#include <stagefright/AVExtensions.h> namespace android { @@ -306,7 +307,7 @@ status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) { // queue. if (nameIsType || !strncasecmp(name.c_str(), "omx.", 4)) { - mCodec = new ACodec; + mCodec = AVFactory::get()->createACodec(); } else if (!nameIsType && !strncasecmp(name.c_str(), "android.filter.", 15)) { mCodec = new MediaFilter; @@ -984,6 +985,9 @@ bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool if (omxFlags & OMX_BUFFERFLAG_EOS) { flags |= BUFFER_FLAG_EOS; } + if (omxFlags & OMX_BUFFERFLAG_EXTRADATA) { + flags |= BUFFER_FLAG_EXTRADATA; + } response->setInt32("flags", flags); response->postReply(replyID); @@ -1140,7 +1144,8 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findString("componentName", &mComponentName)); - if (mComponentName.startsWith("OMX.google.")) { + if (mComponentName.startsWith("OMX.google.") || + mComponentName.startsWith("OMX.ffmpeg.")) { mFlags |= kFlagUsesSoftwareRenderer; } else { mFlags &= ~kFlagUsesSoftwareRenderer; @@ -1177,6 +1182,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { // reset input surface flag mHaveInputSurface = false; + CHECK(msg->findString("componentName", &mComponentName)); CHECK(msg->findMessage("input-format", &mInputFormat)); CHECK(msg->findMessage("output-format", &mOutputFormat)); @@ -2269,6 +2275,7 @@ size_t MediaCodec::updateBuffers( uint32_t bufferID; CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID)); + Mutex::Autolock al(mBufferLock); Vector<BufferInfo> *buffers = &mPortBuffers[portIndex]; diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index 5edc04c..ff94314 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -40,6 +40,7 @@ #include <cutils/properties.h> #include <libexpat/expat.h> +#include <stagefright/AVExtensions.h> namespace android { @@ -174,7 +175,7 @@ MediaCodecList::MediaCodecList() : mInitCheck(NO_INIT), mUpdate(false), mGlobalSettings(new AMessage()) { - parseTopLevelXMLFile("/etc/media_codecs.xml"); + parseTopLevelXMLFile(AVUtils::get()->getCustomCodecsLocation()); parseTopLevelXMLFile("/etc/media_codecs_performance.xml", true/* ignore_errors */); parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */); } @@ -939,7 +940,13 @@ status_t MediaCodecList::addLimit(const char **attrs) { // complexity: range + default bool found; - if (name == "aspect-ratio" || name == "bitrate" || name == "block-count" + // VT specific limits + if (name.find("vt-") == 0) { + AString value; + if (msg->findString("value", &value) && value.size()) { + mCurrentInfo->addDetail(name, value); + } + } else if (name == "aspect-ratio" || name == "bitrate" || name == "block-count" || name == "blocks-per-second" || name == "complexity" || name == "frame-rate" || name == "quality" || name == "size" || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) { diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index 7f9f824..925be14 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -36,6 +36,8 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/PersistentSurface.h> #include <media/stagefright/Utils.h> +#include <OMX_Core.h> +#include <stagefright/AVExtensions.h> namespace android { @@ -399,10 +401,14 @@ status_t MediaCodecSource::initEncoder() { } AString outputMIME; + AString role; CHECK(mOutputFormat->findString("mime", &outputMIME)); - - mEncoder = MediaCodec::CreateByType( + if (AVUtils::get()->useQCHWEncoder(mOutputFormat, role)) { + mEncoder = MediaCodec::CreateByComponentName(mCodecLooper, role.c_str()); + } else { + mEncoder = MediaCodec::CreateByType( mCodecLooper, outputMIME.c_str(), true /* encoder */); + } if (mEncoder == NULL) { return NO_INIT; @@ -514,6 +520,9 @@ void MediaCodecSource::signalEOS(status_t err) { mOutputBufferQueue.clear(); mEncoderReachedEOS = true; mErrorCode = err; + if (err == OMX_ErrorHardware) { + mErrorCode = ERROR_IO; + } mOutputBufferCond.signal(); } @@ -572,6 +581,9 @@ status_t MediaCodecSource::feedEncoderInputBuffers() { // push decoding time for video, or drift time for audio if (mIsVideo) { mDecodingTimeQueue.push_back(timeUs); + if (mFlags & FLAG_USE_METADATA_INPUT) { + AVUtils::get()->addDecodingTimesFromBatch(mbuf, mDecodingTimeQueue); + } } else { #if DEBUG_DRIFT_TIME if (mFirstSampleTimeUs < 0ll) { @@ -591,7 +603,7 @@ status_t MediaCodecSource::feedEncoderInputBuffers() { status_t err = mEncoder->getInputBuffer(bufferIndex, &inbuf); if (err != OK || inbuf == NULL) { mbuf->release(); - signalEOS(); + signalEOS(err); break; } @@ -633,6 +645,9 @@ status_t MediaCodecSource::onStart(MetaData *params) { resume(); } else { CHECK(mPuller != NULL); + if (mIsVideo) { + mEncoder->requestIDRFrame(); + } mPuller->resume(); } return OK; @@ -738,12 +753,14 @@ void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) { sp<ABuffer> outbuf; status_t err = mEncoder->getOutputBuffer(index, &outbuf); if (err != OK || outbuf == NULL) { - signalEOS(); + signalEOS(err); break; } MediaBuffer *mbuf = new MediaBuffer(outbuf->size()); memcpy(mbuf->data(), outbuf->data(), outbuf->size()); + sp<MetaData> meta = mbuf->meta_data(); + AVUtils::get()->setDeferRelease(meta); if (!(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)) { if (mIsVideo) { @@ -799,7 +816,7 @@ void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findInt32("err", &err)); ALOGE("Encoder (%s) reported error : 0x%x", mIsVideo ? "video" : "audio", err); - signalEOS(); + signalEOS(err); } break; } @@ -852,7 +869,7 @@ void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) { } case kWhatPause: { - if (mFlags && FLAG_USE_SURFACE_INPUT) { + if (mFlags & FLAG_USE_SURFACE_INPUT) { suspend(); } else { CHECK(mPuller != NULL); diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 2a50692..089c150 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -64,4 +64,32 @@ const char *MEDIA_MIMETYPE_TEXT_VTT = "text/vtt"; const char *MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608"; const char *MEDIA_MIMETYPE_DATA_TIMED_ID3 = "application/x-id3v4"; +const char *MEDIA_MIMETYPE_VIDEO_FLV1 = "video/x-flv"; +const char *MEDIA_MIMETYPE_VIDEO_MJPEG = "video/x-jpeg"; +const char *MEDIA_MIMETYPE_VIDEO_RV = "video/vnd.rn-realvideo"; +const char *MEDIA_MIMETYPE_VIDEO_VC1 = "video/vc1"; +const char *MEDIA_MIMETYPE_VIDEO_FFMPEG = "video/ffmpeg"; + +const char *MEDIA_MIMETYPE_AUDIO_PCM = "audio/x-pcm"; +const char *MEDIA_MIMETYPE_AUDIO_RA = "audio/vnd.rn-realaudio"; +const char *MEDIA_MIMETYPE_AUDIO_FFMPEG = "audio/ffmpeg"; + +const char *MEDIA_MIMETYPE_CONTAINER_APE = "audio/x-ape"; +const char *MEDIA_MIMETYPE_CONTAINER_DIVX = "video/divx"; +const char *MEDIA_MIMETYPE_CONTAINER_DTS = "audio/vnd.dts"; +const char *MEDIA_MIMETYPE_CONTAINER_FLAC = "audio/flac"; +const char *MEDIA_MIMETYPE_CONTAINER_FLV = "video/x-flv"; +const char *MEDIA_MIMETYPE_CONTAINER_MOV = "video/quicktime"; +const char *MEDIA_MIMETYPE_CONTAINER_MP2 = "audio/mpeg2"; +const char *MEDIA_MIMETYPE_CONTAINER_MPG = "video/mpeg"; +const char *MEDIA_MIMETYPE_CONTAINER_RA = "audio/vnd.rn-realaudio"; +const char *MEDIA_MIMETYPE_CONTAINER_RM = "video/vnd.rn-realvideo"; +const char *MEDIA_MIMETYPE_CONTAINER_TS = "video/mp2t"; +const char *MEDIA_MIMETYPE_CONTAINER_WEBM = "video/webm"; +const char *MEDIA_MIMETYPE_CONTAINER_WMA = "audio/x-ms-wma"; +const char *MEDIA_MIMETYPE_CONTAINER_WMV = "video/x-ms-wmv"; +const char *MEDIA_MIMETYPE_CONTAINER_VC1 = "video/vc1"; +const char *MEDIA_MIMETYPE_CONTAINER_HEVC = "video/hevc"; +const char *MEDIA_MIMETYPE_CONTAINER_FFMPEG = "video/ffmpeg"; + } // namespace android diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index e21fe6e..fc96e2f 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -40,8 +40,12 @@ #include <media/stagefright/MetaData.h> #include <utils/String8.h> +#include <stagefright/AVExtensions.h> + namespace android { +MediaExtractor::Plugin MediaExtractor::sPlugin; + sp<MetaData> MediaExtractor::getMetaData() { return new MetaData; } @@ -52,12 +56,19 @@ uint32_t MediaExtractor::flags() const { // static sp<MediaExtractor> MediaExtractor::Create( - const sp<DataSource> &source, const char *mime) { + const sp<DataSource> &source, const char *mime, + const uint32_t flags) { sp<AMessage> meta; + bool secondPass = false; + String8 tmp; - if (mime == NULL) { +retry: + if (secondPass || mime == NULL) { float confidence; + if (secondPass) { + confidence = 3.14f; + } if (!source->sniff(&tmp, &confidence, &meta)) { ALOGV("FAILED to autodetect media content."); @@ -91,8 +102,14 @@ sp<MediaExtractor> MediaExtractor::Create( } } - MediaExtractor *ret = NULL; - if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) + sp<MediaExtractor> ret = NULL; + AString extractorName; + if ((ret = AVFactory::get()->createExtendedExtractor(source, mime, meta, flags)) != NULL) { + } else if (meta.get() && meta->findString("extended-extractor-use", &extractorName) + && sPlugin.create) { + ALOGI("Use extended extractor for the special mime(%s) or codec", mime); + ret = sPlugin.create(source, mime, meta); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) || !strcasecmp(mime, "audio/mp4")) { ret = new MPEG4Extractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { @@ -119,8 +136,11 @@ sp<MediaExtractor> MediaExtractor::Create( ret = new MPEG2PSExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MIDI)) { ret = new MidiExtractor(source); + } else if (!isDrm && sPlugin.create) { + ret = sPlugin.create(source, mime, meta); } + ret = AVFactory::get()->updateExtractor(ret, source, mime, meta, flags); if (ret != NULL) { if (isDrm) { ret->setDrmFlag(true); @@ -129,6 +149,15 @@ sp<MediaExtractor> MediaExtractor::Create( } } + if (ret != NULL) { + + if (!secondPass && ( ret->countTracks() == 0 || + (!strncasecmp("video/", mime, 6) && ret->countTracks() < 2) ) ) { + secondPass = true; + goto retry; + } + } + return ret; } diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index e69890d..ac925f7 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -181,6 +181,7 @@ bool MuxOMX::isLocalNode_l(node_id node) const { } // static + bool MuxOMX::CanLiveLocally(const char *name) { #ifdef __LP64__ (void)name; // disable unused parameter warning diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index b1dde80..8d0bccd 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -56,6 +56,11 @@ #include "include/avc_utils.h" +#ifdef USE_S3D_SUPPORT +#include "Exynos_OMX_Def.h" +#include "ExynosHWCService.h" +#endif + namespace android { // Treat time out as an error if we have not received any output @@ -1821,6 +1826,10 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { if (mFlags & kEnableGrallocUsageProtected) { usage |= GRALLOC_USAGE_PROTECTED; +#ifdef GRALLOC_USAGE_PRIVATE_NONSECURE + if (!(mFlags & kUseSecureInputBuffers)) + usage |= GRALLOC_USAGE_PRIVATE_NONSECURE; +#endif } err = setNativeWindowSizeFormatAndUsage( @@ -2373,7 +2382,41 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { break; } #endif +#ifdef USE_S3D_SUPPORT + case (OMX_EVENTTYPE)OMX_EventS3DInformation: + { + if (mFlags & kClientNeedsFramebuffer) + break; + sp<IServiceManager> sm = defaultServiceManager(); + sp<android::IExynosHWCService> hwc = interface_cast<android::IExynosHWCService>( + sm->getService(String16("Exynos.HWCService"))); + if (hwc != NULL) { + if (data1 == OMX_TRUE) { + int eS3DMode; + switch (data2) { + case OMX_SEC_FPARGMT_SIDE_BY_SIDE: + eS3DMode = S3D_SBS; + break; + case OMX_SEC_FPARGMT_TOP_BOTTOM: + eS3DMode = S3D_TB; + break; + case OMX_SEC_FPARGMT_CHECKERBRD_INTERL: // unsupport format at HDMI + case OMX_SEC_FPARGMT_COLUMN_INTERL: + case OMX_SEC_FPARGMT_ROW_INTERL: + case OMX_SEC_FPARGMT_TEMPORAL_INTERL: + default: + eS3DMode = S3D_NONE; + } + + hwc->setHdmiResolution(0, eS3DMode); + } + } else { + ALOGE("Exynos.HWCService is unavailable"); + } + break; + } +#endif default: { CODEC_LOGV("EVENT(%d, %u, %u)", event, data1, data2); diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index db33e83..c5018ae 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -28,6 +28,8 @@ #include <media/mediametadataretriever.h> #include <private/media/VideoFrame.h> +#include <stagefright/AVExtensions.h> + namespace android { StagefrightMediaScanner::StagefrightMediaScanner() {} @@ -40,7 +42,11 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf", - ".avi", ".mpeg", ".mpg", ".awb", ".mpga" + ".adts", ".dm", ".m2ts", ".mp3d", ".wmv", ".asf", ".flv", + ".mov", ".ra", ".rm", ".rmvb", ".ac3", ".ape", ".dts", + ".mp1", ".mp2", ".f4v", "hlv", "nrg", "m2v", ".swf", + ".avi", ".mpg", ".mpeg", ".awb", ".vc1", ".vob", ".divx", + ".mpga", ".mov", ".qcp", ".ec3" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); @@ -75,7 +81,8 @@ MediaScanResult StagefrightMediaScanner::processFileInternal( return MEDIA_SCAN_RESULT_SKIPPED; } - if (!FileHasAcceptableExtension(extension)) { + if (!FileHasAcceptableExtension(extension) + && !AVUtils::get()->isEnhancedExtension(extension)) { return MEDIA_SCAN_RESULT_SKIPPED; } diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index e37e909..c3adac4 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -23,6 +23,7 @@ #include <gui/Surface.h> #include "include/StagefrightMetadataRetriever.h" +#include "include/HTTPBase.h" #include <media/ICrypto.h> #include <media/IMediaHTTPService.h> @@ -44,6 +45,8 @@ #include <CharacterEncodingDetector.h> +#include <stagefright/AVExtensions.h> + namespace android { static const int64_t kBufferTimeOutUs = 30000ll; // 30 msec @@ -62,6 +65,12 @@ StagefrightMetadataRetriever::~StagefrightMetadataRetriever() { ALOGV("~StagefrightMetadataRetriever()"); clearMetadata(); mClient.disconnect(); + + if (mSource != NULL && + (mSource->flags() & DataSource::kIsHTTPBasedSource)) { + mExtractor.clear(); + static_cast<HTTPBase *>(mSource.get())->disconnect(); + } } status_t StagefrightMetadataRetriever::setDataSource( @@ -97,6 +106,7 @@ status_t StagefrightMetadataRetriever::setDataSource( fd = dup(fd); ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); + AVUtils::get()->printFileName(fd); clearMetadata(); mSource = new FileSource(fd, offset, length); @@ -154,6 +164,8 @@ static VideoFrame *extractVideoFrame( // TODO: Use Flexible color instead videoFormat->setInt32("color-format", OMX_COLOR_FormatYUV420Planar); + videoFormat->setInt32("thumbnail-mode", 1); + status_t err; sp<ALooper> looper = new ALooper; looper->start(); @@ -214,6 +226,7 @@ static VideoFrame *extractVideoFrame( err = decoder->getInputBuffers(&inputBuffers); if (err != OK) { ALOGW("failed to get input buffers: %d (%s)", err, asString(err)); + source->stop(); decoder->release(); return NULL; } @@ -222,6 +235,7 @@ static VideoFrame *extractVideoFrame( err = decoder->getOutputBuffers(&outputBuffers); if (err != OK) { ALOGW("failed to get output buffers: %d (%s)", err, asString(err)); + source->stop(); decoder->release(); return NULL; } @@ -346,9 +360,11 @@ static VideoFrame *extractVideoFrame( } } - int32_t width, height; + int32_t width, height, stride, slice_height; CHECK(outputFormat->findInt32("width", &width)); CHECK(outputFormat->findInt32("height", &height)); + CHECK(outputFormat->findInt32("stride", &stride)); + CHECK(outputFormat->findInt32("slice-height", &slice_height)); int32_t crop_left, crop_top, crop_right, crop_bottom; if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) { @@ -386,7 +402,7 @@ static VideoFrame *extractVideoFrame( if (converter.isValid()) { err = converter.convert( (const uint8_t *)videoFrameBuffer->data(), - width, height, + stride, slice_height, crop_left, crop_top, crop_right, crop_bottom, frame->mData, frame->mWidth, @@ -442,6 +458,10 @@ VideoFrame *StagefrightMetadataRetriever::getFrameAtTime( for (i = 0; i < n; ++i) { sp<MetaData> meta = mExtractor->getTrackMetaData(i); + if (meta == NULL) { + continue; + } + const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); @@ -481,7 +501,7 @@ VideoFrame *StagefrightMetadataRetriever::getFrameAtTime( mime, false, /* encoder */ NULL, /* matchComponentName */ - OMXCodec::kPreferSoftwareCodecs, + 0 /* OMXCodec::kPreferSoftwareCodecs */, &matchingCodecs); for (size_t i = 0; i < matchingCodecs.size(); ++i) { @@ -612,6 +632,10 @@ void StagefrightMetadataRetriever::parseMetaData() { size_t numTracks = mExtractor->countTracks(); + if (numTracks == 0) { //If no tracks available, corrupt or not valid stream + return; + } + char tmp[32]; sprintf(tmp, "%zu", numTracks); @@ -635,6 +659,9 @@ void StagefrightMetadataRetriever::parseMetaData() { String8 timedTextLang; for (size_t i = 0; i < numTracks; ++i) { sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i); + if (trackMeta == NULL) { + continue; + } int64_t durationUs; if (trackMeta->findInt64(kKeyDuration, &durationUs)) { @@ -661,9 +688,12 @@ void StagefrightMetadataRetriever::parseMetaData() { } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { const char *lang; - trackMeta->findCString(kKeyMediaLanguage, &lang); - timedTextLang.append(String8(lang)); - timedTextLang.append(String8(":")); + if (trackMeta->findCString(kKeyMediaLanguage, &lang)) { + timedTextLang.append(String8(lang)); + timedTextLang.append(String8(":")); + } else { + ALOGE("No language found for timed text"); + } } } } diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index e8abf48..7da5a9f 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -360,7 +360,11 @@ status_t SurfaceMediaSource::read( mNumFramesEncoded++; // Pass the data to the MediaBuffer. Pass in only the metadata - + if (mSlots[mCurrentSlot].mGraphicBuffer == NULL) { + ALOGV("Read: SurfaceMediaSource mGraphicBuffer is null. Returning" + "ERROR_END_OF_STREAM."); + return ERROR_END_OF_STREAM; + } passMetadataBuffer(buffer, mSlots[mCurrentSlot].mGraphicBuffer->handle); (*buffer)->setObserver(this); diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 17f0201..6e4a1a4 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -35,6 +35,9 @@ #include <media/stagefright/Utils.h> #include <media/AudioParameter.h> +#include <stagefright/AVExtensions.h> +#include <media/stagefright/FFMPEGSoftCodec.h> + namespace android { uint16_t U16_AT(const uint8_t *ptr) { @@ -104,7 +107,7 @@ status_t convertMetaDataToMessage( int avgBitRate; if (meta->findInt32(kKeyBitRate, &avgBitRate)) { - msg->setInt32("bit-rate", avgBitRate); + msg->setInt32("bitrate", avgBitRate); } int32_t isSync; @@ -203,6 +206,11 @@ status_t convertMetaDataToMessage( msg->setInt32("frame-rate", fps); } + int32_t bitsPerSample; + if (meta->findInt32(kKeyBitsPerSample, &bitsPerSample)) { + msg->setInt32("bit-width", bitsPerSample); + } + uint32_t type; const void *data; size_t size; @@ -453,8 +461,15 @@ status_t convertMetaDataToMessage( msg->setBuffer("csd-2", buffer); } + AVUtils::get()->convertMetaDataToMessage(meta, &msg); + + FFMPEGSoftCodec::convertMetaDataToMessageFF(meta, &msg); *format = msg; + ALOGI("convertMetaDataToMessage from:"); + meta->dumpToLog(); + ALOGI(" to: %s", msg->debugString(0).c_str()); + return OK; } @@ -646,6 +661,11 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { if (msg->findInt32("is-adts", &isADTS)) { meta->setInt32(kKeyIsADTS, isADTS); } + + int32_t bitsPerSample; + if (msg->findInt32("bit-width", &bitsPerSample)) { + meta->setInt32(kKeyBitsPerSample, bitsPerSample); + } } int32_t maxInputSize; @@ -685,6 +705,8 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { // for transporting the CSD to muxers. reassembleESDS(csd0, esds); meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds)); + } else { + AVUtils::get()->HEVCMuxerUtils().reassembleHEVCCSD(mime, csd0, meta); } } @@ -695,10 +717,10 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { // XXX TODO add whatever other keys there are -#if 0 - ALOGI("converted %s to:", msg->debugString(0).c_str()); + FFMPEGSoftCodec::convertMessageToMetaDataFF(msg, meta); + + ALOGI("convertMessageToMetaData from %s to:", msg->debugString(0).c_str()); meta->dumpToLog(); -#endif } AString MakeUserAgent() { @@ -744,6 +766,7 @@ status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples); } + AVUtils::get()->sendMetaDataToHal(meta, ¶m); ALOGV("sendMetaDataToHal: bitRate %d, sampleRate %d, chanMask %d," "delaySample %d, paddingSample %d", bitRate, sampleRate, channelMask, delaySamples, paddingSamples); @@ -779,7 +802,7 @@ const struct mime_conv_t* p = &mimeLookup[0]; ++p; } - return BAD_VALUE; + return AVUtils::get()->mapMimeToAudioFormat(format, mime); } struct aac_format_conv_t { @@ -833,18 +856,28 @@ bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, } else { ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format); } - + info.format = AVUtils::get()->updateAudioFormat(info.format, meta); if (AUDIO_FORMAT_INVALID == info.format) { // can't offload if we don't know what the source format is ALOGE("mime type \"%s\" not a known audio format", mime); return false; } + if (AVUtils::get()->canOffloadAPE(meta) != true) { + return false; + } + ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format); + // Redefine aac format according to its profile // Offloading depends on audio DSP capabilities. int32_t aacaot = -1; if (meta->findInt32(kKeyAACAOT, &aacaot)) { - mapAACProfileToAudioFormat(info.format,(OMX_AUDIO_AACPROFILETYPE) aacaot); + bool isADTSSupported = false; + isADTSSupported = AVUtils::get()->mapAACProfileToAudioFormat(meta, info.format, + (OMX_AUDIO_AACPROFILETYPE) aacaot); + if (!isADTSSupported) { + mapAACProfileToAudioFormat(info.format,(OMX_AUDIO_AACPROFILETYPE) aacaot); + } } int32_t srate = -1; diff --git a/media/libstagefright/VideoFrameScheduler.cpp b/media/libstagefright/VideoFrameScheduler.cpp index 5fe9bf9..02b8783 100644 --- a/media/libstagefright/VideoFrameScheduler.cpp +++ b/media/libstagefright/VideoFrameScheduler.cpp @@ -257,8 +257,7 @@ void VideoFrameScheduler::PLL::prime(size_t numSamplesToUse) { mPhase = firstTime; } } - ALOGV("priming[%zu] phase:%lld period:%lld", - numSamplesToUse, (long long)mPhase, (long long)mPeriod); + ALOGV("priming[%zu] phase:%lld period:%lld ", numSamplesToUse, (long long)mPhase, (long long)mPeriod); } nsecs_t VideoFrameScheduler::PLL::addSample(nsecs_t time) { diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index 335ac84..d86dffb 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -29,6 +29,7 @@ #include <media/stagefright/MetaData.h> #include <utils/String8.h> #include <cutils/bitops.h> +#include <system/audio.h> #define CHANNEL_MASK_USE_CHANNEL_ORDER 0 @@ -284,6 +285,7 @@ status_t WAVExtractor::init() { case WAVE_FORMAT_PCM: mTrackMeta->setCString( kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + mTrackMeta->setInt32(kKeyBitsPerSample, mBitsPerSample); break; case WAVE_FORMAT_ALAW: mTrackMeta->setCString( @@ -359,15 +361,16 @@ WAVSource::~WAVSource() { } status_t WAVSource::start(MetaData * /* params */) { - ALOGV("WAVSource::start"); - CHECK(!mStarted); + if (mStarted) { + return OK; + } mGroup = new MediaBufferGroup; mGroup->add_buffer(new MediaBuffer(kMaxFrameSize)); - if (mBitsPerSample == 8) { - // As a temporary buffer for 8->16 bit conversion. + if (mBitsPerSample == 8 || mBitsPerSample == 24) { + // As a temporary buffer for 8->16/24->32 bit conversion. mGroup->add_buffer(new MediaBuffer(kMaxFrameSize)); } @@ -427,9 +430,15 @@ status_t WAVSource::read( } // make sure that maxBytesToRead is multiple of 3, in 24-bit case - size_t maxBytesToRead = - mBitsPerSample == 8 ? kMaxFrameSize / 2 : - (mBitsPerSample == 24 ? 3*(kMaxFrameSize/3): kMaxFrameSize); + size_t maxBytesToRead; + if(8 == mBitsPerSample) + maxBytesToRead = kMaxFrameSize / 2; + else if (24 == mBitsPerSample) { + maxBytesToRead = 3*(kMaxFrameSize/4); + } else + maxBytesToRead = kMaxFrameSize; + ALOGV("%s mBitsPerSample %d, kMaxFrameSize %zu, ", + __func__, mBitsPerSample, kMaxFrameSize); size_t maxBytesAvailable = (mCurrentPos - mOffset >= (off64_t)mSize) @@ -488,23 +497,24 @@ status_t WAVSource::read( buffer->release(); buffer = tmp; } else if (mBitsPerSample == 24) { - // Convert 24-bit signed samples to 16-bit signed. - - const uint8_t *src = - (const uint8_t *)buffer->data() + buffer->range_offset(); - int16_t *dst = (int16_t *)src; - - size_t numSamples = buffer->range_length() / 3; - for (size_t i = 0; i < numSamples; ++i) { - int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16); - x = (x << 8) >> 8; // sign extension - - x = x >> 8; - *dst++ = (int16_t)x; - src += 3; + // Padding done here to convert to 32-bit samples + MediaBuffer *tmp; + CHECK_EQ(mGroup->acquire_buffer(&tmp), (status_t)OK); + ssize_t numBytes = buffer->range_length() / 3; + tmp->set_range(0, 4 * numBytes); + int8_t *dst = (int8_t *)tmp->data(); + const uint8_t *src = (const uint8_t *)buffer->data(); + ALOGV("numBytes = %zd", numBytes); + while(numBytes-- > 0) { + *dst++ = 0x0; + *dst++ = src[0]; + *dst++ = src[1]; + *dst++ = src[2]; + src += 3; } - - buffer->set_range(buffer->range_offset(), 2 * numSamples); + buffer->release(); + buffer = tmp; + ALOGV("length = %zu", buffer->range_length()); } } diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index 8ef2dca..98b5c0e 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -25,6 +25,7 @@ #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MetaData.h> #include <utils/misc.h> @@ -393,7 +394,7 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { meta->setInt32(kKeyWidth, width); meta->setInt32(kKeyHeight, height); - if (sarWidth > 1 || sarHeight > 1) { + if (sarWidth > 1 && sarHeight > 1) { // We treat 0:0 (unspecified) as 1:1. meta->setInt32(kKeySARWidth, sarWidth); @@ -443,25 +444,34 @@ bool IsIDR(const sp<ABuffer> &buffer) { } bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit) { - const uint8_t *data = accessUnit->data(); + MediaBuffer *mediaBuffer = + (MediaBuffer *)(accessUnit->getMediaBufferBase()); + const uint8_t *data = + (mediaBuffer != NULL) ? (uint8_t *) mediaBuffer->data() : accessUnit->data(); size_t size = accessUnit->size(); const uint8_t *nalStart; size_t nalSize; + bool bIsReferenceFrame = true; while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) { CHECK_GT(nalSize, 0u); unsigned nalType = nalStart[0] & 0x1f; if (nalType == 5) { - return true; + bIsReferenceFrame = true; + break; } else if (nalType == 1) { unsigned nal_ref_idc = (nalStart[0] >> 5) & 3; - return nal_ref_idc != 0; + bIsReferenceFrame = (nal_ref_idc != 0); + break; } } - return true; + if (mediaBuffer != NULL) { + mediaBuffer->release(); + } + return bIsReferenceFrame; } sp<MetaData> MakeAACCodecSpecificData( diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index 965c55e..c945305 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -68,6 +68,8 @@ SoftAAC2::SoftAAC2( mOutputBufferCount(0), mSignalledError(false), mLastInHeader(NULL), + mLastHeaderTimeUs(-1), + mNextOutBufferTimeUs(0), mOutputPortSettingsChange(NONE) { initPorts(); CHECK_EQ(initDecoder(), (status_t)OK); @@ -492,6 +494,27 @@ int32_t SoftAAC2::outputDelayRingBufferSpaceLeft() { return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable(); } +void SoftAAC2::updateTimeStamp(int64_t inHeaderTimeUs) { + // use new input buffer timestamp as Anchor Time if its + // a) first buffer or + // b) first buffer post seek or + // c) different from last buffer timestamp + //If input buffer timestamp is same as last input buffer timestamp then + //treat this as a erroneous timestamp and ignore new input buffer + //timestamp and use last output buffer timestamp as Anchor Time. + int64_t anchorTimeUs = 0; + if ((mLastHeaderTimeUs != inHeaderTimeUs)) { + anchorTimeUs = inHeaderTimeUs; + mLastHeaderTimeUs = inHeaderTimeUs; + //Store current buffer's timestamp so that it can used as reference + //in cases where first frame/buffer is skipped/dropped. + //e.g to compensate decoder delay + mNextOutBufferTimeUs = inHeaderTimeUs; + } else { + anchorTimeUs = mNextOutBufferTimeUs; + } + mBufferTimestamps.add(anchorTimeUs); +} void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError || mOutputPortSettingsChange != NONE) { @@ -618,7 +641,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { // insert buffer size and time stamp mBufferSizes.add(inBufferLength[0]); if (mLastInHeader != inHeader) { - mBufferTimestamps.add(inHeader->nTimeStamp); + updateTimeStamp(inHeader->nTimeStamp); mLastInHeader = inHeader; } else { int64_t currentTime = mBufferTimestamps.top(); @@ -630,7 +653,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; inBufferLength[0] = inHeader->nFilledLen; mLastInHeader = inHeader; - mBufferTimestamps.add(inHeader->nTimeStamp); + updateTimeStamp(inHeader->nTimeStamp); mBufferSizes.add(inHeader->nFilledLen); } @@ -755,6 +778,14 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { if (inHeader && inHeader->nFilledLen == 0) { inInfo->mOwnedByUs = false; mInputBufferCount++; + + //During Port reconfiguration current frames is skipped and next frame + //is sent for decoding. + //Update mNextOutBufferTimeUs with current frame's timestamp if port reconfiguration is + //happening in last frame of current buffer otherwise LastOutBufferTimeUs + //will be zero(post seek). + mNextOutBufferTimeUs = mBufferTimestamps.top() + mStreamInfo->aacSamplesPerFrame * + 1000000ll / mStreamInfo->aacSampleRate; inQueue.erase(inQueue.begin()); mLastInHeader = NULL; inInfo = NULL; @@ -875,6 +906,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { *currentBufLeft -= decodedSize; *nextTimeStamp += mStreamInfo->aacSamplesPerFrame * 1000000ll / mStreamInfo->aacSampleRate; + mNextOutBufferTimeUs = *nextTimeStamp; ALOGV("adjusted nextTimeStamp/size to %lld/%d", (long long) *nextTimeStamp, *currentBufLeft); } else { @@ -882,6 +914,7 @@ void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { if (mBufferTimestamps.size() > 0) { mBufferTimestamps.removeAt(0); nextTimeStamp = &mBufferTimestamps.editItemAt(0); + mNextOutBufferTimeUs = *nextTimeStamp; mBufferSizes.removeAt(0); currentBufLeft = &mBufferSizes.editItemAt(0); ALOGV("moved to next time/size: %lld/%d", @@ -976,6 +1009,8 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { mDecodedSizes.clear(); mLastInHeader = NULL; mEndOfInput = false; + mLastHeaderTimeUs = -1; + mNextOutBufferTimeUs = 0; } else { int avail; while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) { @@ -1038,6 +1073,8 @@ void SoftAAC2::onReset() { mBufferSizes.clear(); mDecodedSizes.clear(); mLastInHeader = NULL; + mLastHeaderTimeUs = -1; + mNextOutBufferTimeUs = 0; // To make the codec behave the same before and after a reset, we need to invalidate the // streaminfo struct. This does that: diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h index c3e4459..3fe958e 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -59,6 +59,8 @@ private: size_t mOutputBufferCount; bool mSignalledError; OMX_BUFFERHEADERTYPE *mLastInHeader; + int64_t mLastHeaderTimeUs; + int64_t mNextOutBufferTimeUs; Vector<int32_t> mBufferSizes; Vector<int32_t> mDecodedSizes; Vector<int64_t> mBufferTimestamps; @@ -90,6 +92,7 @@ private: int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples); int32_t outputDelayRingBufferSamplesAvailable(); int32_t outputDelayRingBufferSpaceLeft(); + void updateTimeStamp(int64_t inHeaderTimesUs); DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2); }; diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk index 76a7f40..21109d9 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.mk +++ b/media/libstagefright/codecs/amrnb/dec/Android.mk @@ -80,6 +80,7 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_amrnb_common LOCAL_MODULE := libstagefright_soft_amrdec +LOCAL_CLANG := false LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp index a9723ea..f8316fd 100644 --- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp @@ -352,7 +352,14 @@ void SoftAMR::onQueueFilled(OMX_U32 /* portIndex */) { } size_t frameSize = getFrameSize(mode); - CHECK_GE(inHeader->nFilledLen, frameSize); + if (inHeader->nFilledLen < frameSize) { + ALOGE("Filled length vs frameSize %u vs %lu. Corrupt clip?", + inHeader->nFilledLen, frameSize); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } int16_t *outPtr = (int16_t *)outHeader->pBuffer; diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index f743b1c..4c4da60 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -51,7 +51,9 @@ SoftMP3::SoftMP3( mSignalledError(false), mSawInputEos(false), mSignalledOutputEos(false), - mOutputPortSettingsChange(NONE) { + mOutputPortSettingsChange(NONE), + mLastAnchorTimeUs(-1), + mNextOutBufferTimeUs(0) { initPorts(); initDecoder(); } @@ -112,7 +114,7 @@ void SoftMP3::initPorts() { void SoftMP3::initDecoder() { mConfig->equalizerType = flat; mConfig->crcEnabled = false; - + mConfig->samplingRate = mSamplingRate; uint32_t memRequirements = pvmp3_decoderMemRequirements(); mDecoderBuf = malloc(memRequirements); @@ -212,7 +214,7 @@ void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) { List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); - + int64_t tmpTime = 0; while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { BufferInfo *inInfo = NULL; OMX_BUFFERHEADERTYPE *inHeader = NULL; @@ -227,7 +229,20 @@ void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) { if (inHeader) { if (inHeader->nOffset == 0 && inHeader->nFilledLen) { - mAnchorTimeUs = inHeader->nTimeStamp; + // use new input buffer timestamp as Anchor Time if its + // a) first buffer or + // b) first buffer post seek or + // c) different from last buffer timestamp + //If input buffer timestamp is same as last input buffer timestamp then + //treat this as a erroneous timestamp and ignore new input buffer + //timestamp and use last output buffer timestamp as Anchor Time. + if ((mLastAnchorTimeUs != inHeader->nTimeStamp)) { + mAnchorTimeUs = inHeader->nTimeStamp; + mLastAnchorTimeUs = inHeader->nTimeStamp; + } else { + mAnchorTimeUs = mNextOutBufferTimeUs; + } + mNumFramesOutput = 0; } @@ -259,10 +274,13 @@ void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) { if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR && decoderErr != SIDE_INFO_ERROR) { ALOGE("mp3 decoder returned error %d", decoderErr); - - notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); - mSignalledError = true; - return; + if(decoderErr == SYNCH_LOST_ERROR) { + mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); + } else { + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + mSignalledError = true; + return; + } } if (mConfig->outputFrameSize == 0) { @@ -323,7 +341,7 @@ void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) { outHeader->nTimeStamp = mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / mSamplingRate; - + tmpTime = outHeader->nTimeStamp; if (inHeader) { CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); @@ -348,6 +366,10 @@ void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) { notifyFillBufferDone(outHeader); outHeader = NULL; } + + if (tmpTime > 0) { + mNextOutBufferTimeUs = tmpTime; + } } void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) { @@ -359,6 +381,8 @@ void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) { mSignalledError = false; mSawInputEos = false; mSignalledOutputEos = false; + mLastAnchorTimeUs = -1; + mNextOutBufferTimeUs = 0; } } @@ -395,6 +419,8 @@ void SoftMP3::onReset() { mSawInputEos = false; mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; + mLastAnchorTimeUs = -1; + mNextOutBufferTimeUs = 0; } } // namespace android diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h index f9e7b53..c769795 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.h +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -70,6 +70,9 @@ private: AWAITING_ENABLED } mOutputPortSettingsChange; + int64_t mLastAnchorTimeUs; + int64_t mNextOutBufferTimeUs; + void initPorts(); void initDecoder(); diff --git a/media/libstagefright/codecs/raw/SoftRaw.cpp b/media/libstagefright/codecs/raw/SoftRaw.cpp index 9d514a6..0d80098 100644 --- a/media/libstagefright/codecs/raw/SoftRaw.cpp +++ b/media/libstagefright/codecs/raw/SoftRaw.cpp @@ -42,7 +42,8 @@ SoftRaw::SoftRaw( : SimpleSoftOMXComponent(name, callbacks, appData, component), mSignalledError(false), mChannelCount(2), - mSampleRate(44100) { + mSampleRate(44100), + mBitsPerSample(16) { initPorts(); CHECK_EQ(initDecoder(), (status_t)OK); } @@ -58,7 +59,7 @@ void SoftRaw::initPorts() { def.eDir = OMX_DirInput; def.nBufferCountMin = kNumBuffers; def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = 32 * 1024; + def.nBufferSize = 192 * 1024; def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainAudio; @@ -76,7 +77,7 @@ void SoftRaw::initPorts() { def.eDir = OMX_DirOutput; def.nBufferCountMin = kNumBuffers; def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = 32 * 1024; + def.nBufferSize = 192 * 1024; def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainAudio; @@ -110,7 +111,7 @@ OMX_ERRORTYPE SoftRaw::internalGetParameter( pcmParams->eNumData = OMX_NumericalDataSigned; pcmParams->eEndian = OMX_EndianBig; pcmParams->bInterleaved = OMX_TRUE; - pcmParams->nBitPerSample = 16; + pcmParams->nBitPerSample = mBitsPerSample; pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; @@ -154,6 +155,7 @@ OMX_ERRORTYPE SoftRaw::internalSetParameter( mChannelCount = pcmParams->nChannels; mSampleRate = pcmParams->nSamplingRate; + mBitsPerSample = pcmParams->nBitPerSample; return OMX_ErrorNone; } diff --git a/media/libstagefright/codecs/raw/SoftRaw.h b/media/libstagefright/codecs/raw/SoftRaw.h index 015c4a3..894889f 100644 --- a/media/libstagefright/codecs/raw/SoftRaw.h +++ b/media/libstagefright/codecs/raw/SoftRaw.h @@ -43,13 +43,14 @@ protected: private: enum { - kNumBuffers = 4 + kNumBuffers = 8 }; bool mSignalledError; int32_t mChannelCount; int32_t mSampleRate; + int32_t mBitsPerSample; void initPorts(); status_t initDecoder(); diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml index 81a6d00..07e26a2 100644 --- a/media/libstagefright/data/media_codecs_google_video.xml +++ b/media/libstagefright/data/media_codecs_google_video.xml @@ -16,7 +16,14 @@ <Included> <Decoders> - <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es"> + <MediaCodec name="OMX.google.mpeg4.decoder"> + <Type name="video/mp4v-es" /> + <!-- + Use Google mpeg4 decoder for mpeg4 DP content which is not + supported by HW. A component can be used to support several + mimetypes, so non-DP mpeg4 usecases will not be affected by this. + --> + <Type name="video/mp4v-esdp" /> <!-- profiles and levels: ProfileSimple : Level3 --> <Limit name="size" min="2x2" max="352x288" /> <Limit name="alignment" value="2x2" /> diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp index a5b81a8..3ebbbd9 100644 --- a/media/libstagefright/foundation/ABuffer.cpp +++ b/media/libstagefright/foundation/ABuffer.cpp @@ -29,13 +29,9 @@ ABuffer::ABuffer(size_t capacity) mInt32Data(0), mOwnsData(true) { mData = malloc(capacity); - if (mData == NULL) { - mCapacity = 0; - mRangeLength = 0; - } else { - mCapacity = capacity; - mRangeLength = capacity; - } + CHECK(mData != NULL); + mCapacity = capacity; + mRangeLength = capacity; } ABuffer::ABuffer(void *data, size_t capacity) diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 1557401..1b0b1c5 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -54,7 +54,7 @@ const int64_t LiveSession::kReadyMarkUs = 5000000ll; const int64_t LiveSession::kPrepareMarkUs = 1500000ll; const int64_t LiveSession::kUnderflowMarkUs = 1000000ll; -struct LiveSession::BandwidthEstimator : public RefBase { +struct LiveSession::BandwidthEstimator : public LiveSession::BandwidthBaseEstimator { BandwidthEstimator(); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); @@ -1064,6 +1064,7 @@ void LiveSession::onMasterPlaylistFetched(const sp<AMessage> &msg) { itemsWithVideo.push(item); } } +#if 0 // remove the audio-only variants if we have at least one with video if (!itemsWithVideo.empty() && itemsWithVideo.size() < mBandwidthItems.size()) { @@ -1072,7 +1073,7 @@ void LiveSession::onMasterPlaylistFetched(const sp<AMessage> &msg) { mBandwidthItems.push(itemsWithVideo[i]); } } - +#endif CHECK_GT(mBandwidthItems.size(), 0u); initialBandwidth = mBandwidthItems[0].mBandwidth; diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index 90d56d0..0d504e4 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -73,7 +73,7 @@ struct LiveSession : public AHandler { const sp<IMediaHTTPService> &httpService); int64_t calculateMediaTimeUs(int64_t firstTimeUs, int64_t timeUs, int32_t discontinuitySeq); - status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit); + virtual status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit); status_t getStreamFormat(StreamType stream, sp<AMessage> *format); @@ -117,7 +117,6 @@ protected: virtual void onMessageReceived(const sp<AMessage> &msg); -private: friend struct PlaylistFetcher; enum { @@ -142,6 +141,14 @@ private: static const int64_t kPrepareMarkUs; static const int64_t kUnderflowMarkUs; + struct BandwidthBaseEstimator : public RefBase { + virtual void addBandwidthMeasurement(size_t numBytes, int64_t delayUs) = 0; + virtual bool estimateBandwidth( + int32_t *bandwidth, + bool *isStable = NULL, + int32_t *shortTermBps = NULL) = 0; + }; + struct BandwidthEstimator; struct BandwidthItem { size_t mPlaylistIndex; @@ -201,7 +208,7 @@ private: ssize_t mOrigBandwidthIndex; int32_t mLastBandwidthBps; bool mLastBandwidthStable; - sp<BandwidthEstimator> mBandwidthEstimator; + sp<BandwidthBaseEstimator> mBandwidthEstimator; sp<M3UParser> mPlaylist; int32_t mMaxWidth; @@ -251,10 +258,10 @@ private: KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs; KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs; - sp<PlaylistFetcher> addFetcher(const char *uri); + virtual sp<PlaylistFetcher> addFetcher(const char *uri); void onConnect(const sp<AMessage> &msg); - void onMasterPlaylistFetched(const sp<AMessage> &msg); + virtual void onMasterPlaylistFetched(const sp<AMessage> &msg); void onSeek(const sp<AMessage> &msg); bool UriIsSameAsIndex( const AString &uri, int32_t index, bool newUri); @@ -269,7 +276,7 @@ private: float getAbortThreshold( ssize_t currentBWIndex, ssize_t targetBWIndex) const; void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); - size_t getBandwidthIndex(int32_t bandwidthBps); + virtual size_t getBandwidthIndex(int32_t bandwidthBps); ssize_t getLowestValidBandwidthIndex() const; HLSTime latestMediaSegmentStartTime() const; @@ -284,7 +291,7 @@ private: void onChangeConfiguration2(const sp<AMessage> &msg); void onChangeConfiguration3(const sp<AMessage> &msg); - void swapPacketSource(StreamType stream); + virtual void swapPacketSource(StreamType stream); void tryToFinishBandwidthSwitch(const AString &oldUri); void cancelBandwidthSwitch(bool resume = false); bool checkSwitchProgress( @@ -296,7 +303,7 @@ private: void schedulePollBuffering(); void cancelPollBuffering(); void restartPollBuffering(); - void onPollBuffering(); + virtual void onPollBuffering(); bool checkBuffering(bool &underflow, bool &ready, bool &down, bool &up); void startBufferingIfNecessary(); void stopBufferingIfNecessary(); @@ -304,7 +311,7 @@ private: void finishDisconnect(); - void postPrepared(status_t err); + virtual void postPrepared(status_t err); void postError(status_t err); DISALLOW_EVIL_CONSTRUCTORS(LiveSession); diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 72d832e..023de93 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -975,7 +975,9 @@ bool PlaylistFetcher::initDownloadState( if (mSegmentStartTimeUs < 0) { if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) { // If this is a live session, start 3 segments from the end on connect - mSeqNumber = lastSeqNumberInPlaylist - 3; + if (!getSeqNumberInLiveStreaming()) { + mSeqNumber = lastSeqNumberInPlaylist - 3; + } if (mSeqNumber < firstSeqNumberInPlaylist) { mSeqNumber = firstSeqNumberInPlaylist; } @@ -1418,6 +1420,10 @@ void PlaylistFetcher::onDownloadNext() { } } + if (checkSwitchBandwidth()) { + return; + } + ++mSeqNumber; // if adapting, pause after found the next starting point diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h index c8ca457..6b60b65 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.h +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -89,7 +89,6 @@ protected: virtual ~PlaylistFetcher(); virtual void onMessageReceived(const sp<AMessage> &msg); -private: enum { kMaxNumRetries = 5, }; @@ -249,6 +248,8 @@ private: void updateDuration(); void updateTargetDuration(); + virtual bool checkSwitchBandwidth() { return false; } + virtual bool getSeqNumberInLiveStreaming() { return false; } DISALLOW_EVIL_CONSTRUCTORS(PlaylistFetcher); }; diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 4410579..76d65f0 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -503,8 +503,9 @@ void ID3::Iterator::getString(String8 *id, String8 *comment) const { void ID3::Iterator::getstring(String8 *id, bool otherdata) const { id->setTo(""); - const uint8_t *frameData = mFrameData; - if (frameData == NULL) { + size_t size; + const uint8_t *frameData = getData(&size); + if ((size == 0) || (frameData == NULL)) { return; } diff --git a/media/libstagefright/include/AACExtractor.h b/media/libstagefright/include/AACExtractor.h index e98ca82..9a0ba2f 100644 --- a/media/libstagefright/include/AACExtractor.h +++ b/media/libstagefright/include/AACExtractor.h @@ -21,6 +21,7 @@ #include <media/stagefright/MediaExtractor.h> #include <utils/Vector.h> +#include "include/APE.h" namespace android { @@ -48,6 +49,9 @@ private: Vector<uint64_t> mOffsetVector; int64_t mFrameDurationUs; + APE ape; + sp<MetaData> mApeMeta; + AACExtractor(const AACExtractor &); AACExtractor &operator=(const AACExtractor &); }; diff --git a/media/libstagefright/include/APE.h b/media/libstagefright/include/APE.h new file mode 100644 index 0000000..db49bb0 --- /dev/null +++ b/media/libstagefright/include/APE.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * 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 APE_TAG_H_ + +#define APE_TAG_H_ + +#include <utils/RefBase.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MetaData.h> + +namespace android { + +class APE{ +public: + APE(); + ~APE(); + bool isAPE(uint8_t *apeTag) const; + bool parseAPE(const sp<DataSource> &source, off64_t offset, + sp<MetaData> &meta); + +private: + uint32_t itemNumber; + uint32_t itemFlags; + size_t lenValue; +}; + +} //namespace android + +#endif //APE_TAG_H_ diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 758b2c9..c72f9f6 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -193,6 +193,7 @@ private: uint32_t mFlags; uint32_t mExtractorFlags; uint32_t mSinceLastDropped; + bool mDropFramesDisable; // hevc test int64_t mTimeSourceDeltaUs; int64_t mVideoTimeUs; diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h index a29bdf9..afa91ae 100644 --- a/media/libstagefright/include/NuCachedSource2.h +++ b/media/libstagefright/include/NuCachedSource2.h @@ -69,7 +69,7 @@ struct NuCachedSource2 : public DataSource { protected: virtual ~NuCachedSource2(); -private: +protected: friend struct AHandlerReflector<NuCachedSource2>; NuCachedSource2( diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index ecc2573..ff81379 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -31,6 +31,7 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/Utils.h> #include <utils/String8.h> +#include <media/stagefright/foundation/ABitReader.h> #include <inttypes.h> @@ -136,6 +137,7 @@ private: enum Type { AVC, AAC, + HEVC, OTHER }; @@ -234,6 +236,17 @@ MatroskaSource::MatroskaSource( mNALSizeLen = 1 + (avcc[4] & 3); ALOGV("mNALSizeLen = %zu", mNALSizeLen); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) { + mType = HEVC; + + uint32_t type; + const uint8_t *data; + size_t size; + CHECK(meta->findData(kKeyHVCC, &type, (const void **)&data, &size)); + + CHECK(size >= 7); + mNALSizeLen = 1 + (data[14 + 7] & 3); + ALOGV("mNALSizeLen = %zu", mNALSizeLen); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { mType = AAC; } @@ -521,8 +534,9 @@ status_t MatroskaSource::readBlock() { const mkvparser::Block *block = mBlockIter.block(); int64_t timeUs = mBlockIter.blockTimeUs(); + int frameCount = block->GetFrameCount(); - for (int i = 0; i < block->GetFrameCount(); ++i) { + for (int i = 0; i < frameCount; ++i) { const mkvparser::Block::Frame &frame = block->GetFrame(i); MediaBuffer *mbuf = new MediaBuffer(frame.len); @@ -542,6 +556,27 @@ status_t MatroskaSource::readBlock() { mBlockIter.advance(); + if (!mBlockIter.eos() && frameCount > 1) { + // For files with lacing enabled, we need to amend they kKeyTime of + // each frame so that their kKeyTime are advanced accordingly (instead + // of being set to the same value). To do this, we need to find out + // the duration of the block using the start time of the next block. + int64_t duration = mBlockIter.blockTimeUs() - timeUs; + int64_t durationPerFrame = duration / frameCount; + int64_t durationRemainder = duration % frameCount; + + // We split duration to each of the frame, distributing the remainder (if any) + // to the later frames. The later frames are processed first due to the + // use of the iterator for the doubly linked list + List<MediaBuffer *>::iterator it = mPendingFrames.end(); + for (int i = frameCount - 1; i >= 0; --i) { + --it; + int64_t frameRemainder = durationRemainder >= frameCount - i ? 1 : 0; + int64_t frameTimeUs = timeUs + durationPerFrame * i + frameRemainder; + (*it)->meta_data()->setInt64(kKeyTime, frameTimeUs); + } + } + return OK; } @@ -581,7 +616,7 @@ status_t MatroskaSource::read( MediaBuffer *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); - if (mType != AVC) { + if (mType != AVC && mType != HEVC) { if (targetSampleTimeUs >= 0ll) { frame->meta_data()->setInt64( kKeyTargetTime, targetSampleTimeUs); @@ -819,6 +854,17 @@ static void addESDSFromCodecPrivate( const sp<MetaData> &meta, bool isAudio, const void *priv, size_t privSize) { + if(isAudio) { + ABitReader br((const uint8_t *)priv, privSize); + uint32_t objectType = br.getBits(5); + + if (objectType == 31) { // AAC-ELD => additional 6 bits + objectType = 32 + br.getBits(6); + } + + meta->setInt32(kKeyAACAOT, objectType); + } + int privSizeBytesRequired = bytesForSize(privSize); int esdsSize2 = 14 + privSizeBytesRequired + privSize; int esdsSize2BytesRequired = bytesForSize(esdsSize2); @@ -979,6 +1025,10 @@ void MatroskaExtractor::addTracks() { codecID); continue; } + } else if (!strcmp("V_MPEGH/ISO/HEVC", codecID)) { + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC); + meta->setData(kKeyHVCC, kTypeHVCC, codecPrivate, codecPrivateSize); + } else if (!strcmp("V_VP8", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP8); } else if (!strcmp("V_VP9", codecID)) { @@ -1000,7 +1050,9 @@ void MatroskaExtractor::addTracks() { if (!strcmp("A_AAC", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); - CHECK(codecPrivateSize >= 2); + if (codecPrivateSize < 2) { + return; + } addESDSFromCodecPrivate( meta, true, codecPrivate, codecPrivateSize); diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 5f0f567..d16d5df 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -30,6 +30,12 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_foundation \ libdl +ifeq ($(call is-vendor-board-platform,QCOM),true) +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_FLAC_DECODER)),true) + LOCAL_CFLAGS += -DQTI_FLAC_DECODER +endif +endif + LOCAL_MODULE:= libstagefright_omx LOCAL_CFLAGS += -Werror -Wall LOCAL_CLANG := true diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 7f357c9..efb1a1c 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -468,6 +468,10 @@ OMX_ERRORTYPE OMX::OnEvent( findInstance(node)->onEvent(eEvent, nData1, nData2); sp<OMX::CallbackDispatcher> dispatcher = findDispatcher(node); + if (dispatcher == NULL) { + ALOGW("OnEvent Callback dispatcher NULL, skip post"); + return OMX_ErrorNone; + } // output rendered events are not processed as regular events until they hit the observer if (eEvent == OMX_EventOutputRendered) { @@ -513,7 +517,12 @@ OMX_ERRORTYPE OMX::OnEmptyBufferDone( msg.fenceFd = fenceFd; msg.u.buffer_data.buffer = buffer; - findDispatcher(node)->post(msg); + sp<OMX::CallbackDispatcher> callbackDispatcher = findDispatcher(node); + if (callbackDispatcher != NULL) { + callbackDispatcher->post(msg); + } else { + ALOGW("OnEmptyBufferDone Callback dispatcher NULL, skip post"); + } return OMX_ErrorNone; } @@ -532,7 +541,12 @@ OMX_ERRORTYPE OMX::OnFillBufferDone( msg.u.extended_buffer_data.flags = pBuffer->nFlags; msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp; - findDispatcher(node)->post(msg); + sp<OMX::CallbackDispatcher> callbackDispatcher = findDispatcher(node); + if (callbackDispatcher != NULL) { + callbackDispatcher->post(msg); + } else { + ALOGW("OnFillBufferDone Callback dispatcher NULL, skip post"); + } return OMX_ErrorNone; } diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp index ae3cb33..f7bb733 100644 --- a/media/libstagefright/omx/OMXMaster.cpp +++ b/media/libstagefright/omx/OMXMaster.cpp @@ -25,52 +25,58 @@ #include <dlfcn.h> #include <media/stagefright/foundation/ADebug.h> +#include <cutils/properties.h> namespace android { -OMXMaster::OMXMaster() - : mVendorLibHandle(NULL) { +OMXMaster::OMXMaster() { addVendorPlugin(); addPlugin(new SoftOMXPlugin); + addUserPlugin(); } OMXMaster::~OMXMaster() { clearPlugins(); - - if (mVendorLibHandle != NULL) { - dlclose(mVendorLibHandle); - mVendorLibHandle = NULL; - } } void OMXMaster::addVendorPlugin() { addPlugin("libstagefrighthw.so"); } +void OMXMaster::addUserPlugin() { + char plugin[PROPERTY_VALUE_MAX]; + if (property_get("media.sf.omx-plugin", plugin, NULL)) { + addPlugin(plugin); + } +} + void OMXMaster::addPlugin(const char *libname) { - mVendorLibHandle = dlopen(libname, RTLD_NOW); + void* handle = dlopen(libname, RTLD_NOW); - if (mVendorLibHandle == NULL) { + if (handle == NULL) { return; } typedef OMXPluginBase *(*CreateOMXPluginFunc)(); CreateOMXPluginFunc createOMXPlugin = (CreateOMXPluginFunc)dlsym( - mVendorLibHandle, "createOMXPlugin"); + handle, "createOMXPlugin"); if (!createOMXPlugin) createOMXPlugin = (CreateOMXPluginFunc)dlsym( - mVendorLibHandle, "_ZN7android15createOMXPluginEv"); + handle, "_ZN7android15createOMXPluginEv"); if (createOMXPlugin) { - addPlugin((*createOMXPlugin)()); + addPlugin((*createOMXPlugin)(), handle); } } -void OMXMaster::addPlugin(OMXPluginBase *plugin) { +void OMXMaster::addPlugin(OMXPluginBase *plugin, void *handle) { + if (plugin == 0) { + return; + } Mutex::Autolock autoLock(mLock); - mPlugins.push_back(plugin); + mPlugins.add(plugin, handle); OMX_U32 index = 0; @@ -100,21 +106,32 @@ void OMXMaster::clearPlugins() { Mutex::Autolock autoLock(mLock); typedef void (*DestroyOMXPluginFunc)(OMXPluginBase*); - DestroyOMXPluginFunc destroyOMXPlugin = - (DestroyOMXPluginFunc)dlsym( - mVendorLibHandle, "destroyOMXPlugin"); - mPluginByComponentName.clear(); + for (unsigned int i = 0; i < mPlugins.size(); i++) { + OMXPluginBase *plugin = mPlugins.keyAt(i); + if (plugin != NULL) { + void *handle = mPlugins.valueAt(i); + + if (handle != NULL) { + DestroyOMXPluginFunc destroyOMXPlugin = + (DestroyOMXPluginFunc)dlsym( + handle, "destroyOMXPlugin"); + + if (destroyOMXPlugin) + destroyOMXPlugin(plugin); + else + delete plugin; - for (List<OMXPluginBase *>::iterator it = mPlugins.begin(); - it != mPlugins.end(); ++it) { - if (destroyOMXPlugin) - destroyOMXPlugin(*it); - else - delete *it; - *it = NULL; + dlclose(handle); + } else { + delete plugin; + } + + plugin = NULL; + } } + mPluginByComponentName.clear(); mPlugins.clear(); } diff --git a/media/libstagefright/omx/OMXMaster.h b/media/libstagefright/omx/OMXMaster.h index 6069741..c07fed3 100644 --- a/media/libstagefright/omx/OMXMaster.h +++ b/media/libstagefright/omx/OMXMaster.h @@ -51,15 +51,14 @@ struct OMXMaster : public OMXPluginBase { private: Mutex mLock; - List<OMXPluginBase *> mPlugins; + KeyedVector<OMXPluginBase *, void *> mPlugins; KeyedVector<String8, OMXPluginBase *> mPluginByComponentName; KeyedVector<OMX_COMPONENTTYPE *, OMXPluginBase *> mPluginByInstance; - void *mVendorLibHandle; - void addVendorPlugin(); + void addUserPlugin(); void addPlugin(const char *libname); - void addPlugin(OMXPluginBase *plugin); + void addPlugin(OMXPluginBase *plugin, void *handle = NULL); void clearPlugins(); OMXMaster(const OMXMaster &); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 94a213a..4bcc732 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -122,7 +122,8 @@ struct BufferMeta { } // check component returns proper range - sp<ABuffer> codec = getBuffer(header, false /* backup */, true /* limit */); + sp<ABuffer> codec = getBuffer(header, false /* backup */, + !(header->nFlags & OMX_BUFFERFLAG_EXTRADATA)); memcpy((OMX_U8 *)mMem->pointer() + header->nOffset, codec->data(), codec->size()); } @@ -132,9 +133,11 @@ struct BufferMeta { return; } + size_t bytesToCopy = header->nFlags & OMX_BUFFERFLAG_EXTRADATA ? + header->nAllocLen - header->nOffset : header->nFilledLen; memcpy(header->pBuffer + header->nOffset, (const OMX_U8 *)mMem->pointer() + header->nOffset, - header->nFilledLen); + bytesToCopy); } // return either the codec or the backup buffer @@ -279,6 +282,7 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { OMX_STATETYPE state; CHECK_EQ(OMX_GetState(mHandle, &state), OMX_ErrorNone); switch (state) { + case OMX_StatePause: case OMX_StateExecuting: { ALOGV("forcing Executing->Idle"); diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 0f9c00c..4afd5d5 100755 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -59,6 +59,9 @@ static const struct { { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" }, { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" }, { "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" }, +#ifdef QTI_FLAC_DECODER + { "OMX.qti.audio.decoder.flac", "flacdec", "audio_decoder.flac" }, +#endif }; static const size_t kNumComponents = @@ -74,6 +77,7 @@ OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( OMX_COMPONENTTYPE **component) { ALOGV("makeComponentInstance '%s'", name); + dlerror(); // clear any existing error for (size_t i = 0; i < kNumComponents; ++i) { if (strcmp(name, kComponents[i].mName)) { continue; @@ -91,6 +95,8 @@ OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( return OMX_ErrorComponentNotFound; } + ALOGV("load component %s for %s", libName.c_str(), name); + typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)( const char *, const OMX_CALLBACKTYPE *, OMX_PTR, OMX_COMPONENTTYPE **); @@ -101,7 +107,8 @@ OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE" "PvPP17OMX_COMPONENTTYPE"); - if (createSoftOMXComponent == NULL) { + if (const char *error = dlerror()) { + ALOGE("unable to dlsym %s: %s", libName.c_str(), error); dlclose(libHandle); libHandle = NULL; diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp index 75cd911..28594e2 100644 --- a/media/libstagefright/rtsp/AH263Assembler.cpp +++ b/media/libstagefright/rtsp/AH263Assembler.cpp @@ -27,6 +27,8 @@ #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/Utils.h> +#include <mediaplayerservice/AVMediaServiceExtensions.h> + namespace android { AH263Assembler::AH263Assembler(const sp<AMessage> ¬ify) @@ -63,7 +65,8 @@ ARTPAssembler::AssemblyStatus AH263Assembler::addPacket( if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) { break; } - + AVMediaServiceUtils::get()->addH263AdvancedPacket( + *it, &mPackets, mAccessUnitRTPTime); it = queue->erase(it); } diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp index a1a6576..d239e7b 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -420,6 +420,11 @@ sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) { CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size()); offset += mOtherDataLenBits / 8; } + + if (i < mNumSubFrames && offset >= buffer->size()) { + ALOGW("Skip subframes after %d, total %d", (int)i, (int)mNumSubFrames); + break; + } } if (offset < buffer->size()) { diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp index a86ab74..e021b29 100644 --- a/media/libstagefright/rtsp/ARTPConnection.cpp +++ b/media/libstagefright/rtsp/ARTPConnection.cpp @@ -81,7 +81,8 @@ void ARTPConnection::addStream( const sp<ASessionDescription> &sessionDesc, size_t index, const sp<AMessage> ¬ify, - bool injected) { + bool injected, + bool isIPV6) { sp<AMessage> msg = new AMessage(kWhatAddStream, this); msg->setInt32("rtp-socket", rtpSocket); msg->setInt32("rtcp-socket", rtcpSocket); @@ -89,6 +90,7 @@ void ARTPConnection::addStream( msg->setSize("index", index); msg->setMessage("notify", notify); msg->setInt32("injected", injected); + msg->setInt32("isIPV6", isIPV6); msg->post(); } @@ -145,6 +147,10 @@ void ARTPConnection::MakePortPair( TRESPASS(); } +size_t ARTPConnection::sockAddrSize() { + return sizeof(struct sockaddr_in); +} + void ARTPConnection::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatAddStream: @@ -345,7 +351,7 @@ void ARTPConnection::onPollStreams() { n = sendto( s->mRTCPSocket, buffer->data(), buffer->size(), 0, (const struct sockaddr *)&s->mRemoteRTCPAddr, - sizeof(s->mRemoteRTCPAddr)); + sockAddrSize()); } while (n < 0 && errno == EINTR); if (n <= 0) { diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h index edbcc35..057007b 100644 --- a/media/libstagefright/rtsp/ARTPConnection.h +++ b/media/libstagefright/rtsp/ARTPConnection.h @@ -38,7 +38,8 @@ struct ARTPConnection : public AHandler { int rtpSocket, int rtcpSocket, const sp<ASessionDescription> &sessionDesc, size_t index, const sp<AMessage> ¬ify, - bool injected); + bool injected, + bool isIPV6 = false); void removeStream(int rtpSocket, int rtcpSocket); @@ -53,8 +54,8 @@ struct ARTPConnection : public AHandler { protected: virtual ~ARTPConnection(); virtual void onMessageReceived(const sp<AMessage> &msg); + virtual size_t sockAddrSize(); -private: enum { kWhatAddStream, kWhatRemoveStream, @@ -72,7 +73,7 @@ private: bool mPollEventPending; int64_t mLastReceiverReportTimeUs; - void onAddStream(const sp<AMessage> &msg); + virtual void onAddStream(const sp<AMessage> &msg); void onRemoveStream(const sp<AMessage> &msg); void onPollStreams(); void onInjectPacket(const sp<AMessage> &msg); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 855ffdc..b2c1fd1 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -26,6 +26,7 @@ #include <media/stagefright/foundation/base64.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/Utils.h> +#include <mediaplayerservice/AVMediaServiceExtensions.h> #include <arpa/inet.h> #include <fcntl.h> @@ -170,7 +171,7 @@ bool ARTSPConnection::ParseURL( } } - const char *colonPos = strchr(host->c_str(), ':'); + const char *colonPos = AVMediaServiceUtils::get()->parseURL(host); if (colonPos != NULL) { unsigned long x; @@ -252,6 +253,11 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) { ALOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str()); } + performConnect(reply, host, port); +} + +void ARTSPConnection::performConnect(const sp<AMessage> &reply, + AString host, unsigned port) { struct hostent *ent = gethostbyname(host.c_str()); if (ent == NULL) { ALOGE("Unknown host %s", host.c_str()); @@ -381,7 +387,12 @@ void ARTSPConnection::onCompleteConnection(const sp<AMessage> &msg) { socklen_t optionLen = sizeof(err); CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0); CHECK_EQ(optionLen, (socklen_t)sizeof(err)); + performCompleteConnection(msg, err); +} +void ARTSPConnection::performCompleteConnection(const sp<AMessage> &msg, int err) { + sp<AMessage> reply; + CHECK(msg->findMessage("reply", &reply)); if (err != 0) { ALOGE("err = %d (%s)", err, strerror(err)); diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h index 1fe9c99..c4ebe1d 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.h +++ b/media/libstagefright/rtsp/ARTSPConnection.h @@ -42,6 +42,8 @@ struct ARTSPConnection : public AHandler { void observeBinaryData(const sp<AMessage> &reply); + virtual bool isIPV6() { return false; } + static bool ParseURL( const char *url, AString *host, unsigned *port, AString *path, AString *user, AString *pass); @@ -49,8 +51,11 @@ struct ARTSPConnection : public AHandler { protected: virtual ~ARTSPConnection(); virtual void onMessageReceived(const sp<AMessage> &msg); + virtual void performConnect(const sp<AMessage> &reply, + AString host, unsigned port); + virtual void performCompleteConnection(const sp<AMessage> &msg, + int err); -private: enum State { DISCONNECTED, CONNECTING, diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 98498e9..92ad1ec 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -22,7 +22,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> - +#include <mediaplayerservice/AVMediaServiceExtensions.h> #include <stdlib.h> namespace android { @@ -264,7 +264,8 @@ bool ASessionDescription::getDurationUs(int64_t *durationUs) const { } float from, to; - if (!parseNTPRange(value.c_str() + 4, &from, &to)) { + if (!AVMediaServiceUtils::get()->parseNTPRange( + value.c_str() + 4, &from, &to)) { return false; } diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index c5e8c35..8bc4295 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -22,8 +22,10 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES += libcrypto LOCAL_C_INCLUDES:= \ - $(TOP)/frameworks/av/media/libstagefright \ - $(TOP)/frameworks/native/include/media/openmax + $(TOP)/frameworks/av/media/libstagefright \ + $(TOP)/frameworks/av/media/libavextensions \ + $(TOP)/frameworks/native/include/media/openmax \ + $(TOP)/frameworks/av/media/libmediaplayerservice \ LOCAL_MODULE:= libstagefright_rtsp diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 0d0baf3..7290ee2 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -32,6 +32,7 @@ #include "ASessionDescription.h" #include <ctype.h> +#include <cutils/properties.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -39,6 +40,7 @@ #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/Utils.h> +#include <mediaplayerservice/AVMediaServiceExtensions.h> #include <arpa/inet.h> #include <sys/socket.h> @@ -64,6 +66,8 @@ static int64_t kDefaultKeepAliveTimeoutUs = 60000000ll; static int64_t kPauseDelayUs = 3000000ll; +static int64_t kTearDownTimeoutUs = 3000000ll; + namespace android { static bool GetAttribute(const char *s, const char *key, AString *value) { @@ -105,6 +109,8 @@ struct MyHandler : public AHandler { kWhatEOS = 'eos!', kWhatSeekDiscontinuity = 'seeD', kWhatNormalPlayTimeMapping = 'nptM', + kWhatCancelCheck = 'canC', + kWhatByeReceived = 'byeR', }; MyHandler( @@ -115,8 +121,6 @@ struct MyHandler : public AHandler { mUIDValid(uidValid), mUID(uid), mNetLooper(new ALooper), - mConn(new ARTSPConnection(mUIDValid, mUID)), - mRTPConn(new ARTPConnection), mOriginalSessionURL(url), mSessionURL(url), mSetupTracksSuccessful(false), @@ -140,11 +144,22 @@ struct MyHandler : public AHandler { mPausing(false), mPauseGeneration(0), mPlayResponseParsed(false) { + mConn = AVMediaServiceFactory::get()->createARTSPConnection( + mUIDValid, uid); + mRTPConn = AVMediaServiceFactory::get()->createARTPConnection(); mNetLooper->setName("rtsp net"); mNetLooper->start(false /* runOnCallingThread */, false /* canCallJava */, PRIORITY_HIGHEST); + char value[PROPERTY_VALUE_MAX] = {0}; + property_get("rtsp.transport.TCP", value, "false"); + if (!strcasecmp(value, "true")) { + mTryTCPInterleaving = true; + } else { + mTryTCPInterleaving = false; + } + // Strip any authentication info from the session url, we don't // want to transmit user/pass in cleartext. AString host, path, user, pass; @@ -244,6 +259,11 @@ struct MyHandler : public AHandler { msg->post(); } + void cancelTimeoutCheck() { + sp<AMessage> msg = new AMessage('canC', this); + msg->post(); + } + static void addRR(const sp<ABuffer> &buf) { uint8_t *ptr = buf->data() + buf->size(); ptr[0] = 0x80 | 0; @@ -703,6 +723,7 @@ struct MyHandler : public AHandler { timeoutSecs); } } + AVMediaServiceUtils::get()->setServerTimeoutUs(mKeepAliveTimeoutUs); i = mSessionID.find(";"); if (i >= 0) { @@ -722,16 +743,19 @@ struct MyHandler : public AHandler { // We are going to continue even if we were // unable to poke a hole into the firewall... - pokeAHole( + AVMediaServiceUtils::get()->pokeAHole( + this, track->mRTPSocket, track->mRTCPSocket, - transport); + transport, + mSessionHost); } mRTPConn->addStream( track->mRTPSocket, track->mRTCPSocket, mSessionDesc, index, - notify, track->mUsingInterleavedTCP); + notify, track->mUsingInterleavedTCP, + mConn->isIPV6()); mSetupTracksSuccessful = true; } else { @@ -774,6 +798,7 @@ struct MyHandler : public AHandler { request.append(mSessionID); request.append("\r\n"); + AVMediaServiceUtils::get()->appendRange(&request); request.append("\r\n"); sp<AMessage> reply = new AMessage('play', this); @@ -922,6 +947,15 @@ struct MyHandler : public AHandler { request.append("\r\n"); mConn->sendRequest(request.c_str(), reply); + + // If the response of teardown hasn't been received in 3 seconds, + // post 'tear' message to avoid ANR. + if (!msg->findInt32("reconnect", &reconnect) || !reconnect) { + sp<AMessage> teardown = new AMessage('tear', this); + teardown->setInt32("result", -ECONNABORTED); + teardown->post(kTearDownTimeoutUs); + } + break; } @@ -1020,6 +1054,13 @@ struct MyHandler : public AHandler { int32_t eos; if (msg->findInt32("eos", &eos)) { ALOGI("received BYE on track index %zu", trackIndex); + char value[PROPERTY_VALUE_MAX] = {0}; + if (property_get("rtcp.bye.notify", value, "false") + && !strcasecmp(value, "true")) { + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("what", kWhatByeReceived); + msg->post(); + } if (!mAllTracksHaveTime && dataReceivedOnAllChannels()) { ALOGI("No time established => fake existing data"); @@ -1386,6 +1427,13 @@ struct MyHandler : public AHandler { break; } + case 'canC': + { + ALOGV("cancel checking timeout"); + mCheckGeneration++; + break; + } + default: TRESPASS(); break; @@ -1471,7 +1519,9 @@ struct MyHandler : public AHandler { size_t trackIndex = 0; while (trackIndex < mTracks.size() - && !(val == mTracks.editItemAt(trackIndex).mURL)) { + && !(AVMediaServiceUtils::get()->parseTrackURL( + mTracks.editItemAt(trackIndex).mURL, val) + || val == mTracks.editItemAt(trackIndex).mURL)) { ++trackIndex; } CHECK_LT(trackIndex, mTracks.size()); @@ -1648,8 +1698,9 @@ private: request.append(interleaveIndex + 1); } else { unsigned rtpPort; - ARTPConnection::MakePortPair( - &info->mRTPSocket, &info->mRTCPSocket, &rtpPort); + AVMediaServiceUtils::get()->makePortPair( + &info->mRTPSocket, &info->mRTCPSocket, &rtpPort, + mConn->isIPV6()); if (mUIDValid) { HTTPBase::RegisterSocketUserTag(info->mRTPSocket, mUID, @@ -1860,7 +1911,7 @@ private: mLastMediaTimeUs = mediaTimeUs; } - if (mediaTimeUs < 0) { + if (mediaTimeUs < 0 && !mSeekable) { ALOGV("dropping early accessUnit."); return false; } diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index b6de0d9..8aeabf0 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -50,6 +50,10 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/radio \ external/sonic +ifneq ($(BOARD_NUMBER_OF_CAMERAS),) + LOCAL_CFLAGS += -DMAX_CAMERAS=$(BOARD_NUMBER_OF_CAMERAS) +endif + LOCAL_MODULE:= mediaserver LOCAL_32_BIT_ONLY := true diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 07199e3..6a334e6 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -95,6 +95,7 @@ static const MtpEventCode kSupportedEventCodes[] = { MTP_EVENT_STORE_ADDED, MTP_EVENT_STORE_REMOVED, MTP_EVENT_DEVICE_PROP_CHANGED, + MTP_EVENT_OBJECT_PROP_CHANGED, }; MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp, @@ -253,6 +254,11 @@ void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { sendEvent(MTP_EVENT_OBJECT_REMOVED, handle); } +void MtpServer::sendObjectUpdated(MtpObjectHandle handle) { + ALOGV("sendObjectUpdated %d\n", handle); + sendEvent(MTP_EVENT_OBJECT_PROP_CHANGED, handle); +} + void MtpServer::sendStoreAdded(MtpStorageID id) { ALOGV("sendStoreAdded %08X\n", id); sendEvent(MTP_EVENT_STORE_ADDED, id); diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index b3a11e0..8bd0472 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -105,6 +105,7 @@ public: void sendObjectAdded(MtpObjectHandle handle); void sendObjectRemoved(MtpObjectHandle handle); void sendDevicePropertyChanged(MtpDeviceProperty property); + void sendObjectUpdated(MtpObjectHandle handle); private: void sendStoreAdded(MtpStorageID id); diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 9b4ba79..474fb46 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -60,6 +60,14 @@ LOCAL_STATIC_LIBRARIES := \ libcpustats \ libmedia_helper +#QTI Resampler +ifeq ($(call is-vendor-board-platform,QCOM), true) +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_RESAMPLER)), true) +LOCAL_CFLAGS += -DQTI_RESAMPLER +endif +endif +#QTI Resampler + LOCAL_MODULE:= libaudioflinger LOCAL_32_BIT_ONLY := true @@ -123,7 +131,19 @@ LOCAL_C_INCLUDES := \ LOCAL_SHARED_LIBRARIES := \ libcutils \ libdl \ - liblog + liblog \ + libaudioutils + +#QTI Resampler +ifeq ($(call is-vendor-board-platform,QCOM), true) +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_RESAMPLER)), true) +LOCAL_SRC_FILES_$(TARGET_2ND_ARCH) += AudioResamplerQTI.cpp.arm +LOCAL_C_INCLUDES_$(TARGET_2ND_ARCH) += $(TARGET_OUT_HEADERS)/mm-audio/audio-src +LOCAL_SHARED_LIBRARIES_$(TARGET_2ND_ARCH) += libqct_resampler +LOCAL_CFLAGS_$(TARGET_2ND_ARCH) += -DQTI_RESAMPLER +endif +endif +#QTI Resampler LOCAL_MODULE := libaudioresampler diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 9ec5802..d7af22c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -131,6 +131,14 @@ const char *formatToString(audio_format_t format) { case AUDIO_FORMAT_OPUS: return "opus"; case AUDIO_FORMAT_AC3: return "ac-3"; case AUDIO_FORMAT_E_AC3: return "e-ac-3"; + case AUDIO_FORMAT_PCM_OFFLOAD: + switch (format) { + case AUDIO_FORMAT_PCM_16_BIT_OFFLOAD: return "pcm-16bit-offload"; + case AUDIO_FORMAT_PCM_24_BIT_OFFLOAD: return "pcm-24bit-offload"; + default: + break; + } + break; default: break; } @@ -1053,9 +1061,24 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& } mHardwareStatus = AUDIO_HW_IDLE; } - // disable AEC and NS if the device is a BT SCO headset supporting those pre processings + AudioParameter param = AudioParameter(keyValuePairs); - String8 value; + String8 value, key; + key = String8("SND_CARD_STATUS"); + if (param.get(key, value) == NO_ERROR) { + ALOGV("Set keySoundCardStatus:%s", value.string()); + if ((value.find("OFFLINE", 0) != -1) ) { + ALOGV("OFFLINE detected - call InvalidateTracks()"); + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); + if( thread->getOutput()->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ){ + thread->invalidateTracks(AUDIO_STREAM_MUSIC); + } + } + } + } + + // disable AEC and NS if the device is a BT SCO headset supporting those pre processings if (param.get(String8(AUDIO_PARAMETER_KEY_BT_NREC), value) == NO_ERROR) { bool btNrecIsOff = (value == AUDIO_PARAMETER_VALUE_OFF); if (mBtNrecIsOff != btNrecIsOff) { @@ -1353,11 +1376,10 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) : RefBase(), mAudioFlinger(audioFlinger), // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below - mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), + mMemoryDealer(new MemoryDealer(4100*1024, "AudioFlinger::Client")), //4MB + 1 more 4k page mPid(pid), mTimedTrackCount(0) { - // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer } // Client destructor must be called with AudioFlinger::mClientLock held @@ -1818,7 +1840,11 @@ sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_ || !isValidPcmSinkFormat(config->format) || !isValidPcmSinkChannelMask(config->channel_mask)) { thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady); - ALOGV("openOutput_l() created direct output: ID %d thread %p", *output, thread); + ALOGV("openOutput_l() created direct output: ID %d thread %p ", *output, thread); + //Check if this is DirectPCM, if so + if (flags & AUDIO_OUTPUT_FLAG_DIRECT_PCM) { + thread->mIsDirectPcm = true; + } } else { thread = new MixerThread(this, outputStream, *output, devices, mSystemReady); ALOGV("openOutput_l() created mixer output: ID %d thread %p", *output, thread); @@ -2960,6 +2986,7 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand bool firstRead = true; #define TEE_SINK_READ 1024 // frames per I/O operation void *buffer = malloc(TEE_SINK_READ * frameSize); + CHECK (buffer != NULL); for (;;) { size_t count = TEE_SINK_READ; ssize_t actual = teeSource->read(buffer, count, diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 8a9a837..27a2f65 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -85,6 +85,9 @@ static const bool kUseFloat = true; // Set to default copy buffer size in frames for input processing. static const size_t kCopyBufferFrameCount = 256; +#ifdef QTI_RESAMPLER +#define QTI_RESAMPLER_MAX_SAMPLERATE 192000 +#endif namespace android { // ---------------------------------------------------------------------------- @@ -779,6 +782,13 @@ bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSam // but if none exists, it is the channel count (1 for mono). const int resamplerChannelCount = downmixerBufferProvider != NULL ? mMixerChannelCount : channelCount; +#ifdef QTI_RESAMPLER + if ((trackSampleRate <= QTI_RESAMPLER_MAX_SAMPLERATE) && + (trackSampleRate > devSampleRate * 2) && + ((devSampleRate == 48000)||(devSampleRate == 44100))) { + quality = AudioResampler::QTI_QUALITY; + } +#endif ALOGVV("Creating resampler:" " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n", mMixerInFormat, resamplerChannelCount, devSampleRate, quality); @@ -1644,6 +1654,9 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, // Note: In case of later int16_t sink output, // conversion and clamping is done by memcpy_to_i16_from_float(). } while (--outFrames); + //assign fout to out, when no more frames are available, so that 0s + //can be filled at the right place + out = (int32_t *)fout; break; case AUDIO_FORMAT_PCM_16_BIT: if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) { diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index 7165c6c..f0ae4ec 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -137,6 +137,7 @@ public: case AUDIO_FORMAT_PCM_8_BIT: case AUDIO_FORMAT_PCM_16_BIT: case AUDIO_FORMAT_PCM_24_BIT_PACKED: + case AUDIO_FORMAT_PCM_8_24_BIT: case AUDIO_FORMAT_PCM_32_BIT: case AUDIO_FORMAT_PCM_FLOAT: return true; diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index e49b7b1..ab3294a 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -28,6 +28,10 @@ #include "AudioResamplerCubic.h" #include "AudioResamplerDyn.h" +#ifdef QTI_RESAMPLER +#include "AudioResamplerQTI.h" +#endif + #ifdef __arm__ #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 #endif @@ -90,6 +94,9 @@ bool AudioResampler::qualityIsSupported(src_quality quality) case DYN_LOW_QUALITY: case DYN_MED_QUALITY: case DYN_HIGH_QUALITY: +#ifdef QTI_RESAMPLER + case QTI_QUALITY: +#endif return true; default: return false; @@ -110,7 +117,11 @@ void AudioResampler::init_routine() if (*endptr == '\0') { defaultQuality = (src_quality) l; ALOGD("forcing AudioResampler quality to %d", defaultQuality); +#ifdef QTI_RESAMPLER + if (defaultQuality < DEFAULT_QUALITY || defaultQuality > QTI_QUALITY) { +#else if (defaultQuality < DEFAULT_QUALITY || defaultQuality > DYN_HIGH_QUALITY) { +#endif defaultQuality = DEFAULT_QUALITY; } } @@ -129,6 +140,9 @@ uint32_t AudioResampler::qualityMHz(src_quality quality) case HIGH_QUALITY: return 20; case VERY_HIGH_QUALITY: +#ifdef QTI_RESAMPLER + case QTI_QUALITY: //for QTI_QUALITY, currently assuming same as VHQ +#endif return 34; case DYN_LOW_QUALITY: return 4; @@ -204,6 +218,11 @@ AudioResampler* AudioResampler::create(audio_format_t format, int inChannelCount case DYN_HIGH_QUALITY: quality = DYN_MED_QUALITY; break; +#ifdef QTI_RESAMPLER + case QTI_QUALITY: + quality = DYN_HIGH_QUALITY; + break; +#endif } } pthread_mutex_unlock(&mutex); @@ -250,6 +269,12 @@ AudioResampler* AudioResampler::create(audio_format_t format, int inChannelCount } } break; +#ifdef QTI_RESAMPLER + case QTI_QUALITY: + ALOGV("Create QTI_QUALITY Resampler = %d",quality); + resampler = new AudioResamplerQTI(format, inChannelCount, sampleRate); + break; +#endif } // initialize resampler diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index a8e3e6f..6669a85 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -47,6 +47,9 @@ public: DYN_LOW_QUALITY=5, DYN_MED_QUALITY=6, DYN_HIGH_QUALITY=7, +#ifdef QTI_RESAMPLER + QTI_QUALITY=8, +#endif }; static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f; diff --git a/services/audioflinger/AudioResamplerQTI.cpp b/services/audioflinger/AudioResamplerQTI.cpp new file mode 100644 index 0000000..44b741e --- /dev/null +++ b/services/audioflinger/AudioResamplerQTI.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AudioResamplerQTI.h" +#include "QCT_Resampler.h" +#include <sys/time.h> +#include <audio_utils/primitives.h> + +namespace android { +AudioResamplerQTI::AudioResamplerQTI(int format, + int inChannelCount, int32_t sampleRate) + :AudioResampler(inChannelCount, sampleRate, QTI_QUALITY), + mOutFrameCount(0), mTmpBuf(0), mResamplerOutBuf(0), mFrameIndex(0) +{ + stateSize = QCT_Resampler::MemAlloc(format, inChannelCount, sampleRate, sampleRate); + mState = new int16_t[stateSize]; + mVolume[0] = mVolume[1] = 0; + mBuffer.frameCount = 0; +} + +AudioResamplerQTI::~AudioResamplerQTI() +{ + if (mState) { + delete [] mState; + } + if (mTmpBuf) { + delete [] mTmpBuf; + } + if(mResamplerOutBuf) { + delete [] mResamplerOutBuf; + } +} + +size_t AudioResamplerQTI::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + int16_t vl = mVolume[0]; + int16_t vr = mVolume[1]; + int16_t *pBuf; + + size_t inFrameRequest; + size_t inFrameCount = getNumInSample(outFrameCount); + size_t index = 0; + size_t frameIndex = mFrameIndex; + size_t out_count = outFrameCount * 2; + float *fout = reinterpret_cast<float *>(out); + + if (mChannelCount == 1) { + inFrameRequest = inFrameCount; + } else { + inFrameRequest = inFrameCount * 2; + } + + if (mOutFrameCount < outFrameCount) { + mOutFrameCount = outFrameCount; + if (mTmpBuf) { + delete [] mTmpBuf; + } + if(mResamplerOutBuf) { + delete [] mResamplerOutBuf; + } + mTmpBuf = new int16_t[inFrameRequest + 16]; + mResamplerOutBuf = new int32_t[out_count]; + } + + if (mChannelCount == 1) { + // buffer is empty, fetch a new one + while (index < inFrameCount) { + if (!mBuffer.frameCount) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + frameIndex = 0; + } + + if (mBuffer.raw == NULL) { + while (index < inFrameCount) { + mTmpBuf[index++] = 0; + } + QCT_Resampler::Resample90dB(mState, mTmpBuf, mResamplerOutBuf, inFrameCount, outFrameCount); + goto resample_exit; + } + + mTmpBuf[index++] = clamp16_from_float(*((float *)mBuffer.raw + frameIndex++)); + + if (frameIndex >= mBuffer.frameCount) { + provider->releaseBuffer(&mBuffer); + } + } + + QCT_Resampler::Resample90dB(mState, mTmpBuf, mResamplerOutBuf, inFrameCount, outFrameCount); + } else { + pBuf = &mTmpBuf[inFrameCount]; + // buffer is empty, fetch a new one + while (index < inFrameCount) { + if (!mBuffer.frameCount) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + frameIndex = 0; + } + if (mBuffer.raw == NULL) { + while (index < inFrameCount) { + mTmpBuf[index] = 0; + pBuf[index++] = 0; + } + QCT_Resampler::Resample90dB(mState, mTmpBuf, mResamplerOutBuf, inFrameCount, outFrameCount); + goto resample_exit; + } + + mTmpBuf[index] = clamp16_from_float(*((float *)mBuffer.raw + frameIndex++)); + pBuf[index++] = clamp16_from_float(*((float *)mBuffer.raw + frameIndex++)); + if (frameIndex >= mBuffer.frameCount * 2) { + provider->releaseBuffer(&mBuffer); + } + } + + QCT_Resampler::Resample90dB(mState, mTmpBuf, mResamplerOutBuf, inFrameCount, outFrameCount); + } + +resample_exit: + for (int i = 0; i < out_count; i += 2) { + fout[i] += float_from_q4_27(mResamplerOutBuf[i] * vl); + fout[i+1] += float_from_q4_27(mResamplerOutBuf[i+1] * vr); + } + + mFrameIndex = frameIndex; + return index; +} + +void AudioResamplerQTI::setSampleRate(int32_t inSampleRate) +{ + if (mInSampleRate != inSampleRate) { + mInSampleRate = inSampleRate; + init(); + } +} + +void AudioResamplerQTI::init() +{ + QCT_Resampler::Init(mState, mChannelCount, mInSampleRate, mSampleRate); +} + +size_t AudioResamplerQTI::getNumInSample(size_t outFrameCount) +{ + size_t size = (size_t)QCT_Resampler::GetNumInSamp(mState, outFrameCount); + return size; +} + +void AudioResamplerQTI::reset() +{ + AudioResampler::reset(); +} + +}; // namespace android diff --git a/services/audioflinger/AudioResamplerQTI.h b/services/audioflinger/AudioResamplerQTI.h new file mode 100644 index 0000000..0b30a9f --- /dev/null +++ b/services/audioflinger/AudioResamplerQTI.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioResamplerQTI : public AudioResampler { +public: + AudioResamplerQTI(int format, int inChannelCount, int32_t sampleRate); + ~AudioResamplerQTI(); + size_t resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + void setSampleRate(int32_t inSampleRate); + size_t getNumInSample(size_t outFrameCount); + + int16_t *mState; + int16_t *mTmpBuf; + int32_t *mResamplerOutBuf; + size_t mFrameIndex; + size_t stateSize; + size_t mOutFrameCount; + + static const int kNumTmpBufSize = 1024; + + void init(); + void reset(); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp index a8be206..434a514 100644 --- a/services/audioflinger/BufferProviders.cpp +++ b/services/audioflinger/BufferProviders.cpp @@ -24,6 +24,7 @@ #include <media/EffectsFactoryApi.h> #include <utils/Log.h> +#include <media/stagefright/foundation/ADebug.h> #include "Configuration.h" #include "BufferProviders.h" @@ -205,6 +206,7 @@ DownmixerBufferProvider::DownmixerBufferProvider( const int downmixParamSize = sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t); effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize); + CHECK(param != NULL); param->psize = sizeof(downmix_params_t); const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE; memcpy(param->data, &downmixParam, param->psize); diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 949c91d..54ca6c3 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -318,6 +318,7 @@ void AudioFlinger::EffectModule::reset_l() status_t AudioFlinger::EffectModule::configure() { status_t status; + status_t cmdStatus = 0; sp<ThreadBase> thread; uint32_t size; audio_channel_mask_t channelMask; @@ -383,7 +384,6 @@ status_t AudioFlinger::EffectModule::configure() ALOGV("configure() %p thread %p buffer %p framecount %d", this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount); - status_t cmdStatus; size = sizeof(int); status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_CONFIG, @@ -434,7 +434,7 @@ status_t AudioFlinger::EffectModule::init() if (mEffectInterface == NULL) { return NO_INIT; } - status_t cmdStatus; + status_t cmdStatus = 0; uint32_t size = sizeof(status_t); status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_INIT, @@ -476,7 +476,7 @@ status_t AudioFlinger::EffectModule::start_l() if (mStatus != NO_ERROR) { return mStatus; } - status_t cmdStatus; + status_t cmdStatus = 0; uint32_t size = sizeof(status_t); status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_ENABLE, @@ -677,7 +677,7 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, if (isProcessEnabled() && ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL || (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) { - status_t cmdStatus; + status_t cmdStatus = 0; uint32_t volume[2]; uint32_t *pVolume = NULL; uint32_t size = sizeof(volume); @@ -712,7 +712,7 @@ status_t AudioFlinger::EffectModule::setDevice(audio_devices_t device) } status_t status = NO_ERROR; if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) { - status_t cmdStatus; + status_t cmdStatus = 0; uint32_t size = sizeof(status_t); uint32_t cmd = audio_is_output_devices(device) ? EFFECT_CMD_SET_DEVICE : EFFECT_CMD_SET_INPUT_DEVICE; @@ -734,7 +734,7 @@ status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode) } status_t status = NO_ERROR; if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) { - status_t cmdStatus; + status_t cmdStatus = 0; uint32_t size = sizeof(status_t); status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_AUDIO_MODE, @@ -1113,7 +1113,8 @@ status_t AudioFlinger::EffectHandle::enable() mEnabled = false; } else { if (thread != 0) { - if (thread->type() == ThreadBase::OFFLOAD) { + if ((thread->type() == ThreadBase::OFFLOAD) || + (thread->type() == ThreadBase::DIRECT && thread->mIsDirectPcm)) { PlaybackThread *t = (PlaybackThread *)thread.get(); Mutex::Autolock _l(t->mLock); t->broadcast_l(); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index f586291..cdf8b1e 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -544,6 +544,7 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio mSystemReady(systemReady) { memset(&mPatch, 0, sizeof(struct audio_patch)); + mIsDirectPcm = false; } AudioFlinger::ThreadBase::~ThreadBase() @@ -1154,7 +1155,8 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( // Reject any effect on Direct output threads for now, since the format of // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo). - if (mType == DIRECT) { + // Exception: allow effects for Direct PCM + if (mType == DIRECT && !mIsDirectPcm) { ALOGW("createEffect_l() Cannot add effect %s on Direct output type thread %s", desc->name, mThreadName); lStatus = BAD_VALUE; @@ -1171,12 +1173,17 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( } // Allow global effects only on offloaded and mixer threads + // Exception: allow effects for Direct PCM if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { switch (mType) { case MIXER: case OFFLOAD: break; case DIRECT: + if (mIsDirectPcm) { + // Allow effects when direct PCM enabled on Direct output + break; + } case DUPLICATING: case RECORD: default: @@ -1229,7 +1236,13 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( if (lStatus != NO_ERROR) { goto Exit; } - effect->setOffloaded(mType == OFFLOAD, mId); + + bool setVal = false; + if (mType == OFFLOAD || (mType == DIRECT && mIsDirectPcm)) { + setVal = true; + } + + effect->setOffloaded(setVal, mId); lStatus = chain->addEffect_l(effect); if (lStatus != NO_ERROR) { @@ -1313,7 +1326,13 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect) return BAD_VALUE; } - effect->setOffloaded(mType == OFFLOAD, mId); + bool setval = false; + + if ((mType == OFFLOAD) || (mType == DIRECT && mIsDirectPcm)) { + setval = true; + } + + effect->setOffloaded(setval, mId); status_t status = chain->addEffect_l(effect); if (status != NO_ERROR) { @@ -5295,6 +5314,8 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix() } else { if (mMixerBufferValid) { memset(mMixerBuffer, 0, mMixerBufferSize); + } else if (mEffectBufferValid) { + memset(mEffectBuffer, 0, mEffectBufferSize); } else { memset(mSinkBuffer, 0, mSinkBufferSize); } @@ -5316,7 +5337,11 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() } else if (mBytesWritten != 0) { if (mMixerStatus == MIXER_TRACKS_ENABLED) { writeFrames = mNormalFrameCount; - memset(mSinkBuffer, 0, mSinkBufferSize); + if (mMixerBufferValid) { + memset(mMixerBuffer, 0, mMixerBufferSize); + } else { + memset(mSinkBuffer, 0, mSinkBufferSize); + } } else { // flush remaining overflow buffers in output tracks writeFrames = 0; diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 46ac300..9e32ea1 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -457,6 +457,7 @@ protected: static const size_t kLogSize = 4 * 1024; sp<NBLog::Writer> mNBLogWriter; bool mSystemReady; + bool mIsDirectPcm; // flag to indicate unique Direct thread }; // --- PlaybackThread --- diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index b3fac0b..98eb87f 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -24,6 +24,7 @@ #include <math.h> #include <sys/syscall.h> #include <utils/Log.h> +#include <media/stagefright/foundation/ADebug.h> #include <private/media/AudioTrackShared.h> @@ -1774,6 +1775,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frame if (mBufferQueue.size() < kMaxOverFlowBuffers) { pInBuffer = new Buffer; pInBuffer->mBuffer = malloc(inBuffer.frameCount * mFrameSize); + CHECK(pInBuffer->mBuffer != NULL); pInBuffer->frameCount = inBuffer.frameCount; pInBuffer->raw = pInBuffer->mBuffer; memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * mFrameSize); diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk index 8728ff3..8c6a53c 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -31,6 +31,27 @@ LOCAL_C_INCLUDES += \ LOCAL_EXPORT_C_INCLUDE_DIRS := \ $(LOCAL_PATH)/include +ifeq ($(call is-vendor-board-platform,QCOM),true) +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_FLAC_OFFLOAD)),true) +LOCAL_CFLAGS += -DFLAC_OFFLOAD_ENABLED +endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PROXY_DEVICE)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_AFE_PROXY_ENABLED +endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_WMA_OFFLOAD)),true) +LOCAL_CFLAGS += -DWMA_OFFLOAD_ENABLED +endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_ALAC_OFFLOAD)),true) +LOCAL_CFLAGS += -DALAC_OFFLOAD_ENABLED +endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_APE_OFFLOAD)),true) +LOCAL_CFLAGS += -DAPE_OFFLOAD_ENABLED +endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_AAC_ADTS_OFFLOAD)),true) +LOCAL_CFLAGS += -DAAC_ADTS_OFFLOAD_ENABLED +endif +endif + LOCAL_MODULE := libaudiopolicycomponents include $(BUILD_STATIC_LIBRARY) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 50f622d..e1c2999 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -72,6 +72,7 @@ public: sp<AudioPort> mPort; audio_devices_t mDevice; // current device this output is routed to audio_patch_handle_t mPatchHandle; + audio_io_handle_t mIoHandle; // output handle uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output nsecs_t mStopTime[AUDIO_STREAM_CNT]; float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume in dB @@ -116,7 +117,6 @@ public: virtual void toAudioPort(struct audio_port *port) const; const sp<IOProfile> mProfile; // I/O profile this output derives from - audio_io_handle_t mIoHandle; // output handle uint32_t mLatency; // audio_output_flags_t mFlags; // AudioMix *mPolicyMix; // non NULL when used by a dynamic policy diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h index 78d2cdf..4a394bb 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h +++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h @@ -74,6 +74,9 @@ const StringToEnum sDeviceTypeToEnumTable[] = { STRING_TO_ENUM(AUDIO_DEVICE_OUT_FM), STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_LINE), STRING_TO_ENUM(AUDIO_DEVICE_OUT_IP), +#ifdef AUDIO_EXTN_AFE_PROXY_ENABLED + STRING_TO_ENUM(AUDIO_DEVICE_OUT_PROXY), +#endif STRING_TO_ENUM(AUDIO_DEVICE_IN_AMBIENT), STRING_TO_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC), STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), @@ -153,6 +156,7 @@ const StringToEnum sDeviceNameToEnumTable[] = { const StringToEnum sOutputFlagNameToEnumTable[] = { STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT_PCM), STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY), STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST), STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), @@ -198,6 +202,33 @@ const StringToEnum sFormatNameToEnumTable[] = { STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), STRING_TO_ENUM(AUDIO_FORMAT_DTS), STRING_TO_ENUM(AUDIO_FORMAT_DTS_HD), +#ifdef FLAC_OFFLOAD_ENABLED + STRING_TO_ENUM(AUDIO_FORMAT_FLAC), +#endif +#ifdef WMA_OFFLOAD_ENABLED + STRING_TO_ENUM(AUDIO_FORMAT_WMA), + STRING_TO_ENUM(AUDIO_FORMAT_WMA_PRO), +#endif + STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT_OFFLOAD), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_OFFLOAD), +#ifdef ALAC_OFFLOAD_ENABLED + STRING_TO_ENUM(AUDIO_FORMAT_ALAC), +#endif +#ifdef APE_OFFLOAD_ENABLED + STRING_TO_ENUM(AUDIO_FORMAT_APE), +#endif +#ifdef AAC_ADTS_OFFLOAD_ENABLED + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_MAIN), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_LC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_SSR), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_LTP), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_HE_V1), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_SCALABLE), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_ERLC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_LD), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_HE_V2), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADTS_ELD), +#endif }; const StringToEnum sOutChannelsNameToEnumTable[] = { @@ -206,12 +237,17 @@ const StringToEnum sOutChannelsNameToEnumTable[] = { STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD), STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_2POINT1), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_SURROUND), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_PENTA), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_6POINT1), }; const StringToEnum sInChannelsNameToEnumTable[] = { STRING_TO_ENUM(AUDIO_CHANNEL_IN_MONO), STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO), STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_5POINT1), }; const StringToEnum sIndexChannelsNameToEnumTable[] = { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index a278375..5ddeaed 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -33,7 +33,7 @@ namespace android { AudioOutputDescriptor::AudioOutputDescriptor(const sp<AudioPort>& port, AudioPolicyClientInterface *clientInterface) - : mPort(port), mDevice(AUDIO_DEVICE_NONE), + : mPort(port), mDevice(AUDIO_DEVICE_NONE), mIoHandle(0), mPatchHandle(0), mClientInterface(clientInterface), mId(0) { // clear usage count for all stream types @@ -223,7 +223,7 @@ void AudioOutputDescriptor::log(const char* indent) SwAudioOutputDescriptor::SwAudioOutputDescriptor( const sp<IOProfile>& profile, AudioPolicyClientInterface *clientInterface) : AudioOutputDescriptor(profile, clientInterface), - mProfile(profile), mIoHandle(0), mLatency(0), + mProfile(profile), mLatency(0), mFlags((audio_output_flags_t)0), mPolicyMix(NULL), mOutput1(0), mOutput2(0), mDirectOpenCount(0), mGlobalRefCount(0) { diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk index 8d43b89..de84e96 100755 --- a/services/audiopolicy/enginedefault/Android.mk +++ b/services/audiopolicy/enginedefault/Android.mk @@ -31,6 +31,11 @@ LOCAL_C_INCLUDES := \ $(call include-path-for, bionic) \ $(TOPDIR)frameworks/av/services/audiopolicy/common/include +ifeq ($(call is-vendor-board-platform,QCOM),true) +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PROXY_DEVICE)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_AFE_PROXY_ENABLED +endif +endif LOCAL_MODULE := libaudiopolicyenginedefault LOCAL_MODULE_TAGS := optional diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 0686414..8b4a085 100755 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -408,9 +408,10 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; if (device) break; - device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; - if (device) break; } + // Allow voice call on USB ANLG DOCK headset + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_EARPIECE; if (device) break; device = mApmObserver->getDefaultOutputDevice()->type(); @@ -450,6 +451,13 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const } break; } + + if (isInCall() && (device == AUDIO_DEVICE_NONE)) { + // when in call, get the device for Phone strategy + device = getDeviceForStrategy(STRATEGY_PHONE); + break; + } + break; case STRATEGY_SONIFICATION: @@ -498,6 +506,13 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const case STRATEGY_REROUTING: case STRATEGY_MEDIA: { uint32_t device2 = AUDIO_DEVICE_NONE; + + if (isInCall() && (device == AUDIO_DEVICE_NONE)) { + // when in call, get the device for Phone strategy + device = getDeviceForStrategy(STRATEGY_PHONE); + break; + } + if (strategy != STRATEGY_SONIFICATION) { // no sonification on remote submix (e.g. WFD) if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0")) != 0) { @@ -541,14 +556,23 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; } - if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { + if ((strategy != STRATEGY_SONIFICATION) && (device == AUDIO_DEVICE_NONE) + && (device2 == AUDIO_DEVICE_NONE)) { // no sonification on aux digital (e.g. HDMI) device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; } if ((device2 == AUDIO_DEVICE_NONE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { + (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK) + && (strategy != STRATEGY_SONIFICATION)) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; } +#ifdef AUDIO_EXTN_AFE_PROXY_ENABLED + if ((strategy != STRATEGY_SONIFICATION) && (device == AUDIO_DEVICE_NONE) + && (device2 == AUDIO_DEVICE_NONE)) { + // no sonification on WFD sink + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_PROXY; + } +#endif if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; } @@ -671,6 +695,8 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons device = AUDIO_DEVICE_IN_WIRED_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET) { + device = AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { device = AUDIO_DEVICE_IN_BUILTIN_MIC; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 8419ed5..acdd23d 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -351,6 +351,14 @@ void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); if (output != AUDIO_IO_HANDLE_NONE) { + // close active input (if any) before opening new input + audio_io_handle_t activeInput = mInputs.getActiveInput(); + if (activeInput != 0) { + ALOGV("updateCallRouting() close active input before opening new input"); + sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput); + stopInput(activeInput, activeDesc->mSessions.itemAt(0)); + releaseInput(activeInput, activeDesc->mSessions.itemAt(0)); + } sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); ALOG_ASSERT(!outputDesc->isDuplicated(), "updateCallRouting() RX device output is duplicated"); @@ -1336,6 +1344,12 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, ALOGW("getInputForAttr() could not find device for source %d", inputSource); return BAD_VALUE; } + // block request to open input on USB during voice call + if((AUDIO_MODE_IN_CALL == mEngine->getPhoneState()) && + (device == AUDIO_DEVICE_IN_USB_DEVICE)) { + ALOGV("getInputForAttr(): blocking the request to open input on USB device"); + return BAD_VALUE; + } if (policyMix != NULL) { address = policyMix->mRegistrationId; if (policyMix->mMixType == MIX_TYPE_RECORDERS) { @@ -1356,20 +1370,6 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } else { *inputType = API_INPUT_LEGACY; } - // adapt channel selection to input source - switch (inputSource) { - case AUDIO_SOURCE_VOICE_UPLINK: - channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK; - break; - case AUDIO_SOURCE_VOICE_DOWNLINK: - channelMask = AUDIO_CHANNEL_IN_VOICE_DNLINK; - break; - case AUDIO_SOURCE_VOICE_CALL: - channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK; - break; - default: - break; - } if (inputSource == AUDIO_SOURCE_HOTWORD) { ssize_t index = mSoundTriggerSessions.indexOfKey(session); if (index >= 0) { @@ -1773,6 +1773,7 @@ audio_io_handle_t AudioPolicyManager::selectOutputForEffects( audio_io_handle_t outputOffloaded = 0; audio_io_handle_t outputDeepBuffer = 0; + audio_io_handle_t outputDirectPcm = 0; for (size_t i = 0; i < outputs.size(); i++) { sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]); @@ -1780,6 +1781,9 @@ audio_io_handle_t AudioPolicyManager::selectOutputForEffects( if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { outputOffloaded = outputs[i]; } + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT_PCM) != 0) { + outputDirectPcm = outputs[i]; + } if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) { outputDeepBuffer = outputs[i]; } @@ -1790,6 +1794,9 @@ audio_io_handle_t AudioPolicyManager::selectOutputForEffects( if (outputOffloaded != 0) { return outputOffloaded; } + if (outputDirectPcm != 0) { + return outputDirectPcm; + } if (outputDeepBuffer != 0) { return outputDeepBuffer; } @@ -3781,7 +3788,7 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) { audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/); audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/); - SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs); + SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mOutputs); SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs); // also take into account external policy-related changes: add all outputs which are diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index bbdf396..c40a435 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -350,7 +350,7 @@ protected: // handle special cases for sonification strategy while in call: mute streams or replace by // a special tone in the device used for communication - void handleIncallSonification(audio_stream_type_t stream, bool starting, bool stateChange); + virtual void handleIncallSonification(audio_stream_type_t stream, bool starting, bool stateChange); audio_mode_t getPhoneState(); @@ -397,7 +397,7 @@ protected: // must be called every time a condition that affects the device choice for a given output is // changed: connected device, phone state, force use, output start, output stop.. // see getDeviceForStrategy() for the use of fromCache parameter - audio_devices_t getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc, + virtual audio_devices_t getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc, bool fromCache); // updates cache of device used by all strategies (mDeviceForStrategy[]) @@ -484,11 +484,11 @@ protected: // if argument "device" is different from AUDIO_DEVICE_NONE, startSource() will force // the re-evaluation of the output device. - status_t startSource(sp<AudioOutputDescriptor> outputDesc, + virtual status_t startSource(sp<AudioOutputDescriptor> outputDesc, audio_stream_type_t stream, audio_devices_t device, uint32_t *delayMs); - status_t stopSource(sp<AudioOutputDescriptor> outputDesc, + virtual status_t stopSource(sp<AudioOutputDescriptor> outputDesc, audio_stream_type_t stream, bool forceDeviceUpdate); @@ -571,7 +571,7 @@ protected: // Audio Policy Engine Interface. AudioPolicyManagerInterface *mEngine; -private: +protected: // updates device caching and output for streams that can influence the // routing of notifications void handleNotificationRoutingForStream(audio_stream_type_t stream); @@ -586,7 +586,7 @@ private: SortedVector<audio_io_handle_t>& outputs /*out*/); uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; } // internal method to return the output handle for the given device and format - audio_io_handle_t getOutputForDevice( + virtual audio_io_handle_t getOutputForDevice( audio_devices_t device, audio_session_t session, audio_stream_type_t stream, @@ -610,7 +610,7 @@ private: AudioMix **policyMix = NULL); // Called by setDeviceConnectionState(). - status_t setDeviceConnectionStateInt(audio_devices_t device, + virtual status_t setDeviceConnectionStateInt(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, const char *device_name); diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index 282ddeb..e71d7a5 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -442,6 +442,7 @@ effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root) size_t curSize = sizeof(effect_param_t); size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int); effect_param_t *fx_param = (effect_param_t *)malloc(totSize); + CHECK(fx_param != NULL); param = config_find(root, PARAM_TAG); value = config_find(root, VALUE_TAG); diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h index 3dec437..3845050 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.h +++ b/services/audiopolicy/service/AudioPolicyEffects.h @@ -27,6 +27,8 @@ #include <utils/Vector.h> #include <utils/SortedVector.h> +#include <media/stagefright/foundation/ADebug.h> + namespace android { // ---------------------------------------------------------------------------- @@ -102,6 +104,7 @@ private: ((origParam->psize + 3) & ~3) + ((origParam->vsize + 3) & ~3); effect_param_t *dupParam = (effect_param_t *) malloc(origSize); + CHECK(dupParam != NULL); memcpy(dupParam, origParam, origSize); // This works because the param buffer allocation is also done by // multiples of 4 bytes originally. In theory we should memcpy only diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index c77cc45..41dd40c 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -899,10 +899,12 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(sp<AudioCommand>& c } else { data2->mKeyValuePairs = param2.toString(); } - command->mTime = command2->mTime; - // force delayMs to non 0 so that code below does not request to wait for - // command status as the command is now delayed - delayMs = 1; + if (!data2->mKeyValuePairs.compare(data->mKeyValuePairs)) { + command->mTime = command2->mTime; + // force delayMs to non 0 so that code below does not request to wait for + // command status as the command is now delayed + delayMs = 1; + } } break; case SET_VOLUME: { diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 45900c4..ab09cb3 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -79,6 +79,14 @@ LOCAL_C_INCLUDES += \ LOCAL_CFLAGS += -Wall -Wextra +ifeq ($(BOARD_NEEDS_MEMORYHEAPION),true) + LOCAL_CFLAGS += -DUSE_MEMORY_HEAP_ION +endif + +ifneq ($(BOARD_NUMBER_OF_CAMERAS),) + LOCAL_CFLAGS += -DMAX_CAMERAS=$(BOARD_NUMBER_OF_CAMERAS) +endif + LOCAL_MODULE:= libcameraservice include $(BUILD_SHARED_LIBRARY) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 2aaefe9..db6272b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -2082,7 +2082,11 @@ sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras) { - mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); + if (mRemoteCallback != NULL) { + mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); + } else { + ALOGE("mRemoteCallback is NULL!!"); + } } // NOTE: function is idempotent diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index cd97b08..b3903d4 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -49,6 +49,10 @@ #include <memory> #include <utility> +#ifndef MAX_CAMERAS +#define MAX_CAMERAS 2 +#endif + namespace android { extern volatile int32_t gLogLevel; diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp index 38e35cd..1bb2910 100644 --- a/services/camera/libcameraservice/api1/CameraClient.cpp +++ b/services/camera/libcameraservice/api1/CameraClient.cpp @@ -56,6 +56,9 @@ CameraClient::CameraClient(const sp<CameraService>& cameraService, mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT); mLegacyMode = legacyMode; mPlayShutterSound = true; + + mLongshotEnabled = false; + mBurstCnt = 0; LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId); } @@ -360,12 +363,14 @@ status_t CameraClient::setPreviewCallbackTarget( // start preview mode status_t CameraClient::startPreview() { + Mutex::Autolock lock(mLock); LOG1("startPreview (pid %d)", getCallingPid()); return startCameraMode(CAMERA_PREVIEW_MODE); } // start recording mode status_t CameraClient::startRecording() { + Mutex::Autolock lock(mLock); LOG1("startRecording (pid %d)", getCallingPid()); return startCameraMode(CAMERA_RECORDING_MODE); } @@ -373,7 +378,6 @@ status_t CameraClient::startRecording() { // start preview or recording status_t CameraClient::startCameraMode(camera_mode mode) { LOG1("startCameraMode(%d)", mode); - Mutex::Autolock lock(mLock); status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; @@ -553,6 +557,10 @@ status_t CameraClient::takePicture(int msgType) { CAMERA_MSG_COMPRESSED_IMAGE); enableMsgType(picMsgType); + mBurstCnt = mHardware->getParameters().getInt("num-snaps-per-shutter"); + if(mBurstCnt <= 0) + mBurstCnt = 1; + LOG1("mBurstCnt = %d", mBurstCnt); return mHardware->takePicture(); } @@ -655,6 +663,20 @@ status_t CameraClient::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { } else if (cmd == CAMERA_CMD_PING) { // If mHardware is 0, checkPidAndHardware will return error. return OK; + } else if (cmd == CAMERA_CMD_HISTOGRAM_ON) { + enableMsgType(CAMERA_MSG_STATS_DATA); + } else if (cmd == CAMERA_CMD_HISTOGRAM_OFF) { + disableMsgType(CAMERA_MSG_STATS_DATA); + } else if (cmd == CAMERA_CMD_METADATA_ON) { + enableMsgType(CAMERA_MSG_META_DATA); + } else if (cmd == CAMERA_CMD_METADATA_OFF) { + disableMsgType(CAMERA_MSG_META_DATA); + } else if ( cmd == CAMERA_CMD_LONGSHOT_ON ) { + mLongshotEnabled = true; + } else if ( cmd == CAMERA_CMD_LONGSHOT_OFF ) { + mLongshotEnabled = false; + disableMsgType(CAMERA_MSG_SHUTTER); + disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); } return mHardware->sendCommand(cmd, arg1, arg2); @@ -797,7 +819,9 @@ void CameraClient::handleShutter(void) { c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return; } - disableMsgType(CAMERA_MSG_SHUTTER); + if ( !mLongshotEnabled ) { + disableMsgType(CAMERA_MSG_SHUTTER); + } // Shutters only happen in response to takePicture, so mark device as // idle now, until preview is restarted @@ -882,7 +906,13 @@ void CameraClient::handleRawPicture(const sp<IMemory>& mem) { // picture callback - compressed picture ready void CameraClient::handleCompressedPicture(const sp<IMemory>& mem) { - disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); + if (mBurstCnt) + mBurstCnt--; + + if (!mBurstCnt && !mLongshotEnabled) { + LOG1("handleCompressedPicture mBurstCnt = %d", mBurstCnt); + disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); + } sp<ICameraClient> c = mRemoteCallback; mLock.unlock(); diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h index 95616b2..9d2d02f 100644 --- a/services/camera/libcameraservice/api1/CameraClient.h +++ b/services/camera/libcameraservice/api1/CameraClient.h @@ -162,6 +162,9 @@ private: // This function keeps trying to grab mLock, or give up if the message // is found to be disabled. It returns true if mLock is grabbed. bool lockIfMessageWanted(int32_t msgType); + + bool mLongshotEnabled; + int mBurstCnt; }; } diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h index 7f14cd4..35947a9 100644 --- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h +++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h @@ -25,7 +25,10 @@ #include <camera/Camera.h> #include <camera/CameraParameters.h> #include <system/window.h> -#include <hardware/camera.h> +#include "hardware/camera.h" +#ifdef USE_MEMORY_HEAP_ION +#include <binder/MemoryHeapIon.h> +#endif namespace android { @@ -322,6 +325,10 @@ public: void releaseRecordingFrame(const sp<IMemory>& mem) { ALOGV("%s(%s)", __FUNCTION__, mName.string()); + if (mem == NULL) { + ALOGE("%s: NULL memory reference", __FUNCTION__); + return; + } if (mDevice->ops->release_recording_frame) { ssize_t offset; size_t size; @@ -501,7 +508,11 @@ private: mBufSize(buf_size), mNumBufs(num_buffers) { +#ifdef USE_MEMORY_HEAP_ION + mHeap = new MemoryHeapIon(fd, buf_size * num_buffers); +#else mHeap = new MemoryHeapBase(fd, buf_size * num_buffers); +#endif commonInitialization(); } @@ -509,7 +520,11 @@ private: mBufSize(buf_size), mNumBufs(num_buffers) { +#ifdef USE_MEMORY_HEAP_ION + mHeap = new MemoryHeapIon(buf_size * num_buffers); +#else mHeap = new MemoryHeapBase(buf_size * num_buffers); +#endif commonInitialization(); } @@ -541,14 +556,24 @@ private: camera_memory_t handle; }; +#ifdef USE_MEMORY_HEAP_ION + static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs, + void *ion_fd) + { +#else static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs, void *user __attribute__((unused))) { +#endif CameraHeapMemory *mem; if (fd < 0) mem = new CameraHeapMemory(buf_size, num_bufs); else mem = new CameraHeapMemory(fd, buf_size, num_bufs); +#ifdef USE_MEMORY_HEAP_ION + if (ion_fd) + *((int *) ion_fd) = mem->mHeap->getHeapID(); +#endif mem->incStrong(mem); return &mem->handle; } |