Converter.cpp revision ee93c8c0f65cc1965ca09c9e33ae672f8bc9b88c
1/*
2 * Copyright 2012, 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 "Converter"
19#include <utils/Log.h>
20
21#include "Converter.h"
22
23#include <cutils/properties.h>
24#include <gui/SurfaceTextureClient.h>
25#include <media/ICrypto.h>
26#include <media/stagefright/foundation/ABuffer.h>
27#include <media/stagefright/foundation/ADebug.h>
28#include <media/stagefright/foundation/AMessage.h>
29#include <media/stagefright/MediaCodec.h>
30#include <media/stagefright/MediaDefs.h>
31#include <media/stagefright/MediaErrors.h>
32
33namespace android {
34
35Converter::Converter(
36        const sp<AMessage> &notify,
37        const sp<ALooper> &codecLooper,
38        const sp<AMessage> &format)
39    : mInitCheck(NO_INIT),
40      mNotify(notify),
41      mCodecLooper(codecLooper),
42      mInputFormat(format),
43      mIsVideo(false),
44      mDoMoreWorkPending(false) {
45    AString mime;
46    CHECK(mInputFormat->findString("mime", &mime));
47
48    if (!strncasecmp("video/", mime.c_str(), 6)) {
49        mIsVideo = true;
50    }
51
52    mInitCheck = initEncoder();
53}
54
55Converter::~Converter() {
56    if (mEncoder != NULL) {
57        mEncoder->release();
58        mEncoder.clear();
59    }
60
61    AString mime;
62    CHECK(mInputFormat->findString("mime", &mime));
63    ALOGI("encoder (%s) shut down.", mime.c_str());
64}
65
66status_t Converter::initCheck() const {
67    return mInitCheck;
68}
69
70size_t Converter::getInputBufferCount() const {
71    return mEncoderInputBuffers.size();
72}
73
74sp<AMessage> Converter::getOutputFormat() const {
75    return mOutputFormat;
76}
77
78static int32_t getBitrate(const char *propName, int32_t defaultValue) {
79    char val[PROPERTY_VALUE_MAX];
80    if (property_get(propName, val, NULL)) {
81        char *end;
82        unsigned long x = strtoul(val, &end, 10);
83
84        if (*end == '\0' && end > val && x > 0) {
85            return x;
86        }
87    }
88
89    return defaultValue;
90}
91
92status_t Converter::initEncoder() {
93    AString inputMIME;
94    CHECK(mInputFormat->findString("mime", &inputMIME));
95
96    AString outputMIME;
97    bool isAudio = false;
98    if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) {
99        outputMIME = MEDIA_MIMETYPE_AUDIO_AAC;
100        isAudio = true;
101    } else if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_VIDEO_RAW)) {
102        outputMIME = MEDIA_MIMETYPE_VIDEO_AVC;
103    } else {
104        TRESPASS();
105    }
106
107    mEncoder = MediaCodec::CreateByType(
108            mCodecLooper, outputMIME.c_str(), true /* encoder */);
109
110    if (mEncoder == NULL) {
111        return ERROR_UNSUPPORTED;
112    }
113
114    mOutputFormat = mInputFormat->dup();
115    mOutputFormat->setString("mime", outputMIME.c_str());
116
117    int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 64000);
118    int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000);
119
120    ALOGI("using audio bitrate of %d bps, video bitrate of %d bps",
121          audioBitrate, videoBitrate);
122
123    if (isAudio) {
124        mOutputFormat->setInt32("bitrate", audioBitrate);
125    } else {
126        mOutputFormat->setInt32("bitrate", videoBitrate);
127        mOutputFormat->setInt32("frame-rate", 24);
128        mOutputFormat->setInt32("i-frame-interval", 1);  // Iframes every 1 secs
129    }
130
131    ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
132
133    status_t err = mEncoder->configure(
134            mOutputFormat,
135            NULL /* nativeWindow */,
136            NULL /* crypto */,
137            MediaCodec::CONFIGURE_FLAG_ENCODE);
138
139    if (err != OK) {
140        return err;
141    }
142
143    err = mEncoder->start();
144
145    if (err != OK) {
146        return err;
147    }
148
149    err = mEncoder->getInputBuffers(&mEncoderInputBuffers);
150
151    if (err != OK) {
152        return err;
153    }
154
155    return mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
156}
157
158void Converter::feedAccessUnit(const sp<ABuffer> &accessUnit) {
159    sp<AMessage> msg = new AMessage(kWhatFeedAccessUnit, id());
160    msg->setBuffer("accessUnit", accessUnit);
161    msg->post();
162}
163
164void Converter::signalEOS() {
165    (new AMessage(kWhatInputEOS, id()))->post();
166}
167
168void Converter::notifyError(status_t err) {
169    sp<AMessage> notify = mNotify->dup();
170    notify->setInt32("what", kWhatError);
171    notify->setInt32("err", err);
172    notify->post();
173}
174
175void Converter::onMessageReceived(const sp<AMessage> &msg) {
176    switch (msg->what()) {
177        case kWhatFeedAccessUnit:
178        {
179            sp<ABuffer> accessUnit;
180            CHECK(msg->findBuffer("accessUnit", &accessUnit));
181
182            mInputBufferQueue.push_back(accessUnit);
183
184            feedEncoderInputBuffers();
185
186            scheduleDoMoreWork();
187            break;
188        }
189
190        case kWhatInputEOS:
191        {
192            mInputBufferQueue.push_back(NULL);
193
194            feedEncoderInputBuffers();
195
196            scheduleDoMoreWork();
197            break;
198        }
199
200        case kWhatDoMoreWork:
201        {
202            mDoMoreWorkPending = false;
203            status_t err = doMoreWork();
204
205            if (err != OK) {
206                notifyError(err);
207            } else {
208                scheduleDoMoreWork();
209            }
210            break;
211        }
212
213        case kWhatRequestIDRFrame:
214        {
215            if (mIsVideo) {
216                ALOGI("requesting IDR frame");
217                mEncoder->requestIDRFrame();
218            }
219            break;
220        }
221
222        default:
223            TRESPASS();
224    }
225}
226
227void Converter::scheduleDoMoreWork() {
228    if (mDoMoreWorkPending) {
229        return;
230    }
231
232    mDoMoreWorkPending = true;
233    (new AMessage(kWhatDoMoreWork, id()))->post(1000ll);
234}
235
236status_t Converter::feedEncoderInputBuffers() {
237    while (!mInputBufferQueue.empty()
238            && !mAvailEncoderInputIndices.empty()) {
239        sp<ABuffer> buffer = *mInputBufferQueue.begin();
240        mInputBufferQueue.erase(mInputBufferQueue.begin());
241
242        size_t bufferIndex = *mAvailEncoderInputIndices.begin();
243        mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
244
245        int64_t timeUs = 0ll;
246        uint32_t flags = 0;
247
248        if (buffer != NULL) {
249            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
250
251            ALOGV("in: %s timeUs = %lld us", mIsVideo ? "video" : "audio", timeUs);
252
253            memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(),
254                   buffer->data(),
255                   buffer->size());
256
257            void *mediaBuffer;
258            if (buffer->meta()->findPointer("mediaBuffer", &mediaBuffer)
259                    && mediaBuffer != NULL) {
260                mEncoderInputBuffers.itemAt(bufferIndex)->meta()
261                    ->setPointer("mediaBuffer", mediaBuffer);
262
263                buffer->meta()->setPointer("mediaBuffer", NULL);
264            }
265        } else {
266            flags = MediaCodec::BUFFER_FLAG_EOS;
267        }
268
269        status_t err = mEncoder->queueInputBuffer(
270                bufferIndex, 0, (buffer == NULL) ? 0 : buffer->size(),
271                timeUs, flags);
272
273        if (err != OK) {
274            return err;
275        }
276    }
277
278    return OK;
279}
280
281status_t Converter::doMoreWork() {
282    size_t bufferIndex;
283    status_t err = mEncoder->dequeueInputBuffer(&bufferIndex);
284
285    if (err == OK) {
286        mAvailEncoderInputIndices.push_back(bufferIndex);
287        feedEncoderInputBuffers();
288    }
289
290    size_t offset;
291    size_t size;
292    int64_t timeUs;
293    uint32_t flags;
294    err = mEncoder->dequeueOutputBuffer(
295            &bufferIndex, &offset, &size, &timeUs, &flags);
296
297    if (err == OK) {
298        if (flags & MediaCodec::BUFFER_FLAG_EOS) {
299            sp<AMessage> notify = mNotify->dup();
300            notify->setInt32("what", kWhatEOS);
301            notify->post();
302        } else {
303            ALOGV("out: %s timeUs = %lld us", mIsVideo ? "video" : "audio", timeUs);
304
305            sp<ABuffer> buffer = new ABuffer(size);
306            buffer->meta()->setInt64("timeUs", timeUs);
307
308            memcpy(buffer->data(),
309                   mEncoderOutputBuffers.itemAt(bufferIndex)->base() + offset,
310                   size);
311
312            if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
313                mOutputFormat->setBuffer("csd-0", buffer);
314            } else {
315                sp<AMessage> notify = mNotify->dup();
316                notify->setInt32("what", kWhatAccessUnit);
317                notify->setBuffer("accessUnit", buffer);
318                notify->post();
319            }
320        }
321
322        err = mEncoder->releaseOutputBuffer(bufferIndex);
323    } else if (err == -EAGAIN) {
324        err = OK;
325    }
326
327    return err;
328}
329
330void Converter::requestIDRFrame() {
331    (new AMessage(kWhatRequestIDRFrame, id()))->post();
332}
333
334}  // namespace android
335