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