diff options
Diffstat (limited to 'media/libmediaplayerservice/StagefrightRecorder.cpp')
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.cpp | 298 |
1 files changed, 257 insertions, 41 deletions
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index e521fae..442dba1 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" @@ -43,6 +44,7 @@ #include <media/stagefright/MediaCodecSource.h> #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> +#include <media/stagefright/WAVEWriter.h> #include <media/MediaProfiles.h> #include <camera/ICamera.h> #include <camera/CameraParameters.h> @@ -55,9 +57,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 = @@ -75,7 +80,8 @@ StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName) mOutputFd(-1), mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), - mStarted(false) { + mStarted(false), + mRecPaused(false) { ALOGV("Constructor"); reset(); @@ -179,7 +185,8 @@ status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) { status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) { ALOGV("setVideoEncoder: %d", ve); if (ve < VIDEO_ENCODER_DEFAULT || - ve >= VIDEO_ENCODER_LIST_END) { + (ve >= VIDEO_ENCODER_LIST_END && ve <= VIDEO_ENCODER_LIST_VENDOR_START) || + ve >= VIDEO_ENCODER_LIST_VENDOR_END) { ALOGE("Invalid video encoder: %d", ve); return BAD_VALUE; } @@ -249,7 +256,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 +371,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 +423,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 +511,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 +592,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; } @@ -813,9 +824,15 @@ status_t StagefrightRecorder::prepareInternal() { status = setupMPEG2TSRecording(); break; + case OUTPUT_FORMAT_WAVE: + status = setupWAVERecording(); + 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; } @@ -836,6 +853,22 @@ status_t StagefrightRecorder::start() { return INVALID_OPERATION; } + if (mRecPaused == true) { + status_t err = mWriter->start(); + if (err != OK) { + ALOGE("Writer start in StagefrightRecorder pause failed"); + return err; + } + + err = setSourcePause(false); + if (err != OK) { + ALOGE("Source start after pause failed"); + return err; + } + + mRecPaused = false; + return OK; + } status_t status = OK; if (mVideoSource != VIDEO_SOURCE_SURFACE) { @@ -872,6 +905,7 @@ status_t StagefrightRecorder::start() { case OUTPUT_FORMAT_AAC_ADTS: case OUTPUT_FORMAT_RTP_AVP: case OUTPUT_FORMAT_MPEG2TS: + case OUTPUT_FORMAT_WAVE: { status = mWriter->start(); break; @@ -879,8 +913,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 +963,36 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { } } - sp<AudioSource> audioSource = - new AudioSource( - mAudioSource, - mOpPackageName, - sourceSampleRate, - mAudioChannels, - mSampleRate); + // If using QCOM extension (Camera 1 HAL) for slow motion recording + // mCaptureFpsEnable and mCaptureFps will not be set via setCaptureRate + // We need to query from AVUtil, in order to support slow motion audio recording + if (mVideoSourceNode != NULL) { + int hfrRatio = AVUtils::get()->HFRUtils().getHFRRatio(mVideoSourceNode->getFormat()); + if (hfrRatio != 1) { + // Upscale the sample rate for slow motion recording. + // Fail audio source creation if source sample rate is too high, as it could + // cause out-of-memory due to large input buffer size. And audio recording + // probably doesn't make sense in the scenario, since the slow-down factor + // is probably huge (eg. mSampleRate=48K, hfrRatio=240, mFrameRate=1). + const static int32_t SAMPLE_RATE_HZ_MAX = 192000; + sourceSampleRate = + (mSampleRate * hfrRatio + mFrameRate / 2) / mFrameRate; + if (sourceSampleRate < mSampleRate || sourceSampleRate > SAMPLE_RATE_HZ_MAX) { + ALOGE("source sample rate out of range! " + "(mSampleRate %d, hfrRatio %d, mFrameRate %d", + mSampleRate, hfrRatio, mFrameRate); + return NULL; + } + } + } + + + sp<AudioSource> audioSource = AVFactory::get()->createAudioSource( + mAudioSource, + mOpPackageName, + sourceSampleRate, + mAudioChannels, + mSampleRate); status_t err = audioSource->initCheck(); @@ -963,10 +1022,15 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); format->setInt32("aac-profile", OMX_AUDIO_AACObjectELD); break; + case AUDIO_ENCODER_LPCM: + format->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW); + 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,12 +1048,20 @@ 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) { ALOGE("Failed to create audio encoder"); } + mAudioEncoderOMX = audioEncoder; return audioEncoder; } @@ -1155,6 +1227,15 @@ status_t StagefrightRecorder::setupMPEG2TSRecording() { return OK; } +status_t StagefrightRecorder::setupWAVERecording() { + CHECK(mOutputFormat == OUTPUT_FORMAT_WAVE); + CHECK(mAudioEncoder == AUDIO_ENCODER_LPCM); + CHECK(mAudioSource != AUDIO_SOURCE_CNT); + + mWriter = new WAVEWriter(mOutputFd); + return setupRawAudioRecording(); +} + void StagefrightRecorder::clipVideoFrameRate() { ALOGV("clipVideoFrameRate: encoder %d", mVideoEncoder); if (mFrameRate == -1) { @@ -1222,7 +1303,8 @@ status_t StagefrightRecorder::checkVideoEncoderCapabilities() { (mVideoEncoder == VIDEO_ENCODER_H263 ? MEDIA_MIMETYPE_VIDEO_H263 : mVideoEncoder == VIDEO_ENCODER_MPEG_4_SP ? MEDIA_MIMETYPE_VIDEO_MPEG4 : mVideoEncoder == VIDEO_ENCODER_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : - mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : ""), + mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : + mVideoEncoder == VIDEO_ENCODER_H265 ? MEDIA_MIMETYPE_VIDEO_HEVC : ""), false /* decoder */, true /* hwCodec */, &codecs); if (!mCaptureFpsEnable) { @@ -1309,8 +1391,10 @@ void StagefrightRecorder::setDefaultVideoEncoderIfNecessary() { int videoCodec = mEncoderProfiles->getCamcorderProfileParamByName( "vid.codec", mCameraId, CAMCORDER_QUALITY_LOW); - if (videoCodec > VIDEO_ENCODER_DEFAULT && - videoCodec < VIDEO_ENCODER_LIST_END) { + if ((videoCodec > VIDEO_ENCODER_DEFAULT && + videoCodec < VIDEO_ENCODER_LIST_END) || + (videoCodec > VIDEO_ENCODER_LIST_VENDOR_START && + videoCodec < VIDEO_ENCODER_LIST_VENDOR_END)) { mVideoEncoder = (video_encoder)videoCodec; } else { // default to H.264 if camcorder profile not available @@ -1440,22 +1524,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 +1572,15 @@ status_t StagefrightRecorder::setupCameraSource( return OK; } +bool StagefrightRecorder::setCustomVideoEncoderMime(const video_encoder videoEncoder, + sp<AMessage> format) { + if (videoEncoder == VIDEO_ENCODER_H265) { + format->setString("mime", MEDIA_MIMETYPE_VIDEO_HEVC); + return true; + } + return false; +} + status_t StagefrightRecorder::setupVideoEncoder( sp<MediaSource> cameraSource, sp<MediaSource> *source) { @@ -1512,6 +1606,9 @@ status_t StagefrightRecorder::setupVideoEncoder( break; default: + if (setCustomVideoEncoderMime(mVideoEncoder, format)) { + break; + } CHECK(!"Should not be here, unsupported video encoding."); break; } @@ -1535,13 +1632,13 @@ status_t StagefrightRecorder::setupVideoEncoder( format->setInt32("width", mVideoWidth); format->setInt32("height", mVideoHeight); format->setInt32("stride", mVideoWidth); - format->setInt32("slice-height", mVideoWidth); + format->setInt32("slice-height", mVideoHeight); format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque); // 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; } @@ -1553,6 +1650,10 @@ status_t StagefrightRecorder::setupVideoEncoder( format->setInt32("frame-rate", mFrameRate); format->setInt32("i-frame-interval", mIFramesIntervalSec); + if (cameraSource != NULL) { + setupCustomVideoEncoderParams(cameraSource, format); + } + if (mVideoTimeScale > 0) { format->setInt32("time-scale", mVideoTimeScale); } @@ -1594,6 +1695,8 @@ status_t StagefrightRecorder::setupVideoEncoder( mGraphicBufferProducer = encoder->getGraphicBufferProducer(); } + mVideoSourceNode = cameraSource; + mVideoEncoderOMX = encoder; *source = encoder; return OK; @@ -1611,11 +1714,14 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { case AUDIO_ENCODER_AAC: case AUDIO_ENCODER_HE_AAC: case AUDIO_ENCODER_AAC_ELD: + case AUDIO_ENCODER_LPCM: 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(); @@ -1637,7 +1743,7 @@ status_t StagefrightRecorder::setupMPEG4orWEBMRecording() { if (mOutputFormat == OUTPUT_FORMAT_WEBM) { writer = new WebmWriter(mOutputFd); } else { - writer = mp4writer = new MPEG4Writer(mOutputFd); + writer = mp4writer = AVFactory::get()->CreateMPEG4Writer(mOutputFd); } if (mVideoSource < VIDEO_SOURCE_LIST_END) { @@ -1708,6 +1814,8 @@ status_t StagefrightRecorder::setupMPEG4orWEBMRecording() { void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) { int64_t startTimeUs = systemTime() / 1000; (*meta)->setInt64(kKeyTime, startTimeUs); + int64_t startTimeBootUs = systemTime(SYSTEM_TIME_BOOTTIME) / 1000; + (*meta)->setInt64(kKeyTimeBoot, startTimeBootUs); (*meta)->setInt32(kKeyFileType, mOutputFormat); (*meta)->setInt32(kKeyBitRate, mTotalBitRate); if (mMovieTimeScale > 0) { @@ -1726,10 +1834,23 @@ void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) { status_t StagefrightRecorder::pause() { ALOGV("pause"); + status_t err = OK; if (mWriter == NULL) { return UNKNOWN_ERROR; } - mWriter->pause(); + err = setSourcePause(true); + if (err != OK) { + ALOGE("StagefrightRecorder pause failed"); + return err; + } + + err = mWriter->pause(); + if (err != OK) { + ALOGE("Writer pause failed"); + return err; + } + + mRecPaused = true; if (mStarted) { mStarted = false; @@ -1758,6 +1879,16 @@ status_t StagefrightRecorder::stop() { mCameraSourceTimeLapse = NULL; } + if (mRecPaused) { + status_t err = setSourcePause(false); + if (err != OK) { + ALOGE("Source start after pause in StagefrightRecorder stop failed"); + return err; + } + + mRecPaused = false; + } + if (mWriter != NULL) { err = mWriter->stop(); mWriter.clear(); @@ -1927,4 +2058,89 @@ status_t StagefrightRecorder::dump( ::write(fd, result.string(), result.size()); return OK; } + +status_t StagefrightRecorder::setSourcePause(bool pause) { + status_t err = OK; + if (pause) { + if (mVideoEncoderOMX != NULL) { + err = mVideoEncoderOMX->pause(); + if (err != OK) { + ALOGE("OMX VideoEncoder pause failed"); + return err; + } + } + if (mAudioEncoderOMX != NULL) { + if (mAudioEncoderOMX != mAudioSourceNode) { + err = mAudioEncoderOMX->pause(); + if (err != OK) { + ALOGE("OMX AudioEncoder pause failed"); + return err; + } + } else { + // If AudioSource is the same as MediaSource(as in LPCM), + // bypass omx encoder pause() call. + ALOGV("OMX AudioEncoder->pause() bypassed"); + } + } + if (mVideoSourceNode != NULL) { + err = mVideoSourceNode->pause(); + if (err != OK) { + ALOGE("OMX VideoSourceNode pause failed"); + return err; + } + } + if (mAudioSourceNode != NULL) { + err = mAudioSourceNode->pause(); + if (err != OK) { + ALOGE("OMX AudioSourceNode pause failed"); + return err; + } + } + } else { + if (mVideoSourceNode != NULL) { + err = mVideoSourceNode->start(); + if (err != OK) { + ALOGE("OMX VideoSourceNode start failed"); + return err; + } + } + if (mAudioSourceNode != NULL) { + err = mAudioSourceNode->start(); + if (err != OK) { + ALOGE("OMX AudioSourceNode start failed"); + return err; + } + } + if (mVideoEncoderOMX != NULL) { + err = mVideoEncoderOMX->start(); + if (err != OK) { + ALOGE("OMX VideoEncoder start failed"); + return err; + } + } + if (mAudioEncoderOMX != NULL) { + if (mAudioEncoderOMX != mAudioSourceNode) { + err = mAudioEncoderOMX->start(); + if (err != OK) { + ALOGE("OMX AudioEncoder start failed"); + return err; + } + } else { + // If AudioSource is the same as MediaSource(as in LPCM), + // bypass omx encoder start() call. + ALOGV("OMX AudioEncoder->start() bypassed"); + } + } + } + return err; +} + +void StagefrightRecorder::setupCustomVideoEncoderParams(sp<MediaSource> cameraSource, + sp<AMessage> &format) { + + // Setup HFR if needed + AVUtils::get()->HFRUtils().initializeHFR(cameraSource->getFormat(), format, + mMaxFileDurationUs, mVideoEncoder); +} + } // namespace android |