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