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
13#ifdef HAVE_JPEG
14#include "libyuv/mjpeg_decoder.h"
15#endif
16
17#ifdef __cplusplus
18namespace libyuv {
19extern "C" {
20#endif
21
22#ifdef HAVE_JPEG
23struct I420Buffers {
24  uint8* y;
25  int y_stride;
26  uint8* u;
27  int u_stride;
28  uint8* v;
29  int v_stride;
30  int w;
31  int h;
32};
33
34static void JpegCopyI420(void* opaque,
35                         const uint8* const* data,
36                         const int* strides,
37                         int rows) {
38  I420Buffers* dest = (I420Buffers*)(opaque);
39  I420Copy(data[0], strides[0],
40           data[1], strides[1],
41           data[2], strides[2],
42           dest->y, dest->y_stride,
43           dest->u, dest->u_stride,
44           dest->v, dest->v_stride,
45           dest->w, rows);
46  dest->y += rows * dest->y_stride;
47  dest->u += ((rows + 1) >> 1) * dest->u_stride;
48  dest->v += ((rows + 1) >> 1) * dest->v_stride;
49  dest->h -= rows;
50}
51
52static void JpegI422ToI420(void* opaque,
53                           const uint8* const* data,
54                           const int* strides,
55                           int rows) {
56  I420Buffers* dest = (I420Buffers*)(opaque);
57  I422ToI420(data[0], strides[0],
58             data[1], strides[1],
59             data[2], strides[2],
60             dest->y, dest->y_stride,
61             dest->u, dest->u_stride,
62             dest->v, dest->v_stride,
63             dest->w, rows);
64  dest->y += rows * dest->y_stride;
65  dest->u += ((rows + 1) >> 1) * dest->u_stride;
66  dest->v += ((rows + 1) >> 1) * dest->v_stride;
67  dest->h -= rows;
68}
69
70static void JpegI444ToI420(void* opaque,
71                           const uint8* const* data,
72                           const int* strides,
73                           int rows) {
74  I420Buffers* dest = (I420Buffers*)(opaque);
75  I444ToI420(data[0], strides[0],
76             data[1], strides[1],
77             data[2], strides[2],
78             dest->y, dest->y_stride,
79             dest->u, dest->u_stride,
80             dest->v, dest->v_stride,
81             dest->w, rows);
82  dest->y += rows * dest->y_stride;
83  dest->u += ((rows + 1) >> 1) * dest->u_stride;
84  dest->v += ((rows + 1) >> 1) * dest->v_stride;
85  dest->h -= rows;
86}
87
88static void JpegI411ToI420(void* opaque,
89                           const uint8* const* data,
90                           const int* strides,
91                           int rows) {
92  I420Buffers* dest = (I420Buffers*)(opaque);
93  I411ToI420(data[0], strides[0],
94             data[1], strides[1],
95             data[2], strides[2],
96             dest->y, dest->y_stride,
97             dest->u, dest->u_stride,
98             dest->v, dest->v_stride,
99             dest->w, rows);
100  dest->y += rows * dest->y_stride;
101  dest->u += ((rows + 1) >> 1) * dest->u_stride;
102  dest->v += ((rows + 1) >> 1) * dest->v_stride;
103  dest->h -= rows;
104}
105
106static void JpegI400ToI420(void* opaque,
107                           const uint8* const* data,
108                           const int* strides,
109                           int rows) {
110  I420Buffers* dest = (I420Buffers*)(opaque);
111  I400ToI420(data[0], strides[0],
112             dest->y, dest->y_stride,
113             dest->u, dest->u_stride,
114             dest->v, dest->v_stride,
115             dest->w, rows);
116  dest->y += rows * dest->y_stride;
117  dest->u += ((rows + 1) >> 1) * dest->u_stride;
118  dest->v += ((rows + 1) >> 1) * dest->v_stride;
119  dest->h -= rows;
120}
121
122// Query size of MJPG in pixels.
123LIBYUV_API
124int MJPGSize(const uint8* sample, size_t sample_size,
125             int* width, int* height) {
126  MJpegDecoder mjpeg_decoder;
127  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
128  if (ret) {
129    *width = mjpeg_decoder.GetWidth();
130    *height = mjpeg_decoder.GetHeight();
131  }
132  mjpeg_decoder.UnloadFrame();
133  return ret ? 0 : -1;  // -1 for runtime failure.
134}
135
136// MJPG (Motion JPeg) to I420
137// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
138LIBYUV_API
139int MJPGToI420(const uint8* sample,
140               size_t sample_size,
141               uint8* y, int y_stride,
142               uint8* u, int u_stride,
143               uint8* v, int v_stride,
144               int w, int h,
145               int dw, int dh) {
146  if (sample_size == kUnknownDataSize) {
147    // ERROR: MJPEG frame size unknown
148    return -1;
149  }
150
151  // TODO(fbarchard): Port MJpeg to C.
152  MJpegDecoder mjpeg_decoder;
153  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
154  if (ret && (mjpeg_decoder.GetWidth() != w ||
155              mjpeg_decoder.GetHeight() != h)) {
156    // ERROR: MJPEG frame has unexpected dimensions
157    mjpeg_decoder.UnloadFrame();
158    return 1;  // runtime failure
159  }
160  if (ret) {
161    I420Buffers bufs = { y, y_stride, u, u_stride, v, v_stride, dw, dh };
162    // YUV420
163    if (mjpeg_decoder.GetColorSpace() ==
164            MJpegDecoder::kColorSpaceYCbCr &&
165        mjpeg_decoder.GetNumComponents() == 3 &&
166        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
167        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
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(&JpegCopyI420, &bufs, dw, dh);
173    // YUV422
174    } else if (mjpeg_decoder.GetColorSpace() ==
175                   MJpegDecoder::kColorSpaceYCbCr &&
176               mjpeg_decoder.GetNumComponents() == 3 &&
177               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
178               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
179               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
180               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
181               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
182               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
183      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh);
184    // YUV444
185    } else if (mjpeg_decoder.GetColorSpace() ==
186                   MJpegDecoder::kColorSpaceYCbCr &&
187               mjpeg_decoder.GetNumComponents() == 3 &&
188               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
189               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
190               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
191               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
192               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
193               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
194      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh);
195    // YUV411
196    } else if (mjpeg_decoder.GetColorSpace() ==
197                   MJpegDecoder::kColorSpaceYCbCr &&
198               mjpeg_decoder.GetNumComponents() == 3 &&
199               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
200               mjpeg_decoder.GetHorizSampFactor(0) == 4 &&
201               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
202               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
203               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
204               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
205      ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToI420, &bufs, dw, dh);
206    // YUV400
207    } else if (mjpeg_decoder.GetColorSpace() ==
208                   MJpegDecoder::kColorSpaceGrayscale &&
209               mjpeg_decoder.GetNumComponents() == 1 &&
210               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
211               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
212      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh);
213    } else {
214      // TODO(fbarchard): Implement conversion for any other colorspace/sample
215      // factors that occur in practice. 411 is supported by libjpeg
216      // ERROR: Unable to convert MJPEG frame because format is not supported
217      mjpeg_decoder.UnloadFrame();
218      return 1;
219    }
220  }
221  return ret ? 0 : 1;
222}
223
224#ifdef HAVE_JPEG
225struct ARGBBuffers {
226  uint8* argb;
227  int argb_stride;
228  int w;
229  int h;
230};
231
232static void JpegI420ToARGB(void* opaque,
233                         const uint8* const* data,
234                         const int* strides,
235                         int rows) {
236  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
237  I420ToARGB(data[0], strides[0],
238             data[1], strides[1],
239             data[2], strides[2],
240             dest->argb, dest->argb_stride,
241             dest->w, rows);
242  dest->argb += rows * dest->argb_stride;
243  dest->h -= rows;
244}
245
246static void JpegI422ToARGB(void* opaque,
247                           const uint8* const* data,
248                           const int* strides,
249                           int rows) {
250  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
251  I422ToARGB(data[0], strides[0],
252             data[1], strides[1],
253             data[2], strides[2],
254             dest->argb, dest->argb_stride,
255             dest->w, rows);
256  dest->argb += rows * dest->argb_stride;
257  dest->h -= rows;
258}
259
260static void JpegI444ToARGB(void* opaque,
261                           const uint8* const* data,
262                           const int* strides,
263                           int rows) {
264  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
265  I444ToARGB(data[0], strides[0],
266             data[1], strides[1],
267             data[2], strides[2],
268             dest->argb, dest->argb_stride,
269             dest->w, rows);
270  dest->argb += rows * dest->argb_stride;
271  dest->h -= rows;
272}
273
274static void JpegI411ToARGB(void* opaque,
275                           const uint8* const* data,
276                           const int* strides,
277                           int rows) {
278  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
279  I411ToARGB(data[0], strides[0],
280             data[1], strides[1],
281             data[2], strides[2],
282             dest->argb, dest->argb_stride,
283             dest->w, rows);
284  dest->argb += rows * dest->argb_stride;
285  dest->h -= rows;
286}
287
288static void JpegI400ToARGB(void* opaque,
289                           const uint8* const* data,
290                           const int* strides,
291                           int rows) {
292  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
293  I400ToARGB(data[0], strides[0],
294             dest->argb, dest->argb_stride,
295             dest->w, rows);
296  dest->argb += rows * dest->argb_stride;
297  dest->h -= rows;
298}
299
300// MJPG (Motion JPeg) to ARGB
301// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
302LIBYUV_API
303int MJPGToARGB(const uint8* sample,
304               size_t sample_size,
305               uint8* argb, int argb_stride,
306               int w, int h,
307               int dw, int dh) {
308  if (sample_size == kUnknownDataSize) {
309    // ERROR: MJPEG frame size unknown
310    return -1;
311  }
312
313  // TODO(fbarchard): Port MJpeg to C.
314  MJpegDecoder mjpeg_decoder;
315  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
316  if (ret && (mjpeg_decoder.GetWidth() != w ||
317              mjpeg_decoder.GetHeight() != h)) {
318    // ERROR: MJPEG frame has unexpected dimensions
319    mjpeg_decoder.UnloadFrame();
320    return 1;  // runtime failure
321  }
322  if (ret) {
323    ARGBBuffers bufs = { argb, argb_stride, dw, dh };
324    // YUV420
325    if (mjpeg_decoder.GetColorSpace() ==
326            MJpegDecoder::kColorSpaceYCbCr &&
327        mjpeg_decoder.GetNumComponents() == 3 &&
328        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
329        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
330        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
331        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
332        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
333        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
334      ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dw, dh);
335    // YUV422
336    } else if (mjpeg_decoder.GetColorSpace() ==
337                   MJpegDecoder::kColorSpaceYCbCr &&
338               mjpeg_decoder.GetNumComponents() == 3 &&
339               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
340               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
341               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
342               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
343               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
344               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
345      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dw, dh);
346    // YUV444
347    } else if (mjpeg_decoder.GetColorSpace() ==
348                   MJpegDecoder::kColorSpaceYCbCr &&
349               mjpeg_decoder.GetNumComponents() == 3 &&
350               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
351               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
352               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
353               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
354               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
355               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
356      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToARGB, &bufs, dw, dh);
357    // YUV411
358    } else if (mjpeg_decoder.GetColorSpace() ==
359                   MJpegDecoder::kColorSpaceYCbCr &&
360               mjpeg_decoder.GetNumComponents() == 3 &&
361               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
362               mjpeg_decoder.GetHorizSampFactor(0) == 4 &&
363               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
364               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
365               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
366               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
367      ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToARGB, &bufs, dw, dh);
368    // YUV400
369    } else if (mjpeg_decoder.GetColorSpace() ==
370                   MJpegDecoder::kColorSpaceGrayscale &&
371               mjpeg_decoder.GetNumComponents() == 1 &&
372               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
373               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
374      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dw, dh);
375    } else {
376      // TODO(fbarchard): Implement conversion for any other colorspace/sample
377      // factors that occur in practice. 411 is supported by libjpeg
378      // ERROR: Unable to convert MJPEG frame because format is not supported
379      mjpeg_decoder.UnloadFrame();
380      return 1;
381    }
382  }
383  return ret ? 0 : 1;
384}
385#endif
386
387#endif
388
389#ifdef __cplusplus
390}  // extern "C"
391}  // namespace libyuv
392#endif
393