diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/libmedia/IOMX.cpp | 2 | ||||
-rw-r--r-- | media/libmediaplayerservice/Android.mk | 1 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 4 | ||||
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.cpp | 445 | ||||
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.h | 31 | ||||
-rw-r--r-- | media/libstagefright/AMRWriter.cpp | 26 | ||||
-rw-r--r-- | media/libstagefright/AudioSource.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/CameraSource.cpp | 145 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Writer.cpp | 700 | ||||
-rw-r--r-- | media/libstagefright/OMXCodec.cpp | 129 | ||||
-rw-r--r-- | media/libstagefright/omx/OMXNodeInstance.cpp | 6 |
11 files changed, 1182 insertions, 311 deletions
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 01b6737..f3804b8 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -57,7 +57,7 @@ sp<IOMXRenderer> IOMX::createRendererFromJavaSurface( return NULL; } - jfieldID surfaceID = env->GetFieldID(surfaceClass, "mSurface", "I"); + jfieldID surfaceID = env->GetFieldID(surfaceClass, ANDROID_VIEW_SURFACE_JNI_ID, "I"); if (surfaceID == NULL) { LOGE("Can't find Surface.mSurface"); return NULL; diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index cf97b23..3b678cb 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -37,6 +37,7 @@ LOCAL_SHARED_LIBRARIES := \ libvorbisidec \ libsonivox \ libmedia \ + libcamera_client \ libandroid_runtime \ libstagefright \ libstagefright_omx \ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 6a6afa1..11f3016 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -228,14 +228,10 @@ MediaPlayerService::~MediaPlayerService() sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid) { -#ifndef NO_OPENCORE sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid); wp<MediaRecorderClient> w = recorder; Mutex::Autolock lock(mLock); mMediaRecorderClients.add(w); -#else - sp<MediaRecorderClient> recorder = NULL; -#endif LOGV("Create new media recorder client from pid %d", pid); return recorder; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 531fd11..e41a716 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -20,6 +20,7 @@ #include "StagefrightRecorder.h" +#include <binder/IPCThreadState.h> #include <media/stagefright/AudioSource.h> #include <media/stagefright/AMRWriter.h> #include <media/stagefright/CameraSource.h> @@ -30,8 +31,13 @@ #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> #include <camera/ICamera.h> +#include <camera/Camera.h> +#include <camera/CameraParameters.h> #include <surfaceflinger/ISurface.h> #include <utils/Errors.h> +#include <sys/types.h> +#include <unistd.h> +#include <ctype.h> namespace android { @@ -83,6 +89,12 @@ status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) { } status_t StagefrightRecorder::setVideoSize(int width, int height) { + if (width <= 0 || height <= 0) { + LOGE("Invalid video size: %dx%d", width, height); + return BAD_VALUE; + } + + // Additional check on the dimension will be performed later mVideoWidth = width; mVideoHeight = height; @@ -90,13 +102,37 @@ status_t StagefrightRecorder::setVideoSize(int width, int height) { } status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) { + if (frames_per_second <= 0 || frames_per_second > 30) { + LOGE("Invalid video frame rate: %d", frames_per_second); + return BAD_VALUE; + } + + // Additional check on the frame rate will be performed later mFrameRate = frames_per_second; return OK; } status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) { - mCamera = camera; + LOGV("setCamera: pid %d pid %d", IPCThreadState::self()->getCallingPid(), getpid()); + if (camera == 0) { + LOGE("camera is NULL"); + return UNKNOWN_ERROR; + } + + mFlags &= ~ FLAGS_SET_CAMERA | FLAGS_HOT_CAMERA; + mCamera = Camera::create(camera); + if (mCamera == 0) { + LOGE("Unable to connect to camera"); + return UNKNOWN_ERROR; + } + + LOGV("Connected to camera"); + mFlags |= FLAGS_SET_CAMERA; + if (mCamera->previewEnabled()) { + LOGV("camera is hot"); + mFlags |= FLAGS_HOT_CAMERA; + } return OK; } @@ -127,9 +163,216 @@ status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t leng return OK; } -status_t StagefrightRecorder::setParameters(const String8 ¶ms) { - mParams = params; +// Attempt to parse an int64 literal optionally surrounded by whitespace, +// returns true on success, false otherwise. +static bool safe_strtoi64(const char *s, int64_t *val) { + char *end; + *val = strtoll(s, &end, 10); + + if (end == s || errno == ERANGE) { + return false; + } + + // Skip trailing whitespace + while (isspace(*end)) { + ++end; + } + + // For a successful return, the string must contain nothing but a valid + // int64 literal optionally surrounded by whitespace. + return *end == '\0'; +} + +// Return true if the value is in [0, 0x007FFFFFFF] +static bool safe_strtoi32(const char *s, int32_t *val) { + int64_t temp; + if (safe_strtoi64(s, &temp)) { + if (temp >= 0 && temp <= 0x007FFFFFFF) { + *val = static_cast<int32_t>(temp); + return true; + } + } + return false; +} + +// Trim both leading and trailing whitespace from the given string. +static void TrimString(String8 *s) { + size_t num_bytes = s->bytes(); + const char *data = s->string(); + + size_t leading_space = 0; + while (leading_space < num_bytes && isspace(data[leading_space])) { + ++leading_space; + } + + size_t i = num_bytes; + while (i > leading_space && isspace(data[i - 1])) { + --i; + } + + s->setTo(String8(&data[leading_space], i - leading_space)); +} + +status_t StagefrightRecorder::setParamAudioSamplingRate(int32_t sampleRate) { + LOGV("setParamAudioSamplingRate: %d", sampleRate); + if (sampleRate <= 0) { + LOGE("Invalid audio sampling rate: %d", sampleRate); + return BAD_VALUE; + } + + // Additional check on the sample rate will be performed later. + mSampleRate = sampleRate; + return OK; +} + +status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) { + LOGV("setParamAudioNumberOfChannels: %d", channels); + if (channels <= 0 || channels >= 3) { + LOGE("Invalid number of audio channels: %d", channels); + } + + // Additional check on the number of channels will be performed later. + mAudioChannels = channels; + return OK; +} + +status_t StagefrightRecorder::setParamAudioEncodingBitRate(int32_t bitRate) { + LOGV("setParamAudioEncodingBitRate: %d", bitRate); + if (bitRate <= 0) { + LOGE("Invalid audio encoding bit rate: %d", bitRate); + return BAD_VALUE; + } + + // The target bit rate may not be exactly the same as the requested. + // It depends on many factors, such as rate control, and the bit rate + // range that a specific encoder supports. The mismatch between the + // the target and requested bit rate will NOT be treated as an error. + mAudioBitRate = bitRate; + return OK; +} + +status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { + LOGV("setParamVideoEncodingBitRate: %d", bitRate); + if (bitRate <= 0) { + LOGE("Invalid video encoding bit rate: %d", bitRate); + return BAD_VALUE; + } + + // The target bit rate may not be exactly the same as the requested. + // It depends on many factors, such as rate control, and the bit rate + // range that a specific encoder supports. The mismatch between the + // the target and requested bit rate will NOT be treated as an error. + mVideoBitRate = bitRate; + return OK; +} + +status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int64_t limit, + bool limit_is_duration) { + LOGV("setParamMaxDurationOrFileSize: limit (%d) for %s", + limit, limit_is_duration?"duration":"size"); + if (limit_is_duration) { // limit is in ms + if (limit <= 1000) { // XXX: 1 second + LOGE("Max file duration is too short: %lld us", limit); + } + mMaxFileDurationUs = limit * 1000LL; + } else { + if (limit <= 1024) { // XXX: 1 kB + LOGE("Max file size is too small: %lld bytes", limit); + } + mMaxFileSizeBytes = limit; + } + return OK; +} + +status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { + LOGV("setParamInterleaveDuration: %d", durationUs); + if (durationUs <= 20000) { // XXX: 20 ms + LOGE("Audio/video interleave duration is too small: %d us", durationUs); + return BAD_VALUE; + } + mInterleaveDurationUs = durationUs; + return OK; +} + +status_t StagefrightRecorder::setParameter( + const String8 &key, const String8 &value) { + LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); + if (key == "max-duration") { + int64_t max_duration_ms; + if (safe_strtoi64(value.string(), &max_duration_ms)) { + return setParamMaxDurationOrFileSize( + max_duration_ms, true /* limit_is_duration */); + } + } else if (key == "max-filesize") { + int64_t max_filesize_bytes; + if (safe_strtoi64(value.string(), &max_filesize_bytes)) { + return setParamMaxDurationOrFileSize( + max_filesize_bytes, false /* limit is filesize */); + } + } else if (key == "audio-param-sampling-rate") { + int32_t sampling_rate; + if (safe_strtoi32(value.string(), &sampling_rate)) { + return setParamAudioSamplingRate(sampling_rate); + } + } else if (key == "audio-param-number-of-channels") { + int32_t number_of_channels; + if (safe_strtoi32(value.string(), &number_of_channels)) { + return setParamAudioNumberOfChannels(number_of_channels); + } + } else if (key == "audio-param-encoding-bitrate") { + int32_t audio_bitrate; + if (safe_strtoi32(value.string(), &audio_bitrate)) { + return setParamAudioEncodingBitRate(audio_bitrate); + } + } else if (key == "video-param-encoding-bitrate") { + int32_t video_bitrate; + if (safe_strtoi32(value.string(), &video_bitrate)) { + return setParamVideoEncodingBitRate(video_bitrate); + } + } else if (key == "param-interleave-duration-us") { + int32_t durationUs; + if (safe_strtoi32(value.string(), &durationUs)) { + return setParamInterleaveDuration(durationUs); + } + } else { + LOGE("setParameter: failed to find key %s", key.string()); + } + return BAD_VALUE; +} + +status_t StagefrightRecorder::setParameters(const String8 ¶ms) { + LOGV("setParameters: %s", params.string()); + const char *cparams = params.string(); + const char *key_start = cparams; + for (;;) { + const char *equal_pos = strchr(key_start, '='); + if (equal_pos == NULL) { + LOGE("Parameters %s miss a value", cparams); + return BAD_VALUE; + } + String8 key(key_start, equal_pos - key_start); + TrimString(&key); + if (key.length() == 0) { + LOGE("Parameters %s contains an empty key", cparams); + return BAD_VALUE; + } + const char *value_start = equal_pos + 1; + const char *semicolon_pos = strchr(value_start, ';'); + String8 value; + if (semicolon_pos == NULL) { + value.setTo(value_start); + } else { + value.setTo(value_start, semicolon_pos - value_start); + } + if (setParameter(key, value) != OK) { + return BAD_VALUE; + } + if (semicolon_pos == NULL) { + break; // Reaches the end + } + key_start = semicolon_pos + 1; + } return OK; } @@ -158,40 +401,56 @@ status_t StagefrightRecorder::start() { case OUTPUT_FORMAT_AMR_WB: return startAMRRecording(); + case OUTPUT_FORMAT_AAC_ADIF: + case OUTPUT_FORMAT_AAC_ADTS: + return startAACRecording(); + default: return UNKNOWN_ERROR; } } -sp<MediaSource> StagefrightRecorder::createAMRAudioSource() { - uint32_t sampleRate = - mAudioEncoder == AUDIO_ENCODER_AMR_NB ? 8000 : 16000; - +sp<MediaSource> StagefrightRecorder::createAudioSource() { sp<AudioSource> audioSource = new AudioSource( mAudioSource, - sampleRate, + mSampleRate, AudioSystem::CHANNEL_IN_MONO); status_t err = audioSource->initCheck(); if (err != OK) { + LOGE("audio source is not initialized"); return NULL; } sp<MetaData> encMeta = new MetaData; - encMeta->setCString( - kKeyMIMEType, - mAudioEncoder == AUDIO_ENCODER_AMR_NB - ? MEDIA_MIMETYPE_AUDIO_AMR_NB : MEDIA_MIMETYPE_AUDIO_AMR_WB); + const char *mime; + switch (mAudioEncoder) { + case AUDIO_ENCODER_AMR_NB: + case AUDIO_ENCODER_DEFAULT: + mime = MEDIA_MIMETYPE_AUDIO_AMR_NB; + break; + case AUDIO_ENCODER_AMR_WB: + mime = MEDIA_MIMETYPE_AUDIO_AMR_WB; + break; + case AUDIO_ENCODER_AAC: + mime = MEDIA_MIMETYPE_AUDIO_AAC; + break; + default: + LOGE("Unknown audio encoder: %d", mAudioEncoder); + return NULL; + } + encMeta->setCString(kKeyMIMEType, mime); int32_t maxInputSize; CHECK(audioSource->getFormat()->findInt32( kKeyMaxInputSize, &maxInputSize)); encMeta->setInt32(kKeyMaxInputSize, maxInputSize); - encMeta->setInt32(kKeyChannelCount, 1); - encMeta->setInt32(kKeySampleRate, sampleRate); + encMeta->setInt32(kKeyChannelCount, mAudioChannels); + encMeta->setInt32(kKeySampleRate, mSampleRate); + encMeta->setInt32(kKeyBitRate, mAudioBitRate); OMXClient client; CHECK_EQ(client.connect(), OK); @@ -203,22 +462,59 @@ sp<MediaSource> StagefrightRecorder::createAMRAudioSource() { return audioEncoder; } +status_t StagefrightRecorder::startAACRecording() { + CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADIF || + mOutputFormat == OUTPUT_FORMAT_AAC_ADTS); + + CHECK(mAudioEncoder == AUDIO_ENCODER_AAC); + CHECK(mAudioSource != AUDIO_SOURCE_LIST_END); + CHECK(mOutputFd >= 0); + + CHECK(0 == "AACWriter is not implemented yet"); + + return OK; +} + status_t StagefrightRecorder::startAMRRecording() { - if (mAudioSource == AUDIO_SOURCE_LIST_END - || mVideoSource != VIDEO_SOURCE_LIST_END) { + CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB || + mOutputFormat == OUTPUT_FORMAT_AMR_WB); + + if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) { + if (mAudioEncoder != AUDIO_ENCODER_DEFAULT && + mAudioEncoder != AUDIO_ENCODER_AMR_NB) { + LOGE("Invalid encoder %d used for AMRNB recording", + mAudioEncoder); + return UNKNOWN_ERROR; + } + if (mSampleRate != 8000) { + LOGE("Invalid sampling rate %d used for AMRNB recording", + mSampleRate); + return UNKNOWN_ERROR; + } + } else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB + if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) { + LOGE("Invlaid encoder %d used for AMRWB recording", + mAudioEncoder); + return UNKNOWN_ERROR; + } + if (mSampleRate != 16000) { + LOGE("Invalid sample rate %d used for AMRWB recording", + mSampleRate); + return UNKNOWN_ERROR; + } + } + if (mAudioChannels != 1) { + LOGE("Invalid number of audio channels %d used for amr recording", + mAudioChannels); return UNKNOWN_ERROR; } - if (mOutputFormat == OUTPUT_FORMAT_AMR_NB - && mAudioEncoder != AUDIO_ENCODER_DEFAULT - && mAudioEncoder != AUDIO_ENCODER_AMR_NB) { - return UNKNOWN_ERROR; - } else if (mOutputFormat == OUTPUT_FORMAT_AMR_WB - && mAudioEncoder != AUDIO_ENCODER_AMR_WB) { + if (mAudioSource >= AUDIO_SOURCE_LIST_END) { + LOGE("Invalid audio source: %d", mAudioSource); return UNKNOWN_ERROR; } - sp<MediaSource> audioEncoder = createAMRAudioSource(); + sp<MediaSource> audioEncoder = createAudioSource(); if (audioEncoder == NULL) { return UNKNOWN_ERROR; @@ -235,18 +531,71 @@ status_t StagefrightRecorder::startAMRRecording() { status_t StagefrightRecorder::startMPEG4Recording() { mWriter = new MPEG4Writer(dup(mOutputFd)); + // Add audio source first if it exists + if (mAudioSource != AUDIO_SOURCE_LIST_END) { + sp<MediaSource> audioEncoder; + switch(mAudioEncoder) { + case AUDIO_ENCODER_AMR_NB: + case AUDIO_ENCODER_AMR_WB: + case AUDIO_ENCODER_AAC: + audioEncoder = createAudioSource(); + break; + default: + LOGE("Unsupported audio encoder: %d", mAudioEncoder); + return UNKNOWN_ERROR; + } + + if (audioEncoder == NULL) { + return UNKNOWN_ERROR; + } + + mWriter->addSource(audioEncoder); + } if (mVideoSource == VIDEO_SOURCE_DEFAULT || mVideoSource == VIDEO_SOURCE_CAMERA) { CHECK(mCamera != NULL); + if (mCamera == 0) { + mCamera = Camera::connect(0); + } + CHECK(mCamera != NULL); + + // Set the actual video recording frame size + CameraParameters params(mCamera->getParameters()); + params.setPreviewSize(mVideoWidth, mVideoHeight); + params.setPreviewFrameRate(mFrameRate); + String8 s = params.flatten(); + CHECK_EQ(OK, mCamera->setParameters(s)); + CameraParameters newCameraParams(mCamera->getParameters()); + + // Check on video frame size + int frameWidth = 0, frameHeight = 0; + newCameraParams.getPreviewSize(&frameWidth, &frameHeight); + if (frameWidth < 0 || frameWidth != mVideoWidth || + frameHeight < 0 || frameHeight != mVideoHeight) { + LOGE("Failed to set the video frame size to %dx%d", + mVideoWidth, mVideoHeight); + return UNKNOWN_ERROR; + } + + // Check on video frame rate + int frameRate = newCameraParams.getPreviewFrameRate(); + if (frameRate < 0 || (frameRate - mFrameRate) != 0) { + LOGE("Failed to set frame rate to %d", mFrameRate); + return UNKNOWN_ERROR; + } + + CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface)); + sp<CameraSource> cameraSource = - CameraSource::CreateFromICamera(mCamera); + CameraSource::CreateFromCamera(mCamera); CHECK(cameraSource != NULL); - cameraSource->setPreviewSurface(mPreviewSurface); - sp<MetaData> enc_meta = new MetaData; + enc_meta->setInt32(kKeyBitRate, mVideoBitRate); + enc_meta->setInt32(kKeySampleRate, mFrameRate); // XXX: kKeySampleRate? + switch (mVideoEncoder) { case VIDEO_ENCODER_H263: enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); @@ -286,14 +635,10 @@ status_t StagefrightRecorder::startMPEG4Recording() { mWriter->addSource(encoder); } - if (mAudioSource != AUDIO_SOURCE_LIST_END) { - sp<MediaSource> audioEncoder = createAMRAudioSource(); - - if (audioEncoder == NULL) { - return UNKNOWN_ERROR; - } - - mWriter->addSource(audioEncoder); + { + // MPEGWriter specific handling + MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get()); // mWriter is an MPEGWriter + writer->setInterleaveDuration(mInterleaveDurationUs); } mWriter->start(); @@ -314,21 +659,41 @@ status_t StagefrightRecorder::stop() { status_t StagefrightRecorder::close() { stop(); + if (mCamera != 0) { + if ((mFlags & FLAGS_HOT_CAMERA) == 0) { + LOGV("Camera was cold when we started, stopping preview"); + mCamera->stopPreview(); + } + if (mFlags & FLAGS_SET_CAMERA) { + LOGV("Unlocking camera"); + mCamera->unlock(); + } + mFlags = 0; + } return OK; } status_t StagefrightRecorder::reset() { stop(); + // No audio or video source by default mAudioSource = AUDIO_SOURCE_LIST_END; mVideoSource = VIDEO_SOURCE_LIST_END; - mOutputFormat = OUTPUT_FORMAT_LIST_END; - mAudioEncoder = AUDIO_ENCODER_LIST_END; - mVideoEncoder = VIDEO_ENCODER_LIST_END; - mVideoWidth = -1; - mVideoHeight = -1; - mFrameRate = -1; + + // Default parameters + mOutputFormat = OUTPUT_FORMAT_THREE_GPP; + mAudioEncoder = AUDIO_ENCODER_AMR_NB; + mVideoEncoder = VIDEO_ENCODER_H263; + mVideoWidth = 176; + mVideoHeight = 144; + mFrameRate = 20; + mVideoBitRate = 192000; + mSampleRate = 8000; + mAudioChannels = 1; + mAudioBitRate = 12200; + mOutputFd = -1; + mFlags = 0; return OK; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 7ec412d..b7d554b 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -23,6 +23,7 @@ namespace android { +class Camera; struct MediaSource; struct MediaWriter; @@ -52,7 +53,12 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t getMaxAmplitude(int *max); private: - sp<ICamera> mCamera; + enum CameraFlags { + FLAGS_SET_CAMERA = 1L << 0, + FLAGS_HOT_CAMERA = 1L << 1, + }; + + sp<Camera> mCamera; sp<ISurface> mPreviewSurface; sp<IMediaPlayerClient> mListener; sp<MediaWriter> mWriter; @@ -62,14 +68,31 @@ private: output_format mOutputFormat; audio_encoder mAudioEncoder; video_encoder mVideoEncoder; - int mVideoWidth, mVideoHeight; - int mFrameRate; + int32_t mVideoWidth, mVideoHeight; + int32_t mFrameRate; + int32_t mVideoBitRate; + int32_t mAudioBitRate; + int32_t mAudioChannels; + int32_t mSampleRate; + int32_t mInterleaveDurationUs; + int64_t mMaxFileSizeBytes; + int64_t mMaxFileDurationUs; + String8 mParams; int mOutputFd; + int32_t mFlags; status_t startMPEG4Recording(); status_t startAMRRecording(); - sp<MediaSource> createAMRAudioSource(); + status_t startAACRecording(); + sp<MediaSource> createAudioSource(); + status_t setParameter(const String8 &key, const String8 &value); + status_t setParamVideoEncodingBitRate(int32_t bitRate); + status_t setParamAudioEncodingBitRate(int32_t bitRate); + status_t setParamAudioNumberOfChannels(int32_t channles); + status_t setParamAudioSamplingRate(int32_t sampleRate); + status_t setParamInterleaveDuration(int32_t durationUs); + status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration); StagefrightRecorder(const StagefrightRecorder &); StagefrightRecorder &operator=(const StagefrightRecorder &); diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp index bf4424b..73ea56d 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -53,8 +53,6 @@ status_t AMRWriter::initCheck() const { } status_t AMRWriter::addSource(const sp<MediaSource> &source) { - Mutex::Autolock autoLock(mLock); - if (mInitCheck != OK) { return mInitCheck; } @@ -95,8 +93,6 @@ status_t AMRWriter::addSource(const sp<MediaSource> &source) { } status_t AMRWriter::start() { - Mutex::Autolock autoLock(mLock); - if (mInitCheck != OK) { return mInitCheck; } @@ -127,16 +123,12 @@ status_t AMRWriter::start() { } void AMRWriter::stop() { - { - Mutex::Autolock autoLock(mLock); - - if (!mStarted) { - return; - } - - mDone = true; + if (!mStarted) { + return; } + mDone = true; + void *dummy; pthread_join(mThread, &dummy); @@ -153,13 +145,7 @@ void *AMRWriter::ThreadWrapper(void *me) { } void AMRWriter::threadFunc() { - for (;;) { - Mutex::Autolock autoLock(mLock); - - if (mDone) { - break; - } - + while (!mDone) { MediaBuffer *buffer; status_t err = mSource->read(&buffer); @@ -184,12 +170,10 @@ void AMRWriter::threadFunc() { buffer = NULL; } - Mutex::Autolock autoLock(mLock); mReachedEOS = true; } bool AMRWriter::reachedEOS() { - Mutex::Autolock autoLock(mLock); return mReachedEOS; } diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index edabaf9..abd8abc 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "AudioSource" +#include <utils/Log.h> + #include <media/stagefright/AudioSource.h> #include <media/AudioRecord.h> diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 075b1e3..cd26e6b 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include <sys/time.h> +//#define LOG_NDEBUG 0 +#define LOG_TAG "CameraSource" +#include <utils/Log.h> #include <OMX_Component.h> -#include <binder/IServiceManager.h> -#include <cutils/properties.h> // for property_get #include <media/stagefright/CameraSource.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> @@ -27,46 +27,10 @@ #include <media/stagefright/MetaData.h> #include <camera/Camera.h> #include <camera/CameraParameters.h> -#include <ui/GraphicBuffer.h> -#include <ui/Overlay.h> -#include <surfaceflinger/ISurface.h> #include <utils/String8.h> namespace android { -static int64_t getNowUs() { - struct timeval tv; - gettimeofday(&tv, NULL); - - return (int64_t)tv.tv_usec + tv.tv_sec * 1000000; -} - -struct DummySurface : public BnSurface { - DummySurface() {} - - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) { - return NULL; - } - - virtual status_t registerBuffers(const BufferHeap &buffers) { - return OK; - } - - virtual void postBuffer(ssize_t offset) {} - virtual void unregisterBuffers() {} - - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format, int32_t orientation) { - return NULL; - } - -protected: - virtual ~DummySurface() {} - - DummySurface(const DummySurface &); - DummySurface &operator=(const DummySurface &); -}; - struct CameraSourceListener : public CameraListener { CameraSourceListener(const sp<CameraSource> &source); @@ -100,22 +64,20 @@ void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr) { LOGV("postData(%d, ptr:%p, size:%d)", msgType, dataPtr->pointer(), dataPtr->size()); - - sp<CameraSource> source = mSource.promote(); - if (source.get() != NULL) { - source->dataCallback(msgType, dataPtr); - } } void CameraSourceListener::postDataTimestamp( nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) { - LOGV("postDataTimestamp(%lld, %d, ptr:%p, size:%d)", - timestamp, msgType, dataPtr->pointer(), dataPtr->size()); + + sp<CameraSource> source = mSource.promote(); + if (source.get() != NULL) { + source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr); + } } // static CameraSource *CameraSource::Create() { - sp<Camera> camera = Camera::connect(); + sp<Camera> camera = Camera::connect(0); if (camera.get() == NULL) { return NULL; @@ -125,9 +87,7 @@ CameraSource *CameraSource::Create() { } // static -CameraSource *CameraSource::CreateFromICamera(const sp<ICamera> &icamera) { - sp<Camera> camera = Camera::create(icamera); - +CameraSource *CameraSource::CreateFromCamera(const sp<Camera> &camera) { if (camera.get() == NULL) { return NULL; } @@ -140,15 +100,11 @@ CameraSource::CameraSource(const sp<Camera> &camera) mWidth(0), mHeight(0), mFirstFrameTimeUs(0), - mNumFrames(0), + mLastFrameTimestampUs(0), + mNumFramesReceived(0), + mNumFramesEncoded(0), + mNumFramesDropped(0), mStarted(false) { - char value[PROPERTY_VALUE_MAX]; - if (property_get("ro.hardware", value, NULL) && !strcmp(value, "sholes")) { - // The hardware encoder(s) do not support yuv420, but only YCbYCr, - // fortunately the camera also supports this, so we needn't transcode. - mCamera->setParameters(String8("preview-format=yuv422i-yuyv")); - } - String8 s = mCamera->getParameters(); printf("params: \"%s\"\n", s.string()); @@ -162,26 +118,12 @@ CameraSource::~CameraSource() { } } -void CameraSource::setPreviewSurface(const sp<ISurface> &surface) { - mPreviewSurface = surface; -} - status_t CameraSource::start(MetaData *) { + LOGV("start"); CHECK(!mStarted); mCamera->setListener(new CameraSourceListener(this)); - - status_t err = - mCamera->setPreviewDisplay( - mPreviewSurface != NULL ? mPreviewSurface : new DummySurface); - CHECK_EQ(err, OK); - - mCamera->setPreviewCallbackFlags( - FRAME_CALLBACK_FLAG_ENABLE_MASK - | FRAME_CALLBACK_FLAG_COPY_OUT_MASK); - - err = mCamera->startPreview(); - CHECK_EQ(err, OK); + CHECK_EQ(OK, mCamera->startRecording()); mStarted = true; @@ -189,15 +131,32 @@ status_t CameraSource::start(MetaData *) { } status_t CameraSource::stop() { - CHECK(mStarted); - - mCamera->stopPreview(); - + LOGV("stop"); + Mutex::Autolock autoLock(mLock); mStarted = false; + mFrameAvailableCondition.signal(); + mCamera->setListener(NULL); + mCamera->stopRecording(); + + releaseQueuedFrames(); + LOGI("Frames received/encoded/dropped: %d/%d/%d, timestamp (us) last/first: %lld/%lld", + mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, + mLastFrameTimestampUs, mFirstFrameTimeUs); + CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); return OK; } +void CameraSource::releaseQueuedFrames() { + List<sp<IMemory> >::iterator it; + while (!mFrames.empty()) { + it = mFrames.begin(); + mCamera->releaseRecordingFrame(*it); + mFrames.erase(it); + ++mNumFramesDropped; + } +} + sp<MetaData> CameraSource::getFormat() { sp<MetaData> meta = new MetaData; meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); @@ -210,7 +169,7 @@ sp<MetaData> CameraSource::getFormat() { status_t CameraSource::read( MediaBuffer **buffer, const ReadOptions *options) { - CHECK(mStarted); + LOGV("read"); *buffer = NULL; @@ -224,20 +183,24 @@ status_t CameraSource::read( { Mutex::Autolock autoLock(mLock); - while (mFrames.empty()) { + while (mStarted && mFrames.empty()) { mFrameAvailableCondition.wait(mLock); } - + if (!mStarted) { + return OK; + } frame = *mFrames.begin(); mFrames.erase(mFrames.begin()); frameTime = *mFrameTimes.begin(); mFrameTimes.erase(mFrameTimes.begin()); + ++mNumFramesEncoded; } *buffer = new MediaBuffer(frame->size()); memcpy((*buffer)->data(), frame->pointer(), frame->size()); (*buffer)->set_range(0, frame->size()); + mCamera->releaseRecordingFrame(frame); (*buffer)->meta_data()->clear(); (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); @@ -245,17 +208,25 @@ status_t CameraSource::read( return OK; } -void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) { +void CameraSource::dataCallbackTimestamp(int64_t timestampUs, + int32_t msgType, const sp<IMemory> &data) { + LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs); + mLastFrameTimestampUs = timestampUs; Mutex::Autolock autoLock(mLock); + if (!mStarted) { + mCamera->releaseRecordingFrame(data); + ++mNumFramesReceived; + ++mNumFramesDropped; + return; + } - int64_t nowUs = getNowUs(); - if (mNumFrames == 0) { - mFirstFrameTimeUs = nowUs; + if (mNumFramesReceived == 0) { + mFirstFrameTimeUs = timestampUs; } - ++mNumFrames; + ++mNumFramesReceived; mFrames.push_back(data); - mFrameTimes.push_back(nowUs - mFirstFrameTimeUs); + mFrameTimes.push_back(timestampUs - mFirstFrameTimeUs); mFrameAvailableCondition.signal(); } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 2cf0ddf..19cccf7 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "MPEG4Writer" +#include <utils/Log.h> + #include <arpa/inet.h> #include <ctype.h> @@ -24,6 +28,7 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/Utils.h> @@ -52,34 +57,73 @@ private: struct SampleInfo { size_t size; - off_t offset; int64_t timestamp; }; - List<SampleInfo> mSampleInfos; + List<SampleInfo> mSampleInfos; + bool mSamplesHaveSameSize; + + List<MediaBuffer *> mChunkSamples; + List<off_t> mChunkOffsets; + + struct StscTableEntry { + + StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id) + : firstChunk(chunk), + samplesPerChunk(samples), + sampleDescriptionId(id) {} + + uint32_t firstChunk; + uint32_t samplesPerChunk; + uint32_t sampleDescriptionId; + }; + List<StscTableEntry> mStscTableEntries; + + List<int32_t> mStssTableEntries; + + struct SttsTableEntry { + + SttsTableEntry(uint32_t count, uint32_t duration) + : sampleCount(count), sampleDuration(duration) {} + + uint32_t sampleCount; + uint32_t sampleDuration; + }; + List<SttsTableEntry> mSttsTableEntries; void *mCodecSpecificData; size_t mCodecSpecificDataSize; + bool mGotAllCodecSpecificData; bool mReachedEOS; static void *ThreadWrapper(void *me); void threadEntry(); + status_t makeAVCCodecSpecificData( + const uint8_t *data, size_t size); + void writeOneChunk(bool isAvc); + Track(const Track &); Track &operator=(const Track &); }; +#define USE_NALLEN_FOUR 1 + MPEG4Writer::MPEG4Writer(const char *filename) : mFile(fopen(filename, "wb")), mOffset(0), - mMdatOffset(0) { + mMdatOffset(0), + mEstimatedMoovBoxSize(0), + mInterleaveDurationUs(500000) { CHECK(mFile != NULL); } MPEG4Writer::MPEG4Writer(int fd) : mFile(fdopen(fd, "wb")), mOffset(0), - mMdatOffset(0) { + mMdatOffset(0), + mEstimatedMoovBoxSize(0), + mInterleaveDurationUs(500000) { CHECK(mFile != NULL); } @@ -105,15 +149,33 @@ status_t MPEG4Writer::start() { return UNKNOWN_ERROR; } + mStreamableFile = true; + mWriteMoovBoxToMemory = false; + mMoovBoxBuffer = NULL; + mMoovBoxBufferOffset = 0; + beginBox("ftyp"); writeFourcc("isom"); writeInt32(0); writeFourcc("isom"); endBox(); - mMdatOffset = mOffset; - write("\x00\x00\x00\x01mdat????????", 16); + mFreeBoxOffset = mOffset; + if (mEstimatedMoovBoxSize == 0) { + // XXX: Estimate the moov box size + // based on max file size or duration limit + mEstimatedMoovBoxSize = 0x0F00; + } + CHECK(mEstimatedMoovBoxSize >= 8); + fseeko(mFile, mFreeBoxOffset, SEEK_SET); + writeInt32(mEstimatedMoovBoxSize); + write("free", 4); + + mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize; + mOffset = mMdatOffset; + fseeko(mFile, mMdatOffset, SEEK_SET); + write("\x00\x00\x00\x01mdat????????", 16); for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { status_t err = (*it)->start(); @@ -147,14 +209,20 @@ void MPEG4Writer::stop() { } } + // Fix up the size of the 'mdat' chunk. - fseek(mFile, mMdatOffset + 8, SEEK_SET); + fseeko(mFile, mMdatOffset + 8, SEEK_SET); int64_t size = mOffset - mMdatOffset; size = hton64(size); fwrite(&size, 1, 8, mFile); - fseek(mFile, mOffset, SEEK_SET); + fseeko(mFile, mOffset, SEEK_SET); time_t now = time(NULL); + const off_t moovOffset = mOffset; + mWriteMoovBoxToMemory = true; + mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); + mMoovBoxBufferOffset = 0; + CHECK(mMoovBoxBuffer != NULL); beginBox("moov"); @@ -194,15 +262,48 @@ void MPEG4Writer::stop() { } endBox(); // moov + mWriteMoovBoxToMemory = false; + if (mStreamableFile) { + CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize); + + // Moov box + fseeko(mFile, mFreeBoxOffset, SEEK_SET); + mOffset = mFreeBoxOffset; + write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile); + + // Free box + mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset; + fseeko(mFile, mFreeBoxOffset, SEEK_SET); + writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset); + write("free", 4); + + // Free temp memory + free(mMoovBoxBuffer); + mMoovBoxBuffer = NULL; + mMoovBoxBufferOffset = 0; + } + CHECK(mBoxes.empty()); + fflush(mFile); fclose(mFile); mFile = NULL; } -off_t MPEG4Writer::addSample(MediaBuffer *buffer) { - Mutex::Autolock autoLock(mLock); +status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) { + mInterleaveDurationUs = durationUs; + return OK; +} + +void MPEG4Writer::lock() { + mLock.lock(); +} + +void MPEG4Writer::unlock() { + mLock.unlock(); +} +off_t MPEG4Writer::addSample_l(MediaBuffer *buffer) { off_t old_offset = mOffset; fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), @@ -213,31 +314,92 @@ off_t MPEG4Writer::addSample(MediaBuffer *buffer) { return old_offset; } -off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) { - Mutex::Autolock autoLock(mLock); +static void StripStartcode(MediaBuffer *buffer) { + if (buffer->range_length() < 4) { + return; + } + + const uint8_t *ptr = + (const uint8_t *)buffer->data() + buffer->range_offset(); + + if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) { + buffer->set_range( + buffer->range_offset() + 4, buffer->range_length() - 4); + } +} + +off_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) { + StripStartcode(buffer); off_t old_offset = mOffset; size_t length = buffer->range_length(); + +#if USE_NALLEN_FOUR + uint8_t x = length >> 24; + fwrite(&x, 1, 1, mFile); + x = (length >> 16) & 0xff; + fwrite(&x, 1, 1, mFile); + x = (length >> 8) & 0xff; + fwrite(&x, 1, 1, mFile); + x = length & 0xff; + fwrite(&x, 1, 1, mFile); +#else CHECK(length < 65536); uint8_t x = length >> 8; fwrite(&x, 1, 1, mFile); x = length & 0xff; fwrite(&x, 1, 1, mFile); +#endif fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), 1, length, mFile); +#if USE_NALLEN_FOUR + mOffset += length + 4; +#else mOffset += length + 2; +#endif return old_offset; } +size_t MPEG4Writer::write( + const void *ptr, size_t size, size_t nmemb, FILE *stream) { + + const size_t bytes = size * nmemb; + if (mWriteMoovBoxToMemory) { + if (8 + mMoovBoxBufferOffset + bytes > mEstimatedMoovBoxSize) { + for (List<off_t>::iterator it = mBoxes.begin(); + it != mBoxes.end(); ++it) { + (*it) += mOffset; + } + fseeko(mFile, mOffset, SEEK_SET); + fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream); + fwrite(ptr, size, nmemb, stream); + mOffset += (bytes + mMoovBoxBufferOffset); + free(mMoovBoxBuffer); + mMoovBoxBuffer = NULL; + mMoovBoxBufferOffset = 0; + mWriteMoovBoxToMemory = false; + mStreamableFile = false; + } else { + memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes); + mMoovBoxBufferOffset += bytes; + } + } else { + fwrite(ptr, size, nmemb, stream); + mOffset += bytes; + } + return bytes; +} + void MPEG4Writer::beginBox(const char *fourcc) { CHECK_EQ(strlen(fourcc), 4); - mBoxes.push_back(mOffset); + mBoxes.push_back(mWriteMoovBoxToMemory? + mMoovBoxBufferOffset: mOffset); writeInt32(0); writeFourcc(fourcc); @@ -249,51 +411,48 @@ void MPEG4Writer::endBox() { off_t offset = *--mBoxes.end(); mBoxes.erase(--mBoxes.end()); - fseek(mFile, offset, SEEK_SET); - writeInt32(mOffset - offset); - mOffset -= 4; - fseek(mFile, mOffset, SEEK_SET); + if (mWriteMoovBoxToMemory) { + int32_t x = htonl(mMoovBoxBufferOffset - offset); + memcpy(mMoovBoxBuffer + offset, &x, 4); + } else { + fseeko(mFile, offset, SEEK_SET); + writeInt32(mOffset - offset); + mOffset -= 4; + fseeko(mFile, mOffset, SEEK_SET); + } } void MPEG4Writer::writeInt8(int8_t x) { - fwrite(&x, 1, 1, mFile); - ++mOffset; + write(&x, 1, 1, mFile); } void MPEG4Writer::writeInt16(int16_t x) { x = htons(x); - fwrite(&x, 1, 2, mFile); - mOffset += 2; + write(&x, 1, 2, mFile); } void MPEG4Writer::writeInt32(int32_t x) { x = htonl(x); - fwrite(&x, 1, 4, mFile); - mOffset += 4; + write(&x, 1, 4, mFile); } void MPEG4Writer::writeInt64(int64_t x) { x = hton64(x); - fwrite(&x, 1, 8, mFile); - mOffset += 8; + write(&x, 1, 8, mFile); } void MPEG4Writer::writeCString(const char *s) { size_t n = strlen(s); - - fwrite(s, 1, n + 1, mFile); - mOffset += n + 1; + write(s, 1, n + 1, mFile); } void MPEG4Writer::writeFourcc(const char *s) { CHECK_EQ(strlen(s), 4); - fwrite(s, 1, 4, mFile); - mOffset += 4; + write(s, 1, 4, mFile); } void MPEG4Writer::write(const void *data, size_t size) { - fwrite(data, 1, size, mFile); - mOffset += size; + write(data, 1, size, mFile); } bool MPEG4Writer::reachedEOS() { @@ -318,8 +477,10 @@ MPEG4Writer::Track::Track( mSource(source), mDone(false), mMaxTimeStampUs(0), + mSamplesHaveSameSize(true), mCodecSpecificData(NULL), mCodecSpecificDataSize(0), + mGotAllCodecSpecificData(false), mReachedEOS(false) { } @@ -380,73 +541,162 @@ void *MPEG4Writer::Track::ThreadWrapper(void *me) { return NULL; } +#include <ctype.h> +static void hexdump(const void *_data, size_t size) { + const uint8_t *data = (const uint8_t *)_data; + size_t offset = 0; + while (offset < size) { + printf("0x%04x ", offset); + + size_t n = size - offset; + if (n > 16) { + n = 16; + } + + for (size_t i = 0; i < 16; ++i) { + if (i == 8) { + printf(" "); + } + + if (offset + i < size) { + printf("%02x ", data[offset + i]); + } else { + printf(" "); + } + } + + printf(" "); + + for (size_t i = 0; i < n; ++i) { + if (isprint(data[offset + i])) { + printf("%c", data[offset + i]); + } else { + printf("."); + } + } + + printf("\n"); + + offset += 16; + } +} + + +status_t MPEG4Writer::Track::makeAVCCodecSpecificData( + const uint8_t *data, size_t size) { + // hexdump(data, size); + + if (mCodecSpecificData != NULL) { + LOGE("Already have codec specific data"); + return ERROR_MALFORMED; + } + + if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { + LOGE("Must start with a start code"); + return ERROR_MALFORMED; + } + + size_t picParamOffset = 4; + while (picParamOffset + 3 < size + && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) { + ++picParamOffset; + } + + if (picParamOffset + 3 >= size) { + LOGE("Could not find start-code for pictureParameterSet"); + return ERROR_MALFORMED; + } + + size_t seqParamSetLength = picParamOffset - 4; + size_t picParamSetLength = size - picParamOffset - 4; + + mCodecSpecificDataSize = + 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2; + + mCodecSpecificData = malloc(mCodecSpecificDataSize); + uint8_t *header = (uint8_t *)mCodecSpecificData; + header[0] = 1; + header[1] = 0x42; // profile + header[2] = 0x80; + header[3] = 0x1e; // level + +#if USE_NALLEN_FOUR + header[4] = 0xfc | 3; // length size == 4 bytes +#else + header[4] = 0xfc | 1; // length size == 2 bytes +#endif + + header[5] = 0xe0 | 1; + header[6] = seqParamSetLength >> 8; + header[7] = seqParamSetLength & 0xff; + memcpy(&header[8], &data[4], seqParamSetLength); + header += 8 + seqParamSetLength; + header[0] = 1; + header[1] = picParamSetLength >> 8; + header[2] = picParamSetLength & 0xff; + memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength); + + return OK; +} + void MPEG4Writer::Track::threadEntry() { sp<MetaData> meta = mSource->getFormat(); const char *mime; meta->findCString(kKeyMIMEType, &mime); - bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4); + bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || + !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); int32_t count = 0; + const int64_t interleaveDurationUs = mOwner->interleaveDuration(); + int64_t chunkTimestampUs = 0; + int32_t nChunks = 0; + int32_t nZeroLengthFrames = 0; + int64_t lastTimestamp = 0; // Timestamp of the previous sample + int64_t lastDuration = 0; // Time spacing between the previous two samples + int32_t sampleCount = 1; // Sample count in the current stts table entry + uint32_t previousSampleSize = 0; // Size of the previous sample MediaBuffer *buffer; while (!mDone && mSource->read(&buffer) == OK) { if (buffer->range_length() == 0) { buffer->release(); buffer = NULL; - + ++nZeroLengthFrames; continue; } ++count; - if (is_avc && count < 3) { - size_t size = buffer->range_length(); - - switch (count) { - case 1: - { - CHECK_EQ(mCodecSpecificData, NULL); - mCodecSpecificData = malloc(size + 8); - uint8_t *header = (uint8_t *)mCodecSpecificData; - header[0] = 1; - header[1] = 0x42; // profile - header[2] = 0x80; - header[3] = 0x1e; // level - header[4] = 0xfc | 3; - header[5] = 0xe0 | 1; - header[6] = size >> 8; - header[7] = size & 0xff; - memcpy(&header[8], - (const uint8_t *)buffer->data() + buffer->range_offset(), - size); - - mCodecSpecificDataSize = size + 8; - break; - } - - case 2: - { - size_t offset = mCodecSpecificDataSize; - mCodecSpecificDataSize += size + 3; - mCodecSpecificData = realloc(mCodecSpecificData, mCodecSpecificDataSize); - uint8_t *header = (uint8_t *)mCodecSpecificData; - header[offset] = 1; - header[offset + 1] = size >> 8; - header[offset + 2] = size & 0xff; - memcpy(&header[offset + 3], - (const uint8_t *)buffer->data() + buffer->range_offset(), - size); - break; - } + int32_t isCodecConfig; + if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig) + && isCodecConfig) { + CHECK(!mGotAllCodecSpecificData); + + if (is_avc) { + status_t err = makeAVCCodecSpecificData( + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); + CHECK_EQ(OK, err); + } else if (is_mpeg4) { + mCodecSpecificDataSize = buffer->range_length(); + mCodecSpecificData = malloc(mCodecSpecificDataSize); + memcpy(mCodecSpecificData, + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); } buffer->release(); buffer = NULL; + mGotAllCodecSpecificData = true; continue; - } + } else if (!mGotAllCodecSpecificData && + count == 1 && is_mpeg4 && mCodecSpecificData == NULL) { + // The TI mpeg4 encoder does not properly set the + // codec-specific-data flag. - if (mCodecSpecificData == NULL && is_mpeg4) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -474,14 +724,67 @@ void MPEG4Writer::Track::threadEntry() { memcpy(mCodecSpecificData, data, offset); buffer->set_range(buffer->range_offset() + offset, size - offset); - } - off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer) - : mOwner->addSample(buffer); + if (size == offset) { + buffer->release(); + buffer = NULL; + + continue; + } + + mGotAllCodecSpecificData = true; + } else if (!mGotAllCodecSpecificData && is_avc && count < 3) { + // The TI video encoder does not flag codec specific data + // as such and also splits up SPS and PPS across two buffers. + + const uint8_t *data = + (const uint8_t *)buffer->data() + buffer->range_offset(); + + size_t size = buffer->range_length(); + + CHECK(count == 2 || mCodecSpecificData == NULL); + + size_t offset = mCodecSpecificDataSize; + mCodecSpecificDataSize += size + 4; + mCodecSpecificData = + realloc(mCodecSpecificData, mCodecSpecificDataSize); + + memcpy((uint8_t *)mCodecSpecificData + offset, + "\x00\x00\x00\x01", 4); + + memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size); + + buffer->release(); + buffer = NULL; + + if (count == 2) { + void *tmp = mCodecSpecificData; + size = mCodecSpecificDataSize; + mCodecSpecificData = NULL; + mCodecSpecificDataSize = 0; + + status_t err = makeAVCCodecSpecificData( + (const uint8_t *)tmp, size); + free(tmp); + tmp = NULL; + CHECK_EQ(OK, err); + + mGotAllCodecSpecificData = true; + } + + continue; + } SampleInfo info; - info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length(); - info.offset = offset; + info.size = is_avc +#if USE_NALLEN_FOUR + ? buffer->range_length() + 4 +#else + ? buffer->range_length() + 2 +#endif + : buffer->range_length(); + + bool is_audio = !strncasecmp(mime, "audio/", 6); int64_t timestampUs; CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); @@ -492,14 +795,109 @@ void MPEG4Writer::Track::threadEntry() { // Our timestamp is in ms. info.timestamp = (timestampUs + 500) / 1000; - mSampleInfos.push_back(info); + if (mSampleInfos.size() > 2) { + if (lastDuration != info.timestamp - lastTimestamp) { + SttsTableEntry sttsEntry(sampleCount, lastDuration); + mSttsTableEntries.push_back(sttsEntry); + sampleCount = 1; + } else { + ++sampleCount; + } + } + if (mSamplesHaveSameSize) { + if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) { + mSamplesHaveSameSize = false; + } + previousSampleSize = info.size; + } + lastDuration = info.timestamp - lastTimestamp; + lastTimestamp = info.timestamp; + +//////////////////////////////////////////////////////////////////////////////// + // Make a deep copy of the MediaBuffer less Metadata + 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()); + + mChunkSamples.push_back(copy); + if (interleaveDurationUs == 0) { + StscTableEntry stscEntry(++nChunks, 1, 1); + mStscTableEntries.push_back(stscEntry); + writeOneChunk(is_avc); + } else { + if (chunkTimestampUs == 0) { + chunkTimestampUs = timestampUs; + } else { + if (timestampUs - chunkTimestampUs > interleaveDurationUs) { + ++nChunks; + if (nChunks == 1 || // First chunk + (--(mStscTableEntries.end()))->samplesPerChunk != + mChunkSamples.size()) { + StscTableEntry stscEntry(nChunks, + mChunkSamples.size(), 1); + mStscTableEntries.push_back(stscEntry); + } + writeOneChunk(is_avc); + chunkTimestampUs = timestampUs; + } + } + } + + int32_t isSync = false; + if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync) && + isSync != 0) { + mStssTableEntries.push_back(mSampleInfos.size()); + } buffer->release(); buffer = NULL; } + CHECK(!mSampleInfos.empty()); + + // Last chunk + if (!mChunkSamples.empty()) { + ++nChunks; + StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1); + mStscTableEntries.push_back(stscEntry); + writeOneChunk(is_avc); + } + + // We don't really know how long the last frame lasts, since + // there is no frame time after it, just repeat the previous + // frame's duration. + if (mSampleInfos.size() == 1) { + lastDuration = 0; // A single sample's duration + } else { + ++sampleCount; // Count for the last sample + } + SttsTableEntry sttsEntry(sampleCount, lastDuration); + mSttsTableEntries.push_back(sttsEntry); mReachedEOS = true; + LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames", + count, nZeroLengthFrames, mSampleInfos.size()); +} + +void MPEG4Writer::Track::writeOneChunk(bool isAvc) { + mOwner->lock(); + for (List<MediaBuffer *>::iterator it = mChunkSamples.begin(); + it != mChunkSamples.end(); ++it) { + off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it) + : mOwner->addSample_l(*it); + if (it == mChunkSamples.begin()) { + mChunkOffsets.push_back(offset); + } + } + mOwner->unlock(); + while (!mChunkSamples.empty()) { + List<MediaBuffer *>::iterator it = mChunkSamples.begin(); + (*it)->release(); + (*it) = NULL; + mChunkSamples.erase(it); + } + mChunkSamples.clear(); } int64_t MPEG4Writer::Track::getDurationUs() const { @@ -550,8 +948,8 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { success = success && mMeta->findInt32(kKeyHeight, &height); CHECK(success); - mOwner->writeInt32(width); - mOwner->writeInt32(height); + mOwner->writeInt32(width << 16); // 32-bit fixed-point value + mOwner->writeInt32(height << 16); // 32-bit fixed-point value } mOwner->endBox(); // tkhd @@ -569,26 +967,15 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox("hdlr"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(0); // predefined - mOwner->writeFourcc(is_audio ? "soun" : "vide"); + mOwner->writeInt32(0); // component type: should be mhlr + mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved - mOwner->writeCString(""); // name + mOwner->writeCString("SoundHandler"); // name mOwner->endBox(); mOwner->beginBox("minf"); - - mOwner->beginBox("dinf"); - mOwner->beginBox("dref"); - mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(1); - mOwner->beginBox("url "); - mOwner->writeInt32(1); // version=0, flags=1 - mOwner->endBox(); // url - mOwner->endBox(); // dref - mOwner->endBox(); // dinf - if (is_audio) { mOwner->beginBox("smhd"); mOwner->writeInt32(0); // version=0, flags=0 @@ -604,7 +991,18 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->writeInt16(0); mOwner->endBox(); } - mOwner->endBox(); // minf + + mOwner->beginBox("dinf"); + mOwner->beginBox("dref"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(1); + mOwner->beginBox("url "); + mOwner->writeInt32(1); // version=0, flags=1 + mOwner->endBox(); // url + mOwner->endBox(); // dref + mOwner->endBox(); // dinf + + mOwner->endBox(); // minf mOwner->beginBox("stbl"); @@ -617,6 +1015,8 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { fourcc = "samr"; } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { fourcc = "sawb"; + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { + fourcc = "mp4a"; } else { LOGE("Unknown mime type '%s'.", mime); CHECK(!"should not be here, unknown mime type."); @@ -625,10 +1025,12 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox(fourcc); // audio format mOwner->writeInt32(0); // reserved mOwner->writeInt16(0); // reserved - mOwner->writeInt16(0); // data ref index + mOwner->writeInt16(0x1); // data ref index mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved - mOwner->writeInt16(2); // channel count + int32_t nChannels; + CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels)); + mOwner->writeInt16(nChannels); // channel count mOwner->writeInt16(16); // sample size mOwner->writeInt16(0); // predefined mOwner->writeInt16(0); // reserved @@ -638,6 +1040,38 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { CHECK(success); mOwner->writeInt32(samplerate << 16); + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { + mOwner->beginBox("esds"); + + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt8(0x03); // ES_DescrTag + mOwner->writeInt8(23 + mCodecSpecificDataSize); + mOwner->writeInt16(0x0000);// ES_ID + mOwner->writeInt8(0x00); + + mOwner->writeInt8(0x04); // DecoderConfigDescrTag + mOwner->writeInt8(15 + mCodecSpecificDataSize); + mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2 + mOwner->writeInt8(0x15); // streamType AudioStream + + mOwner->writeInt16(0x03); // XXX + mOwner->writeInt8(0x00); // buffer size 24-bit + mOwner->writeInt32(96000); // max bit rate + mOwner->writeInt32(96000); // avg bit rate + + mOwner->writeInt8(0x05); // DecoderSpecificInfoTag + mOwner->writeInt8(mCodecSpecificDataSize); + mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); + + static const uint8_t kData2[] = { + 0x06, // SLConfigDescriptorTag + 0x01, + 0x02 + }; + mOwner->write(kData2, sizeof(kData2)); + + mOwner->endBox(); // esds + } mOwner->endBox(); } else { if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { @@ -698,7 +1132,7 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { 0x00, 0x03, 0xe8, 0x00 }; mOwner->write(kData, sizeof(kData)); - + mOwner->writeInt8(0x05); // DecoderSpecificInfoTag mOwner->writeInt8(mCodecSpecificDataSize); @@ -733,49 +1167,59 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox("stts"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size() - 1); - - List<SampleInfo>::iterator it = mSampleInfos.begin(); - int64_t last = (*it).timestamp; - ++it; - while (it != mSampleInfos.end()) { - mOwner->writeInt32(1); - mOwner->writeInt32((*it).timestamp - last); - - last = (*it).timestamp; - - ++it; + mOwner->writeInt32(mSttsTableEntries.size()); + for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); + it != mSttsTableEntries.end(); ++it) { + mOwner->writeInt32(it->sampleCount); + mOwner->writeInt32(it->sampleDuration); } mOwner->endBox(); // stts + if (!is_audio) { + mOwner->beginBox("stss"); + mOwner->writeInt32(0); // version=0, flags=0 + mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames + for (List<int32_t>::iterator it = mStssTableEntries.begin(); + it != mStssTableEntries.end(); ++it) { + mOwner->writeInt32(*it); + } + mOwner->endBox(); // stss + } + mOwner->beginBox("stsz"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(0); // default sample size + if (mSamplesHaveSameSize) { + List<SampleInfo>::iterator it = mSampleInfos.begin(); + mOwner->writeInt32(it->size); // default sample size + } else { + mOwner->writeInt32(0); + } mOwner->writeInt32(mSampleInfos.size()); - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it) { - mOwner->writeInt32((*it).size); + if (!mSamplesHaveSameSize) { + for (List<SampleInfo>::iterator it = mSampleInfos.begin(); + it != mSampleInfos.end(); ++it) { + mOwner->writeInt32((*it).size); + } } mOwner->endBox(); // stsz mOwner->beginBox("stsc"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size()); - int32_t n = 1; - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it, ++n) { - mOwner->writeInt32(n); - mOwner->writeInt32(1); - mOwner->writeInt32(1); + mOwner->writeInt32(mStscTableEntries.size()); + for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); + it != mStscTableEntries.end(); ++it) { + mOwner->writeInt32(it->firstChunk); + mOwner->writeInt32(it->samplesPerChunk); + mOwner->writeInt32(it->sampleDescriptionId); } mOwner->endBox(); // stsc mOwner->beginBox("co64"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size()); - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it, ++n) { - mOwner->writeInt64((*it).offset); + mOwner->writeInt32(mChunkOffsets.size()); + for (List<off_t>::iterator it = mChunkOffsets.begin(); + it != mChunkOffsets.end(); ++it) { + mOwner->writeInt64((*it)); } mOwner->endBox(); // co64 diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 69da7ef..ce4dd32 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -141,6 +141,7 @@ static const CodecInfo kEncoderInfo[] = { { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" }, + { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" }, }; @@ -523,6 +524,7 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { setAACFormat(numChannels, sampleRate); } + if (!strncasecmp(mMIME, "video/", 6)) { int32_t width, height; bool success = meta->findInt32(kKeyWidth, &width); @@ -570,7 +572,8 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { } if (!strcmp(mComponentName, "OMX.TI.AMR.encode") - || !strcmp(mComponentName, "OMX.TI.WBAMR.encode")) { + || !strcmp(mComponentName, "OMX.TI.WBAMR.encode") + || !strcmp(mComponentName, "OMX.TI.AAC.encode")) { setMinBufferSize(kPortIndexOutput, 8192); // XXX } @@ -683,6 +686,7 @@ static size_t getFrameSize( case OMX_COLOR_FormatCbYCrY: return width * height * 2; + case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatYUV420SemiPlanar: return (width * height * 3) / 2; @@ -713,61 +717,62 @@ void OMXCodec::setVideoInputFormat( colorFormat = OMX_COLOR_FormatYCbYCr; } - CHECK_EQ(setVideoPortFormatType( - kPortIndexInput, OMX_VIDEO_CodingUnused, - colorFormat), OK); - CHECK_EQ(setVideoPortFormatType( - kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused), - OK); + status_t err; OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - def.nPortIndex = kPortIndexOutput; - OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; - status_t err = mOMX->getParameter( - mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + //////////////////////// Input port ///////////////////////// + CHECK_EQ(setVideoPortFormatType( + kPortIndexInput, OMX_VIDEO_CodingUnused, + colorFormat), OK); + InitOMXParams(&def); + def.nPortIndex = kPortIndexInput; + err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); + + def.nBufferSize = getFrameSize(colorFormat, width, height); + CHECK_EQ(def.eDomain, OMX_PortDomainVideo); video_def->nFrameWidth = width; video_def->nFrameHeight = height; + video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; + video_def->eColorFormat = colorFormat; - video_def->eCompressionFormat = compressionFormat; - video_def->eColorFormat = OMX_COLOR_FormatUnused; + video_def->xFramerate = (24 << 16); // Q16 format err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); - //////////////////////////////////////////////////////////////////////////// - + //////////////////////// Output port ///////////////////////// + CHECK_EQ(setVideoPortFormatType( + kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused), + OK); InitOMXParams(&def); - def.nPortIndex = kPortIndexInput; + def.nPortIndex = kPortIndexOutput; err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); - CHECK_EQ(err, OK); - - def.nBufferSize = getFrameSize(colorFormat, width, height); - CODEC_LOGV("Setting nBufferSize = %ld", def.nBufferSize); + CHECK_EQ(err, OK); CHECK_EQ(def.eDomain, OMX_PortDomainVideo); video_def->nFrameWidth = width; video_def->nFrameHeight = height; - video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; - video_def->eColorFormat = colorFormat; - video_def->xFramerate = 24 << 16; // XXX crucial! + video_def->eCompressionFormat = compressionFormat; + video_def->eColorFormat = OMX_COLOR_FormatUnused; err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); + /////////////////// Codec-specific //////////////////////// switch (compressionFormat) { case OMX_VIDEO_CodingMPEG4: { @@ -915,7 +920,7 @@ status_t OMXCodec::setupAVCEncoderParameters() { CHECK_EQ(err, OK); bitrateType.eControlRate = OMX_Video_ControlRateVariable; - bitrateType.nTargetBitrate = 1000000; + bitrateType.nTargetBitrate = 3000000; err = mOMX->setParameter( mNode, OMX_IndexParamVideoBitrate, @@ -2128,11 +2133,24 @@ void OMXCodec::setState(State newState) { void OMXCodec::setRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) { + + // port definition + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = portIndex; + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + CHECK_EQ(err, OK); + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, + &def, sizeof(def)), OK); + + // pcm param OMX_AUDIO_PARAM_PCMMODETYPE pcmParams; InitOMXParams(&pcmParams); pcmParams.nPortIndex = portIndex; - status_t err = mOMX->getParameter( + err = mOMX->getParameter( mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); CHECK_EQ(err, OK); @@ -2172,6 +2190,8 @@ void OMXCodec::setAMRFormat(bool isWAMR) { CHECK_EQ(err, OK); def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + + // XXX: Select bandmode based on bit rate def.eAMRBandMode = isWAMR ? OMX_AUDIO_AMRBandModeWB0 : OMX_AUDIO_AMRBandModeNB0; @@ -2192,8 +2212,60 @@ void OMXCodec::setAMRFormat(bool isWAMR) { } void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate) { + CHECK(numChannels == 1 || numChannels == 2); if (mIsEncoder) { + //////////////// input port //////////////////// setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); + + //////////////// output port //////////////////// + // format + OMX_AUDIO_PARAM_PORTFORMATTYPE format; + format.nPortIndex = kPortIndexOutput; + format.nIndex = 0; + status_t err = OMX_ErrorNone; + while (OMX_ErrorNone == err) { + CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat, + &format, sizeof(format)), OK); + if (format.eEncoding == OMX_AUDIO_CodingAAC) { + break; + } + format.nIndex++; + } + CHECK_EQ(OK, err); + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat, + &format, sizeof(format)), OK); + + // port definition + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexOutput; + CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, + &def, sizeof(def)), OK); + def.format.audio.bFlagErrorConcealment = OMX_TRUE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, + &def, sizeof(def)), OK); + + // profile + OMX_AUDIO_PARAM_AACPROFILETYPE profile; + InitOMXParams(&profile); + profile.nPortIndex = kPortIndexOutput; + CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioAac, + &profile, sizeof(profile)), OK); + profile.nChannels = numChannels; + profile.eChannelMode = (numChannels == 1? + OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo); + profile.nSampleRate = sampleRate; + profile.nBitRate = 96000; // XXX + profile.nAudioBandWidth = 0; + profile.nFrameLength = 0; + profile.nAACtools = OMX_AUDIO_AACToolAll; + profile.nAACERtools = OMX_AUDIO_AACERNone; + profile.eAACProfile = OMX_AUDIO_AACObjectLC; + profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioAac, + &profile, sizeof(profile)), OK); + } else { OMX_AUDIO_PARAM_AACPROFILETYPE profile; InitOMXParams(&profile); @@ -2962,6 +3034,11 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { } else if (audio_def->eEncoding == OMX_AUDIO_CodingAAC) { mOutputFormat->setCString( kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + int32_t numChannels, sampleRate; + inputFormat->findInt32(kKeyChannelCount, &numChannels); + inputFormat->findInt32(kKeySampleRate, &sampleRate); + mOutputFormat->setInt32(kKeyChannelCount, numChannels); + mOutputFormat->setInt32(kKeySampleRate, sampleRate); } else { CHECK(!"Should not be here. Unknown audio encoding."); } diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index c1a010c..5db516e 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -264,6 +264,8 @@ status_t OMXNodeInstance::useBuffer( return UNKNOWN_ERROR; } + CHECK_EQ(header->pAppPrivate, buffer_meta); + *buffer = header; addActiveBuffer(portIndex, *buffer); @@ -294,6 +296,8 @@ status_t OMXNodeInstance::allocateBuffer( return UNKNOWN_ERROR; } + CHECK_EQ(header->pAppPrivate, buffer_meta); + *buffer = header; *buffer_data = header->pBuffer; @@ -325,6 +329,8 @@ status_t OMXNodeInstance::allocateBufferWithBackup( return UNKNOWN_ERROR; } + CHECK_EQ(header->pAppPrivate, buffer_meta); + *buffer = header; addActiveBuffer(portIndex, *buffer); |