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