codec.cpp revision f69e53033f23f9f70fcdb28a3c2e650de0147459
1/* 2 * Copyright (C) 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 "codec" 19#include <utils/Log.h> 20 21#include "SimplePlayer.h" 22 23#include <binder/IServiceManager.h> 24#include <binder/ProcessState.h> 25#include <media/ICrypto.h> 26#include <media/IMediaPlayerService.h> 27#include <media/stagefright/foundation/ABuffer.h> 28#include <media/stagefright/foundation/ADebug.h> 29#include <media/stagefright/foundation/ALooper.h> 30#include <media/stagefright/foundation/AMessage.h> 31#include <media/stagefright/DataSource.h> 32#include <media/stagefright/MediaCodec.h> 33#include <media/stagefright/MediaCodecList.h> 34#include <media/stagefright/MediaDefs.h> 35#include <media/stagefright/NuMediaExtractor.h> 36#include <gui/SurfaceComposerClient.h> 37 38static void usage(const char *me) { 39 fprintf(stderr, "usage: %s [-a] use audio\n" 40 "\t\t[-v] use video\n" 41 "\t\t[-p] playback\n" 42 "\t\t[-S] allocate buffers from a surface\n", 43 me); 44 45 exit(1); 46} 47 48namespace android { 49 50struct CodecState { 51 sp<MediaCodec> mCodec; 52 Vector<sp<ABuffer> > mCSD; 53 size_t mCSDIndex; 54 Vector<sp<ABuffer> > mInBuffers; 55 Vector<sp<ABuffer> > mOutBuffers; 56 bool mSignalledInputEOS; 57 bool mSawOutputEOS; 58 int64_t mNumBuffersDecoded; 59 int64_t mNumBytesDecoded; 60 bool mIsAudio; 61}; 62 63} // namespace android 64 65static int decode( 66 const android::sp<android::ALooper> &looper, 67 const char *path, 68 bool useAudio, 69 bool useVideo, 70 const android::sp<android::Surface> &surface) { 71 using namespace android; 72 73 static int64_t kTimeout = 500ll; 74 75 sp<NuMediaExtractor> extractor = new NuMediaExtractor; 76 if (extractor->setDataSource(path) != OK) { 77 fprintf(stderr, "unable to instantiate extractor.\n"); 78 return 1; 79 } 80 81 KeyedVector<size_t, CodecState> stateByTrack; 82 83 bool haveAudio = false; 84 bool haveVideo = false; 85 for (size_t i = 0; i < extractor->countTracks(); ++i) { 86 sp<AMessage> format; 87 status_t err = extractor->getTrackFormat(i, &format); 88 CHECK_EQ(err, (status_t)OK); 89 90 AString mime; 91 CHECK(format->findString("mime", &mime)); 92 93 bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6); 94 bool isVideo = !strncasecmp(mime.c_str(), "video/", 6); 95 96 if (useAudio && !haveAudio && isAudio) { 97 haveAudio = true; 98 } else if (useVideo && !haveVideo && isVideo) { 99 haveVideo = true; 100 } else { 101 continue; 102 } 103 104 ALOGV("selecting track %d", i); 105 106 err = extractor->selectTrack(i); 107 CHECK_EQ(err, (status_t)OK); 108 109 CodecState *state = 110 &stateByTrack.editValueAt(stateByTrack.add(i, CodecState())); 111 112 state->mNumBytesDecoded = 0; 113 state->mNumBuffersDecoded = 0; 114 state->mIsAudio = isAudio; 115 116 state->mCodec = MediaCodec::CreateByType( 117 looper, mime.c_str(), false /* encoder */); 118 119 CHECK(state->mCodec != NULL); 120 121 err = state->mCodec->configure( 122 format, isVideo ? surface : NULL, 123 NULL /* crypto */, 124 0 /* flags */); 125 126 CHECK_EQ(err, (status_t)OK); 127 128 size_t j = 0; 129 sp<ABuffer> buffer; 130 while (format->findBuffer(StringPrintf("csd-%d", j).c_str(), &buffer)) { 131 state->mCSD.push_back(buffer); 132 133 ++j; 134 } 135 136 state->mCSDIndex = 0; 137 state->mSignalledInputEOS = false; 138 state->mSawOutputEOS = false; 139 140 ALOGV("got %d pieces of codec specific data.", state->mCSD.size()); 141 } 142 143 CHECK(!stateByTrack.isEmpty()); 144 145 int64_t startTimeUs = ALooper::GetNowUs(); 146 147 for (size_t i = 0; i < stateByTrack.size(); ++i) { 148 CodecState *state = &stateByTrack.editValueAt(i); 149 150 sp<MediaCodec> codec = state->mCodec; 151 152 CHECK_EQ((status_t)OK, codec->start()); 153 154 CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers)); 155 CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers)); 156 157 ALOGV("got %d input and %d output buffers", 158 state->mInBuffers.size(), state->mOutBuffers.size()); 159 160 while (state->mCSDIndex < state->mCSD.size()) { 161 size_t index; 162 status_t err = codec->dequeueInputBuffer(&index, -1ll); 163 CHECK_EQ(err, (status_t)OK); 164 165 const sp<ABuffer> &srcBuffer = 166 state->mCSD.itemAt(state->mCSDIndex++); 167 168 const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index); 169 170 memcpy(buffer->data(), srcBuffer->data(), srcBuffer->size()); 171 172 err = codec->queueInputBuffer( 173 index, 174 0 /* offset */, 175 srcBuffer->size(), 176 0ll /* timeUs */, 177 MediaCodec::BUFFER_FLAG_CODECCONFIG); 178 179 CHECK_EQ(err, (status_t)OK); 180 } 181 } 182 183 bool sawInputEOS = false; 184 185 for (;;) { 186 if (!sawInputEOS) { 187 size_t trackIndex; 188 status_t err = extractor->getSampleTrackIndex(&trackIndex); 189 190 if (err != OK) { 191 ALOGV("saw input eos"); 192 sawInputEOS = true; 193 } else { 194 CodecState *state = &stateByTrack.editValueFor(trackIndex); 195 196 size_t index; 197 err = state->mCodec->dequeueInputBuffer(&index, kTimeout); 198 199 if (err == OK) { 200 ALOGV("filling input buffer %d", index); 201 202 const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index); 203 204 err = extractor->readSampleData(buffer); 205 CHECK_EQ(err, (status_t)OK); 206 207 int64_t timeUs; 208 err = extractor->getSampleTime(&timeUs); 209 CHECK_EQ(err, (status_t)OK); 210 211 uint32_t bufferFlags = 0; 212 213 err = state->mCodec->queueInputBuffer( 214 index, 215 0 /* offset */, 216 buffer->size(), 217 timeUs, 218 bufferFlags); 219 220 CHECK_EQ(err, (status_t)OK); 221 222 extractor->advance(); 223 } else { 224 CHECK_EQ(err, -EAGAIN); 225 } 226 } 227 } else { 228 for (size_t i = 0; i < stateByTrack.size(); ++i) { 229 CodecState *state = &stateByTrack.editValueAt(i); 230 231 if (!state->mSignalledInputEOS) { 232 size_t index; 233 status_t err = 234 state->mCodec->dequeueInputBuffer(&index, kTimeout); 235 236 if (err == OK) { 237 ALOGV("signalling input EOS on track %d", i); 238 239 err = state->mCodec->queueInputBuffer( 240 index, 241 0 /* offset */, 242 0 /* size */, 243 0ll /* timeUs */, 244 MediaCodec::BUFFER_FLAG_EOS); 245 246 CHECK_EQ(err, (status_t)OK); 247 248 state->mSignalledInputEOS = true; 249 } else { 250 CHECK_EQ(err, -EAGAIN); 251 } 252 } 253 } 254 } 255 256 bool sawOutputEOSOnAllTracks = true; 257 for (size_t i = 0; i < stateByTrack.size(); ++i) { 258 CodecState *state = &stateByTrack.editValueAt(i); 259 if (!state->mSawOutputEOS) { 260 sawOutputEOSOnAllTracks = false; 261 break; 262 } 263 } 264 265 if (sawOutputEOSOnAllTracks) { 266 break; 267 } 268 269 for (size_t i = 0; i < stateByTrack.size(); ++i) { 270 CodecState *state = &stateByTrack.editValueAt(i); 271 272 if (state->mSawOutputEOS) { 273 continue; 274 } 275 276 size_t index; 277 size_t offset; 278 size_t size; 279 int64_t presentationTimeUs; 280 uint32_t flags; 281 status_t err = state->mCodec->dequeueOutputBuffer( 282 &index, &offset, &size, &presentationTimeUs, &flags, 283 kTimeout); 284 285 if (err == OK) { 286 ALOGV("draining output buffer %d, time = %lld us", 287 index, presentationTimeUs); 288 289 ++state->mNumBuffersDecoded; 290 state->mNumBytesDecoded += size; 291 292 err = state->mCodec->releaseOutputBuffer(index); 293 CHECK_EQ(err, (status_t)OK); 294 295 if (flags & MediaCodec::BUFFER_FLAG_EOS) { 296 ALOGV("reached EOS on output."); 297 298 state->mSawOutputEOS = true; 299 } 300 } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { 301 ALOGV("INFO_OUTPUT_BUFFERS_CHANGED"); 302 CHECK_EQ((status_t)OK, 303 state->mCodec->getOutputBuffers(&state->mOutBuffers)); 304 305 ALOGV("got %d output buffers", state->mOutBuffers.size()); 306 } else if (err == INFO_FORMAT_CHANGED) { 307 sp<AMessage> format; 308 CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format)); 309 310 ALOGV("INFO_FORMAT_CHANGED: %s", format->debugString().c_str()); 311 } else { 312 CHECK_EQ(err, -EAGAIN); 313 } 314 } 315 } 316 317 int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs; 318 319 for (size_t i = 0; i < stateByTrack.size(); ++i) { 320 CodecState *state = &stateByTrack.editValueAt(i); 321 322 CHECK_EQ((status_t)OK, state->mCodec->release()); 323 324 if (state->mIsAudio) { 325 printf("track %d: %lld bytes received. %.2f KB/sec\n", 326 i, 327 state->mNumBytesDecoded, 328 state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs); 329 } else { 330 printf("track %d: %lld frames decoded, %.2f fps. %lld bytes " 331 "received. %.2f KB/sec\n", 332 i, 333 state->mNumBuffersDecoded, 334 state->mNumBuffersDecoded * 1E6 / elapsedTimeUs, 335 state->mNumBytesDecoded, 336 state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs); 337 } 338 } 339 340 return 0; 341} 342 343int main(int argc, char **argv) { 344 using namespace android; 345 346 const char *me = argv[0]; 347 348 bool useAudio = false; 349 bool useVideo = false; 350 bool playback = false; 351 bool useSurface = false; 352 353 int res; 354 while ((res = getopt(argc, argv, "havpSD")) >= 0) { 355 switch (res) { 356 case 'a': 357 { 358 useAudio = true; 359 break; 360 } 361 362 case 'v': 363 { 364 useVideo = true; 365 break; 366 } 367 368 case 'p': 369 { 370 playback = true; 371 break; 372 } 373 374 case 'S': 375 { 376 useSurface = true; 377 break; 378 } 379 380 case '?': 381 case 'h': 382 default: 383 { 384 usage(me); 385 } 386 } 387 } 388 389 argc -= optind; 390 argv += optind; 391 392 if (argc != 1) { 393 usage(me); 394 } 395 396 if (!useAudio && !useVideo) { 397 useAudio = useVideo = true; 398 } 399 400 ProcessState::self()->startThreadPool(); 401 402 DataSource::RegisterDefaultSniffers(); 403 404 sp<ALooper> looper = new ALooper; 405 looper->start(); 406 407 sp<SurfaceComposerClient> composerClient; 408 sp<SurfaceControl> control; 409 sp<Surface> surface; 410 411 if (playback || (useSurface && useVideo)) { 412 composerClient = new SurfaceComposerClient; 413 CHECK_EQ(composerClient->initCheck(), (status_t)OK); 414 415 ssize_t displayWidth = composerClient->getDisplayWidth(0); 416 ssize_t displayHeight = composerClient->getDisplayHeight(0); 417 418 ALOGV("display is %ld x %ld\n", displayWidth, displayHeight); 419 420 control = composerClient->createSurface( 421 String8("A Surface"), 422 0, 423 displayWidth, 424 displayHeight, 425 PIXEL_FORMAT_RGB_565, 426 0); 427 428 CHECK(control != NULL); 429 CHECK(control->isValid()); 430 431 SurfaceComposerClient::openGlobalTransaction(); 432 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); 433 CHECK_EQ(control->show(), (status_t)OK); 434 SurfaceComposerClient::closeGlobalTransaction(); 435 436 surface = control->getSurface(); 437 CHECK(surface != NULL); 438 } 439 440 if (playback) { 441 sp<SimplePlayer> player = new SimplePlayer; 442 looper->registerHandler(player); 443 444 player->setDataSource(argv[0]); 445 player->setSurface(surface->getSurfaceTexture()); 446 player->start(); 447 sleep(60); 448 player->stop(); 449 player->reset(); 450 } else { 451 decode(looper, argv[0], useAudio, useVideo, surface); 452 } 453 454 if (playback || (useSurface && useVideo)) { 455 composerClient->dispose(); 456 } 457 458 looper->stop(); 459 460 return 0; 461} 462