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