Utils.cpp revision 46f80165c595d81dda68f8f3fea27f4fb04937dd
1/* 2 * Copyright (C) 2009 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 "Utils" 19#include <utils/Log.h> 20#include <ctype.h> 21#include <stdio.h> 22#include <sys/stat.h> 23 24#include "include/ESDS.h" 25#include "include/HevcUtils.h" 26 27#include <arpa/inet.h> 28#include <cutils/properties.h> 29#include <media/openmax/OMX_Audio.h> 30#include <media/stagefright/CodecBase.h> 31#include <media/stagefright/foundation/ABuffer.h> 32#include <media/stagefright/foundation/ADebug.h> 33#include <media/stagefright/foundation/AMessage.h> 34#include <media/stagefright/MetaData.h> 35#include <media/stagefright/MediaDefs.h> 36#include <media/AudioSystem.h> 37#include <media/MediaPlayerInterface.h> 38#include <hardware/audio.h> 39#include <media/stagefright/Utils.h> 40#include <media/AudioParameter.h> 41 42namespace android { 43 44uint16_t U16_AT(const uint8_t *ptr) { 45 return ptr[0] << 8 | ptr[1]; 46} 47 48uint32_t U32_AT(const uint8_t *ptr) { 49 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; 50} 51 52uint64_t U64_AT(const uint8_t *ptr) { 53 return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4); 54} 55 56uint16_t U16LE_AT(const uint8_t *ptr) { 57 return ptr[0] | (ptr[1] << 8); 58} 59 60uint32_t U32LE_AT(const uint8_t *ptr) { 61 return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0]; 62} 63 64uint64_t U64LE_AT(const uint8_t *ptr) { 65 return ((uint64_t)U32LE_AT(ptr + 4)) << 32 | U32LE_AT(ptr); 66} 67 68// XXX warning: these won't work on big-endian host. 69uint64_t ntoh64(uint64_t x) { 70 return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32); 71} 72 73uint64_t hton64(uint64_t x) { 74 return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32); 75} 76 77static status_t copyNALUToABuffer(sp<ABuffer> *buffer, const uint8_t *ptr, size_t length) { 78 if (((*buffer)->size() + 4 + length) > ((*buffer)->capacity() - (*buffer)->offset())) { 79 sp<ABuffer> tmpBuffer = new (std::nothrow) ABuffer((*buffer)->size() + 4 + length + 1024); 80 if (tmpBuffer.get() == NULL || tmpBuffer->base() == NULL) { 81 return NO_MEMORY; 82 } 83 memcpy(tmpBuffer->data(), (*buffer)->data(), (*buffer)->size()); 84 tmpBuffer->setRange(0, (*buffer)->size()); 85 (*buffer) = tmpBuffer; 86 } 87 88 memcpy((*buffer)->data() + (*buffer)->size(), "\x00\x00\x00\x01", 4); 89 memcpy((*buffer)->data() + (*buffer)->size() + 4, ptr, length); 90 (*buffer)->setRange((*buffer)->offset(), (*buffer)->size() + 4 + length); 91 return OK; 92} 93 94static void convertMetaDataToMessageInt32( 95 const sp<MetaData> &meta, sp<AMessage> &msg, uint32_t key, const char *name) { 96 int32_t value; 97 if (meta->findInt32(key, &value)) { 98 msg->setInt32(name, value); 99 } 100} 101 102static void convertMetaDataToMessageColorAspects(const sp<MetaData> &meta, sp<AMessage> &msg) { 103 // 0 values are unspecified 104 int32_t range = 0; 105 int32_t primaries = 0; 106 int32_t transferFunction = 0; 107 int32_t colorMatrix = 0; 108 meta->findInt32(kKeyColorRange, &range); 109 meta->findInt32(kKeyColorPrimaries, &primaries); 110 meta->findInt32(kKeyTransferFunction, &transferFunction); 111 meta->findInt32(kKeyColorMatrix, &colorMatrix); 112 ColorAspects colorAspects; 113 memset(&colorAspects, 0, sizeof(colorAspects)); 114 colorAspects.mRange = (ColorAspects::Range)range; 115 colorAspects.mPrimaries = (ColorAspects::Primaries)primaries; 116 colorAspects.mTransfer = (ColorAspects::Transfer)transferFunction; 117 colorAspects.mMatrixCoeffs = (ColorAspects::MatrixCoeffs)colorMatrix; 118 119 int32_t rangeMsg, standardMsg, transferMsg; 120 if (CodecBase::convertCodecColorAspectsToPlatformAspects( 121 colorAspects, &rangeMsg, &standardMsg, &transferMsg) != OK) { 122 return; 123 } 124 125 // save specified values to msg 126 if (rangeMsg != 0) { 127 msg->setInt32("color-range", rangeMsg); 128 } 129 if (standardMsg != 0) { 130 msg->setInt32("color-standard", standardMsg); 131 } 132 if (transferMsg != 0) { 133 msg->setInt32("color-transfer", transferMsg); 134 } 135} 136 137status_t convertMetaDataToMessage( 138 const sp<MetaData> &meta, sp<AMessage> *format) { 139 140 format->clear(); 141 142 if (meta == NULL) { 143 ALOGE("convertMetaDataToMessage: NULL input"); 144 return BAD_VALUE; 145 } 146 147 const char *mime; 148 if (!meta->findCString(kKeyMIMEType, &mime)) { 149 return BAD_VALUE; 150 } 151 152 sp<AMessage> msg = new AMessage; 153 msg->setString("mime", mime); 154 155 int64_t durationUs; 156 if (meta->findInt64(kKeyDuration, &durationUs)) { 157 msg->setInt64("durationUs", durationUs); 158 } 159 160 int32_t avgBitRate; 161 if (meta->findInt32(kKeyBitRate, &avgBitRate)) { 162 msg->setInt32("bitrate", avgBitRate); 163 } 164 165 int32_t maxBitRate; 166 if (meta->findInt32(kKeyMaxBitRate, &maxBitRate)) { 167 msg->setInt32("max-bitrate", maxBitRate); 168 } 169 170 int32_t isSync; 171 if (meta->findInt32(kKeyIsSyncFrame, &isSync) && isSync != 0) { 172 msg->setInt32("is-sync-frame", 1); 173 } 174 175 // this only needs to be translated from meta to message as it is an extractor key 176 int32_t trackID; 177 if (meta->findInt32(kKeyTrackID, &trackID)) { 178 msg->setInt32("track-id", trackID); 179 } 180 181 if (!strncasecmp("video/", mime, 6)) { 182 int32_t width, height; 183 if (!meta->findInt32(kKeyWidth, &width) 184 || !meta->findInt32(kKeyHeight, &height)) { 185 return BAD_VALUE; 186 } 187 188 msg->setInt32("width", width); 189 msg->setInt32("height", height); 190 191 int32_t sarWidth, sarHeight; 192 if (meta->findInt32(kKeySARWidth, &sarWidth) 193 && meta->findInt32(kKeySARHeight, &sarHeight)) { 194 msg->setInt32("sar-width", sarWidth); 195 msg->setInt32("sar-height", sarHeight); 196 } 197 198 int32_t colorFormat; 199 if (meta->findInt32(kKeyColorFormat, &colorFormat)) { 200 msg->setInt32("color-format", colorFormat); 201 } 202 203 int32_t cropLeft, cropTop, cropRight, cropBottom; 204 if (meta->findRect(kKeyCropRect, 205 &cropLeft, 206 &cropTop, 207 &cropRight, 208 &cropBottom)) { 209 msg->setRect("crop", cropLeft, cropTop, cropRight, cropBottom); 210 } 211 212 int32_t rotationDegrees; 213 if (meta->findInt32(kKeyRotation, &rotationDegrees)) { 214 msg->setInt32("rotation-degrees", rotationDegrees); 215 } 216 217 convertMetaDataToMessageInt32(meta, msg, kKeyMinLuminance, "min-luminance"); 218 convertMetaDataToMessageInt32(meta, msg, kKeyMaxLuminance, "max-luminance"); 219 convertMetaDataToMessageColorAspects(meta, msg); 220 } else if (!strncasecmp("audio/", mime, 6)) { 221 int32_t numChannels, sampleRate; 222 if (!meta->findInt32(kKeyChannelCount, &numChannels) 223 || !meta->findInt32(kKeySampleRate, &sampleRate)) { 224 return BAD_VALUE; 225 } 226 227 msg->setInt32("channel-count", numChannels); 228 msg->setInt32("sample-rate", sampleRate); 229 230 int32_t channelMask; 231 if (meta->findInt32(kKeyChannelMask, &channelMask)) { 232 msg->setInt32("channel-mask", channelMask); 233 } 234 235 int32_t delay = 0; 236 if (meta->findInt32(kKeyEncoderDelay, &delay)) { 237 msg->setInt32("encoder-delay", delay); 238 } 239 int32_t padding = 0; 240 if (meta->findInt32(kKeyEncoderPadding, &padding)) { 241 msg->setInt32("encoder-padding", padding); 242 } 243 244 int32_t isADTS; 245 if (meta->findInt32(kKeyIsADTS, &isADTS)) { 246 msg->setInt32("is-adts", isADTS); 247 } 248 249 int32_t aacProfile = -1; 250 if (meta->findInt32(kKeyAACAOT, &aacProfile)) { 251 msg->setInt32("aac-profile", aacProfile); 252 } 253 254 int32_t pcmEncoding; 255 if (meta->findInt32(kKeyPcmEncoding, &pcmEncoding)) { 256 msg->setInt32("pcm-encoding", pcmEncoding); 257 } 258 } 259 260 int32_t maxInputSize; 261 if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) { 262 msg->setInt32("max-input-size", maxInputSize); 263 } 264 265 int32_t maxWidth; 266 if (meta->findInt32(kKeyMaxWidth, &maxWidth)) { 267 msg->setInt32("max-width", maxWidth); 268 } 269 270 int32_t maxHeight; 271 if (meta->findInt32(kKeyMaxHeight, &maxHeight)) { 272 msg->setInt32("max-height", maxHeight); 273 } 274 275 int32_t rotationDegrees; 276 if (meta->findInt32(kKeyRotation, &rotationDegrees)) { 277 msg->setInt32("rotation-degrees", rotationDegrees); 278 } 279 280 int32_t fps; 281 if (meta->findInt32(kKeyFrameRate, &fps) && fps > 0) { 282 msg->setInt32("frame-rate", fps); 283 } 284 285 uint32_t type; 286 const void *data; 287 size_t size; 288 if (meta->findData(kKeyAVCC, &type, &data, &size)) { 289 // Parse the AVCDecoderConfigurationRecord 290 291 const uint8_t *ptr = (const uint8_t *)data; 292 293 if (size < 7 || ptr[0] != 1) { // configurationVersion == 1 294 ALOGE("b/23680780"); 295 return BAD_VALUE; 296 } 297 uint8_t profile __unused = ptr[1]; 298 uint8_t level __unused = ptr[3]; 299 300 // There is decodable content out there that fails the following 301 // assertion, let's be lenient for now... 302 // CHECK((ptr[4] >> 2) == 0x3f); // reserved 303 304 size_t lengthSize __unused = 1 + (ptr[4] & 3); 305 306 // commented out check below as H264_QVGA_500_NO_AUDIO.3gp 307 // violates it... 308 // CHECK((ptr[5] >> 5) == 7); // reserved 309 310 size_t numSeqParameterSets = ptr[5] & 31; 311 312 ptr += 6; 313 size -= 6; 314 315 sp<ABuffer> buffer = new (std::nothrow) ABuffer(1024); 316 if (buffer.get() == NULL || buffer->base() == NULL) { 317 return NO_MEMORY; 318 } 319 buffer->setRange(0, 0); 320 321 for (size_t i = 0; i < numSeqParameterSets; ++i) { 322 if (size < 2) { 323 ALOGE("b/23680780"); 324 return BAD_VALUE; 325 } 326 size_t length = U16_AT(ptr); 327 328 ptr += 2; 329 size -= 2; 330 331 if (size < length) { 332 return BAD_VALUE; 333 } 334 status_t err = copyNALUToABuffer(&buffer, ptr, length); 335 if (err != OK) { 336 return err; 337 } 338 339 ptr += length; 340 size -= length; 341 } 342 343 buffer->meta()->setInt32("csd", true); 344 buffer->meta()->setInt64("timeUs", 0); 345 346 msg->setBuffer("csd-0", buffer); 347 348 buffer = new (std::nothrow) ABuffer(1024); 349 if (buffer.get() == NULL || buffer->base() == NULL) { 350 return NO_MEMORY; 351 } 352 buffer->setRange(0, 0); 353 354 if (size < 1) { 355 ALOGE("b/23680780"); 356 return BAD_VALUE; 357 } 358 size_t numPictureParameterSets = *ptr; 359 ++ptr; 360 --size; 361 362 for (size_t i = 0; i < numPictureParameterSets; ++i) { 363 if (size < 2) { 364 ALOGE("b/23680780"); 365 return BAD_VALUE; 366 } 367 size_t length = U16_AT(ptr); 368 369 ptr += 2; 370 size -= 2; 371 372 if (size < length) { 373 return BAD_VALUE; 374 } 375 status_t err = copyNALUToABuffer(&buffer, ptr, length); 376 if (err != OK) { 377 return err; 378 } 379 380 ptr += length; 381 size -= length; 382 } 383 384 buffer->meta()->setInt32("csd", true); 385 buffer->meta()->setInt64("timeUs", 0); 386 msg->setBuffer("csd-1", buffer); 387 } else if (meta->findData(kKeyHVCC, &type, &data, &size)) { 388 const uint8_t *ptr = (const uint8_t *)data; 389 390 if (size < 23 || ptr[0] != 1) { // configurationVersion == 1 391 ALOGE("b/23680780"); 392 return BAD_VALUE; 393 } 394 uint8_t profile __unused = ptr[1] & 31; 395 uint8_t level __unused = ptr[12]; 396 ptr += 22; 397 size -= 22; 398 399 400 size_t numofArrays = (char)ptr[0]; 401 ptr += 1; 402 size -= 1; 403 size_t j = 0, i = 0; 404 405 sp<ABuffer> buffer = new (std::nothrow) ABuffer(1024); 406 if (buffer.get() == NULL || buffer->base() == NULL) { 407 return NO_MEMORY; 408 } 409 buffer->setRange(0, 0); 410 411 for (i = 0; i < numofArrays; i++) { 412 if (size < 3) { 413 ALOGE("b/23680780"); 414 return BAD_VALUE; 415 } 416 ptr += 1; 417 size -= 1; 418 419 //Num of nals 420 size_t numofNals = U16_AT(ptr); 421 422 ptr += 2; 423 size -= 2; 424 425 for (j = 0; j < numofNals; j++) { 426 if (size < 2) { 427 ALOGE("b/23680780"); 428 return BAD_VALUE; 429 } 430 size_t length = U16_AT(ptr); 431 432 ptr += 2; 433 size -= 2; 434 435 if (size < length) { 436 return BAD_VALUE; 437 } 438 status_t err = copyNALUToABuffer(&buffer, ptr, length); 439 if (err != OK) { 440 return err; 441 } 442 443 ptr += length; 444 size -= length; 445 } 446 } 447 buffer->meta()->setInt32("csd", true); 448 buffer->meta()->setInt64("timeUs", 0); 449 msg->setBuffer("csd-0", buffer); 450 451 } else if (meta->findData(kKeyESDS, &type, &data, &size)) { 452 ESDS esds((const char *)data, size); 453 if (esds.InitCheck() != (status_t)OK) { 454 return BAD_VALUE; 455 } 456 457 const void *codec_specific_data; 458 size_t codec_specific_data_size; 459 esds.getCodecSpecificInfo( 460 &codec_specific_data, &codec_specific_data_size); 461 462 sp<ABuffer> buffer = new (std::nothrow) ABuffer(codec_specific_data_size); 463 if (buffer.get() == NULL || buffer->base() == NULL) { 464 return NO_MEMORY; 465 } 466 467 memcpy(buffer->data(), codec_specific_data, 468 codec_specific_data_size); 469 470 buffer->meta()->setInt32("csd", true); 471 buffer->meta()->setInt64("timeUs", 0); 472 msg->setBuffer("csd-0", buffer); 473 474 uint32_t maxBitrate, avgBitrate; 475 if (esds.getBitRate(&maxBitrate, &avgBitrate) == OK) { 476 if (!meta->hasData(kKeyMaxBitRate) 477 && maxBitrate > 0 && maxBitrate <= INT32_MAX) { 478 msg->setInt32("max-bitrate", (int32_t)maxBitrate); 479 } 480 if (!meta->hasData(kKeyBitRate) 481 && avgBitrate > 0 && avgBitrate <= INT32_MAX) { 482 msg->setInt32("bitrate", (int32_t)avgBitrate); 483 } 484 } 485 } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { 486 sp<ABuffer> buffer = new (std::nothrow) ABuffer(size); 487 if (buffer.get() == NULL || buffer->base() == NULL) { 488 return NO_MEMORY; 489 } 490 memcpy(buffer->data(), data, size); 491 492 buffer->meta()->setInt32("csd", true); 493 buffer->meta()->setInt64("timeUs", 0); 494 msg->setBuffer("csd-0", buffer); 495 496 if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) { 497 return -EINVAL; 498 } 499 500 buffer = new (std::nothrow) ABuffer(size); 501 if (buffer.get() == NULL || buffer->base() == NULL) { 502 return NO_MEMORY; 503 } 504 memcpy(buffer->data(), data, size); 505 506 buffer->meta()->setInt32("csd", true); 507 buffer->meta()->setInt64("timeUs", 0); 508 msg->setBuffer("csd-1", buffer); 509 } else if (meta->findData(kKeyOpusHeader, &type, &data, &size)) { 510 sp<ABuffer> buffer = new (std::nothrow) ABuffer(size); 511 if (buffer.get() == NULL || buffer->base() == NULL) { 512 return NO_MEMORY; 513 } 514 memcpy(buffer->data(), data, size); 515 516 buffer->meta()->setInt32("csd", true); 517 buffer->meta()->setInt64("timeUs", 0); 518 msg->setBuffer("csd-0", buffer); 519 520 if (!meta->findData(kKeyOpusCodecDelay, &type, &data, &size)) { 521 return -EINVAL; 522 } 523 524 buffer = new (std::nothrow) ABuffer(size); 525 if (buffer.get() == NULL || buffer->base() == NULL) { 526 return NO_MEMORY; 527 } 528 memcpy(buffer->data(), data, size); 529 530 buffer->meta()->setInt32("csd", true); 531 buffer->meta()->setInt64("timeUs", 0); 532 msg->setBuffer("csd-1", buffer); 533 534 if (!meta->findData(kKeyOpusSeekPreRoll, &type, &data, &size)) { 535 return -EINVAL; 536 } 537 538 buffer = new (std::nothrow) ABuffer(size); 539 if (buffer.get() == NULL || buffer->base() == NULL) { 540 return NO_MEMORY; 541 } 542 memcpy(buffer->data(), data, size); 543 544 buffer->meta()->setInt32("csd", true); 545 buffer->meta()->setInt64("timeUs", 0); 546 msg->setBuffer("csd-2", buffer); 547 } else if (meta->findData(kKeyVp9CodecPrivate, &type, &data, &size)) { 548 sp<ABuffer> buffer = new (std::nothrow) ABuffer(size); 549 if (buffer.get() == NULL || buffer->base() == NULL) { 550 return NO_MEMORY; 551 } 552 memcpy(buffer->data(), data, size); 553 554 buffer->meta()->setInt32("csd", true); 555 buffer->meta()->setInt64("timeUs", 0); 556 msg->setBuffer("csd-0", buffer); 557 } 558 559 // TODO expose "crypto-key"/kKeyCryptoKey through public api 560 if (meta->findData(kKeyCryptoKey, &type, &data, &size)) { 561 sp<ABuffer> buffer = new (std::nothrow) ABuffer(size); 562 msg->setBuffer("crypto-key", buffer); 563 memcpy(buffer->data(), data, size); 564 } 565 566 *format = msg; 567 568 return OK; 569} 570 571static size_t reassembleAVCC(const sp<ABuffer> &csd0, const sp<ABuffer> csd1, char *avcc) { 572 avcc[0] = 1; // version 573 avcc[1] = 0x64; // profile (default to high) 574 avcc[2] = 0; // constraints (default to none) 575 avcc[3] = 0xd; // level (default to 1.3) 576 avcc[4] = 0xff; // reserved+size 577 578 size_t i = 0; 579 int numparams = 0; 580 int lastparamoffset = 0; 581 int avccidx = 6; 582 do { 583 if (i >= csd0->size() - 4 || 584 memcmp(csd0->data() + i, "\x00\x00\x00\x01", 4) == 0) { 585 if (i >= csd0->size() - 4) { 586 // there can't be another param here, so use all the rest 587 i = csd0->size(); 588 } 589 ALOGV("block at %zu, last was %d", i, lastparamoffset); 590 if (lastparamoffset > 0) { 591 int size = i - lastparamoffset; 592 avcc[avccidx++] = size >> 8; 593 avcc[avccidx++] = size & 0xff; 594 memcpy(avcc+avccidx, csd0->data() + lastparamoffset, size); 595 avccidx += size; 596 numparams++; 597 } 598 i += 4; 599 lastparamoffset = i; 600 } else { 601 i++; 602 } 603 } while(i < csd0->size()); 604 ALOGV("csd0 contains %d params", numparams); 605 606 avcc[5] = 0xe0 | numparams; 607 //and now csd-1 608 i = 0; 609 numparams = 0; 610 lastparamoffset = 0; 611 int numpicparamsoffset = avccidx; 612 avccidx++; 613 do { 614 if (i >= csd1->size() - 4 || 615 memcmp(csd1->data() + i, "\x00\x00\x00\x01", 4) == 0) { 616 if (i >= csd1->size() - 4) { 617 // there can't be another param here, so use all the rest 618 i = csd1->size(); 619 } 620 ALOGV("block at %zu, last was %d", i, lastparamoffset); 621 if (lastparamoffset > 0) { 622 int size = i - lastparamoffset; 623 avcc[avccidx++] = size >> 8; 624 avcc[avccidx++] = size & 0xff; 625 memcpy(avcc+avccidx, csd1->data() + lastparamoffset, size); 626 avccidx += size; 627 numparams++; 628 } 629 i += 4; 630 lastparamoffset = i; 631 } else { 632 i++; 633 } 634 } while(i < csd1->size()); 635 avcc[numpicparamsoffset] = numparams; 636 return avccidx; 637} 638 639static void reassembleESDS(const sp<ABuffer> &csd0, char *esds) { 640 int csd0size = csd0->size(); 641 esds[0] = 3; // kTag_ESDescriptor; 642 int esdescriptorsize = 26 + csd0size; 643 CHECK(esdescriptorsize < 268435456); // 7 bits per byte, so max is 2^28-1 644 esds[1] = 0x80 | (esdescriptorsize >> 21); 645 esds[2] = 0x80 | ((esdescriptorsize >> 14) & 0x7f); 646 esds[3] = 0x80 | ((esdescriptorsize >> 7) & 0x7f); 647 esds[4] = (esdescriptorsize & 0x7f); 648 esds[5] = esds[6] = 0; // es id 649 esds[7] = 0; // flags 650 esds[8] = 4; // kTag_DecoderConfigDescriptor 651 int configdescriptorsize = 18 + csd0size; 652 esds[9] = 0x80 | (configdescriptorsize >> 21); 653 esds[10] = 0x80 | ((configdescriptorsize >> 14) & 0x7f); 654 esds[11] = 0x80 | ((configdescriptorsize >> 7) & 0x7f); 655 esds[12] = (configdescriptorsize & 0x7f); 656 esds[13] = 0x40; // objectTypeIndication 657 // bytes 14-25 are examples from a real file. they are unused/overwritten by muxers. 658 esds[14] = 0x15; // streamType(5), upStream(0), 659 esds[15] = 0x00; // 15-17: bufferSizeDB (6KB) 660 esds[16] = 0x18; 661 esds[17] = 0x00; 662 esds[18] = 0x00; // 18-21: maxBitrate (64kbps) 663 esds[19] = 0x00; 664 esds[20] = 0xfa; 665 esds[21] = 0x00; 666 esds[22] = 0x00; // 22-25: avgBitrate (64kbps) 667 esds[23] = 0x00; 668 esds[24] = 0xfa; 669 esds[25] = 0x00; 670 esds[26] = 5; // kTag_DecoderSpecificInfo; 671 esds[27] = 0x80 | (csd0size >> 21); 672 esds[28] = 0x80 | ((csd0size >> 14) & 0x7f); 673 esds[29] = 0x80 | ((csd0size >> 7) & 0x7f); 674 esds[30] = (csd0size & 0x7f); 675 memcpy((void*)&esds[31], csd0->data(), csd0size); 676 // data following this is ignored, so don't bother appending it 677} 678 679static size_t reassembleHVCC(const sp<ABuffer> &csd0, uint8_t *hvcc, size_t hvccSize, size_t nalSizeLength) { 680 HevcParameterSets paramSets; 681 uint8_t* data = csd0->data(); 682 if (csd0->size() < 4) { 683 ALOGE("csd0 too small"); 684 return 0; 685 } 686 if (memcmp(data, "\x00\x00\x00\x01", 4) != 0) { 687 ALOGE("csd0 doesn't start with a start code"); 688 return 0; 689 } 690 size_t prevNalOffset = 4; 691 status_t err = OK; 692 for (size_t i = 1; i < csd0->size() - 4; ++i) { 693 if (memcmp(&data[i], "\x00\x00\x00\x01", 4) != 0) { 694 continue; 695 } 696 err = paramSets.addNalUnit(&data[prevNalOffset], i - prevNalOffset); 697 if (err != OK) { 698 return 0; 699 } 700 prevNalOffset = i + 4; 701 } 702 err = paramSets.addNalUnit(&data[prevNalOffset], csd0->size() - prevNalOffset); 703 if (err != OK) { 704 return 0; 705 } 706 size_t size = hvccSize; 707 err = paramSets.makeHvcc(hvcc, &size, nalSizeLength); 708 if (err != OK) { 709 return 0; 710 } 711 return size; 712} 713 714static void convertMessageToMetaDataInt32( 715 const sp<AMessage> &msg, sp<MetaData> &meta, uint32_t key, const char *name) { 716 int32_t value; 717 if (msg->findInt32(name, &value)) { 718 meta->setInt32(key, value); 719 } 720} 721 722static void convertMessageToMetaDataColorAspects(const sp<AMessage> &msg, sp<MetaData> &meta) { 723 // 0 values are unspecified 724 int32_t range = 0, standard = 0, transfer = 0; 725 (void)msg->findInt32("color-range", &range); 726 (void)msg->findInt32("color-standard", &standard); 727 (void)msg->findInt32("color-transfer", &transfer); 728 729 ColorAspects colorAspects; 730 memset(&colorAspects, 0, sizeof(colorAspects)); 731 if (CodecBase::convertPlatformColorAspectsToCodecAspects( 732 range, standard, transfer, colorAspects) != OK) { 733 return; 734 } 735 736 // save specified values to meta 737 if (colorAspects.mRange != 0) { 738 meta->setInt32(kKeyColorRange, colorAspects.mRange); 739 } 740 if (colorAspects.mPrimaries != 0) { 741 meta->setInt32(kKeyColorPrimaries, colorAspects.mPrimaries); 742 } 743 if (colorAspects.mTransfer != 0) { 744 meta->setInt32(kKeyTransferFunction, colorAspects.mTransfer); 745 } 746 if (colorAspects.mMatrixCoeffs != 0) { 747 meta->setInt32(kKeyColorMatrix, colorAspects.mMatrixCoeffs); 748 } 749} 750 751void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { 752 AString mime; 753 if (msg->findString("mime", &mime)) { 754 meta->setCString(kKeyMIMEType, mime.c_str()); 755 } else { 756 ALOGW("did not find mime type"); 757 } 758 759 int64_t durationUs; 760 if (msg->findInt64("durationUs", &durationUs)) { 761 meta->setInt64(kKeyDuration, durationUs); 762 } 763 764 int32_t isSync; 765 if (msg->findInt32("is-sync-frame", &isSync) && isSync != 0) { 766 meta->setInt32(kKeyIsSyncFrame, 1); 767 } 768 769 if (mime.startsWith("video/")) { 770 int32_t width; 771 int32_t height; 772 if (msg->findInt32("width", &width) && msg->findInt32("height", &height)) { 773 meta->setInt32(kKeyWidth, width); 774 meta->setInt32(kKeyHeight, height); 775 } else { 776 ALOGW("did not find width and/or height"); 777 } 778 779 int32_t sarWidth, sarHeight; 780 if (msg->findInt32("sar-width", &sarWidth) 781 && msg->findInt32("sar-height", &sarHeight)) { 782 meta->setInt32(kKeySARWidth, sarWidth); 783 meta->setInt32(kKeySARHeight, sarHeight); 784 } 785 786 int32_t colorFormat; 787 if (msg->findInt32("color-format", &colorFormat)) { 788 meta->setInt32(kKeyColorFormat, colorFormat); 789 } 790 791 int32_t cropLeft, cropTop, cropRight, cropBottom; 792 if (msg->findRect("crop", 793 &cropLeft, 794 &cropTop, 795 &cropRight, 796 &cropBottom)) { 797 meta->setRect(kKeyCropRect, cropLeft, cropTop, cropRight, cropBottom); 798 } 799 800 int32_t rotationDegrees; 801 if (msg->findInt32("rotation-degrees", &rotationDegrees)) { 802 meta->setInt32(kKeyRotation, rotationDegrees); 803 } 804 805 convertMessageToMetaDataInt32(msg, meta, kKeyMinLuminance, "min-luminance"); 806 convertMessageToMetaDataInt32(msg, meta, kKeyMaxLuminance, "max-luminance"); 807 convertMessageToMetaDataColorAspects(msg, meta); 808 } else if (mime.startsWith("audio/")) { 809 int32_t numChannels; 810 if (msg->findInt32("channel-count", &numChannels)) { 811 meta->setInt32(kKeyChannelCount, numChannels); 812 } 813 int32_t sampleRate; 814 if (msg->findInt32("sample-rate", &sampleRate)) { 815 meta->setInt32(kKeySampleRate, sampleRate); 816 } 817 int32_t channelMask; 818 if (msg->findInt32("channel-mask", &channelMask)) { 819 meta->setInt32(kKeyChannelMask, channelMask); 820 } 821 int32_t delay = 0; 822 if (msg->findInt32("encoder-delay", &delay)) { 823 meta->setInt32(kKeyEncoderDelay, delay); 824 } 825 int32_t padding = 0; 826 if (msg->findInt32("encoder-padding", &padding)) { 827 meta->setInt32(kKeyEncoderPadding, padding); 828 } 829 830 int32_t isADTS; 831 if (msg->findInt32("is-adts", &isADTS)) { 832 meta->setInt32(kKeyIsADTS, isADTS); 833 } 834 835 int32_t pcmEncoding; 836 if (msg->findInt32("pcm-encoding", &pcmEncoding)) { 837 meta->setInt32(kKeyPcmEncoding, pcmEncoding); 838 } 839 } 840 841 int32_t maxInputSize; 842 if (msg->findInt32("max-input-size", &maxInputSize)) { 843 meta->setInt32(kKeyMaxInputSize, maxInputSize); 844 } 845 846 int32_t maxWidth; 847 if (msg->findInt32("max-width", &maxWidth)) { 848 meta->setInt32(kKeyMaxWidth, maxWidth); 849 } 850 851 int32_t maxHeight; 852 if (msg->findInt32("max-height", &maxHeight)) { 853 meta->setInt32(kKeyMaxHeight, maxHeight); 854 } 855 856 int32_t fps; 857 float fpsFloat; 858 if (msg->findInt32("frame-rate", &fps) && fps > 0) { 859 meta->setInt32(kKeyFrameRate, fps); 860 } else if (msg->findFloat("frame-rate", &fpsFloat) 861 && fpsFloat >= 1 && fpsFloat <= INT32_MAX) { 862 // truncate values to distinguish between e.g. 24 vs 23.976 fps 863 meta->setInt32(kKeyFrameRate, (int32_t)fpsFloat); 864 } 865 866 // reassemble the csd data into its original form 867 sp<ABuffer> csd0; 868 if (msg->findBuffer("csd-0", &csd0)) { 869 if (mime == MEDIA_MIMETYPE_VIDEO_AVC) { 870 sp<ABuffer> csd1; 871 if (msg->findBuffer("csd-1", &csd1)) { 872 char avcc[1024]; // that oughta be enough, right? 873 size_t outsize = reassembleAVCC(csd0, csd1, avcc); 874 meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize); 875 } 876 } else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) { 877 int csd0size = csd0->size(); 878 char esds[csd0size + 31]; 879 // The written ESDS is actually for an audio stream, but it's enough 880 // for transporting the CSD to muxers. 881 reassembleESDS(csd0, esds); 882 meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds)); 883 } else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC) { 884 uint8_t hvcc[1024]; // that oughta be enough, right? 885 size_t outsize = reassembleHVCC(csd0, hvcc, 1024, 4); 886 meta->setData(kKeyHVCC, kKeyHVCC, hvcc, outsize); 887 } 888 } 889 890 int32_t timeScale; 891 if (msg->findInt32("time-scale", &timeScale)) { 892 meta->setInt32(kKeyTimeScale, timeScale); 893 } 894 895 // XXX TODO add whatever other keys there are 896 897#if 0 898 ALOGI("converted %s to:", msg->debugString(0).c_str()); 899 meta->dumpToLog(); 900#endif 901} 902 903AString MakeUserAgent() { 904 AString ua; 905 ua.append("stagefright/1.2 (Linux;Android "); 906 907#if (PROPERTY_VALUE_MAX < 8) 908#error "PROPERTY_VALUE_MAX must be at least 8" 909#endif 910 911 char value[PROPERTY_VALUE_MAX]; 912 property_get("ro.build.version.release", value, "Unknown"); 913 ua.append(value); 914 ua.append(")"); 915 916 return ua; 917} 918 919status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, 920 const sp<MetaData>& meta) 921{ 922 int32_t sampleRate = 0; 923 int32_t bitRate = 0; 924 int32_t channelMask = 0; 925 int32_t delaySamples = 0; 926 int32_t paddingSamples = 0; 927 928 AudioParameter param = AudioParameter(); 929 930 if (meta->findInt32(kKeySampleRate, &sampleRate)) { 931 param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate); 932 } 933 if (meta->findInt32(kKeyChannelMask, &channelMask)) { 934 param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask); 935 } 936 if (meta->findInt32(kKeyBitRate, &bitRate)) { 937 param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate); 938 } 939 if (meta->findInt32(kKeyEncoderDelay, &delaySamples)) { 940 param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples); 941 } 942 if (meta->findInt32(kKeyEncoderPadding, &paddingSamples)) { 943 param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples); 944 } 945 946 ALOGV("sendMetaDataToHal: bitRate %d, sampleRate %d, chanMask %d," 947 "delaySample %d, paddingSample %d", bitRate, sampleRate, 948 channelMask, delaySamples, paddingSamples); 949 950 sink->setParameters(param.toString()); 951 return OK; 952} 953 954struct mime_conv_t { 955 const char* mime; 956 audio_format_t format; 957}; 958 959static const struct mime_conv_t mimeLookup[] = { 960 { MEDIA_MIMETYPE_AUDIO_MPEG, AUDIO_FORMAT_MP3 }, 961 { MEDIA_MIMETYPE_AUDIO_RAW, AUDIO_FORMAT_PCM_16_BIT }, 962 { MEDIA_MIMETYPE_AUDIO_AMR_NB, AUDIO_FORMAT_AMR_NB }, 963 { MEDIA_MIMETYPE_AUDIO_AMR_WB, AUDIO_FORMAT_AMR_WB }, 964 { MEDIA_MIMETYPE_AUDIO_AAC, AUDIO_FORMAT_AAC }, 965 { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS }, 966 { MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS}, 967 { 0, AUDIO_FORMAT_INVALID } 968}; 969 970status_t mapMimeToAudioFormat( audio_format_t& format, const char* mime ) 971{ 972const struct mime_conv_t* p = &mimeLookup[0]; 973 while (p->mime != NULL) { 974 if (0 == strcasecmp(mime, p->mime)) { 975 format = p->format; 976 return OK; 977 } 978 ++p; 979 } 980 981 return BAD_VALUE; 982} 983 984struct aac_format_conv_t { 985 OMX_AUDIO_AACPROFILETYPE eAacProfileType; 986 audio_format_t format; 987}; 988 989static const struct aac_format_conv_t profileLookup[] = { 990 { OMX_AUDIO_AACObjectMain, AUDIO_FORMAT_AAC_MAIN}, 991 { OMX_AUDIO_AACObjectLC, AUDIO_FORMAT_AAC_LC}, 992 { OMX_AUDIO_AACObjectSSR, AUDIO_FORMAT_AAC_SSR}, 993 { OMX_AUDIO_AACObjectLTP, AUDIO_FORMAT_AAC_LTP}, 994 { OMX_AUDIO_AACObjectHE, AUDIO_FORMAT_AAC_HE_V1}, 995 { OMX_AUDIO_AACObjectScalable, AUDIO_FORMAT_AAC_SCALABLE}, 996 { OMX_AUDIO_AACObjectERLC, AUDIO_FORMAT_AAC_ERLC}, 997 { OMX_AUDIO_AACObjectLD, AUDIO_FORMAT_AAC_LD}, 998 { OMX_AUDIO_AACObjectHE_PS, AUDIO_FORMAT_AAC_HE_V2}, 999 { OMX_AUDIO_AACObjectELD, AUDIO_FORMAT_AAC_ELD}, 1000 { OMX_AUDIO_AACObjectNull, AUDIO_FORMAT_AAC}, 1001}; 1002 1003void mapAACProfileToAudioFormat( audio_format_t& format, uint64_t eAacProfile) 1004{ 1005const struct aac_format_conv_t* p = &profileLookup[0]; 1006 while (p->eAacProfileType != OMX_AUDIO_AACObjectNull) { 1007 if (eAacProfile == p->eAacProfileType) { 1008 format = p->format; 1009 return; 1010 } 1011 ++p; 1012 } 1013 format = AUDIO_FORMAT_AAC; 1014 return; 1015} 1016 1017bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, 1018 bool isStreaming, audio_stream_type_t streamType) 1019{ 1020 const char *mime; 1021 if (meta == NULL) { 1022 return false; 1023 } 1024 CHECK(meta->findCString(kKeyMIMEType, &mime)); 1025 1026 audio_offload_info_t info = AUDIO_INFO_INITIALIZER; 1027 1028 info.format = AUDIO_FORMAT_INVALID; 1029 if (mapMimeToAudioFormat(info.format, mime) != OK) { 1030 ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime); 1031 return false; 1032 } else { 1033 ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format); 1034 } 1035 1036 if (AUDIO_FORMAT_INVALID == info.format) { 1037 // can't offload if we don't know what the source format is 1038 ALOGE("mime type \"%s\" not a known audio format", mime); 1039 return false; 1040 } 1041 1042 // Redefine aac format according to its profile 1043 // Offloading depends on audio DSP capabilities. 1044 int32_t aacaot = -1; 1045 if (meta->findInt32(kKeyAACAOT, &aacaot)) { 1046 mapAACProfileToAudioFormat(info.format,(OMX_AUDIO_AACPROFILETYPE) aacaot); 1047 } 1048 1049 int32_t srate = -1; 1050 if (!meta->findInt32(kKeySampleRate, &srate)) { 1051 ALOGV("track of type '%s' does not publish sample rate", mime); 1052 } 1053 info.sample_rate = srate; 1054 1055 int32_t cmask = 0; 1056 if (!meta->findInt32(kKeyChannelMask, &cmask)) { 1057 ALOGV("track of type '%s' does not publish channel mask", mime); 1058 1059 // Try a channel count instead 1060 int32_t channelCount; 1061 if (!meta->findInt32(kKeyChannelCount, &channelCount)) { 1062 ALOGV("track of type '%s' does not publish channel count", mime); 1063 } else { 1064 cmask = audio_channel_out_mask_from_count(channelCount); 1065 } 1066 } 1067 info.channel_mask = cmask; 1068 1069 int64_t duration = 0; 1070 if (!meta->findInt64(kKeyDuration, &duration)) { 1071 ALOGV("track of type '%s' does not publish duration", mime); 1072 } 1073 info.duration_us = duration; 1074 1075 int32_t brate = -1; 1076 if (!meta->findInt32(kKeyBitRate, &brate)) { 1077 ALOGV("track of type '%s' does not publish bitrate", mime); 1078 } 1079 info.bit_rate = brate; 1080 1081 1082 info.stream_type = streamType; 1083 info.has_video = hasVideo; 1084 info.is_streaming = isStreaming; 1085 1086 // Check if offload is possible for given format, stream type, sample rate, 1087 // bit rate, duration, video and streaming 1088 return AudioSystem::isOffloadSupported(info); 1089} 1090 1091AString uriDebugString(const AString &uri, bool incognito) { 1092 if (incognito) { 1093 return AString("<URI suppressed>"); 1094 } 1095 1096 char prop[PROPERTY_VALUE_MAX]; 1097 if (property_get("media.stagefright.log-uri", prop, "false") && 1098 (!strcmp(prop, "1") || !strcmp(prop, "true"))) { 1099 return uri; 1100 } 1101 1102 // find scheme 1103 AString scheme; 1104 const char *chars = uri.c_str(); 1105 for (size_t i = 0; i < uri.size(); i++) { 1106 const char c = chars[i]; 1107 if (!isascii(c)) { 1108 break; 1109 } else if (isalpha(c)) { 1110 continue; 1111 } else if (i == 0) { 1112 // first character must be a letter 1113 break; 1114 } else if (isdigit(c) || c == '+' || c == '.' || c =='-') { 1115 continue; 1116 } else if (c != ':') { 1117 break; 1118 } 1119 scheme = AString(uri, 0, i); 1120 scheme.append("://<suppressed>"); 1121 return scheme; 1122 } 1123 return AString("<no-scheme URI suppressed>"); 1124} 1125 1126HLSTime::HLSTime(const sp<AMessage>& meta) : 1127 mSeq(-1), 1128 mTimeUs(-1ll), 1129 mMeta(meta) { 1130 if (meta != NULL) { 1131 CHECK(meta->findInt32("discontinuitySeq", &mSeq)); 1132 CHECK(meta->findInt64("timeUs", &mTimeUs)); 1133 } 1134} 1135 1136int64_t HLSTime::getSegmentTimeUs() const { 1137 int64_t segmentStartTimeUs = -1ll; 1138 if (mMeta != NULL) { 1139 CHECK(mMeta->findInt64("segmentStartTimeUs", &segmentStartTimeUs)); 1140 1141 int64_t segmentFirstTimeUs; 1142 if (mMeta->findInt64("segmentFirstTimeUs", &segmentFirstTimeUs)) { 1143 segmentStartTimeUs += mTimeUs - segmentFirstTimeUs; 1144 } 1145 1146 // adjust segment time by playlist age (for live streaming) 1147 int64_t playlistTimeUs; 1148 if (mMeta->findInt64("playlistTimeUs", &playlistTimeUs)) { 1149 int64_t playlistAgeUs = ALooper::GetNowUs() - playlistTimeUs; 1150 1151 int64_t durationUs; 1152 CHECK(mMeta->findInt64("segmentDurationUs", &durationUs)); 1153 1154 // round to nearest whole segment 1155 playlistAgeUs = (playlistAgeUs + durationUs / 2) 1156 / durationUs * durationUs; 1157 1158 segmentStartTimeUs -= playlistAgeUs; 1159 if (segmentStartTimeUs < 0) { 1160 segmentStartTimeUs = 0; 1161 } 1162 } 1163 } 1164 return segmentStartTimeUs; 1165} 1166 1167bool operator <(const HLSTime &t0, const HLSTime &t1) { 1168 // we can only compare discontinuity sequence and timestamp. 1169 // (mSegmentTimeUs is not reliable in live streaming case, it's the 1170 // time starting from beginning of playlist but playlist could change.) 1171 return t0.mSeq < t1.mSeq 1172 || (t0.mSeq == t1.mSeq && t0.mTimeUs < t1.mTimeUs); 1173} 1174 1175void writeToAMessage(sp<AMessage> msg, const AudioPlaybackRate &rate) { 1176 msg->setFloat("speed", rate.mSpeed); 1177 msg->setFloat("pitch", rate.mPitch); 1178 msg->setInt32("audio-fallback-mode", rate.mFallbackMode); 1179 msg->setInt32("audio-stretch-mode", rate.mStretchMode); 1180} 1181 1182void readFromAMessage(const sp<AMessage> &msg, AudioPlaybackRate *rate /* nonnull */) { 1183 *rate = AUDIO_PLAYBACK_RATE_DEFAULT; 1184 CHECK(msg->findFloat("speed", &rate->mSpeed)); 1185 CHECK(msg->findFloat("pitch", &rate->mPitch)); 1186 CHECK(msg->findInt32("audio-fallback-mode", (int32_t *)&rate->mFallbackMode)); 1187 CHECK(msg->findInt32("audio-stretch-mode", (int32_t *)&rate->mStretchMode)); 1188} 1189 1190void writeToAMessage(sp<AMessage> msg, const AVSyncSettings &sync, float videoFpsHint) { 1191 msg->setInt32("sync-source", sync.mSource); 1192 msg->setInt32("audio-adjust-mode", sync.mAudioAdjustMode); 1193 msg->setFloat("tolerance", sync.mTolerance); 1194 msg->setFloat("video-fps", videoFpsHint); 1195} 1196 1197void readFromAMessage( 1198 const sp<AMessage> &msg, 1199 AVSyncSettings *sync /* nonnull */, 1200 float *videoFps /* nonnull */) { 1201 AVSyncSettings settings; 1202 CHECK(msg->findInt32("sync-source", (int32_t *)&settings.mSource)); 1203 CHECK(msg->findInt32("audio-adjust-mode", (int32_t *)&settings.mAudioAdjustMode)); 1204 CHECK(msg->findFloat("tolerance", &settings.mTolerance)); 1205 CHECK(msg->findFloat("video-fps", videoFps)); 1206 *sync = settings; 1207} 1208 1209AString nameForFd(int fd) { 1210 const size_t SIZE = 256; 1211 char buffer[SIZE]; 1212 AString result; 1213 snprintf(buffer, SIZE, "/proc/%d/fd/%d", getpid(), fd); 1214 struct stat s; 1215 if (lstat(buffer, &s) == 0) { 1216 if ((s.st_mode & S_IFMT) == S_IFLNK) { 1217 char linkto[256]; 1218 int len = readlink(buffer, linkto, sizeof(linkto)); 1219 if(len > 0) { 1220 if(len > 255) { 1221 linkto[252] = '.'; 1222 linkto[253] = '.'; 1223 linkto[254] = '.'; 1224 linkto[255] = 0; 1225 } else { 1226 linkto[len] = 0; 1227 } 1228 result.append(linkto); 1229 } 1230 } else { 1231 result.append("unexpected type for "); 1232 result.append(buffer); 1233 } 1234 } else { 1235 result.append("couldn't open "); 1236 result.append(buffer); 1237 } 1238 return result; 1239} 1240 1241} // namespace android 1242 1243