Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 439513c6 authored by Kean Mariotti's avatar Kean Mariotti Committed by Automerger Merge Worker
Browse files

Add new screenrecording winscope meta data am: 39c217d0

parents f62bd57c 39c217d0
Loading
Loading
Loading
Loading
+93 −22
Original line number Diff line number Diff line
@@ -13,6 +13,9 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <algorithm>
#include <string_view>
#include <type_traits>

#include <assert.h>
#include <ctype.h>
@@ -100,7 +103,6 @@ static const uint32_t kFallbackWidth = 1280; // 720p
static const uint32_t kFallbackHeight = 720;
static const char* kMimeTypeAvc = "video/avc";
static const char* kMimeTypeApplicationOctetstream = "application/octet-stream";
static const char* kWinscopeMagicString = "#VV1NSC0PET1ME!#";

// Command-line parameters.
static bool gVerbose = false;           // chatty on stdout
@@ -354,14 +356,15 @@ static status_t prepareVirtualDisplay(
}

/*
 * Writes an unsigned integer byte-by-byte in little endian order regardless
 * Writes an unsigned/signed 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;
template <typename T>
static void writeValueLE(T value, uint8_t* buffer) {
    std::remove_const_t<T> temp = value;
    for (int i = 0; i < sizeof(T); ++i) {
        buffer[i] = static_cast<std::uint8_t>(temp & 0xff);
        temp >>= 8;
    }
}

@@ -377,16 +380,18 @@ static void writeValueLE(UINT value, uint8_t* buffer) {
 * - 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,
static status_t writeWinscopeMetadataLegacy(const Vector<int64_t>& timestamps,
        const ssize_t metaTrackIdx, AMediaMuxer *muxer) {
    ALOGV("Writing metadata");
    static constexpr auto kWinscopeMagicStringLegacy = "#VV1NSC0PET1ME!#";

    ALOGV("Writing winscope metadata legacy");
    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));
        + sizeof(uint32_t) + strlen(kWinscopeMagicStringLegacy));
    uint8_t* pos = buffer->data();
    strcpy(reinterpret_cast<char*>(pos), kWinscopeMagicString);
    pos += strlen(kWinscopeMagicString);
    strcpy(reinterpret_cast<char*>(pos), kWinscopeMagicStringLegacy);
    pos += strlen(kWinscopeMagicStringLegacy);
    writeValueLE<uint32_t>(timestamps.size(), pos);
    pos += sizeof(uint32_t);
    for (size_t idx = 0; idx < timestamps.size(); ++idx) {
@@ -395,10 +400,68 @@ static status_t writeWinscopeMetadata(const Vector<int64_t>& timestamps,
        pos += sizeof(uint64_t);
    }
    AMediaCodecBufferInfo bufferInfo = {
        0,
        0 /* offset */,
        static_cast<int32_t>(buffer->size()),
        timestamps[0],
        0
        timestamps[0] /* presentationTimeUs */,
        0 /* flags */
    };
    return AMediaMuxer_writeSampleData(muxer, metaTrackIdx, buffer->data(), &bufferInfo);
}

/*
 * Saves metadata needed by Winscope to synchronize the screen recording playback with other traces.
 *
 * The metadata (version 1) is written as a binary array with the following format:
 * - winscope magic string (#VV1NSC0PET1ME2#, 16B).
 * - the metadata version number (4B).
 * - Realtime-to-monotonic time offset in nanoseconds (8B).
 * - the recorded frames count (8B)
 * - for each recorded frame:
 *     - System time in monotonic clock timebase in nanoseconds (8B).
 *
 * All numbers are Little Endian encoded.
 */
