NdkMediaCodec.cpp revision e22a64b6887240fc9910d6fc8afb0e6f81507047
1/*
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 "NdkMediaCodec"
19
20#include "NdkMediaCodec.h"
21#include "NdkMediaError.h"
22#include "NdkMediaCryptoPriv.h"
23#include "NdkMediaFormatPriv.h"
24
25#include <utils/Log.h>
26#include <utils/StrongPointer.h>
27#include <gui/Surface.h>
28
29#include <media/stagefright/foundation/ALooper.h>
30#include <media/stagefright/foundation/AMessage.h>
31#include <media/stagefright/foundation/ABuffer.h>
32
33#include <media/stagefright/MediaCodec.h>
34#include <media/stagefright/MediaErrors.h>
35
36using namespace android;
37
38
39static media_status_t translate_error(status_t err) {
40    if (err == OK) {
41        return AMEDIA_OK;
42    } else if (err == -EAGAIN) {
43        return (media_status_t) AMEDIACODEC_INFO_TRY_AGAIN_LATER;
44    }
45    ALOGE("sf error code: %d", err);
46    return AMEDIA_ERROR_UNKNOWN;
47}
48
49enum {
50    kWhatActivityNotify,
51    kWhatRequestActivityNotifications,
52    kWhatStopActivityNotifications,
53};
54
55
56class CodecHandler: public AHandler {
57private:
58    AMediaCodec* mCodec;
59public:
60    CodecHandler(AMediaCodec *codec);
61    virtual void onMessageReceived(const sp<AMessage> &msg);
62};
63
64typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
65
66struct AMediaCodec {
67    sp<android::MediaCodec> mCodec;
68    sp<ALooper> mLooper;
69    sp<CodecHandler> mHandler;
70    sp<AMessage> mActivityNotification;
71    int32_t mGeneration;
72    bool mRequestedActivityNotification;
73    OnCodecEvent mCallback;
74    void *mCallbackUserData;
75};
76
77CodecHandler::CodecHandler(AMediaCodec *codec) {
78    mCodec = codec;
79}
80
81void CodecHandler::onMessageReceived(const sp<AMessage> &msg) {
82
83    switch (msg->what()) {
84        case kWhatRequestActivityNotifications:
85        {
86            if (mCodec->mRequestedActivityNotification) {
87                break;
88            }
89
90            mCodec->mCodec->requestActivityNotification(mCodec->mActivityNotification);
91            mCodec->mRequestedActivityNotification = true;
92            break;
93        }
94
95        case kWhatActivityNotify:
96        {
97            {
98                int32_t generation;
99                msg->findInt32("generation", &generation);
100
101                if (generation != mCodec->mGeneration) {
102                    // stale
103                    break;
104                }
105
106                mCodec->mRequestedActivityNotification = false;
107            }
108
109            if (mCodec->mCallback) {
110                mCodec->mCallback(mCodec, mCodec->mCallbackUserData);
111            }
112            break;
113        }
114
115        case kWhatStopActivityNotifications:
116        {
117            uint32_t replyID;
118            msg->senderAwaitsResponse(&replyID);
119
120            mCodec->mGeneration++;
121            mCodec->mRequestedActivityNotification = false;
122
123            sp<AMessage> response = new AMessage;
124            response->postReply(replyID);
125            break;
126        }
127
128        default:
129            ALOGE("shouldn't be here");
130            break;
131    }
132
133}
134
135
136static void requestActivityNotification(AMediaCodec *codec) {
137    (new AMessage(kWhatRequestActivityNotifications, codec->mHandler->id()))->post();
138}
139
140extern "C" {
141
142static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) {
143    AMediaCodec *mData = new AMediaCodec();
144    mData->mLooper = new ALooper;
145    mData->mLooper->setName("NDK MediaCodec_looper");
146    status_t ret = mData->mLooper->start(
147            false,      // runOnCallingThread
148            true,       // canCallJava XXX
149            PRIORITY_FOREGROUND);
150    if (name_is_type) {
151        mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder);
152    } else {
153        mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
154    }
155    mData->mHandler = new CodecHandler(mData);
156    mData->mLooper->registerHandler(mData->mHandler);
157    mData->mGeneration = 1;
158    mData->mRequestedActivityNotification = false;
159    mData->mCallback = NULL;
160
161    return mData;
162}
163
164EXPORT
165AMediaCodec* AMediaCodec_createCodecByName(const char *name) {
166    return createAMediaCodec(name, false, false);
167}
168
169EXPORT
170AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) {
171    return createAMediaCodec(mime_type, true, false);
172}
173
174EXPORT
175AMediaCodec* AMediaCodec_createEncoderByType(const char *name) {
176    return createAMediaCodec(name, true, true);
177}
178
179EXPORT
180media_status_t AMediaCodec_delete(AMediaCodec *mData) {
181    if (mData->mCodec != NULL) {
182        mData->mCodec->release();
183        mData->mCodec.clear();
184    }
185
186    if (mData->mLooper != NULL) {
187        mData->mLooper->unregisterHandler(mData->mHandler->id());
188        mData->mLooper->stop();
189        mData->mLooper.clear();
190    }
191    delete mData;
192    return AMEDIA_OK;
193}
194
195EXPORT
196media_status_t AMediaCodec_configure(
197        AMediaCodec *mData,
198        const AMediaFormat* format,
199        ANativeWindow* window,
200        AMediaCrypto *crypto,
201        uint32_t flags) {
202    sp<AMessage> nativeFormat;
203    AMediaFormat_getFormat(format, &nativeFormat);
204    ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str());
205    sp<Surface> surface = NULL;
206    if (window != NULL) {
207        surface = (Surface*) window;
208    }
209
210    return translate_error(mData->mCodec->configure(nativeFormat, surface,
211            crypto ? crypto->mCrypto : NULL, flags));
212}
213
214EXPORT
215media_status_t AMediaCodec_start(AMediaCodec *mData) {
216    status_t ret =  mData->mCodec->start();
217    if (ret != OK) {
218        return translate_error(ret);
219    }
220    mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler->id());
221    mData->mActivityNotification->setInt32("generation", mData->mGeneration);
222    requestActivityNotification(mData);
223    return AMEDIA_OK;
224}
225
226EXPORT
227media_status_t AMediaCodec_stop(AMediaCodec *mData) {
228    media_status_t ret = translate_error(mData->mCodec->stop());
229
230    sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler->id());
231    sp<AMessage> response;
232    msg->postAndAwaitResponse(&response);
233    mData->mActivityNotification.clear();
234
235    return ret;
236}
237
238EXPORT
239media_status_t AMediaCodec_flush(AMediaCodec *mData) {
240    return translate_error(mData->mCodec->flush());
241}
242
243EXPORT
244ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) {
245    size_t idx;
246    status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs);
247    requestActivityNotification(mData);
248    if (ret == OK) {
249        return idx;
250    }
251    return translate_error(ret);
252}
253
254EXPORT
255uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
256    android::Vector<android::sp<android::ABuffer> > abufs;
257    if (mData->mCodec->getInputBuffers(&abufs) == 0) {
258        size_t n = abufs.size();
259        if (idx >= n) {
260            ALOGE("buffer index %d out of range", idx);
261            return NULL;
262        }
263        if (out_size != NULL) {
264            *out_size = abufs[idx]->capacity();
265        }
266        return abufs[idx]->data();
267    }
268    ALOGE("couldn't get input buffers");
269    return NULL;
270}
271
272EXPORT
273uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
274    android::Vector<android::sp<android::ABuffer> > abufs;
275    if (mData->mCodec->getOutputBuffers(&abufs) == 0) {
276        size_t n = abufs.size();
277        if (idx >= n) {
278            ALOGE("buffer index %d out of range", idx);
279            return NULL;
280        }
281        if (out_size != NULL) {
282            *out_size = abufs[idx]->capacity();
283        }
284        return abufs[idx]->data();
285    }
286    ALOGE("couldn't get output buffers");
287    return NULL;
288}
289
290EXPORT
291media_status_t AMediaCodec_queueInputBuffer(AMediaCodec *mData,
292        size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) {
293
294    AString errorMsg;
295    status_t ret = mData->mCodec->queueInputBuffer(idx, offset, size, time, flags, &errorMsg);
296    return translate_error(ret);
297}
298
299EXPORT
300ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData,
301        AMediaCodecBufferInfo *info, int64_t timeoutUs) {
302    size_t idx;
303    size_t offset;
304    size_t size;
305    uint32_t flags;
306    int64_t presentationTimeUs;
307    status_t ret = mData->mCodec->dequeueOutputBuffer(&idx, &offset, &size, &presentationTimeUs,
308            &flags, timeoutUs);
309    requestActivityNotification(mData);
310    switch (ret) {
311        case OK:
312            info->offset = offset;
313            info->size = size;
314            info->flags = flags;
315            info->presentationTimeUs = presentationTimeUs;
316            return idx;
317        case -EAGAIN:
318            return AMEDIACODEC_INFO_TRY_AGAIN_LATER;
319        case android::INFO_FORMAT_CHANGED:
320            return AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED;
321        case INFO_OUTPUT_BUFFERS_CHANGED:
322            return AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED;
323        default:
324            break;
325    }
326    return translate_error(ret);
327}
328
329EXPORT
330AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) {
331    sp<AMessage> format;
332    mData->mCodec->getOutputFormat(&format);
333    return AMediaFormat_fromMsg(&format);
334}
335
336EXPORT
337media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
338    if (render) {
339        return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
340    } else {
341        return translate_error(mData->mCodec->releaseOutputBuffer(idx));
342    }
343}
344
345EXPORT
346media_status_t AMediaCodec_releaseOutputBufferAtTime(
347        AMediaCodec *mData, size_t idx, int64_t timestampNs) {
348    ALOGV("render @ %lld", timestampNs);
349    return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
350}
351
352//EXPORT
353media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
354    mData->mCallback = callback;
355    mData->mCallbackUserData = userdata;
356    return AMEDIA_OK;
357}
358
359typedef struct AMediaCodecCryptoInfo {
360        int numsubsamples;
361        uint8_t key[16];
362        uint8_t iv[16];
363        cryptoinfo_mode_t mode;
364        size_t *clearbytes;
365        size_t *encryptedbytes;
366} AMediaCodecCryptoInfo;
367
368EXPORT
369media_status_t AMediaCodec_queueSecureInputBuffer(
370        AMediaCodec* codec,
371        size_t idx,
372        off_t offset,
373        AMediaCodecCryptoInfo* crypto,
374        uint64_t time,
375        uint32_t flags) {
376
377    CryptoPlugin::SubSample *subSamples = new CryptoPlugin::SubSample[crypto->numsubsamples];
378    for (int i = 0; i < crypto->numsubsamples; i++) {
379        subSamples[i].mNumBytesOfClearData = crypto->clearbytes[i];
380        subSamples[i].mNumBytesOfEncryptedData = crypto->encryptedbytes[i];
381    }
382
383    AString errormsg;
384    status_t err  = codec->mCodec->queueSecureInputBuffer(idx,
385            offset,
386            subSamples,
387            crypto->numsubsamples,
388            crypto->key,
389            crypto->iv,
390            (CryptoPlugin::Mode) crypto->mode,
391            time,
392            flags,
393            &errormsg);
394    if (err != 0) {
395        ALOGE("queSecureInputBuffer: %s", errormsg.c_str());
396    }
397    delete [] subSamples;
398    return translate_error(err);
399}
400
401
402
403EXPORT
404AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
405        int numsubsamples,
406        uint8_t key[16],
407        uint8_t iv[16],
408        cryptoinfo_mode_t mode,
409        size_t *clearbytes,
410        size_t *encryptedbytes) {
411
412    // size needed to store all the crypto data
413    size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
414    AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
415    if (!ret) {
416        ALOGE("couldn't allocate %d bytes", cryptosize);
417        return NULL;
418    }
419    ret->numsubsamples = numsubsamples;
420    memcpy(ret->key, key, 16);
421    memcpy(ret->iv, iv, 16);
422    ret->mode = mode;
423
424    // clearbytes and encryptedbytes point at the actual data, which follows
425    ret->clearbytes = (size_t*) (ret + 1); // point immediately after the struct
426    ret->encryptedbytes = ret->clearbytes + numsubsamples; // point after the clear sizes
427
428    memcpy(ret->clearbytes, clearbytes, numsubsamples * sizeof(size_t));
429    memcpy(ret->encryptedbytes, encryptedbytes, numsubsamples * sizeof(size_t));
430
431    return ret;
432}
433
434
435EXPORT
436media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
437    free(info);
438    return AMEDIA_OK;
439}
440
441EXPORT
442size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo* ci) {
443    return ci->numsubsamples;
444}
445
446EXPORT
447media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
448    if (!ci) {
449        return AMEDIA_ERROR_INVALID_OBJECT;
450    }
451    if (!dst) {
452        return AMEDIA_ERROR_INVALID_PARAMETER;
453    }
454    memcpy(dst, ci->key, 16);
455    return AMEDIA_OK;
456}
457
458EXPORT
459media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
460    if (!ci) {
461        return AMEDIA_ERROR_INVALID_OBJECT;
462    }
463    if (!dst) {
464        return AMEDIA_ERROR_INVALID_PARAMETER;
465    }
466    memcpy(dst, ci->iv, 16);
467    return AMEDIA_OK;
468}
469
470EXPORT
471cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
472    if (!ci) {
473        return (cryptoinfo_mode_t) AMEDIA_ERROR_INVALID_OBJECT;
474    }
475    return ci->mode;
476}
477
478EXPORT
479media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
480    if (!ci) {
481        return AMEDIA_ERROR_INVALID_OBJECT;
482    }
483    if (!dst) {
484        return AMEDIA_ERROR_INVALID_PARAMETER;
485    }
486    memcpy(dst, ci->clearbytes, sizeof(size_t) * ci->numsubsamples);
487    return AMEDIA_OK;
488}
489
490EXPORT
491media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
492    if (!ci) {
493        return AMEDIA_ERROR_INVALID_OBJECT;
494    }
495    if (!dst) {
496        return AMEDIA_ERROR_INVALID_PARAMETER;
497    }
498    memcpy(dst, ci->encryptedbytes, sizeof(size_t) * ci->numsubsamples);
499    return AMEDIA_OK;
500}
501
502} // extern "C"
503
504