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