static status_t writeWinscopeMetadata(const Vector<std::int64_t>& timestampsMonotonicUs,
        const ssize_t metaTrackIdx, AMediaMuxer *muxer) {
    ALOGV("Writing winscope metadata");

    static constexpr auto kWinscopeMagicString = std::string_view {"#VV1NSC0PET1ME2#"};
    static constexpr std::uint32_t metadataVersion = 1;
    const std::int64_t realToMonotonicTimeOffsetNs =
            systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC);
    const std::uint32_t framesCount = static_cast<std::uint32_t>(timestampsMonotonicUs.size());

    sp<ABuffer> buffer = new ABuffer(
        kWinscopeMagicString.size() +
        sizeof(decltype(metadataVersion)) +
        sizeof(decltype(realToMonotonicTimeOffsetNs)) +
        sizeof(decltype(framesCount)) +
        framesCount * sizeof(std::uint64_t)
    );
    std::uint8_t* pos = buffer->data();

    std::copy(kWinscopeMagicString.cbegin(), kWinscopeMagicString.cend(), pos);
    pos += kWinscopeMagicString.size();

    writeValueLE(metadataVersion, pos);
    pos += sizeof(decltype(metadataVersion));

    writeValueLE(realToMonotonicTimeOffsetNs, pos);
    pos += sizeof(decltype(realToMonotonicTimeOffsetNs));

    writeValueLE(framesCount, pos);
    pos += sizeof(decltype(framesCount));

    for (const auto timestampMonotonicUs : timestampsMonotonicUs) {
        writeValueLE<std::uint64_t>(timestampMonotonicUs * 1000, pos);
        pos += sizeof(std::uint64_t);
    }

    AMediaCodecBufferInfo bufferInfo = {
        0 /* offset */,
        static_cast<std::int32_t>(buffer->size()),
        timestampsMonotonicUs[0] /* presentationTimeUs */,
        0 /* flags */
    };
    return AMediaMuxer_writeSampleData(muxer, metaTrackIdx, buffer->data(), &bufferInfo);
}
@@ -418,11 +481,12 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
    static int kTimeout = 250000;   // be responsive on signal
    status_t err;
    ssize_t trackIdx = -1;
    ssize_t metaLegacyTrackIdx = -1;
    ssize_t metaTrackIdx = -1;
    uint32_t debugNumFrames = 0;
    int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
    int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
    Vector<int64_t> timestamps;
    Vector<int64_t> timestampsMonotonicUs;
    bool firstFrame = true;

    assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL));
@@ -520,9 +584,9 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
                    sp<ABuffer> buffer = new ABuffer(
                            buffers[bufIndex]->data(), buffers[bufIndex]->size());
                    AMediaCodecBufferInfo bufferInfo = {
                        0,
                        0 /* offset */,
                        static_cast<int32_t>(buffer->size()),
                        ptsUsec,
                        ptsUsec /* presentationTimeUs */,
                        flags
                    };
                    err = AMediaMuxer_writeSampleData(muxer, trackIdx, buffer->data(), &bufferInfo);
@@ -532,7 +596,7 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
                        return err;
                    }
                    if (gOutputFormat == FORMAT_MP4) {
                        timestamps.add(ptsUsec);
                        timestampsMonotonicUs.add(ptsUsec);
                    }
                }
                debugNumFrames++;
@@ -565,6 +629,7 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
                    if (gOutputFormat == FORMAT_MP4) {
                        AMediaFormat *metaFormat = AMediaFormat_new();
                        AMediaFormat_setString(metaFormat, AMEDIAFORMAT_KEY_MIME, kMimeTypeApplicationOctetstream);
                        metaLegacyTrackIdx = AMediaMuxer_addTrack(muxer, metaFormat);
                        metaTrackIdx = AMediaMuxer_addTrack(muxer, metaFormat);
                        AMediaFormat_delete(metaFormat);
                    }
@@ -604,10 +669,16 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
                        systemTime(CLOCK_MONOTONIC) - startWhenNsec));
        fflush(stdout);
    }
    if (metaTrackIdx >= 0 && !timestamps.isEmpty()) {
        err = writeWinscopeMetadata(timestamps, metaTrackIdx, muxer);
    if (metaLegacyTrackIdx >= 0 && metaTrackIdx >= 0 && !timestampsMonotonicUs.isEmpty()) {
        err = writeWinscopeMetadataLegacy(timestampsMonotonicUs, metaLegacyTrackIdx, muxer);
        if (err != NO_ERROR) {
            fprintf(stderr, "Failed writing legacy winscope metadata to muxer (err=%d)\n", err);
            return err;
        }

        err = writeWinscopeMetadata(timestampsMonotonicUs, metaTrackIdx, muxer);
        if (err != NO_ERROR) {
            fprintf(stderr, "Failed writing metadata to muxer (err=%d)\n", err);
            fprintf(stderr, "Failed writing winscope metadata to muxer (err=%d)\n", err);
            return err;
        }
    }