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> ¬ify, 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