Loading cmds/screenrecord/screenrecord.cpp +64 −0 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include <binder/IPCThreadState.h> #include <binder/IPCThreadState.h> #include <utils/Errors.h> #include <utils/Errors.h> #include <utils/SystemClock.h> #include <utils/Timers.h> #include <utils/Timers.h> #include <utils/Trace.h> #include <utils/Trace.h> Loading Loading @@ -95,6 +96,8 @@ static const uint32_t kMaxTimeLimitSec = 180; // 3 minutes static const uint32_t kFallbackWidth = 1280; // 720p static const uint32_t kFallbackWidth = 1280; // 720p static const uint32_t kFallbackHeight = 720; static const uint32_t kFallbackHeight = 720; static const char* kMimeTypeAvc = "video/avc"; static const char* kMimeTypeAvc = "video/avc"; static const char* kMimeTypeApplicationOctetstream = "application/octet-stream"; static const char* kWinscopeMagicString = "#VV1NSC0PET1ME!#"; // Command-line parameters. // Command-line parameters. static bool gVerbose = false; // chatty on stdout static bool gVerbose = false; // chatty on stdout Loading Loading @@ -349,6 +352,50 @@ static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, return NO_ERROR; return NO_ERROR; } } /* * Writes an unsigned integer byte-by-byte in little endian order regardless * of the platform endianness. */ template <typename UINT> static void writeValueLE(UINT value, uint8_t* buffer) { for (int i = 0; i < sizeof(UINT); ++i) { buffer[i] = static_cast<uint8_t>(value); value >>= 8; } } /* * Saves frames presentation time relative to the elapsed realtime clock in microseconds * preceded by a Winscope magic string and frame count to a metadata track. * This metadata is used by the Winscope tool to sync video with SurfaceFlinger * and WindowManager traces. * * The metadata is written as a binary array as follows: * - winscope magic string (kWinscopeMagicString constant), without trailing null char, * - the number of recorded frames (as little endian uint32), * - for every frame its presentation time relative to the elapsed realtime clock in microseconds * (as little endian uint64). */ static status_t writeWinscopeMetadata(const Vector<int64_t>& timestamps, const ssize_t metaTrackIdx, const sp<MediaMuxer>& muxer) { ALOGV("Writing metadata"); int64_t systemTimeToElapsedTimeOffsetMicros = (android::elapsedRealtimeNano() - systemTime(SYSTEM_TIME_MONOTONIC)) / 1000; sp<ABuffer> buffer = new ABuffer(timestamps.size() * sizeof(int64_t) + sizeof(uint32_t) + strlen(kWinscopeMagicString)); uint8_t* pos = buffer->data(); strcpy(reinterpret_cast<char*>(pos), kWinscopeMagicString); pos += strlen(kWinscopeMagicString); writeValueLE<uint32_t>(timestamps.size(), pos); pos += sizeof(uint32_t); for (size_t idx = 0; idx < timestamps.size(); ++idx) { writeValueLE<uint64_t>(static_cast<uint64_t>(timestamps[idx] + systemTimeToElapsedTimeOffsetMicros), pos); pos += sizeof(uint64_t); } return muxer->writeSampleData(buffer, metaTrackIdx, timestamps[0], 0); } /* /* * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * input frames are coming from the virtual display as fast as SurfaceFlinger * input frames are coming from the virtual display as fast as SurfaceFlinger Loading @@ -364,10 +411,12 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, static int kTimeout = 250000; // be responsive on signal static int kTimeout = 250000; // be responsive on signal status_t err; status_t err; ssize_t trackIdx = -1; ssize_t trackIdx = -1; ssize_t metaTrackIdx = -1; uint32_t debugNumFrames = 0; uint32_t debugNumFrames = 0; int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); DisplayInfo mainDpyInfo; DisplayInfo mainDpyInfo; Vector<int64_t> timestamps; assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL)); assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL)); Loading Loading @@ -465,6 +514,9 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, "Failed writing data to muxer (err=%d)\n", err); "Failed writing data to muxer (err=%d)\n", err); return err; return err; } } if (gOutputFormat == FORMAT_MP4) { timestamps.add(ptsUsec); } } } debugNumFrames++; debugNumFrames++; } } Loading @@ -491,6 +543,11 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, encoder->getOutputFormat(&newFormat); encoder->getOutputFormat(&newFormat); if (muxer != NULL) { if (muxer != NULL) { trackIdx = muxer->addTrack(newFormat); trackIdx = muxer->addTrack(newFormat); if (gOutputFormat == FORMAT_MP4) { sp<AMessage> metaFormat = new AMessage; metaFormat->setString(KEY_MIME, kMimeTypeApplicationOctetstream); metaTrackIdx = muxer->addTrack(metaFormat); } ALOGV("Starting muxer"); ALOGV("Starting muxer"); err = muxer->start(); err = muxer->start(); if (err != NO_ERROR) { if (err != NO_ERROR) { Loading Loading @@ -527,6 +584,13 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, systemTime(CLOCK_MONOTONIC) - startWhenNsec)); systemTime(CLOCK_MONOTONIC) - startWhenNsec)); fflush(stdout); fflush(stdout); } } if (metaTrackIdx >= 0 && !timestamps.isEmpty()) { err = writeWinscopeMetadata(timestamps, metaTrackIdx, muxer); if (err != NO_ERROR) { fprintf(stderr, "Failed writing metadata to muxer (err=%d)\n", err); return err; } } return NO_ERROR; return NO_ERROR; } } Loading Loading
cmds/screenrecord/screenrecord.cpp +64 −0 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ #include <binder/IPCThreadState.h> #include <binder/IPCThreadState.h> #include <utils/Errors.h> #include <utils/Errors.h> #include <utils/SystemClock.h> #include <utils/Timers.h> #include <utils/Timers.h> #include <utils/Trace.h> #include <utils/Trace.h> Loading Loading @@ -95,6 +96,8 @@ static const uint32_t kMaxTimeLimitSec = 180; // 3 minutes static const uint32_t kFallbackWidth = 1280; // 720p static const uint32_t kFallbackWidth = 1280; // 720p static const uint32_t kFallbackHeight = 720; static const uint32_t kFallbackHeight = 720; static const char* kMimeTypeAvc = "video/avc"; static const char* kMimeTypeAvc = "video/avc"; static const char* kMimeTypeApplicationOctetstream = "application/octet-stream"; static const char* kWinscopeMagicString = "#VV1NSC0PET1ME!#"; // Command-line parameters. // Command-line parameters. static bool gVerbose = false; // chatty on stdout static bool gVerbose = false; // chatty on stdout Loading Loading @@ -349,6 +352,50 @@ static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, return NO_ERROR; return NO_ERROR; } } /* * Writes an unsigned integer byte-by-byte in little endian order regardless * of the platform endianness. */ template <typename UINT> static void writeValueLE(UINT value, uint8_t* buffer) { for (int i = 0; i < sizeof(UINT); ++i) { buffer[i] = static_cast<uint8_t>(value); value >>= 8; } } /* * Saves frames presentation time relative to the elapsed realtime clock in microseconds * preceded by a Winscope magic string and frame count to a metadata track. * This metadata is used by the Winscope tool to sync video with SurfaceFlinger * and WindowManager traces. * * The metadata is written as a binary array as follows: * - winscope magic string (kWinscopeMagicString constant), without trailing null char, * - the number of recorded frames (as little endian uint32), * - for every frame its presentation time relative to the elapsed realtime clock in microseconds * (as little endian uint64). */ static status_t writeWinscopeMetadata(const Vector<int64_t>& timestamps, const ssize_t metaTrackIdx, const sp<MediaMuxer>& muxer) { ALOGV("Writing metadata"); int64_t systemTimeToElapsedTimeOffsetMicros = (android::elapsedRealtimeNano() - systemTime(SYSTEM_TIME_MONOTONIC)) / 1000; sp<ABuffer> buffer = new ABuffer(timestamps.size() * sizeof(int64_t) + sizeof(uint32_t) + strlen(kWinscopeMagicString)); uint8_t* pos = buffer->data(); strcpy(reinterpret_cast<char*>(pos), kWinscopeMagicString); pos += strlen(kWinscopeMagicString); writeValueLE<uint32_t>(timestamps.size(), pos); pos += sizeof(uint32_t); for (size_t idx = 0; idx < timestamps.size(); ++idx) { writeValueLE<uint64_t>(static_cast<uint64_t>(timestamps[idx] + systemTimeToElapsedTimeOffsetMicros), pos); pos += sizeof(uint64_t); } return muxer->writeSampleData(buffer, metaTrackIdx, timestamps[0], 0); } /* /* * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * input frames are coming from the virtual display as fast as SurfaceFlinger * input frames are coming from the virtual display as fast as SurfaceFlinger Loading @@ -364,10 +411,12 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, static int kTimeout = 250000; // be responsive on signal static int kTimeout = 250000; // be responsive on signal status_t err; status_t err; ssize_t trackIdx = -1; ssize_t trackIdx = -1; ssize_t metaTrackIdx = -1; uint32_t debugNumFrames = 0; uint32_t debugNumFrames = 0; int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); DisplayInfo mainDpyInfo; DisplayInfo mainDpyInfo; Vector<int64_t> timestamps; assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL)); assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL)); Loading Loading @@ -465,6 +514,9 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, "Failed writing data to muxer (err=%d)\n", err); "Failed writing data to muxer (err=%d)\n", err); return err; return err; } } if (gOutputFormat == FORMAT_MP4) { timestamps.add(ptsUsec); } } } debugNumFrames++; debugNumFrames++; } } Loading @@ -491,6 +543,11 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, encoder->getOutputFormat(&newFormat); encoder->getOutputFormat(&newFormat); if (muxer != NULL) { if (muxer != NULL) { trackIdx = muxer->addTrack(newFormat); trackIdx = muxer->addTrack(newFormat); if (gOutputFormat == FORMAT_MP4) { sp<AMessage> metaFormat = new AMessage; metaFormat->setString(KEY_MIME, kMimeTypeApplicationOctetstream); metaTrackIdx = muxer->addTrack(metaFormat); } ALOGV("Starting muxer"); ALOGV("Starting muxer"); err = muxer->start(); err = muxer->start(); if (err != NO_ERROR) { if (err != NO_ERROR) { Loading Loading @@ -527,6 +584,13 @@ static status_t runEncoder(const sp<MediaCodec>& encoder, systemTime(CLOCK_MONOTONIC) - startWhenNsec)); systemTime(CLOCK_MONOTONIC) - startWhenNsec)); fflush(stdout); fflush(stdout); } } if (metaTrackIdx >= 0 && !timestamps.isEmpty()) { err = writeWinscopeMetadata(timestamps, metaTrackIdx, muxer); if (err != NO_ERROR) { fprintf(stderr, "Failed writing metadata to muxer (err=%d)\n", err); return err; } } return NO_ERROR; return NO_ERROR; } } Loading