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