diff options
-rw-r--r-- | camera/Android.mk | 5 | ||||
-rw-r--r-- | camera/VendorTagDescriptor.cpp | 164 | ||||
-rw-r--r-- | cmds/stagefright/audioloop.cpp | 170 | ||||
-rw-r--r-- | cmds/stagefright/recordvideo.cpp | 14 | ||||
-rw-r--r-- | include/camera/VendorTagDescriptor.h | 23 | ||||
-rw-r--r-- | include/ndk/NdkMediaCodec.h | 143 | ||||
-rw-r--r-- | include/ndk/NdkMediaExtractor.h | 125 | ||||
-rw-r--r-- | include/ndk/NdkMediaFormat.h | 89 | ||||
-rw-r--r-- | media/ndk/Android.mk | 45 | ||||
-rw-r--r-- | media/ndk/NdkMediaCodec.cpp | 237 | ||||
-rw-r--r-- | media/ndk/NdkMediaExtractor.cpp | 189 | ||||
-rw-r--r-- | media/ndk/NdkMediaFormat.cpp | 198 | ||||
-rw-r--r-- | media/ndk/NdkMediaFormatPriv.h | 44 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 4 | ||||
-rw-r--r-- | services/audiopolicy/AudioPolicyService.cpp | 14 | ||||
-rw-r--r-- | services/audiopolicy/AudioPolicyService.h | 23 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 20 |
17 files changed, 1413 insertions, 94 deletions
diff --git a/camera/Android.mk b/camera/Android.mk index 5774b6f..18800b4 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -52,6 +52,11 @@ LOCAL_C_INCLUDES += \ system/media/camera/include \ system/media/private/camera/include +## Enable asserts for 'eng' builds +ifeq ($(TARGET_BUILD_VARIANT),eng) +LOCAL_CFLAGS += -UNDEBUG +endif + LOCAL_MODULE:= libcamera_client include $(BUILD_SHARED_LIBRARY) diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp index a0a6a51..ba24fcb 100644 --- a/camera/VendorTagDescriptor.cpp +++ b/camera/VendorTagDescriptor.cpp @@ -14,18 +14,20 @@ * limitations under the License. */ -#define LOG_TAG "VenderTagDescriptor" +#define LOG_TAG "VendorTagDescriptor" #include <binder/Parcel.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/Mutex.h> #include <utils/Vector.h> +#include <utils/SortedVector.h> #include <system/camera_metadata.h> #include <camera_metadata_hidden.h> #include "camera/VendorTagDescriptor.h" +#include <stdio.h> #include <string.h> namespace android { @@ -45,7 +47,13 @@ static Mutex sLock; static sp<VendorTagDescriptor> sGlobalVendorTagDescriptor; VendorTagDescriptor::VendorTagDescriptor() {} -VendorTagDescriptor::~VendorTagDescriptor() {} + +VendorTagDescriptor::~VendorTagDescriptor() { + size_t len = mReverseMapping.size(); + for (size_t i = 0; i < len; ++i) { + delete mReverseMapping[i]; + } +} status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vOps, /*out*/ @@ -70,6 +78,9 @@ status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vO sp<VendorTagDescriptor> desc = new VendorTagDescriptor(); desc->mTagCount = tagCount; + SortedVector<String8> sections; + KeyedVector<uint32_t, String8> tagToSectionMap; + for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) { uint32_t tag = tagArray[i]; if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) { @@ -87,7 +98,12 @@ status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vO ALOGE("%s: no section name defined for vendor tag %d.", __FUNCTION__, tag); return BAD_VALUE; } - desc->mTagToSectionMap.add(tag, String8(sectionName)); + + String8 sectionString(sectionName); + + sections.add(sectionString); + tagToSectionMap.add(tag, sectionString); + int tagType = vOps->get_tag_type(vOps, tag); if (tagType < 0 || tagType >= NUM_TYPES) { ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType); @@ -95,6 +111,27 @@ status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vO } desc->mTagToTypeMap.add(tag, tagType); } + + desc->mSections = sections; + + for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) { + uint32_t tag = tagArray[i]; + String8 sectionString = tagToSectionMap.valueFor(tag); + + // Set up tag to section index map + ssize_t index = sections.indexOf(sectionString); + assert(index >= 0); + desc->mTagToSectionMap.add(tag, static_cast<uint32_t>(index)); + + // Set up reverse mapping + ssize_t reverseIndex = -1; + if ((reverseIndex = desc->mReverseMapping.indexOfKey(sectionString)) < 0) { + KeyedVector<String8, uint32_t>* nameMapper = new KeyedVector<String8, uint32_t>(); + reverseIndex = desc->mReverseMapping.add(sectionString, nameMapper); + } + desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag); + } + descriptor = desc; return OK; } @@ -122,8 +159,10 @@ status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel, sp<VendorTagDescriptor> desc = new VendorTagDescriptor(); desc->mTagCount = tagCount; - uint32_t tag; + uint32_t tag, sectionIndex; + uint32_t maxSectionIndex = 0; int32_t tagType; + Vector<uint32_t> allTags; for (int32_t i = 0; i < tagCount; ++i) { if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(&tag))) != OK) { ALOGE("%s: could not read tag id from parcel for index %d", __FUNCTION__, i); @@ -149,15 +188,17 @@ status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel, res = NOT_ENOUGH_DATA; break; } - String8 sectionName = parcel->readString8(); - if (sectionName.isEmpty()) { - ALOGE("%s: parcel section name was NULL for tag %d.", __FUNCTION__, tag); - res = NOT_ENOUGH_DATA; + + if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(§ionIndex))) != OK) { + ALOGE("%s: could not read section index for tag %d.", __FUNCTION__, tag); break; } + maxSectionIndex = (maxSectionIndex >= sectionIndex) ? maxSectionIndex : sectionIndex; + + allTags.add(tag); desc->mTagToNameMap.add(tag, tagName); - desc->mTagToSectionMap.add(tag, sectionName); + desc->mTagToSectionMap.add(tag, sectionIndex); desc->mTagToTypeMap.add(tag, tagType); } @@ -165,6 +206,42 @@ status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel, return res; } + size_t sectionCount; + if (tagCount > 0) { + if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(§ionCount))) != OK) { + ALOGE("%s: could not read section count for.", __FUNCTION__); + return res; + } + if (sectionCount < (maxSectionIndex + 1)) { + ALOGE("%s: Incorrect number of sections defined, received %d, needs %d.", + __FUNCTION__, sectionCount, (maxSectionIndex + 1)); + return BAD_VALUE; + } + assert(desc->mSections.setCapacity(sectionCount) > 0); + for (size_t i = 0; i < sectionCount; ++i) { + String8 sectionName = parcel->readString8(); + if (sectionName.isEmpty()) { + ALOGE("%s: parcel section name was NULL for section %d.", __FUNCTION__, i); + return NOT_ENOUGH_DATA; + } + desc->mSections.add(sectionName); + } + } + + assert(tagCount == allTags.size()); + // Set up reverse mapping + for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) { + uint32_t tag = allTags[i]; + String8 sectionString = desc->mSections[desc->mTagToSectionMap.valueFor(tag)]; + + ssize_t reverseIndex = -1; + if ((reverseIndex = desc->mReverseMapping.indexOfKey(sectionString)) < 0) { + KeyedVector<String8, uint32_t>* nameMapper = new KeyedVector<String8, uint32_t>(); + reverseIndex = desc->mReverseMapping.add(sectionString, nameMapper); + } + desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag); + } + descriptor = desc; return res; } @@ -189,7 +266,7 @@ const char* VendorTagDescriptor::getSectionName(uint32_t tag) const { if (index < 0) { return VENDOR_SECTION_NAME_ERR; } - return mTagToSectionMap.valueAt(index).string(); + return mSections[mTagToSectionMap.valueAt(index)].string(); } const char* VendorTagDescriptor::getTagName(uint32_t tag) const { @@ -220,22 +297,83 @@ status_t VendorTagDescriptor::writeToParcel(Parcel* parcel) const { } size_t size = mTagToNameMap.size(); - uint32_t tag; + uint32_t tag, sectionIndex; int32_t tagType; for (size_t i = 0; i < size; ++i) { tag = mTagToNameMap.keyAt(i); String8 tagName = mTagToNameMap[i]; - String8 sectionName = mTagToSectionMap.valueFor(tag); + sectionIndex = mTagToSectionMap.valueFor(tag); tagType = mTagToTypeMap.valueFor(tag); if ((res = parcel->writeInt32(tag)) != OK) break; if ((res = parcel->writeInt32(tagType)) != OK) break; if ((res = parcel->writeString8(tagName)) != OK) break; - if ((res = parcel->writeString8(sectionName)) != OK) break; + if ((res = parcel->writeInt32(sectionIndex)) != OK) break; + } + + size_t numSections = mSections.size(); + if (numSections > 0) { + if ((res = parcel->writeInt32(numSections)) != OK) return res; + for (size_t i = 0; i < numSections; ++i) { + if ((res = parcel->writeString8(mSections[i])) != OK) return res; + } } return res; } +SortedVector<String8> VendorTagDescriptor::getAllSectionNames() const { + return mSections; +} + +status_t VendorTagDescriptor::lookupTag(String8 name, String8 section, /*out*/uint32_t* tag) const { + ssize_t index = mReverseMapping.indexOfKey(section); + if (index < 0) { + ALOGE("%s: Section '%s' does not exist.", __FUNCTION__, section.string()); + return BAD_VALUE; + } + + ssize_t nameIndex = mReverseMapping[index]->indexOfKey(name); + if (nameIndex < 0) { + ALOGE("%s: Tag name '%s' does not exist.", __FUNCTION__, name.string()); + return BAD_VALUE; + } + + if (tag != NULL) { + *tag = mReverseMapping[index]->valueAt(nameIndex); + } + return OK; +} + +void VendorTagDescriptor::dump(int fd, int verbosity, int indentation) const { + + size_t size = mTagToNameMap.size(); + if (size == 0) { + fdprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n", + indentation, ""); + return; + } + + fdprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n", + indentation, "", size); + for (size_t i = 0; i < size; ++i) { + uint32_t tag = mTagToNameMap.keyAt(i); + + if (verbosity < 1) { + fdprintf(fd, "%*s0x%x\n", indentation + 2, "", tag); + continue; + } + String8 name = mTagToNameMap.valueAt(i); + uint32_t sectionId = mTagToSectionMap.valueFor(tag); + String8 sectionName = mSections[sectionId]; + int type = mTagToTypeMap.valueFor(tag); + const char* typeName = (type >= 0 && type < NUM_TYPES) ? + camera_metadata_type_names[type] : "UNKNOWN"; + fdprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2, + "", tag, name.string(), type, typeName, sectionName.string()); + } + +} + status_t VendorTagDescriptor::setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc) { status_t res = OK; Mutex::Autolock al(sLock); diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp index ed7d6cb..96073f1 100644 --- a/cmds/stagefright/audioloop.cpp +++ b/cmds/stagefright/audioloop.cpp @@ -1,4 +1,18 @@ -#include "SineSource.h" +/* + * Copyright (C) 2014 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 <binder/ProcessState.h> #include <media/mediarecorder.h> @@ -10,41 +24,79 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> - -#include <system/audio.h> +#include "SineSource.h" using namespace android; -int main() { - // We only have an AMR-WB encoder on sholes... - static bool outputWBAMR = false; - static const int32_t kSampleRate = outputWBAMR ? 16000 : 8000; - static const int32_t kNumChannels = 1; +static void usage(const char* name) +{ + fprintf(stderr, "Usage: %s [-d duration] [-m] [-w] [<output-file>]\n", name); + fprintf(stderr, "Encodes either a sine wave or microphone input to AMR format\n"); + fprintf(stderr, " -d duration in seconds, default 5 seconds\n"); + fprintf(stderr, " -m use microphone for input, default sine source\n"); + fprintf(stderr, " -w use AMR wideband (default narrowband)\n"); + fprintf(stderr, " <output-file> output file for AMR encoding," + " if unspecified, decode to speaker.\n"); +} - android::ProcessState::self()->startThreadPool(); +int main(int argc, char* argv[]) +{ + static const int channels = 1; // not permitted to be stereo now + unsigned duration = 5; + bool useMic = false; + bool outputWBAMR = false; + bool playToSpeaker = true; + const char* fileOut = NULL; + int ch; + while ((ch = getopt(argc, argv, "d:mw")) != -1) { + switch (ch) { + case 'd': + duration = atoi(optarg); + break; + case 'm': + useMic = true; + break; + case 'w': + outputWBAMR = true; + break; + default: + usage(argv[0]); + return -1; + } + } + argc -= optind; + argv += optind; + if (argc == 1) { + fileOut = argv[0]; + } + const int32_t kSampleRate = outputWBAMR ? 16000 : 8000; + const int32_t kBitRate = outputWBAMR ? 16000 : 8000; + android::ProcessState::self()->startThreadPool(); OMXClient client; CHECK_EQ(client.connect(), (status_t)OK); - -#if 0 - sp<MediaSource> source = new SineSource(kSampleRate, kNumChannels); -#else - sp<MediaSource> source = new AudioSource( - AUDIO_SOURCE_DEFAULT, - kSampleRate, - audio_channel_in_mask_from_count(kNumChannels)); -#endif + sp<MediaSource> source; + + if (useMic) { + // talk into the appropriate microphone for the duration + source = new AudioSource( + AUDIO_SOURCE_MIC, + kSampleRate, + channels); + } else { + // use a sine source at 500 hz. + source = new SineSource(kSampleRate, channels); + } sp<MetaData> meta = new MetaData; - meta->setCString( kKeyMIMEType, outputWBAMR ? MEDIA_MIMETYPE_AUDIO_AMR_WB - : MEDIA_MIMETYPE_AUDIO_AMR_NB); + : MEDIA_MIMETYPE_AUDIO_AMR_NB); - meta->setInt32(kKeyChannelCount, kNumChannels); + meta->setInt32(kKeyChannelCount, channels); meta->setInt32(kKeySampleRate, kSampleRate); - + meta->setInt32(kKeyBitRate, kBitRate); int32_t maxInputSize; if (source->getFormat()->findInt32(kKeyMaxInputSize, &maxInputSize)) { meta->setInt32(kKeyMaxInputSize, maxInputSize); @@ -55,47 +107,41 @@ int main() { meta, true /* createEncoder */, source); -#if 1 - sp<AMRWriter> writer = new AMRWriter("/sdcard/out.amr"); - writer->addSource(encoder); - writer->start(); - sleep(10); - writer->stop(); -#else - sp<MediaSource> decoder = OMXCodec::Create( - client.interface(), - meta, false /* createEncoder */, - encoder); - -#if 0 - AudioPlayer *player = new AudioPlayer(NULL); - player->setSource(decoder); - - player->start(); - - sleep(10); - - player->stop(); - - delete player; - player = NULL; -#elif 0 - CHECK_EQ(decoder->start(), (status_t)OK); - - MediaBuffer *buffer; - while (decoder->read(&buffer) == OK) { - // do something with buffer - - putchar('.'); - fflush(stdout); - - buffer->release(); - buffer = NULL; + if (fileOut != NULL) { + // target file specified, write encoded AMR output + sp<AMRWriter> writer = new AMRWriter(fileOut); + writer->addSource(encoder); + writer->start(); + sleep(duration); + writer->stop(); + } else { + // otherwise decode to speaker + sp<MediaSource> decoder = OMXCodec::Create( + client.interface(), + meta, false /* createEncoder */, + encoder); + + if (playToSpeaker) { + AudioPlayer *player = new AudioPlayer(NULL); + player->setSource(decoder); + player->start(); + sleep(duration); + source->stop(); // must stop source otherwise delete player will hang + delete player; // there is no player->stop()... + } else { + CHECK_EQ(decoder->start(), (status_t)OK); + MediaBuffer* buffer; + while (decoder->read(&buffer) == OK) { + // do something with buffer (save it eventually?) + // need to stop after some count though... + putchar('.'); + fflush(stdout); + buffer->release(); + buffer = NULL; + } + CHECK_EQ(decoder->stop(), (status_t)OK); + } } - CHECK_EQ(decoder->stop(), (status_t)OK); -#endif -#endif - return 0; } diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp index 1d267f9..9f547c7 100644 --- a/cmds/stagefright/recordvideo.cpp +++ b/cmds/stagefright/recordvideo.cpp @@ -73,7 +73,7 @@ public: return meta; } - virtual status_t start(MetaData *params) { + virtual status_t start(MetaData *params __unused) { mNumFramesOutput = 0; return OK; } @@ -83,7 +83,7 @@ public: } virtual status_t read( - MediaBuffer **buffer, const MediaSource::ReadOptions *options) { + MediaBuffer **buffer, const MediaSource::ReadOptions *options __unused) { if (mNumFramesOutput % 10 == 0) { fprintf(stderr, "."); @@ -100,8 +100,12 @@ public: // We don't care about the contents. we just test video encoder // Also, by skipping the content generation, we can return from // read() much faster. - //char x = (char)((double)rand() / RAND_MAX * 255); - //memset((*buffer)->data(), x, mSize); +#if 0 + // iterate through solid planes of color. + static unsigned char x = 0x60; + memset((*buffer)->data(), x, mSize); + x = x >= 0xa0 ? 0x60 : x + 1; +#endif (*buffer)->set_range(0, mSize); (*buffer)->meta_data()->clear(); (*buffer)->meta_data()->setInt64( @@ -163,7 +167,7 @@ int main(int argc, char **argv) { int level = -1; // Encoder specific default int profile = -1; // Encoder specific default int codec = 0; - char *fileName = "/sdcard/output.mp4"; + const char *fileName = "/sdcard/output.mp4"; bool preferSoftwareCodec = false; android::ProcessState::self()->startThreadPool(); diff --git a/include/camera/VendorTagDescriptor.h b/include/camera/VendorTagDescriptor.h index ea21d31..1758acf 100644 --- a/include/camera/VendorTagDescriptor.h +++ b/include/camera/VendorTagDescriptor.h @@ -16,6 +16,7 @@ #ifndef VENDOR_TAG_DESCRIPTOR_H +#include <utils/Vector.h> #include <utils/KeyedVector.h> #include <utils/String8.h> #include <utils/RefBase.h> @@ -67,6 +68,24 @@ class VendorTagDescriptor /*out*/ Parcel* parcel) const; + /** + * Convenience method to get a vector containing all vendor tag + * sections, or an empty vector if none are defined. + */ + SortedVector<String8> getAllSectionNames() const; + + /** + * Lookup the tag id for a given tag name and section. + * + * Returns OK on success, or a negative error code. + */ + status_t lookupTag(String8 name, String8 section, /*out*/uint32_t* tag) const; + + /** + * Dump the currently configured vendor tags to a file descriptor. + */ + void dump(int fd, int verbosity, int indentation) const; + // Static methods: /** @@ -109,9 +128,11 @@ class VendorTagDescriptor static sp<VendorTagDescriptor> getGlobalVendorTagDescriptor(); protected: VendorTagDescriptor(); + KeyedVector<String8, KeyedVector<String8, uint32_t>*> mReverseMapping; KeyedVector<uint32_t, String8> mTagToNameMap; - KeyedVector<uint32_t, String8> mTagToSectionMap; + KeyedVector<uint32_t, uint32_t> mTagToSectionMap; // Value is offset in mSections KeyedVector<uint32_t, int32_t> mTagToTypeMap; + SortedVector<String8> mSections; // must be int32_t to be compatible with Parcel::writeInt32 int32_t mTagCount; private: diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h new file mode 100644 index 0000000..5067073 --- /dev/null +++ b/include/ndk/NdkMediaCodec.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2014 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. + */ + +/* + * This file defines an NDK API. + * Do not remove methods. + * Do not change method signatures. + * Do not change the value of constants. + * Do not change the size of any of the classes defined in here. + * Do not reference types that are not part of the NDK. + * Do not #include files that aren't part of the NDK. + */ + +#ifndef _NDK_MEDIA_CODEC_H +#define _NDK_MEDIA_CODEC_H + +#include <android/native_window.h> + +#include "NdkMediaFormat.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +struct AMediaCodec; +typedef struct AMediaCodec AMediaCodec; + +struct AMediaCodecBufferInfo { + int32_t offset; + int32_t size; + int64_t presentationTimeUs; + uint32_t flags; +}; +typedef struct AMediaCodecBufferInfo AMediaCodecBufferInfo; + +enum { + AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM = 4, + AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED = -3, + AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED = -2, + AMEDIACODEC_INFO_TRY_AGAIN_LATER = -1 +}; + + +/** + * Create decoder by name. Use this if you know the exact codec you want to use. + */ +AMediaCodec* AMediaCodec_createByCodecName(const char *name); + +/** + * Create codec by mime type. Most applications will use this, specifying a + * mime type obtained from media extractor. + */ +AMediaCodec* AMediaCodec_createByCodecType(const char *mime_type); + +/** + * Create encoder by name. + */ +AMediaCodec* AMediaCodec_createEncoderByName(const char *name); + +/** + * delete the codec and free its resources + */ +int AMediaCodec_delete(AMediaCodec*); + +/** + * Configure the codec. For decoding you would typically get the format from an extractor. + */ +int AMediaCodec_configure(AMediaCodec*, AMediaFormat *format, ANativeWindow* surface); // TODO: other args + +/** + * Start the codec. A codec must be configured before it can be started, and must be started + * before buffers can be sent to it. + */ +int AMediaCodec_start(AMediaCodec*); + +/** + * Stop the codec. + */ +int AMediaCodec_stop(AMediaCodec*); + +/* + * Flush the codec's input and output. All indices previously returned from calls to + * AMediaCodec_dequeueInputBuffer and AMediaCodec_dequeueOutputBuffer become invalid. + */ +int AMediaCodec_flush(AMediaCodec*); + +/** + * Get an input buffer. The specified buffer index must have been previously obtained from + * dequeueInputBuffer, and not yet queued. + */ +uint8_t* AMediaCodec_getInputBuffer(AMediaCodec*, size_t idx, size_t *out_size); + +/** + * Get an output buffer. The specified buffer index must have been previously obtained from + * dequeueOutputBuffer, and not yet queued. + */ +uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec*, size_t idx, size_t *out_size); + +/** + * Get the index of the next available input buffer. An app will typically use this with + * getInputBuffer() to get a pointer to the buffer, then copy the data to be encoded or decoded + * into the buffer before passing it to the codec. + */ +ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec*, int64_t timeoutUs); + +/** + * Send the specified buffer to the codec for processing. + */ +int AMediaCodec_queueInputBuffer(AMediaCodec*, + size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags); + +/** + * Get the index of the next available buffer of processed data. + */ +ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec*, AMediaCodecBufferInfo *info, int64_t timeoutUs); +AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*); + +/** + * Release and optionally render the specified buffer. + */ +int AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render); + + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //_NDK_MEDIA_CODEC_H diff --git a/include/ndk/NdkMediaExtractor.h b/include/ndk/NdkMediaExtractor.h new file mode 100644 index 0000000..a7c32c4 --- /dev/null +++ b/include/ndk/NdkMediaExtractor.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2014 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. + */ + + +/* + * This file defines an NDK API. + * Do not remove methods. + * Do not change method signatures. + * Do not change the value of constants. + * Do not change the size of any of the classes defined in here. + * Do not reference types that are not part of the NDK. + * Do not #include files that aren't part of the NDK. + */ + +#ifndef _NDK_MEDIA_EXTRACTOR_H +#define _NDK_MEDIA_EXTRACTOR_H + +#include <sys/types.h> + +#include "NdkMediaFormat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct AMediaExtractor; +typedef struct AMediaExtractor AMediaExtractor; + + +/** + * Create new media extractor + */ +AMediaExtractor* AMediaExtractor_new(); + +/** + * Delete a previously created media extractor + */ +int AMediaExtractor_delete(AMediaExtractor*); + +/** + * Set the file descriptor from which the extractor will read. + */ +int AMediaExtractor_setDataSourceFd(AMediaExtractor*, int fd, off64_t offset, off64_t length); + +/** + * Set the URI from which the extractor will read. + */ +int AMediaExtractor_setDataSource(AMediaExtractor*, const char *location); // TODO support headers + +/** + * Return the number of tracks in the previously specified media file + */ +int AMediaExtractor_getTrackCount(AMediaExtractor*); + +/** + * Return the format of the specified track. The caller must free the returned format + */ +AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor*, size_t idx); + +/** + * Select the specified track. Subsequent calls to readSampleData, getSampleTrackIndex and + * getSampleTime only retrieve information for the subset of tracks selected. + * Selecting the same track multiple times has no effect, the track is + * only selected once. + */ +int AMediaExtractor_selectTrack(AMediaExtractor*, size_t idx); + +/** + * Unselect the specified track. Subsequent calls to readSampleData, getSampleTrackIndex and + * getSampleTime only retrieve information for the subset of tracks selected.. + */ +int AMediaExtractor_unselectTrack(AMediaExtractor*, size_t idx); + +/** + * Read the current sample. + */ +int AMediaExtractor_readSampleData(AMediaExtractor*, uint8_t *buffer, size_t capacity); + +/** + * Read the current sample's flags. + */ +int AMediaExtractor_getSampleFlags(AMediaExtractor*); // see definitions below + +/** + * Returns the track index the current sample originates from (or -1 + * if no more samples are available) + */ +int AMediaExtractor_getSampleTrackIndex(AMediaExtractor*); + +/** + * Returns the current sample's presentation time in microseconds. + * or -1 if no more samples are available. + */ +int64_t AMediaExtractor_getSampletime(AMediaExtractor*); + +/** + * Advance to the next sample. Returns false if no more sample data + * is available (end of stream). + */ +bool AMediaExtractor_advance(AMediaExtractor*); + +enum { + AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC = 1, + AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2, +}; + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _NDK_MEDIA_EXTRACTOR_H diff --git a/include/ndk/NdkMediaFormat.h b/include/ndk/NdkMediaFormat.h new file mode 100644 index 0000000..16f4348 --- /dev/null +++ b/include/ndk/NdkMediaFormat.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 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. + */ + +/* + * This file defines an NDK API. + * Do not remove methods. + * Do not change method signatures. + * Do not change the value of constants. + * Do not change the size of any of the classes defined in here. + * Do not reference types that are not part of the NDK. + * Do not #include files that aren't part of the NDK. + */ + +#ifndef _NDK_MEDIA_FORMAT_H +#define _NDK_MEDIA_FORMAT_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct AMediaFormat; +typedef struct AMediaFormat AMediaFormat; + +AMediaFormat *AMediaFormat_new(); +int AMediaFormat_delete(AMediaFormat*); + +/** + * Debug string. Caller must free. + */ +const char* AMediaFormat_toString(AMediaFormat*); + +bool AMediaFormat_getInt32(AMediaFormat*, const char *name, int32_t *out); +bool AMediaFormat_getInt64(AMediaFormat*, const char *name, int64_t *out); +bool AMediaFormat_getFloat(AMediaFormat*, const char *name, float *out); +bool AMediaFormat_getDouble(AMediaFormat*, const char *name, double *out); +bool AMediaFormat_getSize(AMediaFormat*, const char *name, size_t *out); +/** + * Caller must free the returned string + */ +bool AMediaFormat_getString(AMediaFormat*, const char *name, const char **out); + +/** + * XXX should these be ints/enums that we look up in a table as needed? + */ +extern const char* AMEDIAFORMAT_KEY_AAC_PROFILE; +extern const char* AMEDIAFORMAT_KEY_BIT_RATE; +extern const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT; +extern const char* AMEDIAFORMAT_KEY_CHANNEL_MASK; +extern const char* AMEDIAFORMAT_KEY_COLOR_FORMAT; +extern const char* AMEDIAFORMAT_KEY_DURATION; +extern const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; +extern const char* AMEDIAFORMAT_KEY_FRAME_RATE; +extern const char* AMEDIAFORMAT_KEY_HEIGHT; +extern const char* AMEDIAFORMAT_KEY_IS_ADTS; +extern const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT; +extern const char* AMEDIAFORMAT_KEY_IS_DEFAULT; +extern const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE; +extern const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL; +extern const char* AMEDIAFORMAT_KEY_LANGUAGE; +extern const char* AMEDIAFORMAT_KEY_MAX_HEIGHT; +extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; +extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH; +extern const char* AMEDIAFORMAT_KEY_MIME; +extern const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP; +extern const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; +extern const char* AMEDIAFORMAT_KEY_SAMPLE_RATE; +extern const char* AMEDIAFORMAT_KEY_WIDTH; +extern const char* AMEDIAFORMAT_KEY_STRIDE; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _NDK_MEDIA_FORMAT_H diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk new file mode 100644 index 0000000..82fb970 --- /dev/null +++ b/media/ndk/Android.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) + +ifneq ($(TARGET_BUILD_PDK), true) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + NdkMediaCodec.cpp \ + NdkMediaExtractor.cpp \ + NdkMediaFormat.cpp \ + +LOCAL_MODULE:= libmediandk + +LOCAL_C_INCLUDES := \ + bionic/libc/private \ + frameworks/base/core/jni \ + frameworks/av/include/ndk + +LOCAL_SHARED_LIBRARIES := \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + liblog \ + libutils \ + libandroid_runtime \ + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp new file mode 100644 index 0000000..5412b9b --- /dev/null +++ b/media/ndk/NdkMediaCodec.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2014 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 "NdkMediaCodec" + +#include "NdkMediaCodec.h" +#include "NdkMediaFormatPriv.h" + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <gui/Surface.h> + +#include <media/ICrypto.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ABuffer.h> + +#include <media/stagefright/MediaCodec.h> +#include <media/stagefright/MediaErrors.h> + +using namespace android; + + +static int translate_error(status_t err) { + if (err == OK) { + return OK; + } else if (err == -EAGAIN) { + return AMEDIACODEC_INFO_TRY_AGAIN_LATER; + } + ALOGE("sf error code: %d", err); + return -1000; +} + + +class CodecHandler: public AHandler { +public: + CodecHandler(sp<android::MediaCodec>); + virtual void onMessageReceived(const sp<AMessage> &msg); +}; + +CodecHandler::CodecHandler(sp<android::MediaCodec>) { + +} + +void CodecHandler::onMessageReceived(const sp<AMessage> &msg) { + ALOGI("handler got message %d", msg->what()); +} + +struct AMediaCodec { + sp<android::MediaCodec> mCodec; + sp<ALooper> mLooper; + sp<CodecHandler> mHandler; +}; + +extern "C" { + +static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) { + AMediaCodec *mData = new AMediaCodec(); + mData->mLooper = new ALooper; + mData->mLooper->setName("NDK MediaCodec_looper"); + status_t ret = mData->mLooper->start( + false, // runOnCallingThread + true, // canCallJava XXX + PRIORITY_FOREGROUND); + ALOGV("looper start: %d", ret); + if (name_is_type) { + mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder); + } else { + mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name); + } + mData->mHandler = new CodecHandler(mData->mCodec); + mData->mLooper->registerHandler(mData->mHandler); + return mData; +} + + +AMediaCodec* AMediaCodec_createByCodecName(const char *name) { + return createAMediaCodec(name, false, false); +} + +AMediaCodec* AMediaCodec_createByCodecType(const char *mime_type) { + return createAMediaCodec(mime_type, true, false); +} + +AMediaCodec* AMediaCodec_createEncoderByName(const char *name) { + return createAMediaCodec(name, false, true); +} + +int AMediaCodec_delete(AMediaCodec *mData) { + if (mData->mCodec != NULL) { + mData->mCodec->release(); + mData->mCodec.clear(); + } + + if (mData->mLooper != NULL) { + mData->mLooper->unregisterHandler(mData->mHandler->id()); + mData->mLooper->stop(); + mData->mLooper.clear(); + } + delete mData; + return OK; +} + +int AMediaCodec_configure(AMediaCodec *mData, AMediaFormat *format, ANativeWindow* window) { + sp<AMessage> nativeFormat; + AMediaFormat_getFormat(format, &nativeFormat); + ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str()); + sp<Surface> surface = NULL; + if (window != NULL) { + surface = (Surface*) window; + } + + return translate_error(mData->mCodec->configure(nativeFormat, surface, NULL, 0)); +} + +int AMediaCodec_start(AMediaCodec *mData) { + return translate_error(mData->mCodec->start()); +} + +int AMediaCodec_stop(AMediaCodec *mData) { + return translate_error(mData->mCodec->stop()); +} + +int AMediaCodec_flush(AMediaCodec *mData) { + return translate_error(mData->mCodec->flush()); +} + +ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) { + size_t idx; + status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs); + if (ret == OK) { + return idx; + } + return translate_error(ret); +} + +uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) { + android::Vector<android::sp<android::ABuffer> > abufs; + if (mData->mCodec->getInputBuffers(&abufs) == 0) { + size_t n = abufs.size(); + if (idx >= n) { + ALOGE("buffer index %d out of range", idx); + return NULL; + } + if (out_size != NULL) { + *out_size = abufs[idx]->capacity(); + } + return abufs[idx]->data(); + } + ALOGE("couldn't get input buffers"); + return NULL; +} + +uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) { + android::Vector<android::sp<android::ABuffer> > abufs; + if (mData->mCodec->getOutputBuffers(&abufs) == 0) { + size_t n = abufs.size(); + if (idx >= n) { + ALOGE("buffer index %d out of range", idx); + return NULL; + } + if (out_size != NULL) { + *out_size = abufs[idx]->capacity(); + } + return abufs[idx]->data(); + } + ALOGE("couldn't get output buffers"); + return NULL; +} + +int AMediaCodec_queueInputBuffer(AMediaCodec *mData, + size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) { + + AString errorMsg; + status_t ret = mData->mCodec->queueInputBuffer(idx, offset, size, time, flags, &errorMsg); + return translate_error(ret); +} + +ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData, + AMediaCodecBufferInfo *info, int64_t timeoutUs) { + size_t idx; + size_t offset; + size_t size; + uint32_t flags; + int64_t presentationTimeUs; + status_t ret = mData->mCodec->dequeueOutputBuffer(&idx, &offset, &size, &presentationTimeUs, + &flags, timeoutUs); + + switch (ret) { + case OK: + info->offset = offset; + info->size = size; + info->flags = flags; + info->presentationTimeUs = presentationTimeUs; + return idx; + case -EAGAIN: + return AMEDIACODEC_INFO_TRY_AGAIN_LATER; + case android::INFO_FORMAT_CHANGED: + return AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED; + case INFO_OUTPUT_BUFFERS_CHANGED: + return AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED; + default: + break; + } + return translate_error(ret); +} + +AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) { + sp<AMessage> format; + mData->mCodec->getOutputFormat(&format); + return AMediaFormat_fromMsg(&format); +} + +int AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) { + if (render) { + return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx)); + } else { + return translate_error(mData->mCodec->releaseOutputBuffer(idx)); + } +} + +} // extern "C" + diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp new file mode 100644 index 0000000..681633a --- /dev/null +++ b/media/ndk/NdkMediaExtractor.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2014 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 "NdkMediaExtractor" + + +#include "NdkMediaExtractor.h" +#include "NdkMediaFormatPriv.h" + + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/NuMediaExtractor.h> +#include <media/IMediaHTTPService.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_util_Binder.h> + +#include <jni.h> + +using namespace android; + +static int translate_error(status_t err) { + if (err == OK) { + return OK; + } + ALOGE("sf error code: %d", err); + return -1000; +} + +struct AMediaExtractor { + sp<NuMediaExtractor> mImpl; + +}; + +extern "C" { + +AMediaExtractor* AMediaExtractor_new() { + ALOGV("ctor"); + AMediaExtractor *mData = new AMediaExtractor(); + mData->mImpl = new NuMediaExtractor(); + return mData; +} + +int AMediaExtractor_delete(AMediaExtractor *mData) { + ALOGV("dtor"); + delete mData; + return OK; +} + +int AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, off64_t length) { + ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + mData->mImpl->setDataSource(fd, offset, length); + return 0; +} + +int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) { + ALOGV("setDataSource(%s)", location); + // TODO: add header support + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jobject service = NULL; + if (env == NULL) { + ALOGE("setDataSource(path) must be called from Java thread"); + env->ExceptionClear(); + return -1; + } + + jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService"); + if (mediahttpclass == NULL) { + ALOGE("can't find MediaHttpService"); + env->ExceptionClear(); + return -1; + } + + jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass, + "createHttpServiceBinderIfNecessary", "(Ljava/lang/String;)Landroid/os/IBinder;"); + if (mediaHttpCreateMethod == NULL) { + ALOGE("can't find method"); + env->ExceptionClear(); + return -1; + } + + jstring jloc = env->NewStringUTF(location); + + service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc); + env->DeleteLocalRef(jloc); + + sp<IMediaHTTPService> httpService; + if (service != NULL) { + sp<IBinder> binder = ibinderForJavaObject(env, service); + httpService = interface_cast<IMediaHTTPService>(binder); + } + + mData->mImpl->setDataSource(httpService, location, NULL); + env->ExceptionClear(); + return 0; +} + +int AMediaExtractor_getTrackCount(AMediaExtractor *mData) { + return mData->mImpl->countTracks(); +} + +AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) { + sp<AMessage> format; + mData->mImpl->getTrackFormat(idx, &format); + return AMediaFormat_fromMsg(&format); +} + +int AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) { + ALOGV("selectTrack(%z)", idx); + return translate_error(mData->mImpl->selectTrack(idx)); +} + +int AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) { + ALOGV("unselectTrack(%z)", idx); + return translate_error(mData->mImpl->unselectTrack(idx)); +} + +bool AMediaExtractor_advance(AMediaExtractor *mData) { + //ALOGV("advance"); + return mData->mImpl->advance(); +} + +int AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) { + //ALOGV("readSampleData"); + sp<ABuffer> tmp = new ABuffer(buffer, capacity); + if (mData->mImpl->readSampleData(tmp) == OK) { + return tmp->size(); + } + return -1; +} + +int AMediaExtractor_getSampleFlags(AMediaExtractor *mData) { + int sampleFlags = 0; + sp<MetaData> meta; + status_t err = mData->mImpl->getSampleMeta(&meta); + if (err != OK) { + return -1; + } + int32_t val; + if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) { + sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_SYNC; + } + + uint32_t type; + const void *data; + size_t size; + if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) { + sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED; + } + return sampleFlags; +} + +int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) { + size_t idx; + if (mData->mImpl->getSampleTrackIndex(&idx) != OK) { + return -1; + } + return idx; +} + +int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) { + int64_t time; + if (mData->mImpl->getSampleTime(&time) != OK) { + return -1; + } + return time; +} + + +} // extern "C" + diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp new file mode 100644 index 0000000..359f37e --- /dev/null +++ b/media/ndk/NdkMediaFormat.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2014 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 "NdkMediaFormat" + + +#include "NdkMediaFormat.h" + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_util_Binder.h> + +#include <jni.h> + +using namespace android; + +struct AMediaFormat { + sp<AMessage> mFormat; +}; + +extern "C" { + +// private functions for conversion to/from AMessage +AMediaFormat* AMediaFormat_fromMsg(void* data) { + ALOGV("private ctor"); + AMediaFormat* mData = new AMediaFormat(); + mData->mFormat = *((sp<AMessage>*)data); + return mData; +} + +void AMediaFormat_getFormat(AMediaFormat* mData, void* dest) { + *((sp<AMessage>*)dest) = mData->mFormat; +} + + +/* + * public function follow + */ +AMediaFormat *AMediaFormat_new() { + ALOGV("ctor"); + sp<AMessage> msg = new AMessage(); + return AMediaFormat_fromMsg(&msg); +} + +int AMediaFormat_delete(AMediaFormat *mData) { + ALOGV("dtor"); + delete mData; + return OK; +} + + +const char* AMediaFormat_toString(AMediaFormat *mData) { + sp<AMessage> f = mData->mFormat; + String8 ret; + int num = f->countEntries(); + for (int i = 0; i < num; i++) { + if (i != 0) { + ret.append(", "); + } + AMessage::Type t; + const char *name = f->getEntryNameAt(i, &t); + ret.append(name); + ret.append(": "); + switch (t) { + case AMessage::kTypeInt32: + { + int32_t val; + f->findInt32(name, &val); + ret.appendFormat("int32(%d)", val); + break; + } + case AMessage::kTypeInt64: + { + int64_t val; + f->findInt64(name, &val); + ret.appendFormat("int64(%lld)", val); + break; + } + case AMessage::kTypeSize: + { + size_t val; + f->findSize(name, &val); + ret.appendFormat("size_t(%d)", val); + break; + } + case AMessage::kTypeFloat: + { + float val; + f->findFloat(name, &val); + ret.appendFormat("float(%f)", val); + break; + } + case AMessage::kTypeDouble: + { + double val; + f->findDouble(name, &val); + ret.appendFormat("double(%f)", val); + break; + } + case AMessage::kTypeString: + { + AString val; + f->findString(name, &val); + ret.appendFormat("string(%s)", val.c_str()); + break; + } + case AMessage::kTypeBuffer: + { + ret.appendFormat("data"); + break; + } + default: + { + ret.appendFormat("unknown(%d)", t); + break; + } + } + } + ret.append("}"); + return strdup(ret.string()); +} + +bool AMediaFormat_getInt32(AMediaFormat* mData, const char *name, int32_t *out) { + return mData->mFormat->findInt32(name, out); +} + +bool AMediaFormat_getInt64(AMediaFormat* mData, const char *name, int64_t *out) { + return mData->mFormat->findInt64(name, out); +} + +bool AMediaFormat_getFloat(AMediaFormat* mData, const char *name, float *out) { + return mData->mFormat->findFloat(name, out); +} + +bool AMediaFormat_getDouble(AMediaFormat* mData, const char *name, double *out) { + return mData->mFormat->findDouble(name, out); +} + +bool AMediaFormat_getSize(AMediaFormat* mData, const char *name, size_t *out) { + return mData->mFormat->findSize(name, out); +} + +bool AMediaFormat_getString(AMediaFormat* mData, const char *name, const char **out) { + AString tmp; + if (mData->mFormat->findString(name, &tmp)) { + *out = strdup(tmp.c_str()); + return true; + } + return false; +} + +const char* AMEDIAFORMAT_KEY_AAC_PROFILE = "aac-profile"; +const char* AMEDIAFORMAT_KEY_BIT_RATE = "bitrate"; +const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT = "channel-count"; +const char* AMEDIAFORMAT_KEY_CHANNEL_MASK = "channel-mask"; +const char* AMEDIAFORMAT_KEY_COLOR_FORMAT = "color-format"; +const char* AMEDIAFORMAT_KEY_DURATION = "durationUs"; +const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level"; +const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate"; +const char* AMEDIAFORMAT_KEY_HEIGHT = "height"; +const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts"; +const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect"; +const char* AMEDIAFORMAT_KEY_IS_DEFAULT = "is-default"; +const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle"; +const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval"; +const char* AMEDIAFORMAT_KEY_LANGUAGE = "language"; +const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height"; +const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size"; +const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width"; +const char* AMEDIAFORMAT_KEY_MIME = "mime"; +const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown"; +const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after"; +const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate"; +const char* AMEDIAFORMAT_KEY_WIDTH = "width"; +const char* AMEDIAFORMAT_KEY_STRIDE = "stride"; + + +} // extern "C" + + diff --git a/media/ndk/NdkMediaFormatPriv.h b/media/ndk/NdkMediaFormatPriv.h new file mode 100644 index 0000000..f67e782 --- /dev/null +++ b/media/ndk/NdkMediaFormatPriv.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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. + */ + +/* + * This file defines an NDK API. + * Do not remove methods. + * Do not change method signatures. + * Do not change the value of constants. + * Do not change the size of any of the classes defined in here. + * Do not reference types that are not part of the NDK. + * Do not #include files that aren't part of the NDK. + */ + +#ifndef _NDK_MEDIA_FORMAT_PRIV_H +#define _NDK_MEDIA_FORMAT_PRIV_H + +#include <NdkMediaFormat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +AMediaFormat* AMediaFormat_fromMsg(void*); +void AMediaFormat_getFormat(AMediaFormat* mData, void* dest); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _NDK_MEDIA_FORMAT_PRIV_H + diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index ae3dd8b..be37436 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1343,7 +1343,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac switch (mType) { case DIRECT: - if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) { + if (audio_is_linear_pcm(format)) { if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) { ALOGE("createTrack_l() Bad parameter: sampleRate %u format %#x, channelMask 0x%08x " "for output %p with format %#x", @@ -1365,7 +1365,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac break; default: - if ((format & AUDIO_FORMAT_MAIN_MASK) != AUDIO_FORMAT_PCM) { + if (!audio_is_linear_pcm(format)) { ALOGE("createTrack_l() Bad parameter: format %#x \"" "for output %p with format %#x", format, mOutput, mFormat); diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp index 4a708a0..918c25c 100644 --- a/services/audiopolicy/AudioPolicyService.cpp +++ b/services/audiopolicy/AudioPolicyService.cpp @@ -270,6 +270,10 @@ AudioPolicyService::AudioCommandThread::~AudioCommandThread() if (!mAudioCommands.isEmpty()) { release_wake_lock(mName.string()); } + for (size_t k=0; k < mAudioCommands.size(); k++) { + delete mAudioCommands[k]->mParam; + delete mAudioCommands[k]; + } mAudioCommands.clear(); delete mpToneGenerator; } @@ -441,7 +445,7 @@ void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::ton ToneData *data = new ToneData(); data->mType = type; data->mStream = stream; - command->mParam = (void *)data; + command->mParam = data; Mutex::Autolock _l(mLock); insertCommand_l(command); ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); @@ -542,7 +546,7 @@ void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t data->mIO = output; data->mStream = stream; data->mSession = session; - command->mParam = (void *)data; + command->mParam = data; Mutex::Autolock _l(mLock); insertCommand_l(command); ALOGV("AudioCommandThread() adding stop output %d", output); @@ -555,7 +559,7 @@ void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handl command->mCommand = RELEASE_OUTPUT; ReleaseOutputData *data = new ReleaseOutputData(); data->mIO = output; - command->mParam = (void *)data; + command->mParam = data; Mutex::Autolock _l(mLock); insertCommand_l(command); ALOGV("AudioCommandThread() adding release output %d", output); @@ -644,6 +648,10 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma for (size_t k = i + 1; k < mAudioCommands.size(); k++) { if (mAudioCommands[k] == removedCommands[j]) { ALOGV("suppressing command: %d", mAudioCommands[k]->mCommand); + // for commands that are not filtered, + // command->mParam is deleted in threadLoop + delete mAudioCommands[k]->mParam; + delete mAudioCommands[k]; mAudioCommands.removeAt(k); break; } diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h index cdc90d0..28e3a4b 100644 --- a/services/audiopolicy/AudioPolicyService.h +++ b/services/audiopolicy/AudioPolicyService.h @@ -198,6 +198,8 @@ private: void insertCommand_l(AudioCommand *command, int delayMs = 0); private: + class AudioCommandData; + // descriptor for requested tone playback event class AudioCommand { @@ -212,41 +214,48 @@ private: Condition mCond; // condition for status return status_t mStatus; // command status bool mWaitStatus; // true if caller is waiting for status - void *mParam; // command parameter (ToneData, VolumeData, ParametersData) + AudioCommandData *mParam; // command specific parameter data + }; + + class AudioCommandData { + public: + virtual ~AudioCommandData() {} + protected: + AudioCommandData() {} }; - class ToneData { + class ToneData : public AudioCommandData { public: ToneGenerator::tone_type mType; // tone type (START_TONE only) audio_stream_type_t mStream; // stream type (START_TONE only) }; - class VolumeData { + class VolumeData : public AudioCommandData { public: audio_stream_type_t mStream; float mVolume; audio_io_handle_t mIO; }; - class ParametersData { + class ParametersData : public AudioCommandData { public: audio_io_handle_t mIO; String8 mKeyValuePairs; }; - class VoiceVolumeData { + class VoiceVolumeData : public AudioCommandData { public: float mVolume; }; - class StopOutputData { + class StopOutputData : public AudioCommandData { public: audio_io_handle_t mIO; audio_stream_type_t mStream; int mSession; }; - class ReleaseOutputData { + class ReleaseOutputData : public AudioCommandData { public: audio_io_handle_t mIO; }; diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 02bca1f..fe1e707 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -575,6 +575,11 @@ status_t CameraService::connectPro( /*out*/ sp<IProCameraUser>& device) { + if (cameraCb == 0) { + ALOGE("%s: Callback must not be null", __FUNCTION__); + return BAD_VALUE; + } + String8 clientName8(clientPackageName); int callingPid = getCallingPid(); @@ -1257,7 +1262,20 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { result.appendFormat("Camera module author: %s\n", mModule->common.author); result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras); + + sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + if (desc == NULL) { + result.appendFormat("Vendor tags left unimplemented.\n"); + } else { + result.appendFormat("Vendor tag definitions:\n"); + } + write(fd, result.string(), result.size()); + + if (desc != NULL) { + desc->dump(fd, /*verbosity*/2, /*indentation*/4); + } + for (int i = 0; i < mNumberOfCameras; i++) { result = String8::format("Camera %d static information:\n", i); camera_info info; @@ -1282,7 +1300,7 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { result.appendFormat(" Device static metadata:\n"); write(fd, result.string(), result.size()); dump_indented_camera_metadata(info.static_camera_characteristics, - fd, 2, 4); + fd, /*verbosity*/2, /*indentation*/4); } else { write(fd, result.string(), result.size()); } |