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