1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "host-common/MediaVpxVideoHelper.h"
16 #include "host-common/YuvConverter.h"
17 #include "android/utils/debug.h"
18 
19 #define MEDIA_VPX_DEBUG 0
20 
21 #if MEDIA_VPX_DEBUG
22 #define MEDIA_DPRINT(fmt, ...)                                           \
23     fprintf(stderr, "media-vpx-video-helper: %s:%d " fmt "\n", __func__, \
24             __LINE__, ##__VA_ARGS__);
25 #else
26 #define MEDIA_DPRINT(fmt, ...)
27 #endif
28 
29 namespace android {
30 namespace emulation {
31 
32 using FrameInfo = MediaSnapshotState::FrameInfo;
33 using ColorAspects = MediaSnapshotState::ColorAspects;
34 
MediaVpxVideoHelper(int type,int threads)35 MediaVpxVideoHelper::MediaVpxVideoHelper(int type, int threads)
36     : mType(type), mThreadCount(threads) {}
37 
~MediaVpxVideoHelper()38 MediaVpxVideoHelper::~MediaVpxVideoHelper() {
39     deInit();
40 }
41 
decode(const uint8_t * data,size_t len,uint64_t pts)42 void MediaVpxVideoHelper::decode(const uint8_t* data,
43                                  size_t len,
44                                  uint64_t pts) {
45     MEDIA_DPRINT("decoding %d bytes", (int)len);
46     vpx_codec_decode(mCtx.get(), data, len, (void*)pts, 0);
47     fetchAllFrames();
48 }
49 
flush()50 void MediaVpxVideoHelper::flush() {
51     MEDIA_DPRINT("calling flush");
52     vpx_codec_decode(mCtx.get(), NULL, 0, NULL, 0);
53     fetchAllFrames();
54 }
55 
init()56 bool MediaVpxVideoHelper::init() {
57     MEDIA_DPRINT("calling init context");
58     mCtx.reset(new vpx_codec_ctx_t);
59     vpx_codec_err_t vpx_err;
60     vpx_codec_dec_cfg_t cfg;
61     vpx_codec_flags_t flags;
62     memset(&cfg, 0, sizeof(cfg));
63     memset(&flags, 0, sizeof(flags));
64     cfg.threads = 1;
65 
66     if (mThreadCount > 1) {
67         cfg.threads = std::min(mThreadCount, 4);
68     }
69 
70     if ((vpx_err = vpx_codec_dec_init(
71                  mCtx.get(),
72                  mType == 8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
73                  &cfg, flags))) {
74         MEDIA_DPRINT("vpx decoder failed to initialize. (%d)", vpx_err);
75         mCtx.reset();
76         return false;
77     }
78     MEDIA_DPRINT("vpx decoder initialize context successfully.");
79     dprint("successfully created libvpx video decoder for VP%d", mType);
80 
81     return true;
82 }
83 
deInit()84 void MediaVpxVideoHelper::deInit() {
85     if (mCtx != nullptr) {
86         MEDIA_DPRINT("calling destroy context");
87         vpx_codec_destroy(mCtx.get());
88         mCtx.reset();
89     }
90 }
91 
copyYV12FrameToOutputBuffer(size_t outputBufferWidth,size_t outputBufferHeight,size_t imgWidth,size_t imgHeight,int32_t bpp,uint8_t * dst,const uint8_t * srcY,const uint8_t * srcU,const uint8_t * srcV,size_t srcYStride,size_t srcUStride,size_t srcVStride)92 void MediaVpxVideoHelper::copyYV12FrameToOutputBuffer(size_t outputBufferWidth,
93                                                       size_t outputBufferHeight,
94                                                       size_t imgWidth,
95                                                       size_t imgHeight,
96                                                       int32_t bpp,
97                                                       uint8_t* dst,
98                                                       const uint8_t* srcY,
99                                                       const uint8_t* srcU,
100                                                       const uint8_t* srcV,
101                                                       size_t srcYStride,
102                                                       size_t srcUStride,
103                                                       size_t srcVStride) {
104     size_t dstYStride = outputBufferWidth * bpp;
105     size_t dstUVStride = dstYStride / 2;
106     size_t dstHeight = outputBufferHeight;
107     uint8_t* dstStart = dst;
108 
109     for (size_t i = 0; i < imgHeight; ++i) {
110         memcpy(dst, srcY, imgWidth * bpp);
111         srcY += srcYStride;
112         dst += dstYStride;
113     }
114 
115     dst = dstStart + dstYStride * dstHeight;
116     for (size_t i = 0; i < imgHeight / 2; ++i) {
117         memcpy(dst, srcU, imgWidth / 2 * bpp);
118         srcU += srcUStride;
119         dst += dstUVStride;
120     }
121 
122     dst = dstStart + (5 * dstYStride * dstHeight) / 4;
123     for (size_t i = 0; i < imgHeight / 2; ++i) {
124         memcpy(dst, srcV, imgWidth / 2 * bpp);
125         srcV += srcVStride;
126         dst += dstUVStride;
127     }
128 }
129 
copyImgToGuest(vpx_image_t * mImg,std::vector<uint8_t> & byteBuffer)130 void MediaVpxVideoHelper::copyImgToGuest(vpx_image_t* mImg,
131                                          std::vector<uint8_t>& byteBuffer) {
132     size_t outputBufferWidth = mImg->d_w;
133     size_t outputBufferHeight = mImg->d_h;
134     size_t mWidth = mImg->d_w;
135     size_t mHeight = mImg->d_h;
136     // assert(mImg->fmt == VPX_IMG_FMT_I420 || mImg->fmt == VPX_IMG_FMT_I42016)
137     assert(mImg->fmt == VPX_IMG_FMT_I420);
138     int32_t bpp = (mImg->fmt == VPX_IMG_FMT_I420) ? 1 : 2;
139 
140     byteBuffer.resize(mWidth * mHeight * bpp * 3 / 2);
141     uint8_t* dst = byteBuffer.data();
142     MEDIA_DPRINT("*** d_w=%d d_h=%d out_w=%d out_h=%d m_w=%d m_h=%d bpp=%d",
143                  mImg->d_w, mImg->d_h, (int)outputBufferWidth,
144                  (int)outputBufferHeight, (int)mWidth, (int)mHeight, (int)bpp);
145 
146     const uint8_t* srcY = (const uint8_t*)mImg->planes[VPX_PLANE_Y];
147     const uint8_t* srcU = (const uint8_t*)mImg->planes[VPX_PLANE_U];
148     const uint8_t* srcV = (const uint8_t*)mImg->planes[VPX_PLANE_V];
149     size_t srcYStride = mImg->stride[VPX_PLANE_Y];
150     size_t srcUStride = mImg->stride[VPX_PLANE_U];
151     size_t srcVStride = mImg->stride[VPX_PLANE_V];
152 
153     copyYV12FrameToOutputBuffer(outputBufferWidth, outputBufferHeight,
154                                 mImg->d_w, mImg->d_h, bpp, dst, srcY, srcU,
155                                 srcV, srcYStride, srcUStride, srcVStride);
156 }
157 
fetchAllFrames()158 void MediaVpxVideoHelper::fetchAllFrames() {
159     mIter = NULL;
160     while (true) {
161         mImg = vpx_codec_get_frame(mCtx.get(), &mIter);
162         if (mImg == NULL) {
163             break;
164         }
165         if (mIgnoreDecoderOutput) {
166             continue;
167         }
168         std::vector<uint8_t> byteBuffer;
169         copyImgToGuest(mImg, byteBuffer);
170         MEDIA_DPRINT("save frame");
171         mSavedDecodedFrames.push_back(MediaSnapshotState::FrameInfo{
172                 std::move(byteBuffer), std::vector<uint32_t>{}, (int)mImg->d_w,
173                 (int)mImg->d_h, (uint64_t)(mImg->user_priv), ColorAspects{}});
174     }
175 }
176 
177 }  // namespace emulation
178 }  // namespace android
179