diff options
Diffstat (limited to 'cmds')
-rw-r--r-- | cmds/screenrecord/screenrecord.cpp | 153 | ||||
-rw-r--r-- | cmds/screenrecord/screenrecord.h | 2 | ||||
-rw-r--r-- | cmds/stagefright/SimplePlayer.cpp | 4 | ||||
-rw-r--r-- | cmds/stagefright/codec.cpp | 3 | ||||
-rw-r--r-- | cmds/stagefright/muxer.cpp | 3 | ||||
-rw-r--r-- | cmds/stagefright/sf2.cpp | 65 | ||||
-rw-r--r-- | cmds/stagefright/stagefright.cpp | 4 | ||||
-rw-r--r-- | cmds/stagefright/stream.cpp | 5 |
8 files changed, 178 insertions, 61 deletions
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index 61f83e3..b6f150c 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -45,6 +45,7 @@ #include <signal.h> #include <getopt.h> #include <sys/wait.h> +#include <termios.h> #include <assert.h> #include "screenrecord.h" @@ -61,6 +62,7 @@ static const uint32_t kFallbackHeight = 720; // Command-line parameters. static bool gVerbose = false; // chatty on stdout static bool gRotate = false; // rotate 90 degrees +static bool gRawOutput = false; // generate raw H.264 byte stream output static bool gSizeSpecified = false; // was size explicitly requested? static bool gWantInfoScreen = false; // do we want initial info screen? static bool gWantFrameTime = false; // do we want times on each frame? @@ -298,10 +300,12 @@ static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, * input frames are coming from the virtual display as fast as SurfaceFlinger * wants to send them. * + * Exactly one of muxer or rawFp must be non-null. + * * The muxer must *not* have been started before calling. */ static status_t runEncoder(const sp<MediaCodec>& encoder, - const sp<MediaMuxer>& muxer, const sp<IBinder>& mainDpy, + const sp<MediaMuxer>& muxer, FILE* rawFp, const sp<IBinder>& mainDpy, const sp<IBinder>& virtualDpy, uint8_t orientation) { static int kTimeout = 250000; // be responsive on signal status_t err; @@ -311,6 +315,8 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); DisplayInfo mainDpyInfo; + assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL)); + Vector<sp<ABuffer> > buffers; err = encoder->getOutputBuffers(&buffers); if (err != NO_ERROR) { @@ -342,15 +348,16 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, case NO_ERROR: // got a buffer if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) { - // ignore this -- we passed the CSD into MediaMuxer when - // we got the format change notification - ALOGV("Got codec config buffer (%u bytes); ignoring", size); - size = 0; + ALOGV("Got codec config buffer (%u bytes)", size); + if (muxer != NULL) { + // ignore this -- we passed the CSD into MediaMuxer when + // we got the format change notification + size = 0; + } } if (size != 0) { ALOGV("Got data in buffer %d, size=%d, pts=%lld", bufIndex, size, ptsUsec); - assert(trackIdx != -1); { // scope ATRACE_NAME("orientation"); @@ -379,14 +386,23 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000; } - // The MediaMuxer docs are unclear, but it appears that we - // need to pass either the full set of BufferInfo flags, or - // (flags & BUFFER_FLAG_SYNCFRAME). - // - // If this blocks for too long we could drop frames. We may - // want to queue these up and do them on a different thread. - { // scope + if (muxer == NULL) { + fwrite(buffers[bufIndex]->data(), 1, size, rawFp); + // Flush the data immediately in case we're streaming. + // We don't want to do this if all we've written is + // the SPS/PPS data because mplayer gets confused. + if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) == 0) { + fflush(rawFp); + } + } else { + // The MediaMuxer docs are unclear, but it appears that we + // need to pass either the full set of BufferInfo flags, or + // (flags & BUFFER_FLAG_SYNCFRAME). + // + // If this blocks for too long we could drop frames. We may + // want to queue these up and do them on a different thread. ATRACE_NAME("write sample"); + assert(trackIdx != -1); err = muxer->writeSampleData(buffers[bufIndex], trackIdx, ptsUsec, flags); if (err != NO_ERROR) { @@ -418,12 +434,14 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, ALOGV("Encoder format changed"); sp<AMessage> newFormat; encoder->getOutputFormat(&newFormat); - trackIdx = muxer->addTrack(newFormat); - ALOGV("Starting muxer"); - err = muxer->start(); - if (err != NO_ERROR) { - fprintf(stderr, "Unable to start muxer (err=%d)\n", err); - return err; + if (muxer != NULL) { + trackIdx = muxer->addTrack(newFormat); + ALOGV("Starting muxer"); + err = muxer->start(); + if (err != NO_ERROR) { + fprintf(stderr, "Unable to start muxer (err=%d)\n", err); + return err; + } } } break; @@ -457,6 +475,44 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, } /* + * Raw H.264 byte stream output requested. Send the output to stdout + * if desired. If the output is a tty, reconfigure it to avoid the + * CRLF line termination that we see with "adb shell" commands. + */ +static FILE* prepareRawOutput(const char* fileName) { + FILE* rawFp = NULL; + + if (strcmp(fileName, "-") == 0) { + if (gVerbose) { + fprintf(stderr, "ERROR: verbose output and '-' not compatible"); + return NULL; + } + rawFp = stdout; + } else { + rawFp = fopen(fileName, "w"); + if (rawFp == NULL) { + fprintf(stderr, "fopen raw failed: %s\n", strerror(errno)); + return NULL; + } + } + + int fd = fileno(rawFp); + if (isatty(fd)) { + // best effort -- reconfigure tty for "raw" + ALOGD("raw video output to tty (fd=%d)", fd); + struct termios term; + if (tcgetattr(fd, &term) == 0) { + cfmakeraw(&term); + if (tcsetattr(fd, TCSANOW, &term) == 0) { + ALOGD("tty successfully configured for raw"); + } + } + } + + return rawFp; +} + +/* * Main "do work" method. * * Configures codec, muxer, and virtual display, then starts moving bits @@ -558,16 +614,26 @@ static status_t recordScreen(const char* fileName) { return err; } - // Configure muxer. We have to wait for the CSD blob from the encoder - // before we can start it. - sp<MediaMuxer> muxer = new MediaMuxer(fileName, - MediaMuxer::OUTPUT_FORMAT_MPEG_4); - if (gRotate) { - muxer->setOrientationHint(90); // TODO: does this do anything? + sp<MediaMuxer> muxer = NULL; + FILE* rawFp = NULL; + if (gRawOutput) { + rawFp = prepareRawOutput(fileName); + if (rawFp == NULL) { + encoder->release(); + return -1; + } + } else { + // Configure muxer. We have to wait for the CSD blob from the encoder + // before we can start it. + muxer = new MediaMuxer(fileName, MediaMuxer::OUTPUT_FORMAT_MPEG_4); + if (gRotate) { + muxer->setOrientationHint(90); // TODO: does this do anything? + } } // Main encoder loop. - err = runEncoder(encoder, muxer, mainDpy, dpy, mainDpyInfo.orientation); + err = runEncoder(encoder, muxer, rawFp, mainDpy, dpy, + mainDpyInfo.orientation); if (err != NO_ERROR) { fprintf(stderr, "Encoder failed (err=%d)\n", err); // fall through to cleanup @@ -584,9 +650,13 @@ static status_t recordScreen(const char* fileName) { overlay->stop(); } encoder->stop(); - // If we don't stop muxer explicitly, i.e. let the destructor run, - // it may hang (b/11050628). - muxer->stop(); + if (muxer != NULL) { + // If we don't stop muxer explicitly, i.e. let the destructor run, + // it may hang (b/11050628). + muxer->stop(); + } else if (rawFp != stdout) { + fclose(rawFp); + } encoder->release(); return err; @@ -753,6 +823,7 @@ int main(int argc, char* const argv[]) { { "show-frame-time", no_argument, NULL, 'f' }, { "bugreport", no_argument, NULL, 'u' }, { "rotate", no_argument, NULL, 'r' }, + { "raw", no_argument, NULL, 'w' }, { NULL, 0, NULL, 0 } }; @@ -818,6 +889,10 @@ int main(int argc, char* const argv[]) { // experimental feature gRotate = true; break; + case 'w': + // experimental feature + gRawOutput = true; + break; default: if (ic != '?') { fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic); @@ -831,17 +906,19 @@ int main(int argc, char* const argv[]) { return 2; } - // MediaMuxer tries to create the file in the constructor, but we don't - // learn about the failure until muxer.start(), which returns a generic - // error code without logging anything. We attempt to create the file - // now for better diagnostics. const char* fileName = argv[optind]; - int fd = open(fileName, O_CREAT | O_RDWR, 0644); - if (fd < 0) { - fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno)); - return 1; + if (!gRawOutput) { + // MediaMuxer tries to create the file in the constructor, but we don't + // learn about the failure until muxer.start(), which returns a generic + // error code without logging anything. We attempt to create the file + // now for better diagnostics. + int fd = open(fileName, O_CREAT | O_RDWR, 0644); + if (fd < 0) { + fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno)); + return 1; + } + close(fd); } - close(fd); status_t err = recordScreen(fileName); if (err == NO_ERROR) { diff --git a/cmds/screenrecord/screenrecord.h b/cmds/screenrecord/screenrecord.h index 95e8a68..9b058c2 100644 --- a/cmds/screenrecord/screenrecord.h +++ b/cmds/screenrecord/screenrecord.h @@ -18,6 +18,6 @@ #define SCREENRECORD_SCREENRECORD_H #define kVersionMajor 1 -#define kVersionMinor 1 +#define kVersionMinor 2 #endif /*SCREENRECORD_SCREENRECORD_H*/ diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp index 5d2d721..1b2f792 100644 --- a/cmds/stagefright/SimplePlayer.cpp +++ b/cmds/stagefright/SimplePlayer.cpp @@ -23,6 +23,7 @@ #include <gui/Surface.h> #include <media/AudioTrack.h> #include <media/ICrypto.h> +#include <media/IMediaHTTPService.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -275,7 +276,8 @@ status_t SimplePlayer::onPrepare() { mExtractor = new NuMediaExtractor; - status_t err = mExtractor->setDataSource(mPath.c_str()); + status_t err = mExtractor->setDataSource( + NULL /* httpService */, mPath.c_str()); if (err != OK) { mExtractor.clear(); diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp index d125ad1..fd02bcc 100644 --- a/cmds/stagefright/codec.cpp +++ b/cmds/stagefright/codec.cpp @@ -24,6 +24,7 @@ #include <binder/IServiceManager.h> #include <binder/ProcessState.h> #include <media/ICrypto.h> +#include <media/IMediaHTTPService.h> #include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -76,7 +77,7 @@ static int decode( static int64_t kTimeout = 500ll; sp<NuMediaExtractor> extractor = new NuMediaExtractor; - if (extractor->setDataSource(path) != OK) { + if (extractor->setDataSource(NULL /* httpService */, path) != OK) { fprintf(stderr, "unable to instantiate extractor.\n"); return 1; } diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp index 90daea2..f4a33e8 100644 --- a/cmds/stagefright/muxer.cpp +++ b/cmds/stagefright/muxer.cpp @@ -20,6 +20,7 @@ #include <utils/Log.h> #include <binder/ProcessState.h> +#include <media/IMediaHTTPService.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> @@ -59,7 +60,7 @@ static int muxing( int trimEndTimeMs, int rotationDegrees) { sp<NuMediaExtractor> extractor = new NuMediaExtractor; - if (extractor->setDataSource(path) != OK) { + if (extractor->setDataSource(NULL /* httpService */, path) != OK) { fprintf(stderr, "unable to instantiate extractor. %s\n", path); return 1; } diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index b2b9ce5..3c0c7ec 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -19,8 +19,12 @@ #include <inttypes.h> #include <utils/Log.h> +#include <signal.h> + #include <binder/ProcessState.h> +#include <media/IMediaHTTPService.h> + #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -43,6 +47,18 @@ using namespace android; +volatile static bool ctrlc = false; + +static sighandler_t oldhandler = NULL; + +static void mysighandler(int signum) { + if (signum == SIGINT) { + ctrlc = true; + return; + } + oldhandler(signum); +} + struct Controller : public AHandler { Controller(const char *uri, bool decodeAudio, const sp<Surface> &surface, bool renderToSurface) @@ -63,7 +79,30 @@ protected: virtual ~Controller() { } + virtual void printStatistics() { + int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs; + + if (mDecodeAudio) { + printf("%" PRId64 " bytes received. %.2f KB/sec\n", + mTotalBytesReceived, + mTotalBytesReceived * 1E6 / 1024 / delayUs); + } else { + printf("%d frames decoded, %.2f fps. %" PRId64 " bytes " + "received. %.2f KB/sec\n", + mNumOutputBuffersReceived, + mNumOutputBuffersReceived * 1E6 / delayUs, + mTotalBytesReceived, + mTotalBytesReceived * 1E6 / 1024 / delayUs); + } + } + virtual void onMessageReceived(const sp<AMessage> &msg) { + if (ctrlc) { + printf("\n"); + printStatistics(); + (new AMessage(kWhatStop, id()))->post(); + ctrlc = false; + } switch (msg->what()) { case kWhatStart: { @@ -76,7 +115,8 @@ protected: #endif sp<DataSource> dataSource = - DataSource::CreateFromURI(mURI.c_str()); + DataSource::CreateFromURI( + NULL /* httpService */, mURI.c_str()); sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); @@ -99,7 +139,10 @@ protected: break; } } - CHECK(mSource != NULL); + if (mSource == NULL) { + printf("no %s track found\n", mDecodeAudio ? "audio" : "video"); + exit (1); + } CHECK_EQ(mSource->start(), (status_t)OK); @@ -181,21 +224,7 @@ protected: || what == ACodec::kWhatError) { printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n"); - int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs; - - if (mDecodeAudio) { - printf("%" PRId64 " bytes received. %.2f KB/sec\n", - mTotalBytesReceived, - mTotalBytesReceived * 1E6 / 1024 / delayUs); - } else { - printf("%d frames decoded, %.2f fps. %" PRId64 " bytes " - "received. %.2f KB/sec\n", - mNumOutputBuffersReceived, - mNumOutputBuffersReceived * 1E6 / delayUs, - mTotalBytesReceived, - mTotalBytesReceived * 1E6 / 1024 / delayUs); - } - + printStatistics(); (new AMessage(kWhatStop, id()))->post(); } else if (what == ACodec::kWhatFlushCompleted) { mSeekState = SEEK_FLUSH_COMPLETED; @@ -639,6 +668,8 @@ int main(int argc, char **argv) { looper->registerHandler(controller); + signal(SIGINT, mysighandler); + controller->startAsync(); CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 8efb39e..daaea27 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -29,6 +29,7 @@ #include <binder/IServiceManager.h> #include <binder/ProcessState.h> +#include <media/IMediaHTTPService.h> #include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/ALooper.h> #include "include/NuCachedSource2.h" @@ -960,7 +961,8 @@ int main(int argc, char **argv) { const char *filename = argv[k]; - sp<DataSource> dataSource = DataSource::CreateFromURI(filename); + sp<DataSource> dataSource = + DataSource::CreateFromURI(NULL /* httpService */, filename); if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) { fprintf(stderr, "Unable to create data source.\n"); diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index dba67a9..b2abc0f 100644 --- a/cmds/stagefright/stream.cpp +++ b/cmds/stagefright/stream.cpp @@ -21,6 +21,7 @@ #include <binder/ProcessState.h> #include <cutils/properties.h> // for property_get +#include <media/IMediaHTTPService.h> #include <media/IStreamSource.h> #include <media/mediaplayer.h> #include <media/stagefright/foundation/ADebug.h> @@ -159,7 +160,9 @@ private: MyConvertingStreamSource::MyConvertingStreamSource(const char *filename) : mCurrentBufferIndex(-1), mCurrentBufferOffset(0) { - sp<DataSource> dataSource = DataSource::CreateFromURI(filename); + sp<DataSource> dataSource = + DataSource::CreateFromURI(NULL /* httpService */, filename); + CHECK(dataSource != NULL); sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); |