1 /*
2  *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS. All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "libyuv/convert.h"
12 #include "libyuv/convert_argb.h"
13 
14 #ifdef HAVE_JPEG
15 #include "libyuv/mjpeg_decoder.h"
16 #endif
17 
18 #ifdef __cplusplus
19 namespace libyuv {
20 extern "C" {
21 #endif
22 
23 #ifdef HAVE_JPEG
24 struct I420Buffers {
25   uint8_t* y;
26   int y_stride;
27   uint8_t* u;
28   int u_stride;
29   uint8_t* v;
30   int v_stride;
31   int w;
32   int h;
33 };
34 
JpegCopyI420(void * opaque,const uint8_t * const * data,const int * strides,int rows)35 static void JpegCopyI420(void* opaque,
36                          const uint8_t* const* data,
37                          const int* strides,
38                          int rows) {
39   I420Buffers* dest = (I420Buffers*)(opaque);
40   I420Copy(data[0], strides[0], data[1], strides[1], data[2], strides[2],
41            dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
42            dest->v_stride, dest->w, rows);
43   dest->y += rows * dest->y_stride;
44   dest->u += ((rows + 1) >> 1) * dest->u_stride;
45   dest->v += ((rows + 1) >> 1) * dest->v_stride;
46   dest->h -= rows;
47 }
48 
JpegI422ToI420(void * opaque,const uint8_t * const * data,const int * strides,int rows)49 static void JpegI422ToI420(void* opaque,
50                            const uint8_t* const* data,
51                            const int* strides,
52                            int rows) {
53   I420Buffers* dest = (I420Buffers*)(opaque);
54   I422ToI420(data[0], strides[0], data[1], strides[1], data[2], strides[2],
55              dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
56              dest->v_stride, dest->w, rows);
57   dest->y += rows * dest->y_stride;
58   dest->u += ((rows + 1) >> 1) * dest->u_stride;
59   dest->v += ((rows + 1) >> 1) * dest->v_stride;
60   dest->h -= rows;
61 }
62 
JpegI444ToI420(void * opaque,const uint8_t * const * data,const int * strides,int rows)63 static void JpegI444ToI420(void* opaque,
64                            const uint8_t* const* data,
65                            const int* strides,
66                            int rows) {
67   I420Buffers* dest = (I420Buffers*)(opaque);
68   I444ToI420(data[0], strides[0], data[1], strides[1], data[2], strides[2],
69              dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
70              dest->v_stride, dest->w, rows);
71   dest->y += rows * dest->y_stride;
72   dest->u += ((rows + 1) >> 1) * dest->u_stride;
73   dest->v += ((rows + 1) >> 1) * dest->v_stride;
74   dest->h -= rows;
75 }
76 
JpegI400ToI420(void * opaque,const uint8_t * const * data,const int * strides,int rows)77 static void JpegI400ToI420(void* opaque,
78                            const uint8_t* const* data,
79                            const int* strides,
80                            int rows) {
81   I420Buffers* dest = (I420Buffers*)(opaque);
82   I400ToI420(data[0], strides[0], dest->y, dest->y_stride, dest->u,
83              dest->u_stride, dest->v, dest->v_stride, dest->w, rows);
84   dest->y += rows * dest->y_stride;
85   dest->u += ((rows + 1) >> 1) * dest->u_stride;
86   dest->v += ((rows + 1) >> 1) * dest->v_stride;
87   dest->h -= rows;
88 }
89 
90 // Query size of MJPG in pixels.
91 LIBYUV_API
MJPGSize(const uint8_t * sample,size_t sample_size,int * width,int * height)92 int MJPGSize(const uint8_t* sample,
93              size_t sample_size,
94              int* width,
95              int* height) {
96   MJpegDecoder mjpeg_decoder;
97   LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
98   if (ret) {
99     *width = mjpeg_decoder.GetWidth();
100     *height = mjpeg_decoder.GetHeight();
101   }
102   mjpeg_decoder.UnloadFrame();
103   return ret ? 0 : -1;  // -1 for runtime failure.
104 }
105 
106 // MJPG (Motion JPeg) to I420
107 // TODO(fbarchard): review src_width and src_height requirement. dst_width and
108 // dst_height may be enough.
109 LIBYUV_API
MJPGToI420(const uint8_t * sample,size_t sample_size,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int src_width,int src_height,int dst_width,int dst_height)110 int MJPGToI420(const uint8_t* sample,
111                size_t sample_size,
112                uint8_t* dst_y,
113                int dst_stride_y,
114                uint8_t* dst_u,
115                int dst_stride_u,
116                uint8_t* dst_v,
117                int dst_stride_v,
118                int src_width,
119                int src_height,
120                int dst_width,
121                int dst_height) {
122   if (sample_size == kUnknownDataSize) {
123     // ERROR: MJPEG frame size unknown
124     return -1;
125   }
126 
127   // TODO(fbarchard): Port MJpeg to C.
128   MJpegDecoder mjpeg_decoder;
129   LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
130   if (ret && (mjpeg_decoder.GetWidth() != src_width ||
131               mjpeg_decoder.GetHeight() != src_height)) {
132     // ERROR: MJPEG frame has unexpected dimensions
133     mjpeg_decoder.UnloadFrame();
134     return 1;  // runtime failure
135   }
136   if (ret) {
137     I420Buffers bufs = {dst_y, dst_stride_y, dst_u,     dst_stride_u,
138                         dst_v, dst_stride_v, dst_width, dst_height};
139     // YUV420
140     if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr &&
141         mjpeg_decoder.GetNumComponents() == 3 &&
142         mjpeg_decoder.GetVertSampFactor(0) == 2 &&
143         mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
144         mjpeg_decoder.GetVertSampFactor(1) == 1 &&
145         mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
146         mjpeg_decoder.GetVertSampFactor(2) == 1 &&
147         mjpeg_decoder.GetHorizSampFactor(2) == 1) {
148       ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dst_width,
149                                            dst_height);
150       // YUV422
151     } else if (mjpeg_decoder.GetColorSpace() ==
152                    MJpegDecoder::kColorSpaceYCbCr &&
153                mjpeg_decoder.GetNumComponents() == 3 &&
154                mjpeg_decoder.GetVertSampFactor(0) == 1 &&
155                mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
156                mjpeg_decoder.GetVertSampFactor(1) == 1 &&
157                mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
158                mjpeg_decoder.GetVertSampFactor(2) == 1 &&
159                mjpeg_decoder.GetHorizSampFactor(2) == 1) {
160       ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dst_width,
161                                            dst_height);
162       // YUV444
163     } else if (mjpeg_decoder.GetColorSpace() ==
164                    MJpegDecoder::kColorSpaceYCbCr &&
165                mjpeg_decoder.GetNumComponents() == 3 &&
166                mjpeg_decoder.GetVertSampFactor(0) == 1 &&
167                mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
168                mjpeg_decoder.GetVertSampFactor(1) == 1 &&
169                mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
170                mjpeg_decoder.GetVertSampFactor(2) == 1 &&
171                mjpeg_decoder.GetHorizSampFactor(2) == 1) {
172       ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dst_width,
173                                            dst_height);
174       // YUV400
175     } else if (mjpeg_decoder.GetColorSpace() ==
176                    MJpegDecoder::kColorSpaceGrayscale &&
177                mjpeg_decoder.GetNumComponents() == 1 &&
178                mjpeg_decoder.GetVertSampFactor(0) == 1 &&
179                mjpeg_decoder.GetHorizSampFactor(0) == 1) {
180       ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dst_width,
181                                            dst_height);
182     } else {
183       // TODO(fbarchard): Implement conversion for any other colorspace/sample
184       // factors that occur in practice.
185       // ERROR: Unable to convert MJPEG frame because format is not supported
186       mjpeg_decoder.UnloadFrame();
187       return 1;
188     }
189   }
190   return ret ? 0 : 1;
191 }
192 
193 #ifdef HAVE_JPEG
194 struct ARGBBuffers {
195   uint8_t* argb;
196   int argb_stride;
197   int w;
198   int h;
199 };
200 
JpegI420ToARGB(void * opaque,const uint8_t * const * data,const int * strides,int rows)201 static void JpegI420ToARGB(void* opaque,
202                            const uint8_t* const* data,
203                            const int* strides,
204                            int rows) {
205   ARGBBuffers* dest = (ARGBBuffers*)(opaque);
206   I420ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
207              dest->argb, dest->argb_stride, dest->w, rows);
208   dest->argb += rows * dest->argb_stride;
209   dest->h -= rows;
210 }
211 
JpegI422ToARGB(void * opaque,const uint8_t * const * data,const int * strides,int rows)212 static void JpegI422ToARGB(void* opaque,
213                            const uint8_t* const* data,
214                            const int* strides,
215                            int rows) {
216   ARGBBuffers* dest = (ARGBBuffers*)(opaque);
217   I422ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
218              dest->argb, dest->argb_stride, dest->w, rows);
219   dest->argb += rows * dest->argb_stride;
220   dest->h -= rows;
221 }
222 
JpegI444ToARGB(void * opaque,const uint8_t * const * data,const int * strides,int rows)223 static void JpegI444ToARGB(void* opaque,
224                            const uint8_t* const* data,
225                            const int* strides,
226                            int rows) {
227   ARGBBuffers* dest = (ARGBBuffers*)(opaque);
228   I444ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
229              dest->argb, dest->argb_stride, dest->w, rows);
230   dest->argb += rows * dest->argb_stride;
231   dest->h -= rows;
232 }
233 
JpegI400ToARGB(void * opaque,const uint8_t * const * data,const int * strides,int rows)234 static void JpegI400ToARGB(void* opaque,
235                            const uint8_t* const* data,
236                            const int* strides,
237                            int rows) {
238   ARGBBuffers* dest = (ARGBBuffers*)(opaque);
239   I400ToARGB(data[0], strides[0], dest->argb, dest->argb_stride, dest->w, rows);
240   dest->argb += rows * dest->argb_stride;
241   dest->h -= rows;
242 }
243 
244 // MJPG (Motion JPeg) to ARGB
245 // TODO(fbarchard): review src_width and src_height requirement. dst_width and
246 // dst_height may be enough.
247 LIBYUV_API
MJPGToARGB(const uint8_t * sample,size_t sample_size,uint8_t * dst_argb,int dst_stride_argb,int src_width,int src_height,int dst_width,int dst_height)248 int MJPGToARGB(const uint8_t* sample,
249                size_t sample_size,
250                uint8_t* dst_argb,
251                int dst_stride_argb,
252                int src_width,
253                int src_height,
254                int dst_width,
255                int dst_height) {
256   if (sample_size == kUnknownDataSize) {
257     // ERROR: MJPEG frame size unknown
258     return -1;
259   }
260 
261   // TODO(fbarchard): Port MJpeg to C.
262   MJpegDecoder mjpeg_decoder;
263   LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
264   if (ret && (mjpeg_decoder.GetWidth() != src_width ||
265               mjpeg_decoder.GetHeight() != src_height)) {
266     // ERROR: MJPEG frame has unexpected dimensions
267     mjpeg_decoder.UnloadFrame();
268     return 1;  // runtime failure
269   }
270   if (ret) {
271     ARGBBuffers bufs = {dst_argb, dst_stride_argb, dst_width, dst_height};
272     // YUV420
273     if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr &&
274         mjpeg_decoder.GetNumComponents() == 3 &&
275         mjpeg_decoder.GetVertSampFactor(0) == 2 &&
276         mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
277         mjpeg_decoder.GetVertSampFactor(1) == 1 &&
278         mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
279         mjpeg_decoder.GetVertSampFactor(2) == 1 &&
280         mjpeg_decoder.GetHorizSampFactor(2) == 1) {
281       ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dst_width,
282                                            dst_height);
283       // YUV422
284     } else if (mjpeg_decoder.GetColorSpace() ==
285                    MJpegDecoder::kColorSpaceYCbCr &&
286                mjpeg_decoder.GetNumComponents() == 3 &&
287                mjpeg_decoder.GetVertSampFactor(0) == 1 &&
288                mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
289                mjpeg_decoder.GetVertSampFactor(1) == 1 &&
290                mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
291                mjpeg_decoder.GetVertSampFactor(2) == 1 &&
292                mjpeg_decoder.GetHorizSampFactor(2) == 1) {
293       ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dst_width,
294                                            dst_height);
295       // YUV444
296     } else if (mjpeg_decoder.GetColorSpace() ==
297                    MJpegDecoder::kColorSpaceYCbCr &&
298                mjpeg_decoder.GetNumComponents() == 3 &&
299                mjpeg_decoder.GetVertSampFactor(0) == 1 &&
300                mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
301                mjpeg_decoder.GetVertSampFactor(1) == 1 &&
302                mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
303                mjpeg_decoder.GetVertSampFactor(2) == 1 &&
304                mjpeg_decoder.GetHorizSampFactor(2) == 1) {
305       ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToARGB, &bufs, dst_width,
306                                            dst_height);
307       // YUV400
308     } else if (mjpeg_decoder.GetColorSpace() ==
309                    MJpegDecoder::kColorSpaceGrayscale &&
310                mjpeg_decoder.GetNumComponents() == 1 &&
311                mjpeg_decoder.GetVertSampFactor(0) == 1 &&
312                mjpeg_decoder.GetHorizSampFactor(0) == 1) {
313       ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dst_width,
314                                            dst_height);
315     } else {
316       // TODO(fbarchard): Implement conversion for any other colorspace/sample
317       // factors that occur in practice.
318       // ERROR: Unable to convert MJPEG frame because format is not supported
319       mjpeg_decoder.UnloadFrame();
320       return 1;
321     }
322   }
323   return ret ? 0 : 1;
324 }
325 #endif
326 
327 #endif
328 
329 #ifdef __cplusplus
330 }  // extern "C"
331 }  // namespace libyuv
332 #endif
333