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