Snap for 9679998 from ccca500200c421d3aefd6b451f4256fa4a47a3c4 to sdk-release

Change-Id: Ia5476d5e12298eff6c12b36df323ad616f00f319
diff --git a/Android.bp b/Android.bp
index 60ffc69..64a9333 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,32 @@
+package {
+    default_applicable_licenses: ["hardware_google_aemu_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "hardware_google_aemu_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+        "SPDX-license-identifier-MIT",
+    ],
+    // large-scale-change unable to identify any license_text files
+}
+
 cc_library_headers {
     name: "aemu_common_headers",
     host_supported: true,
diff --git a/base/Android.bp b/base/Android.bp
index 27fa87e..e767958 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_google_aemu_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_google_aemu_license"],
+}
+
 cc_library_static {
     name: "gfxstream_base",
     defaults: ["gfxstream_defaults"],
diff --git a/base/Tracing.cpp b/base/Tracing.cpp
index 7f372bc..2d9df3a 100644
--- a/base/Tracing.cpp
+++ b/base/Tracing.cpp
@@ -67,35 +67,35 @@
 #endif
 
 __attribute__((always_inline)) void beginTrace(const char* name) {
-    if (CC_LIKELY(*tracingDisabledPtr)) return;
+    if (CC_LIKELY(!tracingDisabledPtr)) return;
 #ifdef USE_PERFETTO_TRACING
     virtualdeviceperfetto::beginTrace(name);
 #endif
 }
 
 __attribute__((always_inline)) void endTrace() {
-    if (CC_LIKELY(*tracingDisabledPtr)) return;
+    if (CC_LIKELY(!tracingDisabledPtr)) return;
 #ifdef USE_PERFETTO_TRACING
     virtualdeviceperfetto::endTrace();
 #endif
 }
 
 __attribute__((always_inline)) void traceCounter(const char* name, int64_t value) {
-    if (CC_LIKELY(*tracingDisabledPtr)) return;
+    if (CC_LIKELY(!tracingDisabledPtr)) return;
 #ifdef USE_PERFETTO_TRACING
     virtualdeviceperfetto::traceCounter(name, value);
 #endif
 }
 
 ScopedTrace::ScopedTrace(const char* name) {
-    if (CC_LIKELY(*tracingDisabledPtr)) return;
+    if (CC_LIKELY(!tracingDisabledPtr)) return;
 #ifdef USE_PERFETTO_TRACING
     virtualdeviceperfetto::beginTrace(name);
 #endif
 }
 
 ScopedTrace::~ScopedTrace() {
-    if (CC_LIKELY(*tracingDisabledPtr)) return;
+    if (CC_LIKELY(!tracingDisabledPtr)) return;
 #ifdef USE_PERFETTO_TRACING
     virtualdeviceperfetto::endTrace();
 #endif
diff --git a/host-common/Android.bp b/host-common/Android.bp
index bac2451..f004d7a 100644
--- a/host-common/Android.bp
+++ b/host-common/Android.bp
@@ -1,3 +1,14 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_google_aemu_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-BSD
+    //   SPDX-license-identifier-MIT
+    default_applicable_licenses: ["hardware_google_aemu_license"],
+}
+
 cc_library_static {
     name: "gfxstream_host_common",
     defaults: [ "gfxstream_defaults" ],
diff --git a/host-common/MediaCudaUtils.cpp b/host-common/MediaCudaUtils.cpp
index 8b5c1ee..d131dd3 100644
--- a/host-common/MediaCudaUtils.cpp
+++ b/host-common/MediaCudaUtils.cpp
@@ -104,7 +104,8 @@
 
 void media_cuda_utils_nv12_updater(void* privData,
                                    uint32_t type,
-                                   uint32_t* textures) {
+                                   uint32_t* textures,
+                                   void* callerData) {
     constexpr uint32_t kFRAMEWORK_FORMAT_NV12 = 3;
     if (type != kFRAMEWORK_FORMAT_NV12) {
         return;
diff --git a/host-common/MediaH264DecoderCuvid.cpp b/host-common/MediaH264DecoderCuvid.cpp
index 4bd2ba8..65c7463 100644
--- a/host-common/MediaH264DecoderCuvid.cpp
+++ b/host-common/MediaH264DecoderCuvid.cpp
@@ -599,7 +599,10 @@
     NVDEC_API_CALL(cuGraphicsUnregisterResource(CudaRes));
 }
 
-void cuda_nv12_updater(void* privData, uint32_t type, uint32_t* textures) {
+void cuda_nv12_updater(void* privData,
+                       uint32_t type,
+                       uint32_t* textures,
+                       void* callerData) {
     constexpr uint32_t kFRAMEWORK_FORMAT_NV12 = 3;
     if (type != kFRAMEWORK_FORMAT_NV12) {
         return;
diff --git a/host-common/MediaH264DecoderGeneric.cpp b/host-common/MediaH264DecoderGeneric.cpp
index 29edcd4..d2c82f2 100644
--- a/host-common/MediaH264DecoderGeneric.cpp
+++ b/host-common/MediaH264DecoderGeneric.cpp
@@ -22,6 +22,8 @@
 // for Linux and Window, Cuvid is available
 #include "host-common/MediaCudaDriverHelper.h"
 #include "host-common/MediaCudaVideoHelper.h"
+#else
+#include "host-common/MediaVideoToolBoxVideoHelper.h"
 #endif
 
 #include <cstdint>
@@ -68,15 +70,11 @@
 }
 
 bool canDecodeToGpuTexture() {
-#ifndef __APPLE__
     if (emuglConfig_get_current_renderer() == SELECTED_RENDERER_HOST) {
         return true;
     } else {
         return false;
     }
-#else
-    return false;
-#endif
 }
 };  // end namespace
 
@@ -147,7 +145,30 @@
             H264_DPRINT("succeeded to init cuda decoder");
         }
     }
-// TODO: add video toolbox for apple
+#else
+    //TODO: once all the CTS passed with VTB, remove this
+    const bool is_vtb_allowed = android::base::System::getEnvironmentVariable(
+                            "ANDROID_EMU_MEDIA_DECODER_VTB") == "1";
+
+    if (is_vtb_allowed) {
+        MediaVideoToolBoxVideoHelper::FrameStorageMode fMode =
+            (mParser.version() >= 200 && mUseGpuTexture)
+                    ? MediaVideoToolBoxVideoHelper::FrameStorageMode::
+                              USE_GPU_TEXTURE
+                    : MediaVideoToolBoxVideoHelper::FrameStorageMode::
+                              USE_BYTE_BUFFER;
+        auto macDecoder = new MediaVideoToolBoxVideoHelper(
+            mOutputWidth, mOutputHeight,
+            MediaVideoToolBoxVideoHelper::OutputTreatmentMode::SAVE_RESULT,
+            fMode);
+
+        if (mUseGpuTexture && mParser.version() >= 200) {
+            H264_DPRINT("use gpu texture on OSX");
+            macDecoder->resetTexturePool(mRenderer.getTexturePool());
+        }
+        mHwVideoHelper.reset(macDecoder);
+        mHwVideoHelper->init();
+    }
 #endif
 
     mSnapshotHelper.reset(
@@ -214,12 +235,15 @@
 
     *retSzBytes = szBytes;
     *retErr = (int32_t)Err::NoErr;
+
+    H264_DPRINT("done decoding this frame");
 }
 
 void MediaH264DecoderGeneric::decodeFrameInternal(const uint8_t* data,
                                                   size_t len,
                                                   uint64_t pts) {
     if (mTrialPeriod) {
+        H264_DPRINT("still in trial period");
         try_decode(data, len, pts);
     } else {
         mVideoHelper->decode(data, len, pts);
@@ -314,6 +338,11 @@
     bool needToCopyToGuest = true;
     if (mParser.version() == 200) {
         if (mUseGpuTexture && pFrame->texture[0] > 0 && pFrame->texture[1] > 0) {
+            H264_DPRINT(
+                    "calling rendering to host side color buffer with id %d "
+                    "tex %d tex %d",
+                    param.hostColorBufferId, pFrame->texture[0],
+                    pFrame->texture[1]);
             mRenderer.renderToHostColorBufferWithTextures(
                     param.hostColorBufferId, pFrame->width, pFrame->height,
                     TextureFrame{pFrame->texture[0], pFrame->texture[1]});
diff --git a/host-common/MediaVideoToolBoxUtils.cpp b/host-common/MediaVideoToolBoxUtils.cpp
new file mode 100644
index 0000000..bfe2efa
--- /dev/null
+++ b/host-common/MediaVideoToolBoxUtils.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 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/MediaVideoToolBoxUtils.h"
+#include "OpenglRender/MediaNative.h"
+
+#define MEDIA_VTB_DEBUG 0
+
+#if MEDIA_VTB_DEBUG
+#define VTB_DPRINT(fmt, ...)                                          \
+    fprintf(stderr, "vtb-utils: %s:%d " fmt "\n", __func__, __LINE__, \
+            ##__VA_ARGS__);
+#else
+#define VTB_DPRINT(fmt, ...)
+#endif
+
+extern "C" {
+#define MEDIA_VTB_COPY_Y_TEXTURE 1
+#define MEDIA_VTB_COPY_UV_TEXTURE 2
+
+void media_vtb_utils_nv12_updater(void* privData,
+                                  uint32_t type,
+                                  uint32_t* textures,
+                                  void* callerData) {
+    constexpr uint32_t kFRAMEWORK_FORMAT_NV12 = 3;
+    if (type != kFRAMEWORK_FORMAT_NV12) {
+        return;
+    }
+    // get the textures
+    media_vtb_utils_copy_context* myctx =
+            (media_vtb_utils_copy_context*)privData;
+    int ww = myctx->dest_width;
+    int hh = myctx->dest_height;
+    void* iosurface = myctx->iosurface;
+    int yy = 0;
+    int uv = 0;
+
+    MediaNativeCallerData* cdata = (struct MediaNativeCallerData*)callerData;
+
+    void* nscontext = cdata->ctx;
+    auto converter = cdata->converter;
+    converter(nscontext, iosurface, textures[0], textures[1]);
+
+    VTB_DPRINT("done ");
+}
+
+}  // end of extern C
diff --git a/host-common/MediaVideoToolBoxVideoHelper.cpp b/host-common/MediaVideoToolBoxVideoHelper.cpp
new file mode 100644
index 0000000..5c4772c
--- /dev/null
+++ b/host-common/MediaVideoToolBoxVideoHelper.cpp
@@ -0,0 +1,714 @@
+// Copyright (C) 2021 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/MediaVideoToolBoxUtils.h"
+#include "host-common/MediaVideoToolBoxVideoHelper.h"
+#include "host-common/YuvConverter.h"
+#include "android/utils/debug.h"
+
+#define MEDIA_VTB_DEBUG 0
+
+#if MEDIA_VTB_DEBUG
+#define VTB_DPRINT(fmt, ...)                                             \
+    fprintf(stderr, "media-vtb-video-helper: %s:%d " fmt "\n", __func__, \
+            __LINE__, ##__VA_ARGS__);
+#else
+#define VTB_DPRINT(fmt, ...)
+#endif
+
+namespace android {
+namespace emulation {
+
+using TextureFrame = MediaTexturePool::TextureFrame;
+using FrameInfo = MediaSnapshotState::FrameInfo;
+using ColorAspects = MediaSnapshotState::ColorAspects;
+using H264NaluType = H264NaluParser::H264NaluType;
+
+MediaVideoToolBoxVideoHelper::MediaVideoToolBoxVideoHelper(
+        int w,
+        int h,
+        OutputTreatmentMode oMode,
+        FrameStorageMode fMode)
+    : mOutputWidth(w),
+      mOutputHeight(h),
+      mUseGpuTexture(fMode == FrameStorageMode::USE_GPU_TEXTURE) {
+    mIgnoreDecoderOutput = (oMode == OutputTreatmentMode::IGNORE_RESULT);
+}
+
+MediaVideoToolBoxVideoHelper::~MediaVideoToolBoxVideoHelper() {
+    deInit();
+}
+
+void MediaVideoToolBoxVideoHelper::deInit() {
+    VTB_DPRINT("deInit calling");
+
+    resetDecoderSession();
+
+    resetFormatDesc();
+
+    mVtbReady = false;
+
+    mFfmpegVideoHelper.reset();
+
+    mSavedDecodedFrames.clear();
+}
+
+bool MediaVideoToolBoxVideoHelper::init() {
+    VTB_DPRINT("init calling");
+    mVtbReady = false;
+    return true;
+}
+
+static void dumpBytes(const uint8_t* img, size_t szBytes, bool all = false) {
+    printf("data=");
+    size_t numBytes = szBytes;
+    if (!all) {
+        numBytes = 32;
+    }
+
+    for (size_t i = 0; i < (numBytes > szBytes ? szBytes : numBytes); ++i) {
+        if (i % 8 == 0) {
+            printf("\n");
+        }
+        printf("0x%02x ", img[i]);
+    }
+    printf("\n");
+
+    fflush(stdout);
+}
+
+void MediaVideoToolBoxVideoHelper::extractFrameInfo() {
+    // at this moment, ffmpeg should have decoded the first frame to obtain
+    // w/h/colorspace info and number of b frame buffers
+
+    mFfmpegVideoHelper->flush();
+    MediaSnapshotState::FrameInfo frame;
+    bool success = mFfmpegVideoHelper->receiveFrame(&frame);
+    if (success) {
+        mColorAspects = frame.color;
+        mVtbBufferSize = mFfmpegVideoHelper->frameReorderBufferSize();
+        VTB_DPRINT("vtb buffer size is %d", mVtbBufferSize);
+    }
+}
+
+void MediaVideoToolBoxVideoHelper::decode(const uint8_t* frame,
+                                          size_t szBytes,
+                                          uint64_t inputPts) {
+    VTB_DPRINT("%s(frame=%p, sz=%zu)", __func__, frame, szBytes);
+
+    ++mTotalFrames;
+    const bool parseOk = parseInputFrames(frame, szBytes);
+    if (!parseOk) {
+        // cannot parse, probably cannot decode either
+        // just fail
+        VTB_DPRINT("Failed to parse frame=%p, sz=%zu, give up.", frame, szBytes);
+        mIsGood = false;
+        return;
+    }
+
+    // has to go in the FIFO order
+    for (int i = 0; i < mInputFrames.size(); ++i) {
+        InputFrame& f = mInputFrames[i];
+        switch (f.type) {
+            case H264NaluType::SPS:
+                mSPS.assign(f.data, f.data + f.size);
+                VTB_DPRINT("this is an SPS frame");
+                // dumpBytes(mSPS.data(), mSPS.size());
+                mVtbReady = false;
+                {   // create ffmpeg decoder with only 1 thread to avoid frame delay
+                    constexpr int numthreads = 1;
+                    mFfmpegVideoHelper.reset(new MediaFfmpegVideoHelper(264, numthreads));
+                    mFfmpegVideoHelper->init();
+                }
+                break;
+            case H264NaluType::PPS:
+                VTB_DPRINT("this is an PPS frame");
+                mPPS.assign(f.data, f.data + f.size);
+                // dumpBytes(mPPS.data(), mPPS.size());
+                mVtbReady = false;
+                break;
+            case H264NaluType::SEI:
+                VTB_DPRINT("this is SEI frame");
+                break;
+            case H264NaluType::CodedSliceIDR:
+                VTB_DPRINT("this is an IDR frame");
+                if (mFfmpegVideoHelper) {
+                    mFfmpegVideoHelper->decode(frame, szBytes, inputPts);
+                    extractFrameInfo();
+                    mFfmpegVideoHelper.reset();
+                }
+                if (!mVtbReady) {
+                    createCMFormatDescription();
+                    Boolean needNewSession = ( VTDecompressionSessionCanAcceptFormatDescription(mDecoderSession, mCmFmtDesc) == false);
+                    if (needNewSession) {
+                        resetDecoderSession();
+                    }
+                    if (!mDecoderSession) {
+                        // all the previously decoded frames need to be
+                        // retrieved
+                        flush();
+                        recreateDecompressionSession();
+                    }
+                    if (mIsGood) {
+                        mVtbReady = true;
+                    }
+                }
+                handleIDRFrame(f.data, f.size, inputPts);
+                break;
+            case H264NaluType::CodedSliceNonIDR:
+                VTB_DPRINT("this is an non-IDR frame");
+                handleIDRFrame(f.data, f.size, inputPts);
+                break;
+            default:
+                VTB_DPRINT("Support for nalu_type=%u not implemented",
+                           (uint8_t)f.type);
+                // cannot handle such video, give up so we can fall
+                // back to ffmpeg
+                mIsGood = false;
+                break;
+        }
+    }
+
+    if (mFfmpegVideoHelper) {
+        // we have not reached idr frame yet, keep decoding
+        mFfmpegVideoHelper->decode(frame, szBytes, inputPts);
+    }
+
+    mInputFrames.clear();
+}
+
+void MediaVideoToolBoxVideoHelper::flush() {
+    VTB_DPRINT("started flushing");
+    for (auto& iter : mVtbBufferMap) {
+        mSavedDecodedFrames.push_back(std::move(iter.second));
+    }
+    mVtbBufferMap.clear();
+    VTB_DPRINT("done one flushing");
+}
+
+//------------------------  private  helper functions
+//-----------------------------------------
+
+void MediaVideoToolBoxVideoHelper::handleIDRFrame(const uint8_t* ptr,
+                                                  size_t szBytes,
+                                                  uint64_t pts) {
+    uint8_t* fptr = const_cast<uint8_t*>(ptr);
+
+    // We can assume fptr has a valid start code header because it has already
+    // gone through validation in H264NaluParser.
+    uint8_t startHeaderSz = fptr[2] == 1 ? 3 : 4;
+    uint32_t dataSz = szBytes - startHeaderSz;
+    std::unique_ptr<uint8_t> idr(new uint8_t[dataSz + 4]);
+    uint32_t dataSzNl = htonl(dataSz);
+
+    // AVCC format requires us to replace the start code header on this NALU
+    // with the size of the data. Start code is either 0x000001 or 0x00000001.
+    // The size needs to be the first four bytes in network byte order.
+    memcpy(idr.get(), &dataSzNl, 4);
+    memcpy(idr.get() + 4, ptr + startHeaderSz, dataSz);
+
+    CMSampleBufferRef sampleBuf = nullptr;
+    sampleBuf = createSampleBuffer(mCmFmtDesc, (void*)idr.get(), dataSz + 4);
+    if (!sampleBuf) {
+        VTB_DPRINT("%s: Failed to create CMSampleBufferRef", __func__);
+        return;
+    }
+
+    CMSampleBufferSetOutputPresentationTimeStamp(sampleBuf, CMTimeMake(pts, 1));
+
+    OSStatus status;
+    status = VTDecompressionSessionDecodeFrame(mDecoderSession, sampleBuf,
+                                               0,     // decodeFlags
+                                               NULL,  // sourceFrameRefCon
+                                               0);    // infoFlagsOut
+
+    if (status == noErr) {
+        // TODO: this call blocks until the frame has been decoded. Perhaps it
+        // will be more efficient to signal the guest when the frame is ready to
+        // be read instead.
+        status = VTDecompressionSessionWaitForAsynchronousFrames(
+                mDecoderSession);
+        VTB_DPRINT("Success decoding IDR frame");
+    } else {
+        VTB_DPRINT("%s: Failed to decompress frame (err=%d)", __func__, status);
+    }
+
+    CFRelease(sampleBuf);
+}
+
+// chop the frame into sub frames that video tool box will consume
+// one by one
+bool MediaVideoToolBoxVideoHelper::parseInputFrames(const uint8_t* frame,
+                                                    size_t sz) {
+    mInputFrames.clear();
+    VTB_DPRINT("input frame %d bytes", (int)sz);
+    while (1) {
+        const uint8_t* remainingFrame = parseOneFrame(frame, sz);
+        if (remainingFrame == nullptr)
+            break;
+        int consumed = (remainingFrame - frame);
+        VTB_DPRINT("consumed %d bytes", consumed);
+        frame = remainingFrame;
+        sz = sz - consumed;
+    }
+    return mInputFrames.size() > 0;
+}
+
+const uint8_t* MediaVideoToolBoxVideoHelper::parseOneFrame(const uint8_t* frame,
+                                                           size_t szBytes) {
+    if (frame == nullptr || szBytes <= 0) {
+        return nullptr;
+    }
+
+    const uint8_t* currentNalu =
+            H264NaluParser::getNextStartCodeHeader(frame, szBytes);
+    if (currentNalu == nullptr) {
+        VTB_DPRINT("No start code header found in this frame");
+        return nullptr;
+    }
+
+    const uint8_t* nextNalu = nullptr;
+    size_t remaining = szBytes - (currentNalu - frame);
+
+    // Figure out the size of |currentNalu|.
+    size_t currentNaluSize = remaining;
+    // 3 is the minimum size of the start code header (3 or 4 bytes).
+    // dumpBytes(currentNalu, currentNaluSize);
+
+    // usually guest sends one frame a time, but sometime, SEI prefixes IDR
+    // frame as one frame from guest, we need to separate SEI out from IDR:
+    // video tool box cannot handle SEI+IDR combo-frame; for other cases, there
+    // is no need to check.
+    if (H264NaluType::SEI == H264NaluParser::getFrameNaluType(frame, szBytes, nullptr)
+     || H264NaluType::SPS == H264NaluParser::getFrameNaluType(frame, szBytes, nullptr)
+     || H264NaluType::PPS == H264NaluParser::getFrameNaluType(frame, szBytes, nullptr)
+
+        ) {
+        nextNalu = H264NaluParser::getNextStartCodeHeader(currentNalu + 3,
+                                                          remaining - 3);
+        if (nextNalu != nullptr) {
+            currentNaluSize = nextNalu - currentNalu;
+        }
+    }
+
+    const uint8_t* remainingFrame = currentNalu + currentNaluSize;
+
+    // |data| is currentNalu, but with the start code header discarded.
+    uint8_t* data = nullptr;
+    H264NaluType naluType = H264NaluParser::getFrameNaluType(
+            currentNalu, currentNaluSize, &data);
+
+    if (naluType == H264NaluType::Undefined)
+        return nullptr;
+
+    size_t dataSize = currentNaluSize - (data - currentNalu);
+    const std::string naluTypeStr = H264NaluParser::naluTypeToString(naluType);
+    VTB_DPRINT("Got frame type=%u (%s)", (uint8_t)naluType,
+               naluTypeStr.c_str());
+
+    if (naluType == H264NaluType::SPS || naluType == H264NaluType::PPS) {
+        mInputFrames.push_back(InputFrame(naluType, data, dataSize));
+    } else {
+        mInputFrames.push_back(
+                InputFrame(naluType, currentNalu, currentNaluSize));
+    }
+    return remainingFrame;
+}
+
+// static
+void MediaVideoToolBoxVideoHelper::videoToolboxDecompressCallback(
+        void* opaque,
+        void* sourceFrameRefCon,
+        OSStatus status,
+        VTDecodeInfoFlags flags,
+        CVPixelBufferRef image_buffer,
+        CMTime pts,
+        CMTime duration) {
+    VTB_DPRINT("%s", __func__);
+    auto ptr = static_cast<MediaVideoToolBoxVideoHelper*>(opaque);
+
+    // if (ptr->mDecodedFrame) {
+    //     CVPixelBufferRelease(ptr->mDecodedFrame);
+    //     ptr->mDecodedFrame = nullptr;
+    // }
+
+    if (!image_buffer) {
+        VTB_DPRINT("%s: output image buffer is null", __func__);
+        ptr->mIsGood = false;
+        return;
+    }
+
+    ptr->mOutputPts = pts.value;
+    ptr->mDecodedFrame = CVPixelBufferRetain(image_buffer);
+    CVOpenGLTextureCacheRef _CVGLTextureCache;
+    CVOpenGLTextureRef _CVGLTexture;
+    CGLPixelFormatObj _CGLPixelFormat;
+
+    // Image is ready to be comsumed
+    ptr->copyFrame();
+    ptr->mImageReady = true;
+    VTB_DPRINT("Got decoded frame");
+    CVPixelBufferRelease(ptr->mDecodedFrame);
+    ptr->mDecodedFrame = nullptr;
+}
+
+// static
+CFDictionaryRef MediaVideoToolBoxVideoHelper::createOutputBufferAttributes(
+        int width,
+        int height,
+        OSType pix_fmt) {
+    CFMutableDictionaryRef buffer_attributes;
+    CFMutableDictionaryRef io_surface_properties;
+    CFNumberRef cv_pix_fmt;
+    CFNumberRef w;
+    CFNumberRef h;
+
+    w = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &width);
+    h = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &height);
+    cv_pix_fmt =
+            CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pix_fmt);
+
+    buffer_attributes = CFDictionaryCreateMutable(
+            kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks,
+            &kCFTypeDictionaryValueCallBacks);
+    io_surface_properties = CFDictionaryCreateMutable(
+            kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
+            &kCFTypeDictionaryValueCallBacks);
+
+    if (pix_fmt) {
+        CFDictionarySetValue(buffer_attributes,
+                             kCVPixelBufferPixelFormatTypeKey, cv_pix_fmt);
+    }
+    CFDictionarySetValue(buffer_attributes,
+                         kCVPixelBufferIOSurfacePropertiesKey,
+                         io_surface_properties);
+    CFDictionarySetValue(buffer_attributes, kCVPixelBufferWidthKey, w);
+    CFDictionarySetValue(buffer_attributes, kCVPixelBufferHeightKey, h);
+    // Not sure if this will work becuase we are passing the pixel buffer back
+    // into the guest
+    CFDictionarySetValue(buffer_attributes,
+                         kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey,
+                         kCFBooleanTrue);
+
+    CFRelease(io_surface_properties);
+    CFRelease(cv_pix_fmt);
+    CFRelease(w);
+    CFRelease(h);
+
+    return buffer_attributes;
+}
+
+// static
+CMSampleBufferRef MediaVideoToolBoxVideoHelper::createSampleBuffer(
+        CMFormatDescriptionRef fmtDesc,
+        void* buffer,
+        size_t sz) {
+    OSStatus status;
+    CMBlockBufferRef blockBuf = nullptr;
+    CMSampleBufferRef sampleBuf = nullptr;
+
+    status = CMBlockBufferCreateWithMemoryBlock(
+            kCFAllocatorDefault,  // structureAllocator
+            buffer,               // memoryBlock
+            sz,                   // blockLength
+            kCFAllocatorNull,     // blockAllocator
+            NULL,                 // customBlockSource
+            0,                    // offsetToData
+            sz,                   // dataLength
+            0,                    // flags
+            &blockBuf);
+
+    if (!status) {
+        status = CMSampleBufferCreate(kCFAllocatorDefault,  // allocator
+                                      blockBuf,             // dataBuffer
+                                      TRUE,                 // dataReady
+                                      0,        // makeDataReadyCallback
+                                      0,        // makeDataReadyRefCon
+                                      fmtDesc,  // formatDescription
+                                      1,        // numSamples
+                                      0,        // numSampleTimingEntries
+                                      NULL,     // sampleTimingArray
+                                      0,        // numSampleSizeEntries
+                                      NULL,     // sampleSizeArray
+                                      &sampleBuf);
+    }
+
+    if (blockBuf) {
+        CFRelease(blockBuf);
+    }
+
+    return sampleBuf;
+}
+
+void MediaVideoToolBoxVideoHelper::resetDecoderSession() {
+    if (mDecoderSession) {
+        VTDecompressionSessionInvalidate(mDecoderSession);
+        CFRelease(mDecoderSession);
+        mDecoderSession = nullptr;
+    }
+    if (mDecodedFrame) {
+        CVPixelBufferRelease(mDecodedFrame);
+        mDecodedFrame = nullptr;
+    }
+}
+
+void MediaVideoToolBoxVideoHelper::getOutputWH() {
+    if (!mCmFmtDesc)
+        return;
+
+    CMVideoDimensions vsize = CMVideoFormatDescriptionGetDimensions(mCmFmtDesc);
+
+    if (mOutputWidth == vsize.width && mOutputHeight == vsize.height) {
+        VTB_DPRINT("initial w/h is the same as vtb w/h");
+    } else {
+        VTB_DPRINT("initial w/h are not the same as vtb w/h");
+    }
+    mOutputWidth = vsize.width;
+    mOutputHeight = vsize.height;
+}
+
+void MediaVideoToolBoxVideoHelper::resetFormatDesc() {
+    if (mCmFmtDesc) {
+        CFRelease(mCmFmtDesc);
+        mCmFmtDesc = nullptr;
+    }
+}
+
+void MediaVideoToolBoxVideoHelper::createCMFormatDescription() {
+    uint8_t* parameterSets[2] = {mSPS.data(), mPPS.data()};
+    size_t parameterSetSizes[2] = {mSPS.size(), mPPS.size()};
+
+    resetFormatDesc();
+
+    OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
+            kCFAllocatorDefault, 2, (const uint8_t* const*)parameterSets,
+            parameterSetSizes, 4, &mCmFmtDesc);
+
+    if (status == noErr) {
+        getOutputWH();
+        VTB_DPRINT("Created CMFormatDescription from SPS/PPS sets w %d h %d",
+                   mOutputWidth, mOutputHeight);
+    } else {
+        VTB_DPRINT("Unable to create CMFormatDescription (%d)\n", (int)status);
+    }
+}
+
+void MediaVideoToolBoxVideoHelper::recreateDecompressionSession() {
+    if (mCmFmtDesc == nullptr) {
+        VTB_DPRINT("CMFormatDescription not created. Need sps and pps NALUs.");
+        return;
+    }
+
+    CMVideoCodecType codecType = kCMVideoCodecType_H264;
+    CFMutableDictionaryRef decoder_spec = CFDictionaryCreateMutable(
+            kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
+            &kCFTypeDictionaryValueCallBacks);
+    // enable hardware acceleration, but allows vtb to fallback to
+    // its software implementation.
+    CFDictionarySetValue(
+            decoder_spec,
+            kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder,
+            kCFBooleanTrue);
+
+    CFDictionaryRef bufAttr;
+
+    if (mUseGpuTexture) {
+        // use NV12 format
+        bufAttr = createOutputBufferAttributes(
+                mOutputWidth, mOutputHeight,
+                kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange);
+    } else {
+        bufAttr = createOutputBufferAttributes(
+                mOutputWidth, mOutputHeight,
+                kCVPixelFormatType_420YpCbCr8Planar);
+    }
+
+    VTDecompressionOutputCallbackRecord decoderCb;
+    decoderCb.decompressionOutputCallback = videoToolboxDecompressCallback;
+    decoderCb.decompressionOutputRefCon = this;
+
+    OSStatus status;
+    status = VTDecompressionSessionCreate(
+            NULL,               // allocator
+            mCmFmtDesc,         // videoFormatDescription
+            decoder_spec,       // videoDecoderSpecification
+            bufAttr,            // destinationImageBufferAttributes
+            &decoderCb,         // outputCallback
+            &mDecoderSession);  // decompressionSessionOut
+
+    if (decoder_spec) {
+        CFRelease(decoder_spec);
+    }
+    if (bufAttr) {
+        CFRelease(bufAttr);
+    }
+
+    mIsGood = false;
+    switch (status) {
+        case kVTVideoDecoderNotAvailableNowErr:
+            VTB_DPRINT("VideoToolbox session not available");
+            return;
+        case kVTVideoDecoderUnsupportedDataFormatErr:
+            VTB_DPRINT("VideoToolbox does not support this format");
+            return;
+        case kVTVideoDecoderMalfunctionErr:
+            VTB_DPRINT("VideoToolbox malfunction");
+            return;
+        case kVTVideoDecoderBadDataErr:
+            VTB_DPRINT("VideoToolbox reported invalid data");
+            return;
+        case 0:
+            dprint("VideoToolBox decoding session is created successfully");
+            mIsGood = true;
+            return;
+        default:
+            VTB_DPRINT("Unknown VideoToolbox session creation error %d",
+                       status);
+            return;
+    }
+}
+
+void MediaVideoToolBoxVideoHelper::copyFrameToTextures() {
+    // TODO
+    auto startTime = std::chrono::steady_clock::now();
+    TextureFrame texFrame;
+    if (mUseGpuTexture && mTexturePool != nullptr) {
+        VTB_DPRINT("decoded surface is %p w %d h %d",
+                   CVPixelBufferGetIOSurface(mDecodedFrame), mOutputWidth,
+                   mOutputHeight);
+        auto my_copy_context = media_vtb_utils_copy_context{
+                CVPixelBufferGetIOSurface(mDecodedFrame), mOutputWidth,
+                mOutputHeight};
+        texFrame = mTexturePool->getTextureFrame(mOutputWidth, mOutputHeight);
+        VTB_DPRINT("ask videocore to copy to %d and %d", texFrame.Ytex,
+                   texFrame.UVtex);
+        mTexturePool->saveDecodedFrameToTexture(
+                texFrame, &my_copy_context,
+                (void*)media_vtb_utils_nv12_updater);
+    }
+
+    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
+            std::chrono::steady_clock::now() - startTime);
+    VTB_DPRINT("used %d ms", (int)elapsed.count());
+
+    auto ptspair = std::make_pair(mOutputPts, mTotalFrames);
+    mVtbBufferMap[ptspair] = MediaSnapshotState::FrameInfo{
+            std::vector<uint8_t>(),
+            std::vector<uint32_t>{texFrame.Ytex, texFrame.UVtex},
+            (int)mOutputWidth,
+            (int)mOutputHeight,
+            (uint64_t)(mOutputPts),
+            ColorAspects{mColorAspects}};
+}
+
+void MediaVideoToolBoxVideoHelper::copyFrameToCPU() {
+    int imageSize = CVPixelBufferGetDataSize(mDecodedFrame);
+    int stride = CVPixelBufferGetBytesPerRow(mDecodedFrame);
+
+    mOutBufferSize = mOutputWidth * mOutputHeight * 3 / 2;
+
+    VTB_DPRINT("copying size=%d dimension=[%dx%d] stride=%d", imageSize,
+               mOutputWidth, mOutputHeight, stride);
+
+    // Copies the image data to the guest.
+    mSavedDecodedFrame.resize(mOutputWidth * mOutputHeight * 3 / 2);
+    uint8_t* dst = mSavedDecodedFrame.data();
+
+    CVPixelBufferLockBaseAddress(mDecodedFrame, kCVPixelBufferLock_ReadOnly);
+    if (CVPixelBufferIsPlanar(mDecodedFrame)) {
+        imageSize = 0;  // add up the size from the planes
+        int planes = CVPixelBufferGetPlaneCount(mDecodedFrame);
+        for (int i = 0; i < planes; ++i) {
+            void* planeData =
+                    CVPixelBufferGetBaseAddressOfPlane(mDecodedFrame, i);
+            int linesize = CVPixelBufferGetBytesPerRowOfPlane(mDecodedFrame, i);
+            int planeWidth = CVPixelBufferGetWidthOfPlane(mDecodedFrame, i);
+            int planeHeight = CVPixelBufferGetHeightOfPlane(mDecodedFrame, i);
+            VTB_DPRINT("plane=%d data=%p linesize=%d pwidth=%d pheight=%d", i,
+                       planeData, linesize, planeWidth, planeHeight);
+            // For kCVPixelFormatType_420YpCbCr8Planar, plane 0 is Y, UV planes
+            // are 1 and 2
+            if (planeWidth != mOutputWidth && planeWidth != mOutputWidth / 2) {
+                VTB_DPRINT("ERROR: Unable to determine YUV420 plane type");
+                continue;
+            }
+
+            // Sometimes the buffer stride can be longer than the actual data
+            // width. This means that the extra bytes are just padding and need
+            // to be discarded.
+            if (linesize <= planeWidth) {
+                int sz = planeHeight * planeWidth;
+                imageSize += sz;
+                memcpy(dst, planeData, sz);
+                dst += sz;
+            } else {
+                // Need to copy line by line
+                int sz = planeWidth;
+                for (int j = 0; j < planeHeight; ++j) {
+                    uint8_t* ptr = (uint8_t*)planeData;
+                    ptr += linesize * j;
+                    memcpy(dst, ptr, sz);
+                    imageSize += sz;
+                    dst += sz;
+                }
+            }
+        }
+        if (imageSize != mOutBufferSize) {
+            VTB_DPRINT(
+                    "ERROR: Total size of planes not same as guestSz "
+                    "(guestSz=%u, imageSize=%d)",
+                    mOutBufferSize, imageSize);
+        }
+    } else {
+        if (imageSize > mOutBufferSize) {
+            VTB_DPRINT(
+                    "Buffer size mismatch (guestSz=%u, imageSize=%d). Using "
+                    "guestSz instead.",
+                    mOutBufferSize, imageSize);
+            imageSize = mOutBufferSize;
+        }
+
+        // IMPORTANT: mDecodedFrame must be locked before accessing the contents
+        // with CPU
+        void* data = CVPixelBufferGetBaseAddress(mDecodedFrame);
+        memcpy(dst, data, imageSize);
+    }
+    CVPixelBufferUnlockBaseAddress(mDecodedFrame, kCVPixelBufferLock_ReadOnly);
+
+    auto ptspair = std::make_pair(mOutputPts, mTotalFrames);
+    mVtbBufferMap[ptspair] = MediaSnapshotState::FrameInfo{
+            std::vector<uint8_t>(), std::vector<uint32_t>{},
+            (int)mOutputWidth,      (int)mOutputHeight,
+            (uint64_t)(mOutputPts), ColorAspects{mColorAspects}};
+    mVtbBufferMap[ptspair].data.swap(mSavedDecodedFrame);
+}
+
+void MediaVideoToolBoxVideoHelper::copyFrame() {
+    mOutputWidth = CVPixelBufferGetWidth(mDecodedFrame);
+    mOutputHeight = CVPixelBufferGetHeight(mDecodedFrame);
+
+    if (mUseGpuTexture) {
+        copyFrameToTextures();
+    } else {
+        copyFrameToCPU();
+    }
+
+    if (mVtbBufferMap.size() > mVtbBufferSize) {
+        mSavedDecodedFrames.push_back(std::move(mVtbBufferMap.begin()->second));
+        mVtbBufferMap.erase(mVtbBufferMap.begin());
+    }
+}
+
+}  // namespace emulation
+}  // namespace android
diff --git a/host-common/include/host-common/MediaVideoToolBoxUtils.h b/host-common/include/host-common/MediaVideoToolBoxUtils.h
new file mode 100644
index 0000000..c7902d2
--- /dev/null
+++ b/host-common/include/host-common/MediaVideoToolBoxUtils.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 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.
+
+#pragma once
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <cstdint>
+
+extern "C" {
+struct media_vtb_utils_copy_context {
+    media_vtb_utils_copy_context(void* surface, unsigned int w, unsigned int h)
+        : iosurface(surface), dest_width(w), dest_height(h){};
+    void* iosurface;
+    unsigned int dest_width;
+    unsigned int dest_height;
+};
+
+void media_vtb_utils_nv12_updater(void* privData,
+                                  uint32_t type,
+                                  uint32_t* textures,
+                                  void* callerData);
+
+}  // end of extern C
diff --git a/host-common/include/host-common/MediaVideoToolBoxVideoHelper.h b/host-common/include/host-common/MediaVideoToolBoxVideoHelper.h
new file mode 100644
index 0000000..a61ba56
--- /dev/null
+++ b/host-common/include/host-common/MediaVideoToolBoxVideoHelper.h
@@ -0,0 +1,189 @@
+// Copyright (C) 2021 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.
+
+#pragma once
+
+#include "host-common/H264NaluParser.h"
+#include "host-common/MediaFfmpegVideoHelper.h"
+#include "host-common/MediaSnapshotState.h"
+#include "host-common/MediaTexturePool.h"
+#include "host-common/MediaVideoHelper.h"
+#include "host-common/YuvConverter.h"
+
+// this is apple's video tool box header
+#include <VideoToolbox/VideoToolbox.h>
+
+#ifndef kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder
+#define kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder \
+    CFSTR("RequireHardwareAcceleratedVideoDecoder")
+#endif
+
+#include <cstdint>
+#include <list>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <stddef.h>
+
+namespace android {
+namespace emulation {
+
+class MediaVideoToolBoxVideoHelper : public MediaVideoHelper {
+public:
+    enum class FrameStorageMode {
+        USE_BYTE_BUFFER = 1,
+        USE_GPU_TEXTURE = 2,
+    };
+
+    enum class OutputTreatmentMode {
+        IGNORE_RESULT = 1,
+        SAVE_RESULT = 2,
+    };
+
+    MediaVideoToolBoxVideoHelper(int w,
+                                 int h,
+                                 OutputTreatmentMode outMode,
+                                 FrameStorageMode fMode);
+    ~MediaVideoToolBoxVideoHelper() override;
+
+    // return true if success; false otherwise
+    bool init() override;
+    void decode(const uint8_t* frame,
+                size_t szBytes,
+                uint64_t inputPts) override;
+    void flush() override;
+    void deInit() override;
+
+    void resetTexturePool(MediaTexturePool* pool = nullptr) {
+        mTexturePool = pool;
+    }
+
+    virtual int error() const override { return mErrorCode; }
+    virtual bool good() const override { return mIsGood; }
+    virtual bool fatal() const override { return false; }
+
+private:
+    // static
+    static void videoToolboxDecompressCallback(void* opaque,
+                                               void* sourceFrameRefCon,
+                                               OSStatus status,
+                                               VTDecodeInfoFlags flags,
+                                               CVPixelBufferRef image_buffer,
+                                               CMTime pts,
+                                               CMTime duration);
+    static CFDictionaryRef createOutputBufferAttributes(int width,
+                                                        int height,
+                                                        OSType pix_fmt);
+    static CMSampleBufferRef createSampleBuffer(CMFormatDescriptionRef fmtDesc,
+                                                void* buffer,
+                                                size_t sz);
+
+    void copyFrame();
+    void copyFrameToTextures();
+    void copyFrameToCPU();
+    void createCMFormatDescription();
+    void recreateDecompressionSession();
+    void getOutputWH();
+    void resetDecoderSession();
+    void resetFormatDesc();
+
+    struct InputFrame {
+        InputFrame(H264NaluParser::H264NaluType in_type,
+                   const uint8_t* in_data,
+                   int in_size)
+            : type(in_type), data(in_data), size(in_size) {}
+
+        H264NaluParser::H264NaluType type;
+        const uint8_t* data;
+        int size;
+    };
+
+    std::vector<InputFrame> mInputFrames;
+
+    bool parseInputFrames(const uint8_t* frame, size_t sz);
+
+    // returns the remaining part of the frame, nullptr if none
+    const uint8_t* parseOneFrame(const uint8_t* frame, size_t szBytes);
+
+    void handleIDRFrame(const uint8_t* ptr, size_t szBytes, uint64_t pts);
+
+    std::vector<uint8_t> mSPS;  // sps NALU
+    std::vector<uint8_t> mPPS;  // pps NALU
+
+    // turn on gpu texture mode
+    bool mUseGpuTexture = true;
+    MediaTexturePool* mTexturePool = nullptr;
+
+    uint64_t mNumInputFrame{0};
+    uint64_t mNumOutputFrame{0};
+    int mErrorCode = 0;
+    bool mIsGood = true;
+    unsigned int mHeight = 0;
+    unsigned int mWidth = 0;
+    unsigned int mOutputHeight = 0;
+    unsigned int mOutputWidth = 0;
+    unsigned int mSurfaceHeight = 0;
+    unsigned int mBPP = 0;
+    unsigned int mSurfaceWidth = 0;
+    unsigned int mLumaWidth = 0;
+    unsigned int mLumaHeight = 0;
+    unsigned int mChromaHeight = 0;
+    unsigned int mOutBufferSize = 0;
+
+    uint64_t mOutputPts = 0;
+    bool mImageReady = false;
+
+    // used in vtb callback to saved decoded frame
+    std::vector<uint8_t> mSavedDecodedFrame;
+
+    // TODO: get color aspects from some where
+    MediaSnapshotState::ColorAspects mColorAspects{0};
+
+    std::mutex mFrameLock;
+
+    // this is only set to true after video session is created without errors
+    // it is reset to false when new sps/pps comes
+    bool mVtbReady = false;
+    // videotoolbox stuff
+    // the fmt, this will be recreated each time
+    // we get a sps+pps frames
+    CMFormatDescriptionRef mCmFmtDesc = nullptr;
+    // The VideoToolbox decoder session: this could fail to
+    // create due to incompatible formats coming from android guest
+    VTDecompressionSessionRef mDecoderSession = nullptr;
+    // where the decoded frame is stored
+    CVPixelBufferRef mDecodedFrame = nullptr;
+
+    // need this ffmpeg helper to get the w/h/colorspace info
+    // TODO: replace it with webrtc h264 parser once it is built
+    // for all platforms
+    std::unique_ptr<MediaFfmpegVideoHelper> mFfmpegVideoHelper;
+    void extractFrameInfo();
+
+    uint64_t mTotalFrames = 0;
+    // vtb decoder does not reorder output frames, that means
+    // the video could see jumps all the times
+    int mVtbBufferSize = 8;
+    using PtsPair = std::pair<uint64_t, uint64_t>;
+    std::map<PtsPair, MediaSnapshotState::FrameInfo> mVtbBufferMap;
+
+};  // MediaVideoToolBoxVideoHelper
+
+}  // namespace emulation
+}  // namespace android
diff --git a/host-common/include/host-common/window_agent.h b/host-common/include/host-common/window_agent.h
index 23a475c..a9489e0 100644
--- a/host-common/include/host-common/window_agent.h
+++ b/host-common/include/host-common/window_agent.h
@@ -127,6 +127,7 @@
     void (*show_virtual_scene_controls)(bool);
     void (*quit_request)(void);
     void (*getWindowPosition)(int*, int*);
+    bool (*hasWindow)();
 } QAndroidEmulatorWindowAgent;
 
 #ifndef USING_ANDROID_BP
diff --git a/snapshot/Android.bp b/snapshot/Android.bp
index 58b14ed..5252536 100644
--- a/snapshot/Android.bp
+++ b/snapshot/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_google_aemu_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_google_aemu_license"],
+}
+
 cc_library_static {
     name: "gfxstream_snapshot",
     defaults: [ "gfxstream_defaults" ],