Converter.cpp revision 308bcaa44e578279e61be32b572fdb0b11b1e4c7
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 "MediaPuller.h"
24
25#include <cutils/properties.h>
26#include <gui/Surface.h>
27#include <media/ICrypto.h>
28#include <media/stagefright/foundation/ABuffer.h>
29#include <media/stagefright/foundation/ADebug.h>
30#include <media/stagefright/foundation/AMessage.h>
31#include <media/stagefright/MediaBuffer.h>
32#include <media/stagefright/MediaCodec.h>
33#include <media/stagefright/MediaDefs.h>
34#include <media/stagefright/MediaErrors.h>
35
36#include <OMX_Video.h>
37
38namespace android {
39
40Converter::Converter(
41        const sp<AMessage> &notify,
42        const sp<ALooper> &codecLooper,
43        const sp<AMessage> &format,
44        bool usePCMAudio)
45    : mInitCheck(NO_INIT),
46      mNotify(notify),
47      mCodecLooper(codecLooper),
48      mInputFormat(format),
49      mIsVideo(false),
50      mIsPCMAudio(usePCMAudio),
51      mNeedToManuallyPrependSPSPPS(false),
52      mDoMoreWorkPending(false)
53#if ENABLE_SILENCE_DETECTION
54      ,mFirstSilentFrameUs(-1ll)
55      ,mInSilentMode(false)
56#endif
57      ,mPrevVideoBitrate(-1)
58      ,mNumFramesToDrop(0)
59    {
60    AString mime;
61    CHECK(mInputFormat->findString("mime", &mime));
62
63    if (!strncasecmp("video/", mime.c_str(), 6)) {
64        mIsVideo = true;
65    }
66
67    CHECK(!usePCMAudio || !mIsVideo);
68
69    mInitCheck = initEncoder();
70
71    if (mInitCheck != OK) {
72        releaseEncoder();
73    }
74}
75
76static void ReleaseMediaBufferReference(const sp<ABuffer> &accessUnit) {
77    void *mbuf;
78    if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
79            && mbuf != NULL) {
80        ALOGV("releasing mbuf %p", mbuf);
81
82        accessUnit->meta()->setPointer("mediaBuffer", NULL);
83
84        static_cast<MediaBuffer *>(mbuf)->release();
85        mbuf = NULL;
86    }
87}
88
89void Converter::releaseEncoder() {
90    if (mEncoder == NULL) {
91        return;
92    }
93
94    mEncoder->release();
95    mEncoder.clear();
96
97    while (!mInputBufferQueue.empty()) {
98        sp<ABuffer> accessUnit = *mInputBufferQueue.begin();
99        mInputBufferQueue.erase(mInputBufferQueue.begin());
100
101        ReleaseMediaBufferReference(accessUnit);
102    }
103
104    for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
105        sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
106        ReleaseMediaBufferReference(accessUnit);
107    }
108
109    mEncoderInputBuffers.clear();
110    mEncoderOutputBuffers.clear();
111}
112
113Converter::~Converter() {
114    CHECK(mEncoder == NULL);
115}
116
117void Converter::shutdownAsync() {
118    ALOGV("shutdown");
119    (new AMessage(kWhatShutdown, id()))->post();
120}
121
122status_t Converter::initCheck() const {
123    return mInitCheck;
124}
125
126size_t Converter::getInputBufferCount() const {
127    return mEncoderInputBuffers.size();
128}
129
130sp<AMessage> Converter::getOutputFormat() const {
131    return mOutputFormat;
132}
133
134bool Converter::needToManuallyPrependSPSPPS() const {
135    return mNeedToManuallyPrependSPSPPS;
136}
137
138// static
139int32_t Converter::GetInt32Property(
140        const char *propName, int32_t defaultValue) {
141    char val[PROPERTY_VALUE_MAX];
142    if (property_get(propName, val, NULL)) {
143        char *end;
144        unsigned long x = strtoul(val, &end, 10);
145
146        if (*end == '\0' && end > val && x > 0) {
147            return x;
148        }
149    }
150
151    return defaultValue;
152}
153
154status_t Converter::initEncoder() {
155    AString inputMIME;
156    CHECK(mInputFormat->findString("mime", &inputMIME));
157
158    AString outputMIME;
159    bool isAudio = false;
160    if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) {
161        if (mIsPCMAudio) {
162            outputMIME = MEDIA_MIMETYPE_AUDIO_RAW;
163        } else {
164            outputMIME = MEDIA_MIMETYPE_AUDIO_AAC;
165        }
166        isAudio = true;
167    } else if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_VIDEO_RAW)) {
168        outputMIME = MEDIA_MIMETYPE_VIDEO_AVC;
169    } else {
170        TRESPASS();
171    }
172
173    if (!mIsPCMAudio) {
174        mEncoder = MediaCodec::CreateByType(
175                mCodecLooper, outputMIME.c_str(), true /* encoder */);
176
177        if (mEncoder == NULL) {
178            return ERROR_UNSUPPORTED;
179        }
180    }
181
182    mOutputFormat = mInputFormat->dup();
183
184    if (mIsPCMAudio) {
185        return OK;
186    }
187
188    mOutputFormat->setString("mime", outputMIME.c_str());
189
190    int32_t audioBitrate = GetInt32Property("media.wfd.audio-bitrate", 128000);
191    int32_t videoBitrate = GetInt32Property("media.wfd.video-bitrate", 5000000);
192    mPrevVideoBitrate = videoBitrate;
193
194    ALOGI("using audio bitrate of %d bps, video bitrate of %d bps",
195          audioBitrate, videoBitrate);
196
197    if (isAudio) {
198        mOutputFormat->setInt32("bitrate", audioBitrate);
199    } else {
200        mOutputFormat->setInt32("bitrate", videoBitrate);
201        mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant);
202        mOutputFormat->setInt32("frame-rate", 30);
203        mOutputFormat->setInt32("i-frame-interval", 15);  // Iframes every 15 secs
204
205        // Configure encoder to use intra macroblock refresh mode
206        mOutputFormat->setInt32("intra-refresh-mode", OMX_VIDEO_IntraRefreshCyclic);
207
208        int width, height, mbs;
209        if (!mOutputFormat->findInt32("width", &width)
210                || !mOutputFormat->findInt32("height", &height)) {
211            return ERROR_UNSUPPORTED;
212        }
213
214        // Update macroblocks in a cyclic fashion with 10% of all MBs within
215        // frame gets updated at one time. It takes about 10 frames to
216        // completely update a whole video frame. If the frame rate is 30,
217        // it takes about 333 ms in the best case (if next frame is not an IDR)
218        // to recover from a lost/corrupted packet.
219        mbs = (((width + 15) / 16) * ((height + 15) / 16) * 10) / 100;
220        mOutputFormat->setInt32("intra-refresh-CIR-mbs", mbs);
221    }
222
223    ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
224
225    mNeedToManuallyPrependSPSPPS = false;
226
227    status_t err = NO_INIT;
228
229    if (!isAudio) {
230        sp<AMessage> tmp = mOutputFormat->dup();
231        tmp->setInt32("prepend-sps-pps-to-idr-frames", 1);
232
233        err = mEncoder->configure(
234                tmp,
235                NULL /* nativeWindow */,
236                NULL /* crypto */,
237                MediaCodec::CONFIGURE_FLAG_ENCODE);
238
239        if (err == OK) {
240            // Encoder supported prepending SPS/PPS, we don't need to emulate
241            // it.
242            mOutputFormat = tmp;
243        } else {
244            mNeedToManuallyPrependSPSPPS = true;
245
246            ALOGI("We going to manually prepend SPS and PPS to IDR frames.");
247        }
248    }
249
250    if (err != OK) {
251        // We'll get here for audio or if we failed to configure the encoder
252        // to automatically prepend SPS/PPS in the case of video.
253
254        err = mEncoder->configure(
255                    mOutputFormat,
256                    NULL /* nativeWindow */,
257                    NULL /* crypto */,
258                    MediaCodec::CONFIGURE_FLAG_ENCODE);
259    }
260
261    if (err != OK) {
262        return err;
263    }
264
265    err = mEncoder->start();
266
267    if (err != OK) {
268        return err;
269    }
270
271    err = mEncoder->getInputBuffers(&mEncoderInputBuffers);
272
273    if (err != OK) {
274        return err;
275    }
276
277    return mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
278}
279
280void Converter::notifyError(status_t err) {
281    sp<AMessage> notify = mNotify->dup();
282    notify->setInt32("what", kWhatError);
283    notify->setInt32("err", err);
284    notify->post();
285}
286
287// static
288bool Converter::IsSilence(const sp<ABuffer> &accessUnit) {
289    const uint8_t *ptr = accessUnit->data();
290    const uint8_t *end = ptr + accessUnit->size();
291    while (ptr < end) {
292        if (*ptr != 0) {
293            return false;
294        }
295        ++ptr;
296    }
297
298    return true;
299}
300
301void Converter::onMessageReceived(const sp<AMessage> &msg) {
302    switch (msg->what()) {
303        case kWhatMediaPullerNotify:
304        {
305            int32_t what;
306            CHECK(msg->findInt32("what", &what));
307
308            if (!mIsPCMAudio && mEncoder == NULL) {
309                ALOGV("got msg '%s' after encoder shutdown.",
310                      msg->debugString().c_str());
311
312                if (what == MediaPuller::kWhatAccessUnit) {
313                    sp<ABuffer> accessUnit;
314                    CHECK(msg->findBuffer("accessUnit", &accessUnit));
315
316                    ReleaseMediaBufferReference(accessUnit);
317                }
318                break;
319            }
320
321            if (what == MediaPuller::kWhatEOS) {
322                mInputBufferQueue.push_back(NULL);
323
324                feedEncoderInputBuffers();
325
326                scheduleDoMoreWork();
327            } else {
328                CHECK_EQ(what, MediaPuller::kWhatAccessUnit);
329
330                sp<ABuffer> accessUnit;
331                CHECK(msg->findBuffer("accessUnit", &accessUnit));
332
333                if (mIsVideo && mNumFramesToDrop) {
334                    --mNumFramesToDrop;
335                    ALOGI("dropping frame.");
336                    ReleaseMediaBufferReference(accessUnit);
337                    break;
338                }
339
340#if 0
341                void *mbuf;
342                if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
343                        && mbuf != NULL) {
344                    ALOGI("queueing mbuf %p", mbuf);
345                }
346#endif
347
348#if ENABLE_SILENCE_DETECTION
349                if (!mIsVideo) {
350                    if (IsSilence(accessUnit)) {
351                        if (mInSilentMode) {
352                            break;
353                        }
354
355                        int64_t nowUs = ALooper::GetNowUs();
356
357                        if (mFirstSilentFrameUs < 0ll) {
358                            mFirstSilentFrameUs = nowUs;
359                        } else if (nowUs >= mFirstSilentFrameUs + 10000000ll) {
360                            mInSilentMode = true;
361                            ALOGI("audio in silent mode now.");
362                            break;
363                        }
364                    } else {
365                        if (mInSilentMode) {
366                            ALOGI("audio no longer in silent mode.");
367                        }
368                        mInSilentMode = false;
369                        mFirstSilentFrameUs = -1ll;
370                    }
371                }
372#endif
373
374                mInputBufferQueue.push_back(accessUnit);
375
376                feedEncoderInputBuffers();
377
378                scheduleDoMoreWork();
379            }
380            break;
381        }
382
383        case kWhatEncoderActivity:
384        {
385#if 0
386            int64_t whenUs;
387            if (msg->findInt64("whenUs", &whenUs)) {
388                int64_t nowUs = ALooper::GetNowUs();
389                ALOGI("[%s] kWhatEncoderActivity after %lld us",
390                      mIsVideo ? "video" : "audio", nowUs - whenUs);
391            }
392#endif
393
394            mDoMoreWorkPending = false;
395
396            if (mEncoder == NULL) {
397                break;
398            }
399
400            status_t err = doMoreWork();
401
402            if (err != OK) {
403                notifyError(err);
404            } else {
405                scheduleDoMoreWork();
406            }
407            break;
408        }
409
410        case kWhatRequestIDRFrame:
411        {
412            if (mEncoder == NULL) {
413                break;
414            }
415
416            if (mIsVideo) {
417                ALOGI("requesting IDR frame");
418                mEncoder->requestIDRFrame();
419            }
420            break;
421        }
422
423        case kWhatShutdown:
424        {
425            ALOGI("shutting down %s encoder", mIsVideo ? "video" : "audio");
426
427            releaseEncoder();
428
429            AString mime;
430            CHECK(mInputFormat->findString("mime", &mime));
431            ALOGI("encoder (%s) shut down.", mime.c_str());
432            break;
433        }
434
435        case kWhatDropAFrame:
436        {
437            ++mNumFramesToDrop;
438            break;
439        }
440
441        case kWhatReleaseOutputBuffer:
442        {
443            if (mEncoder != NULL) {
444                size_t bufferIndex;
445                CHECK(msg->findInt32("bufferIndex", (int32_t*)&bufferIndex));
446                CHECK(bufferIndex < mEncoderOutputBuffers.size());
447                mEncoder->releaseOutputBuffer(bufferIndex);
448            }
449            break;
450        }
451
452        default:
453            TRESPASS();
454    }
455}
456
457void Converter::scheduleDoMoreWork() {
458    if (mIsPCMAudio) {
459        // There's no encoder involved in this case.
460        return;
461    }
462
463    if (mDoMoreWorkPending) {
464        return;
465    }
466
467    mDoMoreWorkPending = true;
468
469#if 1
470    if (mEncoderActivityNotify == NULL) {
471        mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, id());
472    }
473    mEncoder->requestActivityNotification(mEncoderActivityNotify->dup());
474#else
475    sp<AMessage> notify = new AMessage(kWhatEncoderActivity, id());
476    notify->setInt64("whenUs", ALooper::GetNowUs());
477    mEncoder->requestActivityNotification(notify);
478#endif
479}
480
481status_t Converter::feedRawAudioInputBuffers() {
482    // Split incoming PCM audio into buffers of 6 AUs of 80 audio frames each
483    // and add a 4 byte header according to the wifi display specs.
484
485    while (!mInputBufferQueue.empty()) {
486        sp<ABuffer> buffer = *mInputBufferQueue.begin();
487        mInputBufferQueue.erase(mInputBufferQueue.begin());
488
489        int16_t *ptr = (int16_t *)buffer->data();
490        int16_t *stop = (int16_t *)(buffer->data() + buffer->size());
491        while (ptr < stop) {
492            *ptr = htons(*ptr);
493            ++ptr;
494        }
495
496        static const size_t kFrameSize = 2 * sizeof(int16_t);  // stereo
497        static const size_t kFramesPerAU = 80;
498        static const size_t kNumAUsPerPESPacket = 6;
499
500        if (mPartialAudioAU != NULL) {
501            size_t bytesMissingForFullAU =
502                kNumAUsPerPESPacket * kFramesPerAU * kFrameSize
503                - mPartialAudioAU->size() + 4;
504
505            size_t copy = buffer->size();
506            if(copy > bytesMissingForFullAU) {
507                copy = bytesMissingForFullAU;
508            }
509
510            memcpy(mPartialAudioAU->data() + mPartialAudioAU->size(),
511                   buffer->data(),
512                   copy);
513
514            mPartialAudioAU->setRange(0, mPartialAudioAU->size() + copy);
515
516            buffer->setRange(buffer->offset() + copy, buffer->size() - copy);
517
518            int64_t timeUs;
519            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
520
521            int64_t copyUs = (int64_t)((copy / kFrameSize) * 1E6 / 48000.0);
522            timeUs += copyUs;
523            buffer->meta()->setInt64("timeUs", timeUs);
524
525            if (bytesMissingForFullAU == copy) {
526                sp<AMessage> notify = mNotify->dup();
527                notify->setInt32("what", kWhatAccessUnit);
528                notify->setBuffer("accessUnit", mPartialAudioAU);
529                notify->post();
530
531                mPartialAudioAU.clear();
532            }
533        }
534
535        while (buffer->size() > 0) {
536            sp<ABuffer> partialAudioAU =
537                new ABuffer(
538                        4
539                        + kNumAUsPerPESPacket * kFrameSize * kFramesPerAU);
540
541            uint8_t *ptr = partialAudioAU->data();
542            ptr[0] = 0xa0;  // 10100000b
543            ptr[1] = kNumAUsPerPESPacket;
544            ptr[2] = 0;  // reserved, audio _emphasis_flag = 0
545
546            static const unsigned kQuantizationWordLength = 0;  // 16-bit
547            static const unsigned kAudioSamplingFrequency = 2;  // 48Khz
548            static const unsigned kNumberOfAudioChannels = 1;  // stereo
549
550            ptr[3] = (kQuantizationWordLength << 6)
551                    | (kAudioSamplingFrequency << 3)
552                    | kNumberOfAudioChannels;
553
554            size_t copy = buffer->size();
555            if (copy > partialAudioAU->size() - 4) {
556                copy = partialAudioAU->size() - 4;
557            }
558
559            memcpy(&ptr[4], buffer->data(), copy);
560
561            partialAudioAU->setRange(0, 4 + copy);
562            buffer->setRange(buffer->offset() + copy, buffer->size() - copy);
563
564            int64_t timeUs;
565            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
566
567            partialAudioAU->meta()->setInt64("timeUs", timeUs);
568
569            int64_t copyUs = (int64_t)((copy / kFrameSize) * 1E6 / 48000.0);
570            timeUs += copyUs;
571            buffer->meta()->setInt64("timeUs", timeUs);
572
573            if (copy == partialAudioAU->capacity() - 4) {
574                sp<AMessage> notify = mNotify->dup();
575                notify->setInt32("what", kWhatAccessUnit);
576                notify->setBuffer("accessUnit", partialAudioAU);
577                notify->post();
578
579                partialAudioAU.clear();
580                continue;
581            }
582
583            mPartialAudioAU = partialAudioAU;
584        }
585    }
586
587    return OK;
588}
589
590status_t Converter::feedEncoderInputBuffers() {
591    if (mIsPCMAudio) {
592        return feedRawAudioInputBuffers();
593    }
594
595    while (!mInputBufferQueue.empty()
596            && !mAvailEncoderInputIndices.empty()) {
597        sp<ABuffer> buffer = *mInputBufferQueue.begin();
598        mInputBufferQueue.erase(mInputBufferQueue.begin());
599
600        size_t bufferIndex = *mAvailEncoderInputIndices.begin();
601        mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
602
603        int64_t timeUs = 0ll;
604        uint32_t flags = 0;
605
606        if (buffer != NULL) {
607            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
608
609            memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(),
610                   buffer->data(),
611                   buffer->size());
612
613            void *mediaBuffer;
614            if (buffer->meta()->findPointer("mediaBuffer", &mediaBuffer)
615                    && mediaBuffer != NULL) {
616                mEncoderInputBuffers.itemAt(bufferIndex)->meta()
617                    ->setPointer("mediaBuffer", mediaBuffer);
618
619                buffer->meta()->setPointer("mediaBuffer", NULL);
620            }
621        } else {
622            flags = MediaCodec::BUFFER_FLAG_EOS;
623        }
624
625        status_t err = mEncoder->queueInputBuffer(
626                bufferIndex, 0, (buffer == NULL) ? 0 : buffer->size(),
627                timeUs, flags);
628
629        if (err != OK) {
630            return err;
631        }
632    }
633
634    return OK;
635}
636
637status_t Converter::doMoreWork() {
638    status_t err;
639
640    for (;;) {
641        size_t bufferIndex;
642        err = mEncoder->dequeueInputBuffer(&bufferIndex);
643
644        if (err != OK) {
645            break;
646        }
647
648        mAvailEncoderInputIndices.push_back(bufferIndex);
649    }
650
651    feedEncoderInputBuffers();
652
653    for (;;) {
654        size_t bufferIndex;
655        size_t offset;
656        size_t size;
657        int64_t timeUs;
658        uint32_t flags;
659        native_handle_t* handle = NULL;
660        err = mEncoder->dequeueOutputBuffer(
661                &bufferIndex, &offset, &size, &timeUs, &flags);
662
663        if (err != OK) {
664            if (err == INFO_FORMAT_CHANGED) {
665                continue;
666            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
667                mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
668                continue;
669            }
670
671            if (err == -EAGAIN) {
672                err = OK;
673            }
674            break;
675        }
676
677        if (flags & MediaCodec::BUFFER_FLAG_EOS) {
678            sp<AMessage> notify = mNotify->dup();
679            notify->setInt32("what", kWhatEOS);
680            notify->post();
681        } else {
682            sp<ABuffer> buffer;
683            sp<ABuffer> outbuf = mEncoderOutputBuffers.itemAt(bufferIndex);
684
685            if (outbuf->meta()->findPointer("handle", (void**)&handle) &&
686                    handle != NULL) {
687                int32_t rangeLength, rangeOffset;
688                CHECK(outbuf->meta()->findInt32("rangeOffset", &rangeOffset));
689                CHECK(outbuf->meta()->findInt32("rangeLength", &rangeLength));
690                outbuf->meta()->setPointer("handle", NULL);
691
692                // MediaSender will post the following message when HDCP
693                // is done, to release the output buffer back to encoder.
694                sp<AMessage> notify(new AMessage(
695                        kWhatReleaseOutputBuffer, id()));
696                notify->setInt32("bufferIndex", bufferIndex);
697
698                buffer = new ABuffer(
699                        rangeLength > (int32_t)size ? rangeLength : size);
700                buffer->meta()->setPointer("handle", handle);
701                buffer->meta()->setInt32("rangeOffset", rangeOffset);
702                buffer->meta()->setInt32("rangeLength", rangeLength);
703                buffer->meta()->setMessage("notify", notify);
704            } else {
705                buffer = new ABuffer(size);
706            }
707
708            buffer->meta()->setInt64("timeUs", timeUs);
709
710            ALOGV("[%s] time %lld us (%.2f secs)",
711                  mIsVideo ? "video" : "audio", timeUs, timeUs / 1E6);
712
713            memcpy(buffer->data(), outbuf->base() + offset, size);
714
715            if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
716                if (!handle) {
717                    mOutputFormat->setBuffer("csd-0", buffer);
718                }
719            } else {
720                sp<AMessage> notify = mNotify->dup();
721                notify->setInt32("what", kWhatAccessUnit);
722                notify->setBuffer("accessUnit", buffer);
723                notify->post();
724            }
725        }
726
727        if (!handle) {
728            mEncoder->releaseOutputBuffer(bufferIndex);
729        }
730
731        if (flags & MediaCodec::BUFFER_FLAG_EOS) {
732            break;
733        }
734    }
735
736    return err;
737}
738
739void Converter::requestIDRFrame() {
740    (new AMessage(kWhatRequestIDRFrame, id()))->post();
741}
742
743void Converter::dropAFrame() {
744    (new AMessage(kWhatDropAFrame, id()))->post();
745}
746
747int32_t Converter::getVideoBitrate() const {
748    return mPrevVideoBitrate;
749}
750
751void Converter::setVideoBitrate(int32_t bitRate) {
752    if (mIsVideo && mEncoder != NULL && bitRate != mPrevVideoBitrate) {
753        sp<AMessage> params = new AMessage;
754        params->setInt32("videoBitrate", bitRate);
755
756        mEncoder->setParameters(params);
757
758        mPrevVideoBitrate = bitRate;
759    }
760}
761
762}  // namespace android
763