// Copyright (C) 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "host-common/MediaSnapshotHelper.h" #include "host-common/H264NaluParser.h" #include "host-common/VpxFrameParser.h" #define MEDIA_SNAPSHOT_DEBUG 0 #if MEDIA_SNAPSHOT_DEBUG #define SNAPSHOT_DPRINT(fmt, ...) \ fprintf(stderr, "media-snapshot-helper: %s:%d " fmt "\n", __func__, \ __LINE__, ##__VA_ARGS__); #else #define SNAPSHOT_DPRINT(fmt, ...) #endif namespace android { namespace emulation { using PacketInfo = MediaSnapshotState::PacketInfo; using ColorAspects = MediaSnapshotState::ColorAspects; void MediaSnapshotHelper::savePacket(const uint8_t* frame, size_t szBytes, uint64_t inputPts) { if (mType == CodecType::H264) { return saveH264Packet(frame, szBytes, inputPts); } else { return saveVPXPacket(frame, szBytes, inputPts); } } void MediaSnapshotHelper::saveVPXPacket(const uint8_t* data, size_t len, uint64_t user_priv) { const bool enableSnapshot = true; if (enableSnapshot) { VpxFrameParser fparser(mType == CodecType::VP8 ? 8 : 9, data, (size_t)len); SNAPSHOT_DPRINT("found frame type: %s frame", fparser.isKeyFrame() ? "KEY" : "NON-KEY"); std::vector v; v.assign(data, data + len); bool isIFrame = fparser.isKeyFrame(); if (isIFrame) { mSnapshotState.savedPackets.clear(); } const bool saveOK = mSnapshotState.savePacket(v, user_priv); if (saveOK) { SNAPSHOT_DPRINT("saving packet of isze %d; total is %d", (int)(v.size()), (int)(mSnapshotState.savedPackets.size())); } else { SNAPSHOT_DPRINT("saving packet; has duplicate, skip; total is %d", (int)(mSnapshotState.savedPackets.size())); } } } void MediaSnapshotHelper::saveH264Packet(const uint8_t* frame, size_t szBytes, uint64_t inputPts) { const bool enableSnapshot = true; if (enableSnapshot) { std::vector v; v.assign(frame, frame + szBytes); bool hasSps = H264NaluParser::checkSpsFrame(frame, szBytes); if (hasSps) { SNAPSHOT_DPRINT("create new snapshot state"); MediaSnapshotState newSnapshotState{}; // we need to keep the frames, the guest might not have retrieved // them yet; otherwise, we might loose some frames std::swap(newSnapshotState.savedFrames, mSnapshotState.savedFrames); std::swap(newSnapshotState, mSnapshotState); mSnapshotState.saveSps(v); } else { bool hasPps = H264NaluParser::checkPpsFrame(frame, szBytes); if (hasPps) { mSnapshotState.savePps(v); mSnapshotState.savedPackets.clear(); mSnapshotState.savedDecodedFrame.data.clear(); } else { bool isIFrame = H264NaluParser::checkIFrame(frame, szBytes); if (isIFrame) { mSnapshotState.savedPackets.clear(); } mSnapshotState.savePacket(std::move(v), inputPts); SNAPSHOT_DPRINT("saving packet; total is %d", (int)(mSnapshotState.savedPackets.size())); } } } } void MediaSnapshotHelper::save(base::Stream* stream) const { SNAPSHOT_DPRINT("saving packets now %d", (int)(mSnapshotState.savedPackets.size())); if (mType == CodecType::H264) { stream->putBe32(264); } else if (mType == CodecType::VP8) { stream->putBe32(8); } else if (mType == CodecType::VP9) { stream->putBe32(9); } mSnapshotState.save(stream); } void MediaSnapshotHelper::replay( std::function oneShotDecode) { if (mSnapshotState.sps.size() > 0) { oneShotDecode(mSnapshotState.sps.data(), mSnapshotState.sps.size(), 0); if (mSnapshotState.pps.size() > 0) { oneShotDecode(mSnapshotState.pps.data(), mSnapshotState.pps.size(), 0); for (int i = 0; i < mSnapshotState.savedPackets.size(); ++i) { MediaSnapshotState::PacketInfo& pkt = mSnapshotState.savedPackets[i]; SNAPSHOT_DPRINT("reloading frame %d size %d", i, (int)pkt.data.size()); oneShotDecode(pkt.data.data(), pkt.data.size(), pkt.pts); } } } } void MediaSnapshotHelper::load( base::Stream* stream, std::function oneShotDecode) { int type = stream->getBe32(); if (type == 264) { mType = CodecType::H264; } else if (type == 8) { mType = CodecType::VP8; } else if (type == 9) { mType = CodecType::VP9; } mSnapshotState.load(stream); SNAPSHOT_DPRINT("loaded packets %d, now restore decoder", (int)(mSnapshotState.savedPackets.size())); replay(oneShotDecode); SNAPSHOT_DPRINT("Done loading snapshots frames\n\n"); } } // namespace emulation } // namespace android