NuMediaExtractor.cpp revision 2d8bedd05437b6fccdbc6bf70f673ffd86744d59
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 "NuMediaExtractor" 19#include <utils/Log.h> 20 21#include <media/stagefright/NuMediaExtractor.h> 22 23#include "include/ESDS.h" 24 25#include <media/stagefright/foundation/ABuffer.h> 26#include <media/stagefright/foundation/ADebug.h> 27#include <media/stagefright/foundation/AMessage.h> 28#include <media/stagefright/DataSource.h> 29#include <media/stagefright/MediaBuffer.h> 30#include <media/stagefright/MediaDefs.h> 31#include <media/stagefright/MediaErrors.h> 32#include <media/stagefright/MediaExtractor.h> 33#include <media/stagefright/MediaSource.h> 34#include <media/stagefright/MetaData.h> 35#include <media/stagefright/Utils.h> 36 37namespace android { 38 39NuMediaExtractor::NuMediaExtractor() { 40} 41 42NuMediaExtractor::~NuMediaExtractor() { 43 releaseTrackSamples(); 44 45 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 46 TrackInfo *info = &mSelectedTracks.editItemAt(i); 47 48 CHECK_EQ((status_t)OK, info->mSource->stop()); 49 } 50 51 mSelectedTracks.clear(); 52} 53 54status_t NuMediaExtractor::setDataSource(const char *path) { 55 sp<DataSource> dataSource = DataSource::CreateFromURI(path); 56 57 if (dataSource == NULL) { 58 return -ENOENT; 59 } 60 61 mImpl = MediaExtractor::Create(dataSource); 62 63 if (mImpl == NULL) { 64 return ERROR_UNSUPPORTED; 65 } 66 67 return OK; 68} 69 70size_t NuMediaExtractor::countTracks() const { 71 return mImpl == NULL ? 0 : mImpl->countTracks(); 72} 73 74status_t NuMediaExtractor::getTrackFormat( 75 size_t index, sp<AMessage> *format) const { 76 *format = NULL; 77 78 if (mImpl == NULL) { 79 return -EINVAL; 80 } 81 82 if (index >= mImpl->countTracks()) { 83 return -ERANGE; 84 } 85 86 sp<MetaData> meta = mImpl->getTrackMetaData(index); 87 88 const char *mime; 89 CHECK(meta->findCString(kKeyMIMEType, &mime)); 90 91 sp<AMessage> msg = new AMessage; 92 msg->setString("mime", mime); 93 94 if (!strncasecmp("video/", mime, 6)) { 95 int32_t width, height; 96 CHECK(meta->findInt32(kKeyWidth, &width)); 97 CHECK(meta->findInt32(kKeyHeight, &height)); 98 99 msg->setInt32("width", width); 100 msg->setInt32("height", height); 101 } else { 102 CHECK(!strncasecmp("audio/", mime, 6)); 103 104 int32_t numChannels, sampleRate; 105 CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); 106 CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); 107 108 msg->setInt32("channel-count", numChannels); 109 msg->setInt32("sample-rate", sampleRate); 110 } 111 112 int32_t maxInputSize; 113 if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) { 114 msg->setInt32("max-input-size", maxInputSize); 115 } 116 117 uint32_t type; 118 const void *data; 119 size_t size; 120 if (meta->findData(kKeyAVCC, &type, &data, &size)) { 121 // Parse the AVCDecoderConfigurationRecord 122 123 const uint8_t *ptr = (const uint8_t *)data; 124 125 CHECK(size >= 7); 126 CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 127 uint8_t profile = ptr[1]; 128 uint8_t level = ptr[3]; 129 130 // There is decodable content out there that fails the following 131 // assertion, let's be lenient for now... 132 // CHECK((ptr[4] >> 2) == 0x3f); // reserved 133 134 size_t lengthSize = 1 + (ptr[4] & 3); 135 136 // commented out check below as H264_QVGA_500_NO_AUDIO.3gp 137 // violates it... 138 // CHECK((ptr[5] >> 5) == 7); // reserved 139 140 size_t numSeqParameterSets = ptr[5] & 31; 141 142 ptr += 6; 143 size -= 6; 144 145 sp<ABuffer> buffer = new ABuffer(1024); 146 buffer->setRange(0, 0); 147 148 for (size_t i = 0; i < numSeqParameterSets; ++i) { 149 CHECK(size >= 2); 150 size_t length = U16_AT(ptr); 151 152 ptr += 2; 153 size -= 2; 154 155 CHECK(size >= length); 156 157 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); 158 memcpy(buffer->data() + buffer->size() + 4, ptr, length); 159 buffer->setRange(0, buffer->size() + 4 + length); 160 161 ptr += length; 162 size -= length; 163 } 164 165 buffer->meta()->setInt32("csd", true); 166 buffer->meta()->setInt64("timeUs", 0); 167 168 msg->setBuffer("csd-0", buffer); 169 170 buffer = new ABuffer(1024); 171 buffer->setRange(0, 0); 172 173 CHECK(size >= 1); 174 size_t numPictureParameterSets = *ptr; 175 ++ptr; 176 --size; 177 178 for (size_t i = 0; i < numPictureParameterSets; ++i) { 179 CHECK(size >= 2); 180 size_t length = U16_AT(ptr); 181 182 ptr += 2; 183 size -= 2; 184 185 CHECK(size >= length); 186 187 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); 188 memcpy(buffer->data() + buffer->size() + 4, ptr, length); 189 buffer->setRange(0, buffer->size() + 4 + length); 190 191 ptr += length; 192 size -= length; 193 } 194 195 buffer->meta()->setInt32("csd", true); 196 buffer->meta()->setInt64("timeUs", 0); 197 msg->setBuffer("csd-1", buffer); 198 } else if (meta->findData(kKeyESDS, &type, &data, &size)) { 199 ESDS esds((const char *)data, size); 200 CHECK_EQ(esds.InitCheck(), (status_t)OK); 201 202 const void *codec_specific_data; 203 size_t codec_specific_data_size; 204 esds.getCodecSpecificInfo( 205 &codec_specific_data, &codec_specific_data_size); 206 207 sp<ABuffer> buffer = new ABuffer(codec_specific_data_size); 208 209 memcpy(buffer->data(), codec_specific_data, 210 codec_specific_data_size); 211 212 buffer->meta()->setInt32("csd", true); 213 buffer->meta()->setInt64("timeUs", 0); 214 msg->setBuffer("csd-0", buffer); 215 } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { 216 sp<ABuffer> buffer = new ABuffer(size); 217 memcpy(buffer->data(), data, size); 218 219 buffer->meta()->setInt32("csd", true); 220 buffer->meta()->setInt64("timeUs", 0); 221 msg->setBuffer("csd-0", buffer); 222 223 if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) { 224 return -EINVAL; 225 } 226 227 buffer = new ABuffer(size); 228 memcpy(buffer->data(), data, size); 229 230 buffer->meta()->setInt32("csd", true); 231 buffer->meta()->setInt64("timeUs", 0); 232 msg->setBuffer("csd-1", buffer); 233 } 234 235 *format = msg; 236 237 return OK; 238} 239 240status_t NuMediaExtractor::selectTrack(size_t index) { 241 if (mImpl == NULL) { 242 return -EINVAL; 243 } 244 245 if (index >= mImpl->countTracks()) { 246 return -ERANGE; 247 } 248 249 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 250 TrackInfo *info = &mSelectedTracks.editItemAt(i); 251 252 if (info->mTrackIndex == index) { 253 // This track has already been selected. 254 return OK; 255 } 256 } 257 258 sp<MediaSource> source = mImpl->getTrack(index); 259 260 CHECK_EQ((status_t)OK, source->start()); 261 262 mSelectedTracks.push(); 263 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); 264 265 info->mSource = source; 266 info->mTrackIndex = index; 267 info->mFinalResult = OK; 268 info->mSample = NULL; 269 info->mSampleTimeUs = -1ll; 270 info->mFlags = 0; 271 272 const char *mime; 273 CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); 274 275 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 276 info->mFlags |= kIsVorbis; 277 } 278 279 return OK; 280} 281 282void NuMediaExtractor::releaseTrackSamples() { 283 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 284 TrackInfo *info = &mSelectedTracks.editItemAt(i); 285 286 if (info->mSample != NULL) { 287 info->mSample->release(); 288 info->mSample = NULL; 289 290 info->mSampleTimeUs = -1ll; 291 } 292 } 293} 294 295ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) { 296 TrackInfo *minInfo = NULL; 297 ssize_t minIndex = -1; 298 299 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 300 TrackInfo *info = &mSelectedTracks.editItemAt(i); 301 302 if (seekTimeUs >= 0ll) { 303 info->mFinalResult = OK; 304 305 if (info->mSample != NULL) { 306 info->mSample->release(); 307 info->mSample = NULL; 308 info->mSampleTimeUs = -1ll; 309 } 310 } else if (info->mFinalResult != OK) { 311 continue; 312 } 313 314 if (info->mSample == NULL) { 315 MediaSource::ReadOptions options; 316 if (seekTimeUs >= 0ll) { 317 options.setSeekTo(seekTimeUs); 318 } 319 status_t err = info->mSource->read(&info->mSample, &options); 320 321 if (err != OK) { 322 CHECK(info->mSample == NULL); 323 324 info->mFinalResult = err; 325 info->mSampleTimeUs = -1ll; 326 continue; 327 } else { 328 CHECK(info->mSample != NULL); 329 CHECK(info->mSample->meta_data()->findInt64( 330 kKeyTime, &info->mSampleTimeUs)); 331 } 332 } 333 334 if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) { 335 minInfo = info; 336 minIndex = i; 337 } 338 } 339 340 return minIndex; 341} 342 343status_t NuMediaExtractor::seekTo(int64_t timeUs) { 344 return fetchTrackSamples(timeUs); 345} 346 347status_t NuMediaExtractor::advance() { 348 ssize_t minIndex = fetchTrackSamples(); 349 350 if (minIndex < 0) { 351 return ERROR_END_OF_STREAM; 352 } 353 354 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 355 356 info->mSample->release(); 357 info->mSample = NULL; 358 info->mSampleTimeUs = -1ll; 359 360 return OK; 361} 362 363status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { 364 ssize_t minIndex = fetchTrackSamples(); 365 366 if (minIndex < 0) { 367 return ERROR_END_OF_STREAM; 368 } 369 370 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 371 372 size_t sampleSize = info->mSample->range_length(); 373 374 if (info->mFlags & kIsVorbis) { 375 // Each sample's data is suffixed by the number of page samples 376 // or -1 if not available. 377 sampleSize += sizeof(int32_t); 378 } 379 380 if (buffer->capacity() < sampleSize) { 381 return -ENOMEM; 382 } 383 384 const uint8_t *src = 385 (const uint8_t *)info->mSample->data() 386 + info->mSample->range_offset(); 387 388 memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); 389 390 if (info->mFlags & kIsVorbis) { 391 int32_t numPageSamples; 392 if (!info->mSample->meta_data()->findInt32( 393 kKeyValidSamples, &numPageSamples)) { 394 numPageSamples = -1; 395 } 396 397 memcpy((uint8_t *)buffer->data() + info->mSample->range_length(), 398 &numPageSamples, 399 sizeof(numPageSamples)); 400 } 401 402 buffer->setRange(0, sampleSize); 403 404 return OK; 405} 406 407status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 408 ssize_t minIndex = fetchTrackSamples(); 409 410 if (minIndex < 0) { 411 return ERROR_END_OF_STREAM; 412 } 413 414 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 415 *trackIndex = info->mTrackIndex; 416 417 return OK; 418} 419 420status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 421 ssize_t minIndex = fetchTrackSamples(); 422 423 if (minIndex < 0) { 424 return ERROR_END_OF_STREAM; 425 } 426 427 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 428 *sampleTimeUs = info->mSampleTimeUs; 429 430 return OK; 431} 432 433} // namespace android 434