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