blob: 0a66988086192a258290db20892ca1f3c280f9ed [file] [log] [blame]
Marco Nelissen0c3be872014-05-01 10:14:44 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_NDEBUG 0
18#define LOG_TAG "NdkMediaExtractor"
19
20
Marco Nelissen050eb322014-05-09 15:10:23 -070021#include "NdkMediaError.h"
Marco Nelissen0c3be872014-05-01 10:14:44 -070022#include "NdkMediaExtractor.h"
23#include "NdkMediaFormatPriv.h"
24
25
26#include <utils/Log.h>
27#include <utils/StrongPointer.h>
Marco Nelissen050eb322014-05-09 15:10:23 -070028#include <media/hardware/CryptoAPI.h>
Marco Nelissen0c3be872014-05-01 10:14:44 -070029#include <media/stagefright/foundation/ABuffer.h>
30#include <media/stagefright/foundation/AMessage.h>
31#include <media/stagefright/MetaData.h>
32#include <media/stagefright/NuMediaExtractor.h>
33#include <media/IMediaHTTPService.h>
34#include <android_runtime/AndroidRuntime.h>
35#include <android_util_Binder.h>
36
37#include <jni.h>
38
39using namespace android;
40
41static int translate_error(status_t err) {
42 if (err == OK) {
43 return OK;
44 }
45 ALOGE("sf error code: %d", err);
Marco Nelissen050eb322014-05-09 15:10:23 -070046 return AMEDIAERROR_GENERIC;
Marco Nelissen0c3be872014-05-01 10:14:44 -070047}
48
49struct AMediaExtractor {
50 sp<NuMediaExtractor> mImpl;
Marco Nelissen050eb322014-05-09 15:10:23 -070051 sp<ABuffer> mPsshBuf;
Marco Nelissen0c3be872014-05-01 10:14:44 -070052
53};
54
55extern "C" {
56
57AMediaExtractor* AMediaExtractor_new() {
58 ALOGV("ctor");
59 AMediaExtractor *mData = new AMediaExtractor();
60 mData->mImpl = new NuMediaExtractor();
61 return mData;
62}
63
64int AMediaExtractor_delete(AMediaExtractor *mData) {
65 ALOGV("dtor");
66 delete mData;
67 return OK;
68}
69
70int AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, off64_t length) {
71 ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
72 mData->mImpl->setDataSource(fd, offset, length);
73 return 0;
74}
75
76int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
77 ALOGV("setDataSource(%s)", location);
78 // TODO: add header support
79
80 JNIEnv *env = AndroidRuntime::getJNIEnv();
81 jobject service = NULL;
82 if (env == NULL) {
83 ALOGE("setDataSource(path) must be called from Java thread");
84 env->ExceptionClear();
Marco Nelissen050eb322014-05-09 15:10:23 -070085 return AMEDIAERROR_UNSUPPORTED;
Marco Nelissen0c3be872014-05-01 10:14:44 -070086 }
87
88 jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService");
89 if (mediahttpclass == NULL) {
90 ALOGE("can't find MediaHttpService");
91 env->ExceptionClear();
Marco Nelissen050eb322014-05-09 15:10:23 -070092 return AMEDIAERROR_UNSUPPORTED;
Marco Nelissen0c3be872014-05-01 10:14:44 -070093 }
94
95 jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass,
96 "createHttpServiceBinderIfNecessary", "(Ljava/lang/String;)Landroid/os/IBinder;");
97 if (mediaHttpCreateMethod == NULL) {
98 ALOGE("can't find method");
99 env->ExceptionClear();
Marco Nelissen050eb322014-05-09 15:10:23 -0700100 return AMEDIAERROR_UNSUPPORTED;
Marco Nelissen0c3be872014-05-01 10:14:44 -0700101 }
102
103 jstring jloc = env->NewStringUTF(location);
104
105 service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc);
106 env->DeleteLocalRef(jloc);
107
108 sp<IMediaHTTPService> httpService;
109 if (service != NULL) {
110 sp<IBinder> binder = ibinderForJavaObject(env, service);
111 httpService = interface_cast<IMediaHTTPService>(binder);
112 }
113
114 mData->mImpl->setDataSource(httpService, location, NULL);
115 env->ExceptionClear();
Marco Nelissen050eb322014-05-09 15:10:23 -0700116 return OK;
Marco Nelissen0c3be872014-05-01 10:14:44 -0700117}
118
119int AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
120 return mData->mImpl->countTracks();
121}
122
123AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) {
124 sp<AMessage> format;
125 mData->mImpl->getTrackFormat(idx, &format);
126 return AMediaFormat_fromMsg(&format);
127}
128
129int AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) {
130 ALOGV("selectTrack(%z)", idx);
131 return translate_error(mData->mImpl->selectTrack(idx));
132}
133
134int AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) {
135 ALOGV("unselectTrack(%z)", idx);
136 return translate_error(mData->mImpl->unselectTrack(idx));
137}
138
139bool AMediaExtractor_advance(AMediaExtractor *mData) {
140 //ALOGV("advance");
141 return mData->mImpl->advance();
142}
143
144int AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) {
145 //ALOGV("readSampleData");
146 sp<ABuffer> tmp = new ABuffer(buffer, capacity);
147 if (mData->mImpl->readSampleData(tmp) == OK) {
148 return tmp->size();
149 }
150 return -1;
151}
152
153int AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
154 int sampleFlags = 0;
155 sp<MetaData> meta;
156 status_t err = mData->mImpl->getSampleMeta(&meta);
157 if (err != OK) {
158 return -1;
159 }
160 int32_t val;
161 if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
162 sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
163 }
164
165 uint32_t type;
166 const void *data;
167 size_t size;
168 if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
169 sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
170 }
171 return sampleFlags;
172}
173
174int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) {
175 size_t idx;
176 if (mData->mImpl->getSampleTrackIndex(&idx) != OK) {
177 return -1;
178 }
179 return idx;
180}
181
182int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) {
183 int64_t time;
184 if (mData->mImpl->getSampleTime(&time) != OK) {
185 return -1;
186 }
187 return time;
188}
189
Marco Nelissen050eb322014-05-09 15:10:23 -0700190PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor *ex) {
191
192 if (ex->mPsshBuf != NULL) {
193 return (PsshInfo*) ex->mPsshBuf->data();
194 }
195
196 sp<AMessage> format;
197 ex->mImpl->getFileFormat(&format);
198 sp<ABuffer> buffer;
199 if(!format->findBuffer("pssh", &buffer)) {
200 return NULL;
201 }
202
203 // the format of the buffer is 1 or more of:
204 // {
205 // 16 byte uuid
206 // 4 byte data length N
207 // N bytes of data
208 // }
209
210 // Determine the number of entries in the source data.
211 // Since we got the data from stagefright, we trust it is valid and properly formatted.
212 const uint8_t* data = buffer->data();
213 size_t len = buffer->size();
214 size_t numentries = 0;
215 while (len > 0) {
216 numentries++;
217
218 // skip uuid
219 data += 16;
220 len -= 16;
221
222 // get data length
223 uint32_t datalen = *((uint32_t*)data);
224 data += 4;
225 len -= 4;
226
227 // skip the data
228 data += datalen;
229 len -= datalen;
230 }
231
232 // there are <numentries> in the buffer, we need
233 // (source buffer size) + 4 + (4 * numentries) bytes for the PsshInfo structure
234 size_t newsize = buffer->size() + 4 + (4 * numentries);
235 ex->mPsshBuf = new ABuffer(newsize);
236 ex->mPsshBuf->setRange(0, newsize);
237
238 // copy data
239 const uint8_t* src = buffer->data();
240 uint8_t* dst = ex->mPsshBuf->data();
241 uint8_t* dstdata = dst + 4 + numentries * sizeof(PsshEntry);
242 *((uint32_t*)dst) = numentries;
243 dst += 4;
244 for (size_t i = 0; i < numentries; i++) {
245 // copy uuid
246 memcpy(dst, src, 16);
247 src += 16;
248 dst += 16;
249
250 // get/copy data length
251 uint32_t datalen = *((uint32_t*)src);
252 memcpy(dst, src, 4);
253 src += 4;
254 dst += 4;
255
256 // the next entry in the destination is a pointer to the actual data, which we store
257 // after the array of PsshEntry
258 memcpy(dst, &dstdata, sizeof(dstdata));
259 dst += 4;
260
261 // copy the actual data
262 memcpy(dstdata, src, datalen);
263 dstdata += datalen;
264 src += datalen;
265 }
266
267 return (PsshInfo*) ex->mPsshBuf->data();
268}
269
270AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) {
271 sp<MetaData> meta;
272 if(ex->mImpl->getSampleMeta(&meta) != 0) {
273 return NULL;
274 }
275
276 uint32_t type;
277 const void *crypteddata;
278 size_t cryptedsize;
279 if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
280 return NULL;
281 }
282 size_t numSubSamples = cryptedsize / sizeof(size_t);
283
284 const void *cleardata;
285 size_t clearsize;
286 if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
287 if (clearsize != cryptedsize) {
288 // The two must be of the same length.
289 return NULL;
290 }
291 }
292
293 const void *key;
294 size_t keysize;
295 if (meta->findData(kKeyCryptoIV, &type, &key, &keysize)) {
296 if (keysize != 16) {
297 // IVs must be 16 bytes in length.
298 return NULL;
299 }
300 }
301
302 const void *iv;
303 size_t ivsize;
304 if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
305 if (ivsize != 16) {
306 // IVs must be 16 bytes in length.
307 return NULL;
308 }
309 }
310
311 int32_t mode;
312 if (!meta->findInt32(kKeyCryptoMode, &mode)) {
313 mode = CryptoPlugin::kMode_AES_CTR;
314 }
315
316 return AMediaCodecCryptoInfo_new(
317 numSubSamples,
318 (uint8_t*) key,
319 (uint8_t*) iv,
320 mode,
321 (size_t*) cleardata,
322 (size_t*) crypteddata);
323}
324
Marco Nelissen0c3be872014-05-01 10:14:44 -0700325
326} // extern "C"
327