1 /**
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <android-base/unique_fd.h>
21 #include <android/imagedecoder.h>
22 #include <ImsMediaTrace.h>
23 #include "ImsMediaPauseImageSource.h"
24
25 // TODO: Pause images from Irvine source are used. Get new pause images from UX team and replace.
26 // TODO: Write unit test cases for this class
27 #define DEFAULT_FHD_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_fhd_p.jpg"
28 #define DEFAULT_FHD_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_fhd_l.jpg"
29 #define DEFAULT_HD_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_hd_p.jpg"
30 #define DEFAULT_HD_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_hd_l.jpg"
31 #define DEFAULT_VGA_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_vga_p.jpg"
32 #define DEFAULT_VGA_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_vga_l.jpg"
33 #define DEFAULT_QVGA_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_qvga_p.jpg"
34 #define DEFAULT_QVGA_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_qvga_l.jpg"
35 #define DEFAULT_CIF_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_cif_p.jpg"
36 #define DEFAULT_CIF_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_cif_l.jpg"
37 #define DEFAULT_QCIF_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_qcif_p.jpg"
38 #define DEFAULT_QCIF_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_qcif_l.jpg"
39 #define DEFAULT_SIF_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_sif_p.jpg"
40 #define DEFAULT_SIF_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_sif_l.jpg"
41 #define DEFAULT_SQCIF_PORTRAIT_PAUSE_IMG_PATH "pause_images/pause_img_sqcif_p.jpg"
42 #define DEFAULT_SQCIF_LANDSCAPE_PAUSE_IMG_PATH "pause_images/pause_img_sqcif_l.jpg"
43
44 extern AAssetManager* gpAssetManager;
45
ImsMediaPauseImageSource()46 ImsMediaPauseImageSource::ImsMediaPauseImageSource()
47 {
48 mYuvImageBuffer = nullptr;
49 mBufferSize = 0;
50 }
51
~ImsMediaPauseImageSource()52 ImsMediaPauseImageSource::~ImsMediaPauseImageSource()
53 {
54 Uninitialize();
55 }
56
Uninitialize()57 void ImsMediaPauseImageSource::Uninitialize()
58 {
59 if (mYuvImageBuffer != nullptr)
60 {
61 free(mYuvImageBuffer);
62 mYuvImageBuffer = nullptr;
63 }
64 }
65
Initialize(int width,int height,int stride)66 bool ImsMediaPauseImageSource::Initialize(int width, int height, int stride)
67 {
68 IMLOGD3("[ImsMediaPauseImageSource] Init(width:%d, height:%d, stride:%d)", width, height,
69 stride);
70 mWidth = width;
71 mHeight = height;
72
73 // Decode JPEG image and save in YUV buffer.
74 AAsset* asset = getImageAsset();
75 if (asset == nullptr)
76 {
77 IMLOGE0("[ImsMediaPauseImageSource] Failed to open pause image");
78 return false;
79 }
80
81 AImageDecoder* decoder;
82 int result = AImageDecoder_createFromAAsset(asset, &decoder);
83 if (result != ANDROID_IMAGE_DECODER_SUCCESS)
84 {
85 IMLOGE0("[ImsMediaPauseImageSource] Failed to decode pause image");
86 return false;
87 }
88
89 const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder);
90 int32_t JpegWidth = AImageDecoderHeaderInfo_getWidth(info);
91 int32_t JpegHeight = AImageDecoderHeaderInfo_getHeight(info);
92 if (JpegWidth != mWidth || JpegHeight != mHeight)
93 {
94 IMLOGE0("[ImsMediaPauseImageSource] Decoded image resolution doesn't match with JPEG image"
95 "resolution");
96 AImageDecoder_delete(decoder);
97 AAsset_close(asset);
98 return false;
99 }
100
101 /*
102 * TODO: AImageDecoder output should be in ANDROID_BITMAP_FORMAT_RGBA_8888 format.
103 * If not, configure AImageDecoder accordingly.
104 *
105 * AndroidBitmapFormat format =
106 * (AndroidBitmapFormat)AImageDecoderHeaderInfo_getAndroidBitmapFormat(info);
107 */
108
109 size_t decStride = AImageDecoder_getMinimumStride(decoder); // Image decoder does not
110 // use padding by default
111 size_t size = height * decStride;
112 int8_t* pixels = reinterpret_cast<int8_t*>(malloc(size));
113
114 result = AImageDecoder_decodeImage(decoder, pixels, decStride, size);
115 if (result != ANDROID_IMAGE_DECODER_SUCCESS)
116 {
117 IMLOGE0("[ImsMediaPauseImageSource] error occurred, and the file could not be decoded.");
118 AImageDecoder_delete(decoder);
119 AAsset_close(asset);
120 return false;
121 }
122
123 mYuvImageBuffer = ConvertRgbaToYuv(pixels, width, height, stride);
124
125 AImageDecoder_delete(decoder);
126 free(pixels);
127 AAsset_close(asset);
128 return true;
129 }
130
GetYuvImage(uint8_t * buffer,size_t len)131 size_t ImsMediaPauseImageSource::GetYuvImage(uint8_t* buffer, size_t len)
132 {
133 if (buffer == nullptr)
134 {
135 IMLOGE0("[ImsMediaPauseImageSource] GetYuvImage. buffer == nullptr");
136 return 0;
137 }
138
139 if (len >= mBufferSize)
140 {
141 memcpy(buffer, mYuvImageBuffer, mBufferSize);
142 return mBufferSize;
143 }
144
145 IMLOGE2("[ImsMediaPauseImageSource] buffer size is smaller. Expected Bufsize[%d], passed[%d]",
146 mBufferSize, len);
147 return 0;
148 }
149
getImageAsset()150 AAsset* ImsMediaPauseImageSource::getImageAsset()
151 {
152 IMLOGD0("[ImsMediaPauseImageSource] getImageFileFd");
153 if (gpAssetManager == nullptr)
154 {
155 IMLOGE0("[ImsMediaPauseImageSource] AssetManager is nullptr");
156 return nullptr;
157 }
158
159 const char* filePath = getImageFilePath();
160 return AAssetManager_open(gpAssetManager, filePath, AASSET_MODE_RANDOM);
161 }
162
getImageFilePath()163 const char* ImsMediaPauseImageSource::getImageFilePath()
164 {
165 if (mWidth == 1920 && mHeight == 1080)
166 return DEFAULT_FHD_LANDSCAPE_PAUSE_IMG_PATH;
167 else if (mWidth == 1080 && mHeight == 1920)
168 return DEFAULT_FHD_PORTRAIT_PAUSE_IMG_PATH;
169 else if (mWidth == 1280 && mHeight == 720)
170 return DEFAULT_HD_LANDSCAPE_PAUSE_IMG_PATH;
171 else if (mWidth == 720 && mHeight == 1280)
172 return DEFAULT_HD_PORTRAIT_PAUSE_IMG_PATH;
173 else if (mWidth == 640 && mHeight == 480)
174 return DEFAULT_VGA_LANDSCAPE_PAUSE_IMG_PATH;
175 else if (mWidth == 480 && mHeight == 640)
176 return DEFAULT_VGA_PORTRAIT_PAUSE_IMG_PATH;
177 else if (mWidth == 352 && mHeight == 288)
178 return DEFAULT_CIF_LANDSCAPE_PAUSE_IMG_PATH;
179 else if (mWidth == 288 && mHeight == 352)
180 return DEFAULT_CIF_PORTRAIT_PAUSE_IMG_PATH;
181 else if (mWidth == 352 && mHeight == 240)
182 return DEFAULT_SIF_LANDSCAPE_PAUSE_IMG_PATH;
183 else if (mWidth == 240 && mHeight == 352)
184 return DEFAULT_SIF_PORTRAIT_PAUSE_IMG_PATH;
185 else if (mWidth == 320 && mHeight == 240)
186 return DEFAULT_QVGA_LANDSCAPE_PAUSE_IMG_PATH;
187 else if (mWidth == 240 && mHeight == 320)
188 return DEFAULT_QVGA_PORTRAIT_PAUSE_IMG_PATH;
189 else if (mWidth == 176 && mHeight == 144)
190 return DEFAULT_QCIF_LANDSCAPE_PAUSE_IMG_PATH;
191 else if (mWidth == 144 && mHeight == 176)
192 return DEFAULT_QCIF_PORTRAIT_PAUSE_IMG_PATH;
193 else if (mWidth == 128 && mHeight == 96)
194 return DEFAULT_SQCIF_LANDSCAPE_PAUSE_IMG_PATH;
195 else if (mWidth == 96 && mHeight == 128)
196 return DEFAULT_SQCIF_PORTRAIT_PAUSE_IMG_PATH;
197 else
198 {
199 IMLOGE2("Resolution [%dx%d] pause image is not available", mWidth, mHeight);
200 }
201
202 return nullptr;
203 }
204
ConvertRgbaToYuv(int8_t * pixels,int width,int height,int stride)205 int8_t* ImsMediaPauseImageSource::ConvertRgbaToYuv(
206 int8_t* pixels, int width, int height, int stride)
207 {
208 // src array must be integer array, data have no padding alignment
209 int32_t* pSrcArray = reinterpret_cast<int32_t*>(pixels);
210 mBufferSize = stride * height * 1.5;
211 int8_t* pDstArray = reinterpret_cast<int8_t*>(malloc(mBufferSize));
212 int32_t nYIndex = 0;
213 int32_t nUVIndex = stride * height;
214 int32_t r, g, b, padLen = stride - width;
215 double y, u, v;
216
217 for (int32_t j = 0; j < height; j++)
218 {
219 int32_t nIndex = width * j;
220 for (int32_t i = 0; i < width; i++)
221 {
222 r = (pSrcArray[nIndex] & 0xff0000) >> 16;
223 g = (pSrcArray[nIndex] & 0xff00) >> 8;
224 b = (pSrcArray[nIndex] & 0xff) >> 0;
225 nIndex++;
226
227 // rgb to yuv
228 y = 0.257 * r + 0.504 * g + 0.098 * b + 16;
229 u = 128 + 0.439 * r - 0.368 * g - 0.071 * b;
230 v = 128 - 0.148 * r - 0.291 * g + 0.439 * b;
231
232 // clip y
233 pDstArray[nYIndex++] = (uint8_t)((y < 0) ? 0 : ((y > 255) ? 255 : y));
234
235 if (j % 2 == 0 && nIndex % 2 == 0)
236 {
237 pDstArray[nUVIndex++] = (uint8_t)((v < 0) ? 0 : ((v > 255) ? 255 : v));
238 pDstArray[nUVIndex++] = (uint8_t)((u < 0) ? 0 : ((u > 255) ? 255 : u));
239 }
240 }
241
242 // Add padding if stride > width
243 if (padLen > 0)
244 {
245 nYIndex += padLen;
246
247 if (j % 2 == 0)
248 {
249 nUVIndex += padLen;
250 }
251 }
252 }
253
254 mBufferSize -= padLen;
255 return pDstArray;
256 }
257