summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmds/screenrecord/FrameOutput.cpp25
-rw-r--r--cmds/screenrecord/FrameOutput.h2
-rw-r--r--cmds/screenrecord/screenrecord.cpp14
-rw-r--r--include/ndk/NdkMediaCodec.h47
-rw-r--r--include/ndk/NdkMediaCrypto.h55
-rw-r--r--include/ndk/NdkMediaDrm.h470
-rw-r--r--include/ndk/NdkMediaError.h48
-rw-r--r--include/ndk/NdkMediaExtractor.h29
-rw-r--r--media/ndk/Android.mk6
-rw-r--r--media/ndk/NdkMediaCodec.cpp161
-rw-r--r--media/ndk/NdkMediaCrypto.cpp121
-rw-r--r--media/ndk/NdkMediaCryptoPriv.h41
-rw-r--r--media/ndk/NdkMediaDrm.cpp627
-rw-r--r--media/ndk/NdkMediaExtractor.cpp163
-rw-r--r--media/ndk/NdkMediaFormat.cpp62
-rw-r--r--media/ndk/NdkMediaMuxer.cpp8
-rw-r--r--services/audioflinger/AudioFlinger.h1
-rw-r--r--services/audioflinger/FastThreadState.cpp2
-rw-r--r--services/audioflinger/PlaybackTracks.h3
-rw-r--r--services/audioflinger/RecordTracks.h2
-rw-r--r--services/audioflinger/StateQueue.h2
-rw-r--r--services/audioflinger/Threads.cpp2
-rw-r--r--services/audioflinger/TrackBase.h3
-rw-r--r--services/audioflinger/Tracks.cpp12
24 files changed, 1841 insertions, 65 deletions
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
index 06b1f70..4da16bc 100644
--- a/cmds/screenrecord/FrameOutput.cpp
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -87,7 +87,7 @@ status_t FrameOutput::createInputSurface(int width, int height,
return NO_ERROR;
}
-status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) {
+status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec, bool rawFrames) {
Mutex::Autolock _l(mMutex);
ALOGV("copyFrame %ld\n", timeoutUsec);
@@ -152,16 +152,20 @@ status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) {
(endWhenNsec - pixWhenNsec) / 1000000.0);
}
- // Fill out the header.
- size_t headerLen = sizeof(uint32_t) * 5;
size_t rgbDataLen = width * height * kOutBytesPerPixel;
- size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
- uint8_t header[headerLen];
- setValueLE(&header[0], packetLen);
- setValueLE(&header[4], width);
- setValueLE(&header[8], height);
- setValueLE(&header[12], width * kOutBytesPerPixel);
- setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
+
+ if (!rawFrames) {
+ // Fill out the header.
+ size_t headerLen = sizeof(uint32_t) * 5;
+ size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
+ uint8_t header[headerLen];
+ setValueLE(&header[0], packetLen);
+ setValueLE(&header[4], width);
+ setValueLE(&header[8], height);
+ setValueLE(&header[12], width * kOutBytesPerPixel);
+ setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
+ fwrite(header, 1, headerLen, fp);
+ }
// Currently using buffered I/O rather than writev(). Not expecting it
// to make much of a difference, but it might be worth a test for larger
@@ -169,7 +173,6 @@ status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) {
if (kShowTiming) {
startWhenNsec = systemTime(CLOCK_MONOTONIC);
}
- fwrite(header, 1, headerLen, fp);
fwrite(mPixelBuf, 1, rgbDataLen, fp);
fflush(fp);
if (kShowTiming) {
diff --git a/cmds/screenrecord/FrameOutput.h b/cmds/screenrecord/FrameOutput.h
index c1148d0..c49ec3b 100644
--- a/cmds/screenrecord/FrameOutput.h
+++ b/cmds/screenrecord/FrameOutput.h
@@ -45,7 +45,7 @@ public:
// specified number of microseconds.
//
// Returns ETIMEDOUT if the timeout expired before we found a frame.
- status_t copyFrame(FILE* fp, long timeoutUsec);
+ status_t copyFrame(FILE* fp, long timeoutUsec, bool rawFrames);
// Prepare to copy frames. Makes the EGL context used by this object current.
void prepareToCopy() {
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index a17fc51..02ed53a 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -65,7 +65,7 @@ static const char* kMimeTypeAvc = "video/avc";
static bool gVerbose = false; // chatty on stdout
static bool gRotate = false; // rotate 90 degrees
static enum {
- FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES
+ FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
} gOutputFormat = FORMAT_MP4; // data format for output
static bool gSizeSpecified = false; // was size explicitly requested?
static bool gWantInfoScreen = false; // do we want initial info screen?
@@ -563,7 +563,7 @@ static status_t recordScreen(const char* fileName) {
sp<MediaCodec> encoder;
sp<FrameOutput> frameOutput;
sp<IGraphicBufferProducer> encoderInputSurface;
- if (gOutputFormat != FORMAT_FRAMES) {
+ if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) {
err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
if (err != NO_ERROR && !gSizeSpecified) {
@@ -643,7 +643,8 @@ static status_t recordScreen(const char* fileName) {
break;
}
case FORMAT_H264:
- case FORMAT_FRAMES: {
+ case FORMAT_FRAMES:
+ case FORMAT_RAW_FRAMES: {
rawFp = prepareRawOutput(fileName);
if (rawFp == NULL) {
if (encoder != NULL) encoder->release();
@@ -656,7 +657,7 @@ static status_t recordScreen(const char* fileName) {
abort();
}
- if (gOutputFormat == FORMAT_FRAMES) {
+ if (gOutputFormat == FORMAT_FRAMES || gOutputFormat == FORMAT_RAW_FRAMES) {
// TODO: if we want to make this a proper feature, we should output
// an outer header with version info. Right now we never change
// the frame size or format, so we could conceivably just send
@@ -676,7 +677,8 @@ static status_t recordScreen(const char* fileName) {
// stop was requested, but this will do for now. (It almost
// works because wait() wakes when a signal hits, but we
// need to handle the edge cases.)
- err = frameOutput->copyFrame(rawFp, 250000);
+ bool rawFrames = gOutputFormat == FORMAT_RAW_FRAMES;
+ err = frameOutput->copyFrame(rawFp, 250000, rawFrames);
if (err == ETIMEDOUT) {
err = NO_ERROR;
} else if (err != NO_ERROR) {
@@ -950,6 +952,8 @@ int main(int argc, char* const argv[]) {
gOutputFormat = FORMAT_H264;
} else if (strcmp(optarg, "frames") == 0) {
gOutputFormat = FORMAT_FRAMES;
+ } else if (strcmp(optarg, "raw-frames") == 0) {
+ gOutputFormat = FORMAT_RAW_FRAMES;
} else {
fprintf(stderr, "Unknown format '%s'\n", optarg);
return 2;
diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h
index c35c6b3..28d121c 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -29,6 +29,7 @@
#include <android/native_window.h>
+#include "NdkMediaCrypto.h"
#include "NdkMediaFormat.h"
#ifdef __cplusplus
@@ -46,6 +47,7 @@ struct AMediaCodecBufferInfo {
uint32_t flags;
};
typedef struct AMediaCodecBufferInfo AMediaCodecBufferInfo;
+typedef struct AMediaCodecCryptoInfo AMediaCodecCryptoInfo;
enum {
AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM = 4,
@@ -81,8 +83,12 @@ int AMediaCodec_delete(AMediaCodec*);
/**
* Configure the codec. For decoding you would typically get the format from an extractor.
*/
-int AMediaCodec_configure(AMediaCodec*, const AMediaFormat* format,
- ANativeWindow* surface, uint32_t flags); // TODO: other args
+int AMediaCodec_configure(
+ AMediaCodec*,
+ const AMediaFormat* format,
+ ANativeWindow* surface,
+ AMediaCrypto *crypto,
+ uint32_t flags);
/**
* Start the codec. A codec must be configured before it can be started, and must be started
@@ -127,6 +133,12 @@ int AMediaCodec_queueInputBuffer(AMediaCodec*,
size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags);
/**
+ * Send the specified buffer to the codec for processing.
+ */
+int AMediaCodec_queueSecureInputBuffer(AMediaCodec*,
+ size_t idx, off_t offset, AMediaCodecCryptoInfo*, 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);
@@ -138,7 +150,6 @@ AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*);
int AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
-
typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
/**
@@ -150,6 +161,36 @@ typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
int AMediaCodec_setNotificationCallback(AMediaCodec*, OnCodecEvent callback, void *userdata);
+enum {
+ AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
+ AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1
+};
+
+/**
+ * create an AMediaCodecCryptoInfo from scratch. Use this if you need to use custom
+ * crypto info, rather than one obtained from AMediaExtractor.
+ */
+AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
+ int numsubsamples,
+ uint8_t key[16],
+ uint8_t iv[16],
+ uint32_t mode,
+ size_t *clearbytes,
+ size_t *encryptedbytes);
+
+/**
+ * delete an AMediaCodecCryptoInfo created previously with AMediaCodecCryptoInfo_new, or
+ * obtained from AMediaExtractor
+ */
+int AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*);
+
+size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo*);
+int AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo*, uint8_t *dst);
+int AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo*, uint8_t *dst);
+uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*);
+int AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_t *dst);
+int AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/ndk/NdkMediaCrypto.h b/include/ndk/NdkMediaCrypto.h
new file mode 100644
index 0000000..83eaad2
--- /dev/null
+++ b/include/ndk/NdkMediaCrypto.h
@@ -0,0 +1,55 @@
+/*
+ * 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_CRYPTO_H
+#define _NDK_MEDIA_CRYPTO_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AMediaCrypto;
+typedef struct AMediaCrypto AMediaCrypto;
+
+typedef uint8_t AMediaUUID[16];
+
+bool AMediaCrypto_isCryptoSchemeSupported(const AMediaUUID uuid);
+
+bool AMediaCrypto_requiresSecureDecoderComponent(const char *mime);
+
+AMediaCrypto* AMediaCrypto_new(const AMediaUUID uuid, const void *initData, size_t initDataSize);
+
+void AMediaCrypto_delete(AMediaCrypto* crypto);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _NDK_MEDIA_CRYPTO_H
diff --git a/include/ndk/NdkMediaDrm.h b/include/ndk/NdkMediaDrm.h
new file mode 100644
index 0000000..1322a9d
--- /dev/null
+++ b/include/ndk/NdkMediaDrm.h
@@ -0,0 +1,470 @@
+/*
+ * 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_DRM_H
+#define _NDK_MEDIA_DRM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+struct AMediaDrm;
+typedef struct AMediaDrm AMediaDrm;
+
+typedef struct {
+ const uint8_t *ptr;
+ size_t length;
+} AMediaDrmByteArray;
+
+typedef AMediaDrmByteArray AMediaDrmSessionId;
+typedef AMediaDrmByteArray AMediaDrmScope;
+typedef AMediaDrmByteArray AMediaDrmKeySetId;
+typedef AMediaDrmByteArray AMediaDrmSecureStop;
+
+#define MEDIADRM_ERROR_BASE -2000
+
+typedef enum {
+ MEDIADRM_OK = 0,
+ MEDIADRM_NOT_PROVISIONED_ERROR = MEDIADRM_ERROR_BASE - 1,
+ MEDIADRM_RESOURCE_BUSY_ERROR = MEDIADRM_ERROR_BASE - 2,
+ MEDIADRM_DEVICE_REVOKED_ERROR = MEDIADRM_ERROR_BASE - 3,
+ MEDIADRM_SHORT_BUFFER = MEDIADRM_ERROR_BASE - 4,
+ MEDIADRM_INVALID_OBJECT_ERROR = MEDIADRM_ERROR_BASE - 5,
+ MEDIADRM_INVALID_PARAMETER_ERROR = MEDIADRM_ERROR_BASE - 6,
+ MEDIADRM_SESSION_NOT_OPENED_ERROR = MEDIADRM_ERROR_BASE - 7,
+ MEDIADRM_TAMPER_DETECTED_ERROR = MEDIADRM_ERROR_BASE - 8,
+ MEDIADRM_VERIFY_FAILED = MEDIADRM_ERROR_BASE - 9,
+ MEDIADRM_NEED_KEY_ERROR = MEDIADRM_ERROR_BASE - 10,
+ MEDIADRM_LICENSE_EXPIRED_ERROR = MEDIADRM_ERROR_BASE - 11,
+ MEDIADRM_UNKNOWN_ERROR = MEDIADRM_ERROR_BASE - 12,
+} mediadrm_status_t;
+
+typedef enum AMediaDrmEventType {
+ /**
+ * This event type indicates that the app needs to request a certificate from
+ * the provisioning server. The request message data is obtained using
+ * AMediaDrm_getProvisionRequest.
+ */
+ EVENT_PROVISION_REQUIRED = 1,
+
+ /**
+ * This event type indicates that the app needs to request keys from a license
+ * server. The request message data is obtained using AMediaDrm_getKeyRequest.
+ */
+ EVENT_KEY_REQUIRED = 2,
+
+ /**
+ * This event type indicates that the licensed usage duration for keys in a session
+ * has expired. The keys are no longer valid.
+ */
+ EVENT_KEY_EXPIRED = 3,
+
+ /**
+ * This event may indicate some specific vendor-defined condition, see your
+ * DRM provider documentation for details
+ */
+ EVENT_VENDOR_DEFINED = 4
+} AMediaDrmEventType;
+
+typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+ AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize);
+
+
+/**
+ * Query if the given scheme identified by its UUID is supported on this device, and
+ * whether the drm plugin is able to handle the media container format specified by mimeType.
+ *
+ * uuid identifies the universal unique ID of the crypto scheme. uuid must be 16 bytes.
+ * mimeType is the MIME type of the media container, e.g. "video/mp4". If mimeType
+ * is not known or required, it can be provided as NULL.
+ */
+bool AMediaDrm_isCryptoSchemeSupported(const uint8_t *uuid, const char *mimeType);
+
+/**
+ * Create a MediaDrm instance from a UUID
+ * uuid identifies the universal unique ID of the crypto scheme. uuid must be 16 bytes.
+ */
+AMediaDrm* AMediaDrm_createByUUID(const uint8_t *uuid);
+
+/**
+ * Release a MediaDrm object
+ */
+void AMediaDrm_release(AMediaDrm *);
+
+/**
+ * Register a callback to be invoked when an event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+void AMediaDrm_setOnEventListener(AMediaDrm *, AMediaDrmEventListener listener);
+
+/**
+ * Open a new session with the MediaDrm object. A session ID is returned.
+ *
+ * returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed
+ * returns MEDIADRM_RESOURCE_BUSY_ERROR if required resources are in use
+ */
+mediadrm_status_t AMediaDrm_openSession(AMediaDrm *, AMediaDrmSessionId &sessionId);
+
+/**
+ * Close a session on the MediaDrm object that was previously opened
+ * with AMediaDrm_openSession.
+ */
+mediadrm_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId &sessionId);
+
+typedef enum AMediaDrmKeyType {
+ /**
+ * This key request type species that the keys will be for online use, they will
+ * not be saved to the device for subsequent use when the device is not connected
+ * to a network.
+ */
+ KEY_TYPE_STREAMING = 1,
+
+ /**
+ * This key request type specifies that the keys will be for offline use, they
+ * will be saved to the device for use when the device is not connected to a network.
+ */
+ KEY_TYPE_OFFLINE = 2,
+
+ /**
+ * This key request type specifies that previously saved offline keys should be released.
+ */
+ KEY_TYPE_RELEASE = 3
+} AMediaDrmKeyType;
+
+/**
+ * Data type containing {key, value} pair
+ */
+typedef struct AMediaDrmKeyValuePair {
+ const char *mKey;
+ const char *mValue;
+} AMediaDrmKeyValue;
+
+/**
+ * A key request/response exchange occurs between the app and a license server
+ * to obtain or release keys used to decrypt encrypted content.
+ * AMediaDrm_getKeyRequest is used to obtain an opaque key request byte array that
+ * is delivered to the license server. The opaque key request byte array is
+ * returned in KeyRequest.data. The recommended URL to deliver the key request to
+ * is returned in KeyRequest.defaultUrl.
+ *
+ * After the app has received the key request response from the server,
+ * it should deliver to the response to the DRM engine plugin using the method
+ * AMediaDrm_provideKeyResponse.
+ *
+ * scope may be a sessionId or a keySetId, depending on the specified keyType.
+ * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, scope should be set
+ * to the sessionId the keys will be provided to. When the keyType is
+ * KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys being released.
+ * Releasing keys from a device invalidates them for all sessions.
+ *
+ * init container-specific data, its meaning is interpreted based on the mime type
+ * provided in the mimeType parameter. It could contain, for example, the content
+ * ID, key ID or other data obtained from the content metadata that is required in
+ * generating the key request. init may be null when keyType is KEY_TYPE_RELEASE.
+ *
+ * initSize is the number of bytes of initData
+ *
+ * mimeType identifies the mime type of the content.
+ *
+ * keyType specifes the type of the request. The request may be to acquire keys for
+ * streaming or offline content, or to release previously acquired keys, which are
+ * identified by a keySetId.
+ *
+ * optionalParameters are included in the key request message to allow a client
+ * application to provide additional message parameters to the server.
+ *
+ * numOptionalParameters indicates the number of optional parameters provided
+ * by the caller
+ *
+ * On exit:
+ * 1. The keyRequest pointer will reference the opaque key request data. It
+ * will reside in memory owned by the AMediaDrm object, and will remain
+ * accessible until the next call to AMediaDrm_getKeyRequest or until the
+ * MediaDrm object is released.
+ * 2. keyRequestSize will be set to the size of the request
+ *
+ * returns MEDIADRM_NOT_PROVISIONED_ERROR if reprovisioning is needed, due to a
+ * problem with the device certificate.
+*/
+mediadrm_status_t AMediaDrm_getKeyRequest(AMediaDrm *, const AMediaDrmScope &scope,
+ const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
+ const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
+ const uint8_t *&keyRequest, size_t &keyRequestSize);
+
+/**
+ * A key response is received from the license server by the app, then it is
+ * provided to the DRM engine plugin using provideKeyResponse. When the
+ * response is for an offline key request, a keySetId is returned that can be
+ * used to later restore the keys to a new session with AMediaDrm_restoreKeys.
+ * When the response is for a streaming or release request, a null keySetId is
+ * returned.
+ *
+ * scope may be a sessionId or keySetId depending on the type of the
+ * response. Scope should be set to the sessionId when the response is for either
+ * streaming or offline key requests. Scope should be set to the keySetId when
+ * the response is for a release request.
+ *
+ * response points to the opaque response from the server
+ * responseSize should be set to the size of the response in bytes
+ */
+
+mediadrm_status_t AMediaDrm_provideKeyResponse(AMediaDrm *, const AMediaDrmScope &scope,
+ const uint8_t *response, size_t responseSize, AMediaDrmKeySetId &keySetId);
+
+/**
+ * Restore persisted offline keys into a new session. keySetId identifies the
+ * keys to load, obtained from a prior call to AMediaDrm_provideKeyResponse.
+ *
+ * sessionId is the session ID for the DRM session
+ * keySetId identifies the saved key set to restore
+ */
+mediadrm_status_t AMediaDrm_restoreKeys(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+ const AMediaDrmKeySetId &keySetId);
+
+/**
+ * Remove the current keys from a session.
+ *
+ * keySetId identifies keys to remove
+ */
+mediadrm_status_t AMediaDrm_removeKeys(AMediaDrm *, const AMediaDrmSessionId &keySetId);
+
+/**
+ * Request an informative description of the key status for the session. The status is
+ * in the form of {key, value} pairs. Since DRM license policies vary by vendor,
+ * the specific status field names are determined by each DRM vendor. Refer to your
+ * DRM provider documentation for definitions of the field names for a particular
+ * DRM engine plugin.
+ *
+ * On entry, numPairs should be set by the caller to the maximum number of pairs
+ * that can be returned (the size of the array). On exit, numPairs will be set
+ * to the number of entries written to the array. If the number of {key, value} pairs
+ * to be returned is greater than *numPairs, MEDIADRM_SHORT_BUFFER will be returned
+ * and numPairs will be set to the number of pairs available.
+ */
+mediadrm_status_t AMediaDrm_queryKeyStatus(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+ AMediaDrmKeyValue *keyValuePairs, size_t &numPairs);
+
+
+/**
+ * A provision request/response exchange occurs between the app and a provisioning
+ * server to retrieve a device certificate. If provisionining is required, the
+ * EVENT_PROVISION_REQUIRED event will be sent to the event handler.
+ * getProvisionRequest is used to obtain the opaque provision request byte array that
+ * should be delivered to the provisioning server.
+ * On exit:
+ * 1. The provision request data will be referenced by provisionRequest, in
+ * memory owned by the AMediaDrm object. It will remain accessible until the
+ * next call to getProvisionRequest.
+ * 2. provisionRequestSize will be set to the size of the request data.
+ * 3. serverUrl will reference a NULL terminated string containing the URL
+ * the provisioning request should be sent to. It will remain accessible until
+ * the next call to getProvisionRequest.
+ */
+mediadrm_status_t AMediaDrm_getProvisionRequest(AMediaDrm *, const uint8_t *&provisionRequest,
+ size_t &provisionRequestSize, const char *&serverUrl);
+
+
+/**
+ * After a provision response is received by the app, it is provided to the DRM
+ * engine plugin using this method.
+ *
+ * response is the opaque provisioning response byte array to provide to the
+ * DRM engine plugin.
+ * responseSize is the length of the provisioning response in bytes.
+ *
+ * returns MEDIADRM_DEVICE_REVOKED_ERROR if the response indicates that the
+ * server rejected the request
+ */
+mediadrm_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *,
+ const uint8_t *response, size_t responseSize);
+
+
+/**
+ * A means of enforcing limits on the number of concurrent streams per subscriber
+ * across devices is provided via SecureStop. This is achieved by securely
+ * monitoring the lifetime of sessions.
+ *
+ * Information from the server related to the current playback session is written
+ * to persistent storage on the device when each MediaCrypto object is created.
+ *
+ * In the normal case, playback will be completed, the session destroyed and the
+ * Secure Stops will be queried. The app queries secure stops and forwards the
+ * secure stop message to the server which verifies the signature and notifies the
+ * server side database that the session destruction has been confirmed. The persisted
+ * record on the client is only removed after positive confirmation that the server
+ * received the message using releaseSecureStops().
+ *
+ * numSecureStops is set by the caller to the maximum number of secure stops to
+ * return. On exit, *numSecureStops will be set to the number actually returned.
+ * If *numSecureStops is too small for the number of secure stops available,
+ * MEDIADRM_SHORT_BUFFER will be returned and *numSecureStops will be set to the
+ * number required.
+ */
+mediadrm_status_t AMediaDrm_getSecureStops(AMediaDrm *,
+ AMediaDrmSecureStop *secureStops, size_t &numSecureStops);
+
+/**
+ * Process the SecureStop server response message ssRelease. After authenticating
+ * the message, remove the SecureStops identified in the response.
+ *
+ * ssRelease is the server response indicating which secure stops to release
+ */
+mediadrm_status_t AMediaDrm_releaseSecureStops(AMediaDrm *,
+ const AMediaDrmSecureStop &ssRelease);
+
+/**
+ * String property name: identifies the maker of the DRM engine plugin
+ */
+const char *PROPERTY_VENDOR = "vendor";
+
+/**
+ * String property name: identifies the version of the DRM engine plugin
+ */
+const char *PROPERTY_VERSION = "version";
+
+/**
+ * String property name: describes the DRM engine plugin
+ */
+const char *PROPERTY_DESCRIPTION = "description";
+
+/**
+ * String property name: a comma-separated list of cipher and mac algorithms
+ * supported by CryptoSession. The list may be empty if the DRM engine
+ * plugin does not support CryptoSession operations.
+ */
+const char *PROPERTY_ALGORITHMS = "algorithms";
+
+/**
+ * Read a DRM engine plugin String property value, given the property name string.
+ *
+ * propertyName identifies the property to query
+ * On return, propertyValue will be set to point to the property value. The
+ * memory that the value resides in is owned by the NDK MediaDrm API and
+ * will remain valid until the next call to AMediaDrm_getPropertyString.
+ */
+mediadrm_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName,
+ const char *&propertyValue);
+
+/**
+ * Byte array property name: the device unique identifier is established during
+ * device provisioning and provides a means of uniquely identifying each device.
+ */
+const char *PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
+
+/**
+ * Read a DRM engine plugin byte array property value, given the property name string.
+ * On return, *propertyValue will be set to point to the property value. The
+ * memory that the value resides in is owned by the NDK MediaDrm API and
+ * will remain valid until the next call to AMediaDrm_getPropertyByteArray.
+ */
+mediadrm_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *, const char *propertyName,
+ AMediaDrmByteArray &propertyValue);
+
+/**
+ * Set a DRM engine plugin String property value.
+ */
+mediadrm_status_t AMediaDrm_setPropertyString(AMediaDrm *, const char *propertyName,
+ const char *value);
+
+/**
+ * Set a DRM engine plugin byte array property value.
+ */
+mediadrm_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *, const char *propertyName,
+ const uint8_t *value, size_t valueSize);
+
+/**
+ * In addition to supporting decryption of DASH Common Encrypted Media, the
+ * MediaDrm APIs provide the ability to securely deliver session keys from
+ * an operator's session key server to a client device, based on the factory-installed
+ * root of trust, and then perform encrypt, decrypt, sign and verify operations
+ * with the session key on arbitrary user data.
+ *
+ * Operators create session key servers that receive session key requests and provide
+ * encrypted session keys which can be used for general purpose crypto operations.
+ *
+ * Generic encrypt/decrypt/sign/verify methods are based on the established session
+ * keys. These keys are exchanged using the getKeyRequest/provideKeyResponse methods.
+ *
+ * Applications of this capability include securing various types of purchased or
+ * private content, such as applications, books and other media, photos or media
+ * delivery protocols.
+ */
+
+/*
+ * Encrypt the data referenced by input of length dataSize using algorithm specified
+ * by cipherAlgorithm, and write the encrypted result into output. The caller must
+ * ensure that the output buffer is large enough to accept dataSize bytes. The key
+ * to use is identified by the 16 byte keyId. The key must have been loaded into
+ * the session using provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_encrypt(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+ const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+ const uint8_t *input, uint8_t *output, size_t dataSize);
+
+/*
+ * Decrypt the data referenced by input of length dataSize using algorithm specified
+ * by cipherAlgorithm, and write the decrypted result into output. The caller must
+ * ensure that the output buffer is large enough to accept dataSize bytes. The key
+ * to use is identified by the 16 byte keyId. The key must have been loaded into
+ * the session using provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_decrypt(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+ const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+ const uint8_t *input, uint8_t *output, size_t dataSize);
+
+/*
+ * Generate a signature using the specified macAlgorithm over the message data
+ * referenced by message of size messageSize and store the signature in the
+ * buffer referenced signature of max size *signatureSize. If the buffer is not
+ * large enough to hold the signature, MEDIADRM_SHORT_BUFFER is returned and
+ * *signatureSize is set to the buffer size required. The key to use is identified
+ * by the 16 byte keyId. The key must have been loaded into the session using
+ * provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_sign(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+ const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
+ uint8_t *signature, size_t *signatureSize);
+
+/*
+ * Perform a signature verification using the specified macAlgorithm over the message
+ * data referenced by the message parameter of size messageSize. Returns MEDIADRM_OK
+ * if the signature matches, otherwise MEDAIDRM_VERIFY_FAILED is returned. The key to
+ * use is identified by the 16 byte keyId. The key must have been loaded into the
+ * session using provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_verify(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+ const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
+ const uint8_t *signature, size_t signatureSize);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //_NDK_MEDIA_DRM_H
diff --git a/include/ndk/NdkMediaError.h b/include/ndk/NdkMediaError.h
new file mode 100644
index 0000000..b89a10e
--- /dev/null
+++ b/include/ndk/NdkMediaError.h
@@ -0,0 +1,48 @@
+/*
+ * 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_ERROR_H
+#define _NDK_MEDIA_ERROR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ AMEDIAERROR_BASE = -10000,
+
+ AMEDIAERROR_GENERIC = AMEDIAERROR_BASE,
+ AMEDIAERROR_MALFORMED = AMEDIAERROR_BASE - 1,
+ AMEDIAERROR_UNSUPPORTED = AMEDIAERROR_BASE - 2
+};
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _NDK_MEDIA_ERROR_H
diff --git a/include/ndk/NdkMediaExtractor.h b/include/ndk/NdkMediaExtractor.h
index a7c32c4..9e50ec0 100644
--- a/include/ndk/NdkMediaExtractor.h
+++ b/include/ndk/NdkMediaExtractor.h
@@ -30,7 +30,9 @@
#include <sys/types.h>
+#include "NdkMediaCodec.h"
#include "NdkMediaFormat.h"
+#include "NdkMediaCrypto.h"
#ifdef __cplusplus
extern "C" {
@@ -112,6 +114,33 @@ int64_t AMediaExtractor_getSampletime(AMediaExtractor*);
*/
bool AMediaExtractor_advance(AMediaExtractor*);
+
+/**
+ * mapping of crypto scheme uuid to the scheme specific data for that scheme
+ */
+typedef struct PsshEntry {
+ AMediaUUID uuid;
+ size_t datalen;
+ void *data;
+} PsshEntry;
+
+/**
+ * list of crypto schemes and their data
+ */
+typedef struct PsshInfo {
+ size_t numentries;
+ PsshEntry entries[0];
+} PsshInfo;
+
+/**
+ * Get the PSSH info if present.
+ */
+PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor*);
+
+
+AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *);
+
+
enum {
AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC = 1,
AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2,
diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk
index b8dd19e..8f795cd 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -22,9 +22,11 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
NdkMediaCodec.cpp \
+ NdkMediaCrypto.cpp \
NdkMediaExtractor.cpp \
NdkMediaFormat.cpp \
NdkMediaMuxer.cpp \
+ NdkMediaDrm.cpp \
LOCAL_MODULE:= libmediandk
@@ -33,13 +35,17 @@ LOCAL_C_INCLUDES := \
frameworks/base/core/jni \
frameworks/av/include/ndk
+LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
+
LOCAL_SHARED_LIBRARIES := \
+ libbinder \
libmedia \
libstagefright \
libstagefright_foundation \
liblog \
libutils \
libandroid_runtime \
+ libbinder \
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 1789f75..a7c06d5 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -18,13 +18,14 @@
#define LOG_TAG "NdkMediaCodec"
#include "NdkMediaCodec.h"
+#include "NdkMediaError.h"
+#include "NdkMediaCryptoPriv.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>
@@ -42,7 +43,7 @@ static int translate_error(status_t err) {
return AMEDIACODEC_INFO_TRY_AGAIN_LATER;
}
ALOGE("sf error code: %d", err);
- return -1000;
+ return AMEDIAERROR_GENERIC;
}
enum {
@@ -158,19 +159,22 @@ static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool
return mData;
}
-
+EXPORT
AMediaCodec* AMediaCodec_createCodecByName(const char *name) {
return createAMediaCodec(name, false, false);
}
+EXPORT
AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) {
return createAMediaCodec(mime_type, true, false);
}
+EXPORT
AMediaCodec* AMediaCodec_createEncoderByType(const char *name) {
return createAMediaCodec(name, true, true);
}
+EXPORT
int AMediaCodec_delete(AMediaCodec *mData) {
if (mData->mCodec != NULL) {
mData->mCodec->release();
@@ -186,8 +190,13 @@ int AMediaCodec_delete(AMediaCodec *mData) {
return OK;
}
+EXPORT
int AMediaCodec_configure(
- AMediaCodec *mData, const AMediaFormat* format, ANativeWindow* window, uint32_t flags) {
+ AMediaCodec *mData,
+ const AMediaFormat* format,
+ ANativeWindow* window,
+ AMediaCrypto *crypto,
+ uint32_t flags) {
sp<AMessage> nativeFormat;
AMediaFormat_getFormat(format, &nativeFormat);
ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str());
@@ -196,9 +205,11 @@ int AMediaCodec_configure(
surface = (Surface*) window;
}
- return translate_error(mData->mCodec->configure(nativeFormat, surface, NULL, flags));
+ return translate_error(mData->mCodec->configure(nativeFormat, surface,
+ crypto ? crypto->mCrypto : NULL, flags));
}
+EXPORT
int AMediaCodec_start(AMediaCodec *mData) {
status_t ret = mData->mCodec->start();
if (ret != OK) {
@@ -210,6 +221,7 @@ int AMediaCodec_start(AMediaCodec *mData) {
return OK;
}
+EXPORT
int AMediaCodec_stop(AMediaCodec *mData) {
int ret = translate_error(mData->mCodec->stop());
@@ -221,10 +233,12 @@ int AMediaCodec_stop(AMediaCodec *mData) {
return ret;
}
+EXPORT
int AMediaCodec_flush(AMediaCodec *mData) {
return translate_error(mData->mCodec->flush());
}
+EXPORT
ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) {
size_t idx;
status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs);
@@ -235,6 +249,7 @@ ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) {
return translate_error(ret);
}
+EXPORT
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) {
@@ -252,6 +267,7 @@ uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_
return NULL;
}
+EXPORT
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) {
@@ -269,6 +285,7 @@ uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out
return NULL;
}
+EXPORT
int AMediaCodec_queueInputBuffer(AMediaCodec *mData,
size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) {
@@ -277,6 +294,7 @@ int AMediaCodec_queueInputBuffer(AMediaCodec *mData,
return translate_error(ret);
}
+EXPORT
ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData,
AMediaCodecBufferInfo *info, int64_t timeoutUs) {
size_t idx;
@@ -306,12 +324,14 @@ ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData,
return translate_error(ret);
}
+EXPORT
AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) {
sp<AMessage> format;
mData->mCodec->getOutputFormat(&format);
return AMediaFormat_fromMsg(&format);
}
+EXPORT
int AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
if (render) {
return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
@@ -320,12 +340,143 @@ int AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render)
}
}
+EXPORT
int AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
mData->mCallback = callback;
mData->mCallbackUserData = userdata;
return OK;
}
+typedef struct AMediaCodecCryptoInfo {
+ int numsubsamples;
+ uint8_t key[16];
+ uint8_t iv[16];
+ uint32_t mode;
+ size_t *clearbytes;
+ size_t *encryptedbytes;
+} AMediaCodecCryptoInfo;
+
+EXPORT
+int AMediaCodec_queueSecureInputBuffer(
+ AMediaCodec* codec,
+ size_t idx,
+ off_t offset,
+ AMediaCodecCryptoInfo* crypto,
+ uint64_t time,
+ uint32_t flags) {
+
+ CryptoPlugin::SubSample *subSamples = new CryptoPlugin::SubSample[crypto->numsubsamples];
+ for (int i = 0; i < crypto->numsubsamples; i++) {
+ subSamples[i].mNumBytesOfClearData = crypto->clearbytes[i];
+ subSamples[i].mNumBytesOfEncryptedData = crypto->encryptedbytes[i];
+ }
+
+ AString errormsg;
+ status_t err = codec->mCodec->queueSecureInputBuffer(idx,
+ offset,
+ subSamples,
+ crypto->numsubsamples,
+ crypto->key,
+ crypto->iv,
+ (CryptoPlugin::Mode) crypto->mode,
+ time,
+ flags,
+ &errormsg);
+ if (err != 0) {
+ ALOGE("queSecureInputBuffer: %s", errormsg.c_str());
+ }
+ delete [] subSamples;
+ return translate_error(err);
+}
+
+
+
+EXPORT
+AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
+ int numsubsamples,
+ uint8_t key[16],
+ uint8_t iv[16],
+ uint32_t mode,
+ size_t *clearbytes,
+ size_t *encryptedbytes) {
+
+ // size needed to store all the crypto data
+ size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
+ AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
+ if (!ret) {
+ ALOGE("couldn't allocate %d bytes", cryptosize);
+ return NULL;
+ }
+ ret->numsubsamples = numsubsamples;
+ memcpy(ret->key, key, 16);
+ memcpy(ret->iv, iv, 16);
+ ret->mode = mode;
+
+ // clearbytes and encryptedbytes point at the actual data, which follows
+ ret->clearbytes = (size_t*) (ret + 1); // point immediately after the struct
+ ret->encryptedbytes = ret->clearbytes + numsubsamples; // point after the clear sizes
+
+ memcpy(ret->clearbytes, clearbytes, numsubsamples * sizeof(size_t));
+ memcpy(ret->encryptedbytes, encryptedbytes, numsubsamples * sizeof(size_t));
+
+ return ret;
+}
+
+
+EXPORT
+int AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
+ free(info);
+ return OK;
+}
+
+EXPORT
+size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo* ci) {
+ return ci->numsubsamples;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
+ if (!dst || !ci) {
+ return AMEDIAERROR_UNSUPPORTED;
+ }
+ memcpy(dst, ci->key, 16);
+ return OK;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
+ if (!dst || !ci) {
+ return AMEDIAERROR_UNSUPPORTED;
+ }
+ memcpy(dst, ci->iv, 16);
+ return OK;
+}
+
+EXPORT
+uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
+ if (!ci) {
+ return AMEDIAERROR_UNSUPPORTED;
+ }
+ return ci->mode;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
+ if (!dst || !ci) {
+ return AMEDIAERROR_UNSUPPORTED;
+ }
+ memcpy(dst, ci->clearbytes, sizeof(size_t) * ci->numsubsamples);
+ return OK;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
+ if (!dst || !ci) {
+ return AMEDIAERROR_UNSUPPORTED;
+ }
+ memcpy(dst, ci->encryptedbytes, sizeof(size_t) * ci->numsubsamples);
+ return OK;
+}
} // extern "C"
diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp
new file mode 100644
index 0000000..c686273
--- /dev/null
+++ b/media/ndk/NdkMediaCrypto.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 "NdkMediaCrypto"
+
+
+#include "NdkMediaCrypto.h"
+#include "NdkMediaCodec.h"
+#include "NdkMediaFormatPriv.h"
+
+
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <binder/IServiceManager.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.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;
+}
+
+
+static sp<ICrypto> makeCrypto() {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ sp<IBinder> binder =
+ sm->getService(String16("media.player"));
+
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ if (service == NULL) {
+ return NULL;
+ }
+
+ sp<ICrypto> crypto = service->makeCrypto();
+
+ if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) {
+ return NULL;
+ }
+
+ return crypto;
+}
+
+struct AMediaCrypto {
+ sp<ICrypto> mCrypto;
+};
+
+
+extern "C" {
+
+
+EXPORT
+bool AMediaCrypto_isCryptoSchemeSupported(const AMediaUUID uuid) {
+ sp<ICrypto> crypto = makeCrypto();
+ if (crypto == NULL) {
+ return false;
+ }
+ return crypto->isCryptoSchemeSupported(uuid);
+}
+
+EXPORT
+bool AMediaCrypto_requiresSecureDecoderComponent(const char *mime) {
+ sp<ICrypto> crypto = makeCrypto();
+ if (crypto == NULL) {
+ return false;
+ }
+ return crypto->requiresSecureDecoderComponent(mime);
+}
+
+EXPORT
+AMediaCrypto* AMediaCrypto_new(const AMediaUUID uuid, const void *data, size_t datasize) {
+
+ sp<ICrypto> tmp = makeCrypto();
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ if (tmp->createPlugin(uuid, data, datasize) != 0) {
+ return NULL;
+ }
+
+ AMediaCrypto *crypto = new AMediaCrypto();
+ crypto->mCrypto = tmp;
+
+ return crypto;
+}
+
+EXPORT
+void AMediaCrypto_delete(AMediaCrypto* crypto) {
+ delete crypto;
+}
+
+
+
+} // extern "C"
+
diff --git a/media/ndk/NdkMediaCryptoPriv.h b/media/ndk/NdkMediaCryptoPriv.h
new file mode 100644
index 0000000..14ea928
--- /dev/null
+++ b/media/ndk/NdkMediaCryptoPriv.h
@@ -0,0 +1,41 @@
+/*
+ * 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_CRYPTO_PRIV_H
+#define _NDK_MEDIA_CRYPTO_PRIV_H
+
+#include <sys/types.h>
+#include <utils/StrongPointer.h>
+#include <media/ICrypto.h>
+
+using namespace android;
+
+struct AMediaCrypto {
+ sp<ICrypto> mCrypto;
+};
+
+#endif // _NDK_MEDIA_CRYPTO_PRIV_H
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
new file mode 100644
index 0000000..5e50418
--- /dev/null
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -0,0 +1,627 @@
+/*
+ * 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 "NdkMediaDrm"
+
+#include "NdkMediaDrm.h"
+
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <gui/Surface.h>
+
+#include <media/IDrm.h>
+#include <media/IDrmClient.h>
+#include <media/stagefright/MediaErrors.h>
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <ndk/NdkMediaCrypto.h>
+
+
+using namespace android;
+
+typedef Vector<uint8_t> idvec_t;
+
+struct AMediaDrm {
+ sp<IDrm> mDrm;
+ sp<IDrmClient> mDrmClient;
+ AMediaDrmEventListener mListener;
+ List<idvec_t> mIds;
+ KeyedVector<String8, String8> mQueryResults;
+ Vector<uint8_t> mKeyRequest;
+ Vector<uint8_t> mProvisionRequest;
+ String8 mProvisionUrl;
+ String8 mPropertyString;
+ Vector<uint8_t> mPropertyByteArray;
+ List<Vector<uint8_t> > mSecureStops;
+};
+
+extern "C" {
+
+static mediadrm_status_t translateStatus(status_t status) {
+ mediadrm_status_t result = MEDIADRM_UNKNOWN_ERROR;
+ switch (status) {
+ case OK:
+ result = MEDIADRM_OK;
+ break;
+ case android::ERROR_DRM_NOT_PROVISIONED:
+ result = MEDIADRM_NOT_PROVISIONED_ERROR;
+ break;
+ case android::ERROR_DRM_RESOURCE_BUSY:
+ result = MEDIADRM_RESOURCE_BUSY_ERROR;
+ break;
+ case android::ERROR_DRM_DEVICE_REVOKED:
+ result = MEDIADRM_DEVICE_REVOKED_ERROR;
+ break;
+ case android::ERROR_DRM_CANNOT_HANDLE:
+ result = MEDIADRM_INVALID_PARAMETER_ERROR;
+ break;
+ case android::ERROR_DRM_TAMPER_DETECTED:
+ result = MEDIADRM_TAMPER_DETECTED_ERROR;
+ break;
+ case android::ERROR_DRM_SESSION_NOT_OPENED:
+ result = MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ break;
+ case android::ERROR_DRM_NO_LICENSE:
+ result = MEDIADRM_NEED_KEY_ERROR;
+ break;
+ case android::ERROR_DRM_LICENSE_EXPIRED:
+ result = MEDIADRM_LICENSE_EXPIRED_ERROR;
+ break;
+ default:
+ result = MEDIADRM_UNKNOWN_ERROR;
+ break;
+ }
+ return result;
+}
+
+static sp<IDrm> CreateDrm() {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ sp<IBinder> binder =
+ sm->getService(String16("media.player"));
+
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ if (service == NULL) {
+ return NULL;
+ }
+
+ sp<IDrm> drm = service->makeDrm();
+
+ if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
+ return NULL;
+ }
+
+ return drm;
+}
+
+
+static sp<IDrm> CreateDrmFromUUID(const AMediaUUID uuid) {
+ sp<IDrm> drm = CreateDrm();
+
+ if (drm == NULL) {
+ return NULL;
+ }
+
+ status_t err = drm->createPlugin(uuid);
+
+ if (err != OK) {
+ return NULL;
+ }
+
+ return drm;
+}
+
+EXPORT
+bool AMediaDrm_isCryptoSchemeSupported(const AMediaUUID uuid, const char *mimeType) {
+ sp<IDrm> drm = CreateDrm();
+
+ if (drm == NULL) {
+ return false;
+ }
+
+ String8 mimeStr = mimeType ? String8(mimeType) : String8("");
+ return drm->isCryptoSchemeSupported(uuid, mimeStr);
+}
+
+EXPORT
+AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) {
+ AMediaDrm *mObj = new AMediaDrm();
+ mObj->mDrm = CreateDrmFromUUID(uuid);
+ return mObj;
+}
+
+EXPORT
+void AMediaDrm_release(AMediaDrm *mObj) {
+ if (mObj->mDrm != NULL) {
+ mObj->mDrm->setListener(NULL);
+ mObj->mDrm->destroyPlugin();
+ mObj->mDrm.clear();
+ }
+ delete mObj;
+}
+
+#if 0
+void AMediaDrm_setOnEventListener(AMediaDrm *mObj, AMediaDrmEventListener listener) {
+ mObj->mListener = listener;
+}
+#endif
+
+
+static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
+ iter = mObj->mIds.begin();
+ while (iter != mObj->mIds.end()) {
+ if (iter->array() == id.ptr && iter->size() == id.length) {
+ return true;
+ }
+ }
+ return false;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId &sessionId) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ Vector<uint8_t> session;
+ status_t status = mObj->mDrm->openSession(session);
+ if (status == OK) {
+ mObj->mIds.push_front(session);
+ List<idvec_t>::iterator iter = mObj->mIds.begin();
+ sessionId.ptr = iter->array();
+ sessionId.length = iter->size();
+ }
+ return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_closeSession(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, sessionId, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+ mObj->mDrm->closeSession(*iter);
+ mObj->mIds.erase(iter);
+ return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getKeyRequest(AMediaDrm *mObj, const AMediaDrmScope &scope,
+ const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
+ const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
+ const uint8_t *&keyRequest, size_t &keyRequestSize) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ if (!mimeType) {
+ return MEDIADRM_INVALID_PARAMETER_ERROR;
+ }
+
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, scope, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+
+ Vector<uint8_t> mdInit;
+ mdInit.appendArray(init, initSize);
+ DrmPlugin::KeyType mdKeyType;
+ switch (keyType) {
+ case KEY_TYPE_STREAMING:
+ mdKeyType = DrmPlugin::kKeyType_Streaming;
+ break;
+ case KEY_TYPE_OFFLINE:
+ mdKeyType = DrmPlugin::kKeyType_Offline;
+ break;
+ case KEY_TYPE_RELEASE:
+ mdKeyType = DrmPlugin::kKeyType_Release;
+ break;
+ default:
+ return MEDIADRM_INVALID_PARAMETER_ERROR;
+ }
+ KeyedVector<String8, String8> mdOptionalParameters;
+ for (size_t i = 0; i < numOptionalParameters; i++) {
+ mdOptionalParameters.add(String8(optionalParameters[i].mKey),
+ String8(optionalParameters[i].mValue));
+ }
+ String8 defaultUrl;
+ status_t status = mObj->mDrm->getKeyRequest(*iter, mdInit, String8(mimeType),
+ mdKeyType, mdOptionalParameters, mObj->mKeyRequest, defaultUrl);
+ if (status != OK) {
+ return translateStatus(status);
+ } else {
+ keyRequest = mObj->mKeyRequest.array();
+ keyRequestSize = mObj->mKeyRequest.size();
+ }
+ return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_provideKeyResponse(AMediaDrm *mObj, const AMediaDrmScope &scope,
+ const uint8_t *response, size_t responseSize, AMediaDrmKeySetId &keySetId) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ if (!response || !responseSize) {
+ return MEDIADRM_INVALID_PARAMETER_ERROR;
+ }
+
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, scope, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+ Vector<uint8_t> mdResponse;
+ mdResponse.appendArray(response, responseSize);
+
+ Vector<uint8_t> mdKeySetId;
+ status_t status = mObj->mDrm->provideKeyResponse(*iter, mdResponse, mdKeySetId);
+ if (status == OK) {
+ mObj->mIds.push_front(mdKeySetId);
+ List<idvec_t>::iterator iter = mObj->mIds.begin();
+ keySetId.ptr = iter->array();
+ keySetId.length = iter->size();
+ } else {
+ keySetId.ptr = NULL;
+ keySetId.length = 0;
+ }
+ return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_restoreKeys(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+ const AMediaDrmKeySetId &keySetId) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, sessionId, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+ Vector<uint8_t> keySet;
+ keySet.appendArray(keySetId.ptr, keySetId.length);
+ return translateStatus(mObj->mDrm->restoreKeys(*iter, keySet));
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_removeKeys(AMediaDrm *mObj, const AMediaDrmSessionId &keySetId) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ List<idvec_t>::iterator iter;
+ status_t status;
+ if (!findId(mObj, keySetId, iter)) {
+ Vector<uint8_t> keySet;
+ keySet.appendArray(keySetId.ptr, keySetId.length);
+ status = mObj->mDrm->removeKeys(keySet);
+ } else {
+ status = mObj->mDrm->removeKeys(*iter);
+ mObj->mIds.erase(iter);
+ }
+ return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_queryKeyStatus(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+ AMediaDrmKeyValue *keyValuePairs, size_t &numPairs) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, sessionId, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+
+ status_t status = mObj->mDrm->queryKeyStatus(*iter, mObj->mQueryResults);
+ if (status != OK) {
+ numPairs = 0;
+ return translateStatus(status);
+ }
+
+ if (mObj->mQueryResults.size() > numPairs) {
+ numPairs = mObj->mQueryResults.size();
+ return MEDIADRM_SHORT_BUFFER;
+ }
+
+ for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
+ keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
+ keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
+ }
+ numPairs = mObj->mQueryResults.size();
+ return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getProvisionRequest(AMediaDrm *mObj, const uint8_t *&provisionRequest,
+ size_t &provisionRequestSize, const char *&serverUrl) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ if (!provisionRequestSize || !serverUrl) {
+ return MEDIADRM_INVALID_PARAMETER_ERROR;
+ }
+
+ status_t status = mObj->mDrm->getProvisionRequest(String8(""), String8(""),
+ mObj->mProvisionRequest, mObj->mProvisionUrl);
+ if (status != OK) {
+ return translateStatus(status);
+ } else {
+ provisionRequest = mObj->mProvisionRequest.array();
+ provisionRequestSize = mObj->mProvisionRequest.size();
+ serverUrl = mObj->mProvisionUrl.string();
+ }
+ return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *mObj,
+ const uint8_t *response, size_t responseSize) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ if (!response || !responseSize) {
+ return MEDIADRM_INVALID_PARAMETER_ERROR;
+ }
+
+ Vector<uint8_t> mdResponse;
+ mdResponse.appendArray(response, responseSize);
+
+ Vector<uint8_t> unused;
+ return translateStatus(mObj->mDrm->provideProvisionResponse(mdResponse, unused, unused));
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getSecureStops(AMediaDrm *mObj,
+ AMediaDrmSecureStop *secureStops, size_t &numSecureStops) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ status_t status = mObj->mDrm->getSecureStops(mObj->mSecureStops);
+ if (status != OK) {
+ numSecureStops = 0;
+ return translateStatus(status);
+ }
+ if (numSecureStops < mObj->mSecureStops.size()) {
+ return MEDIADRM_SHORT_BUFFER;
+ }
+ List<Vector<uint8_t> >::iterator iter = mObj->mSecureStops.begin();
+ size_t i = 0;
+ while (iter != mObj->mSecureStops.end()) {
+ secureStops[i].ptr = iter->array();
+ secureStops[i].length = iter->size();
+ ++iter;
+ ++i;
+ }
+ numSecureStops = mObj->mSecureStops.size();
+ return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_releaseSecureStops(AMediaDrm *mObj,
+ const AMediaDrmSecureStop &ssRelease) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+
+ Vector<uint8_t> release;
+ release.appendArray(ssRelease.ptr, ssRelease.length);
+ return translateStatus(mObj->mDrm->releaseSecureStops(release));
+}
+
+
+EXPORT
+mediadrm_status_t AMediaDrm_getPropertyString(AMediaDrm *mObj, const char *propertyName,
+ const char *&propertyValue) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+
+ status_t status = mObj->mDrm->getPropertyString(String8(propertyName),
+ mObj->mPropertyString);
+
+ if (status == OK) {
+ propertyValue = mObj->mPropertyString.string();
+ } else {
+ propertyValue = NULL;
+ }
+ return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *mObj,
+ const char *propertyName, AMediaDrmByteArray &propertyValue) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+
+ status_t status = mObj->mDrm->getPropertyByteArray(String8(propertyName),
+ mObj->mPropertyByteArray);
+
+ if (status == OK) {
+ propertyValue.ptr = mObj->mPropertyByteArray.array();
+ propertyValue.length = mObj->mPropertyByteArray.size();
+ } else {
+ propertyValue.ptr = NULL;
+ propertyValue.length = 0;
+ }
+ return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_setPropertyString(AMediaDrm *mObj,
+ const char *propertyName, const char *value) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+
+ return translateStatus(mObj->mDrm->setPropertyString(String8(propertyName),
+ String8(value)));
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *mObj,
+ const char *propertyName, const uint8_t *value, size_t valueSize) {
+
+ Vector<uint8_t> byteArray;
+ byteArray.appendArray(value, valueSize);
+
+ return translateStatus(mObj->mDrm->getPropertyByteArray(String8(propertyName),
+ byteArray));
+}
+
+
+static mediadrm_status_t encrypt_decrypt_common(AMediaDrm *mObj,
+ const AMediaDrmSessionId &sessionId,
+ const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+ const uint8_t *input, uint8_t *output, size_t dataSize, bool encrypt) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, sessionId, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+
+ status_t status = mObj->mDrm->setCipherAlgorithm(*iter, String8(cipherAlgorithm));
+ if (status != OK) {
+ return translateStatus(status);
+ }
+
+ Vector<uint8_t> keyIdVec;
+ const size_t kKeyIdSize = 16;
+ keyIdVec.appendArray(keyId, kKeyIdSize);
+
+ Vector<uint8_t> inputVec;
+ inputVec.appendArray(input, dataSize);
+
+ Vector<uint8_t> ivVec;
+ const size_t kIvSize = 16;
+ ivVec.appendArray(iv, kIvSize);
+
+ Vector<uint8_t> outputVec;
+ if (encrypt) {
+ status_t status = mObj->mDrm->encrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
+ } else {
+ status_t status = mObj->mDrm->decrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
+ }
+ if (status == OK) {
+ memcpy(output, outputVec.array(), outputVec.size());
+ }
+ return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_encrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+ const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+ const uint8_t *input, uint8_t *output, size_t dataSize) {
+ return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
+ input, output, dataSize, true);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_decrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+ const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+ const uint8_t *input, uint8_t *output, size_t dataSize) {
+ return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
+ input, output, dataSize, false);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_sign(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+ const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
+ uint8_t *signature, size_t *signatureSize) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, sessionId, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+
+ status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
+ if (status != OK) {
+ return translateStatus(status);
+ }
+
+ Vector<uint8_t> keyIdVec;
+ const size_t kKeyIdSize = 16;
+ keyIdVec.appendArray(keyId, kKeyIdSize);
+
+ Vector<uint8_t> messageVec;
+ messageVec.appendArray(message, messageSize);
+
+ Vector<uint8_t> signatureVec;
+ status = mObj->mDrm->sign(*iter, keyIdVec, messageVec, signatureVec);
+ if (signatureVec.size() > *signatureSize) {
+ return MEDIADRM_SHORT_BUFFER;
+ }
+ if (status == OK) {
+ memcpy(signature, signatureVec.array(), signatureVec.size());
+ }
+ return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_verify(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+ const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
+ const uint8_t *signature, size_t signatureSize) {
+
+ if (!mObj || mObj->mDrm == NULL) {
+ return MEDIADRM_INVALID_OBJECT_ERROR;
+ }
+ List<idvec_t>::iterator iter;
+ if (!findId(mObj, sessionId, iter)) {
+ return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+ }
+
+ status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
+ if (status != OK) {
+ return translateStatus(status);
+ }
+
+ Vector<uint8_t> keyIdVec;
+ const size_t kKeyIdSize = 16;
+ keyIdVec.appendArray(keyId, kKeyIdSize);
+
+ Vector<uint8_t> messageVec;
+ messageVec.appendArray(message, messageSize);
+
+ Vector<uint8_t> signatureVec;
+ signatureVec.appendArray(signature, signatureSize);
+
+ bool match;
+ status = mObj->mDrm->verify(*iter, keyIdVec, messageVec, signatureVec, match);
+ if (status == OK) {
+ return match ? MEDIADRM_OK : MEDIADRM_VERIFY_FAILED;
+ }
+ return translateStatus(status);
+}
+
+} // extern "C"
+
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 681633a..e23adf3 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -18,12 +18,14 @@
#define LOG_TAG "NdkMediaExtractor"
+#include "NdkMediaError.h"
#include "NdkMediaExtractor.h"
#include "NdkMediaFormatPriv.h"
#include <utils/Log.h>
#include <utils/StrongPointer.h>
+#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MetaData.h>
@@ -41,16 +43,18 @@ static int translate_error(status_t err) {
return OK;
}
ALOGE("sf error code: %d", err);
- return -1000;
+ return AMEDIAERROR_GENERIC;
}
struct AMediaExtractor {
sp<NuMediaExtractor> mImpl;
+ sp<ABuffer> mPsshBuf;
};
extern "C" {
+EXPORT
AMediaExtractor* AMediaExtractor_new() {
ALOGV("ctor");
AMediaExtractor *mData = new AMediaExtractor();
@@ -58,18 +62,21 @@ AMediaExtractor* AMediaExtractor_new() {
return mData;
}
+EXPORT
int AMediaExtractor_delete(AMediaExtractor *mData) {
ALOGV("dtor");
delete mData;
return OK;
}
+EXPORT
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;
}
+EXPORT
int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
ALOGV("setDataSource(%s)", location);
// TODO: add header support
@@ -79,14 +86,14 @@ int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location)
if (env == NULL) {
ALOGE("setDataSource(path) must be called from Java thread");
env->ExceptionClear();
- return -1;
+ return AMEDIAERROR_UNSUPPORTED;
}
jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService");
if (mediahttpclass == NULL) {
ALOGE("can't find MediaHttpService");
env->ExceptionClear();
- return -1;
+ return AMEDIAERROR_UNSUPPORTED;
}
jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass,
@@ -94,7 +101,7 @@ int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location)
if (mediaHttpCreateMethod == NULL) {
ALOGE("can't find method");
env->ExceptionClear();
- return -1;
+ return AMEDIAERROR_UNSUPPORTED;
}
jstring jloc = env->NewStringUTF(location);
@@ -110,34 +117,40 @@ int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location)
mData->mImpl->setDataSource(httpService, location, NULL);
env->ExceptionClear();
- return 0;
+ return OK;
}
+EXPORT
int AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
return mData->mImpl->countTracks();
}
+EXPORT
AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) {
sp<AMessage> format;
mData->mImpl->getTrackFormat(idx, &format);
return AMediaFormat_fromMsg(&format);
}
+EXPORT
int AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) {
ALOGV("selectTrack(%z)", idx);
return translate_error(mData->mImpl->selectTrack(idx));
}
+EXPORT
int AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) {
ALOGV("unselectTrack(%z)", idx);
return translate_error(mData->mImpl->unselectTrack(idx));
}
+EXPORT
bool AMediaExtractor_advance(AMediaExtractor *mData) {
//ALOGV("advance");
return mData->mImpl->advance();
}
+EXPORT
int AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) {
//ALOGV("readSampleData");
sp<ABuffer> tmp = new ABuffer(buffer, capacity);
@@ -147,6 +160,7 @@ int AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size
return -1;
}
+EXPORT
int AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
int sampleFlags = 0;
sp<MetaData> meta;
@@ -168,6 +182,7 @@ int AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
return sampleFlags;
}
+EXPORT
int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) {
size_t idx;
if (mData->mImpl->getSampleTrackIndex(&idx) != OK) {
@@ -176,6 +191,7 @@ int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) {
return idx;
}
+EXPORT
int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) {
int64_t time;
if (mData->mImpl->getSampleTime(&time) != OK) {
@@ -184,6 +200,143 @@ int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) {
return time;
}
+EXPORT
+PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor *ex) {
+
+ if (ex->mPsshBuf != NULL) {
+ return (PsshInfo*) ex->mPsshBuf->data();
+ }
+
+ sp<AMessage> format;
+ ex->mImpl->getFileFormat(&format);
+ sp<ABuffer> buffer;
+ if(!format->findBuffer("pssh", &buffer)) {
+ return NULL;
+ }
+
+ // the format of the buffer is 1 or more of:
+ // {
+ // 16 byte uuid
+ // 4 byte data length N
+ // N bytes of data
+ // }
+
+ // Determine the number of entries in the source data.
+ // Since we got the data from stagefright, we trust it is valid and properly formatted.
+ const uint8_t* data = buffer->data();
+ size_t len = buffer->size();
+ size_t numentries = 0;
+ while (len > 0) {
+ numentries++;
+
+ // skip uuid
+ data += 16;
+ len -= 16;
+
+ // get data length
+ uint32_t datalen = *((uint32_t*)data);
+ data += 4;
+ len -= 4;
+
+ // skip the data
+ data += datalen;
+ len -= datalen;
+ }
+
+ // there are <numentries> in the buffer, we need
+ // (source buffer size) + 4 + (4 * numentries) bytes for the PsshInfo structure
+ size_t newsize = buffer->size() + 4 + (4 * numentries);
+ ex->mPsshBuf = new ABuffer(newsize);
+ ex->mPsshBuf->setRange(0, newsize);
+
+ // copy data
+ const uint8_t* src = buffer->data();
+ uint8_t* dst = ex->mPsshBuf->data();
+ uint8_t* dstdata = dst + 4 + numentries * sizeof(PsshEntry);
+ *((uint32_t*)dst) = numentries;
+ dst += 4;
+ for (size_t i = 0; i < numentries; i++) {
+ // copy uuid
+ memcpy(dst, src, 16);
+ src += 16;
+ dst += 16;
+
+ // get/copy data length
+ uint32_t datalen = *((uint32_t*)src);
+ memcpy(dst, src, 4);
+ src += 4;
+ dst += 4;
+
+ // the next entry in the destination is a pointer to the actual data, which we store
+ // after the array of PsshEntry
+ memcpy(dst, &dstdata, sizeof(dstdata));
+ dst += 4;
+
+ // copy the actual data
+ memcpy(dstdata, src, datalen);
+ dstdata += datalen;
+ src += datalen;
+ }
+
+ return (PsshInfo*) ex->mPsshBuf->data();
+}
+
+EXPORT
+AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) {
+ sp<MetaData> meta;
+ if(ex->mImpl->getSampleMeta(&meta) != 0) {
+ return NULL;
+ }
+
+ uint32_t type;
+ const void *crypteddata;
+ size_t cryptedsize;
+ if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
+ return NULL;
+ }
+ size_t numSubSamples = cryptedsize / sizeof(size_t);
+
+ const void *cleardata;
+ size_t clearsize;
+ if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
+ if (clearsize != cryptedsize) {
+ // The two must be of the same length.
+ return NULL;
+ }
+ }
+
+ const void *key;
+ size_t keysize;
+ if (meta->findData(kKeyCryptoIV, &type, &key, &keysize)) {
+ if (keysize != 16) {
+ // IVs must be 16 bytes in length.
+ return NULL;
+ }
+ }
+
+ const void *iv;
+ size_t ivsize;
+ if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
+ if (ivsize != 16) {
+ // IVs must be 16 bytes in length.
+ return NULL;
+ }
+ }
+
+ int32_t mode;
+ if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+ mode = CryptoPlugin::kMode_AES_CTR;
+ }
+
+ return AMediaCodecCryptoInfo_new(
+ numSubSamples,
+ (uint8_t*) key,
+ (uint8_t*) iv,
+ mode,
+ (size_t*) cleardata,
+ (size_t*) crypteddata);
+}
+
} // extern "C"
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index c08814f..e1d8c95 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -56,12 +56,14 @@ void AMediaFormat_getFormat(const AMediaFormat* mData, void* dest) {
/*
* public function follow
*/
+EXPORT
AMediaFormat *AMediaFormat_new() {
ALOGV("ctor");
sp<AMessage> msg = new AMessage();
return AMediaFormat_fromMsg(&msg);
}
+EXPORT
int AMediaFormat_delete(AMediaFormat *mData) {
ALOGV("dtor");
delete mData;
@@ -69,6 +71,7 @@ int AMediaFormat_delete(AMediaFormat *mData) {
}
+EXPORT
const char* AMediaFormat_toString(AMediaFormat *mData) {
sp<AMessage> f = mData->mFormat;
String8 ret;
@@ -141,22 +144,27 @@ const char* AMediaFormat_toString(AMediaFormat *mData) {
return mData->mDebug.string();
}
+EXPORT
bool AMediaFormat_getInt32(AMediaFormat* format, const char *name, int32_t *out) {
return format->mFormat->findInt32(name, out);
}
+EXPORT
bool AMediaFormat_getInt64(AMediaFormat* format, const char *name, int64_t *out) {
return format->mFormat->findInt64(name, out);
}
+EXPORT
bool AMediaFormat_getFloat(AMediaFormat* format, const char *name, float *out) {
return format->mFormat->findFloat(name, out);
}
+EXPORT
bool AMediaFormat_getSize(AMediaFormat* format, const char *name, size_t *out) {
return format->mFormat->findSize(name, out);
}
+EXPORT
bool AMediaFormat_getBuffer(AMediaFormat* format, const char *name, void** data, size_t *outsize) {
sp<ABuffer> buf;
if (format->mFormat->findBuffer(name, &buf)) {
@@ -167,6 +175,7 @@ bool AMediaFormat_getBuffer(AMediaFormat* format, const char *name, void** data,
return false;
}
+EXPORT
bool AMediaFormat_getString(AMediaFormat* mData, const char *name, const char **out) {
for (size_t i = 0; i < mData->mStringCache.size(); i++) {
@@ -186,23 +195,28 @@ bool AMediaFormat_getString(AMediaFormat* mData, const char *name, const char **
return false;
}
+EXPORT
void AMediaFormat_setInt32(AMediaFormat* format, const char *name, int32_t value) {
format->mFormat->setInt32(name, value);
}
+EXPORT
void AMediaFormat_setInt64(AMediaFormat* format, const char *name, int64_t value) {
format->mFormat->setInt64(name, value);
}
+EXPORT
void AMediaFormat_setFloat(AMediaFormat* format, const char* name, float value) {
format->mFormat->setFloat(name, value);
}
+EXPORT
void AMediaFormat_setString(AMediaFormat* format, const char* name, const char* value) {
// AMessage::setString() makes a copy of the string
format->mFormat->setString(name, value, strlen(value));
}
+EXPORT
void AMediaFormat_setBuffer(AMediaFormat* format, const char* name, void* data, size_t size) {
// the ABuffer(void*, size_t) constructor doesn't take ownership of the data, so create
// a new buffer and copy the data into it
@@ -214,30 +228,30 @@ void AMediaFormat_setBuffer(AMediaFormat* format, const char* name, void* data,
}
-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";
+EXPORT const char* AMEDIAFORMAT_KEY_AAC_PROFILE = "aac-profile";
+EXPORT const char* AMEDIAFORMAT_KEY_BIT_RATE = "bitrate";
+EXPORT const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT = "channel-count";
+EXPORT const char* AMEDIAFORMAT_KEY_CHANNEL_MASK = "channel-mask";
+EXPORT const char* AMEDIAFORMAT_KEY_COLOR_FORMAT = "color-format";
+EXPORT const char* AMEDIAFORMAT_KEY_DURATION = "durationUs";
+EXPORT const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
+EXPORT const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_DEFAULT = "is-default";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle";
+EXPORT const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval";
+EXPORT const char* AMEDIAFORMAT_KEY_LANGUAGE = "language";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
+EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
+EXPORT const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
+EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
+EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
+EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
} // extern "C"
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index 98129cb..aa78740 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -52,6 +52,7 @@ struct AMediaMuxer {
extern "C" {
+EXPORT
AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format) {
ALOGV("ctor");
AMediaMuxer *mData = new AMediaMuxer();
@@ -59,34 +60,41 @@ AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format) {
return mData;
}
+EXPORT
int AMediaMuxer_delete(AMediaMuxer *muxer) {
ALOGV("dtor");
delete muxer;
return OK;
}
+EXPORT
int AMediaMuxer_setLocation(AMediaMuxer *muxer, float latitude, float longtitude) {
return translate_error(muxer->mImpl->setLocation(latitude * 10000, longtitude * 10000));
}
+EXPORT
int AMediaMuxer_setOrientationHint(AMediaMuxer *muxer, int degrees) {
return translate_error(muxer->mImpl->setOrientationHint(degrees));
}
+EXPORT
ssize_t AMediaMuxer_addTrack(AMediaMuxer *muxer, const AMediaFormat *format) {
sp<AMessage> msg;
AMediaFormat_getFormat(format, &msg);
return translate_error(muxer->mImpl->addTrack(msg));
}
+EXPORT
int AMediaMuxer_start(AMediaMuxer *muxer) {
return translate_error(muxer->mImpl->start());
}
+EXPORT
int AMediaMuxer_stop(AMediaMuxer *muxer) {
return translate_error(muxer->mImpl->stop());
}
+EXPORT
int AMediaMuxer_writeSampleData(AMediaMuxer *muxer,
size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info) {
sp<ABuffer> buf = new ABuffer((void*)(data + info.offset), info.size);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c1d4c08..d69d6a2 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -18,6 +18,7 @@
#ifndef ANDROID_AUDIO_FLINGER_H
#define ANDROID_AUDIO_FLINGER_H
+#include "Configuration.h"
#include <stdint.h>
#include <sys/types.h>
#include <limits.h>
diff --git a/services/audioflinger/FastThreadState.cpp b/services/audioflinger/FastThreadState.cpp
index d4d6255..6994872 100644
--- a/services/audioflinger/FastThreadState.cpp
+++ b/services/audioflinger/FastThreadState.cpp
@@ -35,7 +35,7 @@ FastThreadDumpState::FastThreadDumpState() :
/* mMeasuredWarmupTs({0, 0}), */
mWarmupCycles(0)
#ifdef FAST_MIXER_STATISTICS
- , mSamplingN(0), mBounds(0)
+ , mSamplingN(1), mBounds(0)
#endif
{
mMeasuredWarmupTs.tv_sec = 0;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index e9c6834..08b1728 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -115,7 +115,6 @@ public:
void invalidate();
bool isInvalid() const { return mIsInvalid; }
virtual bool isTimedTrack() const { return false; }
- bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
int fastIndex() const { return mFastIndex; }
protected:
@@ -142,8 +141,6 @@ protected:
// audio HAL when this track will be fully rendered
// zero means not monitoring
private:
- IAudioFlinger::track_flags_t mFlags;
-
// The following fields are only for fast tracks, and should be in a subclass
int mFastIndex; // index within FastMixerState::mFastTracks[];
// either mFastIndex == -1 if not isFastTrack()
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 4ca2ad4..fe15571 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -30,7 +30,7 @@ public:
size_t frameCount,
int sessionId,
int uid,
- bool isFast);
+ IAudioFlinger::track_flags_t flags);
virtual ~RecordTrack();
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h
index 9cde642..9e176c4 100644
--- a/services/audioflinger/StateQueue.h
+++ b/services/audioflinger/StateQueue.h
@@ -89,6 +89,8 @@
// arithmetic on the state pointers. However to the mutator, the state pointers
// are in a definite circular order.
+#include "Configuration.h"
+
namespace android {
#ifdef STATE_QUEUE_DUMP
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8243a8b..2d4e025 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5142,7 +5142,7 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe
track = new RecordTrack(this, client, sampleRate,
format, channelMask, frameCount, sessionId, uid,
- (*flags & IAudioFlinger::TRACK_FAST) != 0);
+ *flags);
lStatus = track->initCheck();
if (lStatus != NO_ERROR) {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 06023fd..5f13be3 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -48,6 +48,7 @@ public:
const sp<IMemory>& sharedBuffer,
int sessionId,
int uid,
+ IAudioFlinger::track_flags_t flags,
bool isOut,
bool useReadOnlyHeap = false);
virtual ~TrackBase();
@@ -63,6 +64,7 @@ public:
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
sp<IMemory> getBuffers() const { return mBufferMemory; }
+ bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
protected:
TrackBase(const TrackBase&);
@@ -134,6 +136,7 @@ protected:
const int mSessionId;
int mUid;
Vector < sp<SyncEvent> >mSyncEvents;
+ const IAudioFlinger::track_flags_t mFlags;
const bool mIsOut;
ServerProxy* mServerProxy;
const int mId;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 88ead74..6dc7f30 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -69,6 +69,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
const sp<IMemory>& sharedBuffer,
int sessionId,
int clientUid,
+ IAudioFlinger::track_flags_t flags,
bool isOut,
bool useReadOnlyHeap)
: RefBase(),
@@ -85,6 +86,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
mFrameCount(frameCount),
mSessionId(sessionId),
+ mFlags(flags),
mIsOut(isOut),
mServerProxy(NULL),
mId(android_atomic_inc(&nextTrackId)),
@@ -349,7 +351,7 @@ AudioFlinger::PlaybackThread::Track::Track(
int uid,
IAudioFlinger::track_flags_t flags)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
- sessionId, uid, true /*isOut*/),
+ sessionId, uid, flags, true /*isOut*/),
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
@@ -359,7 +361,6 @@ AudioFlinger::PlaybackThread::Track::Track(
mAuxBuffer(NULL),
mAuxEffectId(0), mHasVolumeController(false),
mPresentationCompleteFrames(0),
- mFlags(flags),
mFastIndex(-1),
mCachedVolume(1.0),
mIsInvalid(false),
@@ -1833,10 +1834,11 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
size_t frameCount,
int sessionId,
int uid,
- bool isFast)
+ IAudioFlinger::track_flags_t flags)
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/,
- isFast /*useReadOnlyHeap*/),
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid,
+ flags, false /*isOut*/,
+ (flags & IAudioFlinger::TRACK_FAST) != 0 /*useReadOnlyHeap*/),
mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
// See real initialization of mRsmpInFront at RecordThread::start()
mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)