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 19namespace libyuv { 20extern "C" { 21#endif 22 23#ifdef HAVE_JPEG 24struct I420Buffers { 25 uint8* y; 26 int y_stride; 27 uint8* u; 28 int u_stride; 29 uint8* v; 30 int v_stride; 31 int w; 32 int h; 33}; 34 35static void JpegCopyI420(void* opaque, 36 const uint8* 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 49static void JpegI422ToI420(void* opaque, 50 const uint8* 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 63static void JpegI444ToI420(void* opaque, 64 const uint8* 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 77static void JpegI400ToI420(void* opaque, 78 const uint8* 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. 91LIBYUV_API 92int MJPGSize(const uint8* sample, size_t sample_size, int* width, int* height) { 93 MJpegDecoder mjpeg_decoder; 94 LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size); 95 if (ret) { 96 *width = mjpeg_decoder.GetWidth(); 97 *height = mjpeg_decoder.GetHeight(); 98 } 99 mjpeg_decoder.UnloadFrame(); 100 return ret ? 0 : -1; // -1 for runtime failure. 101} 102 103// MJPG (Motion JPeg) to I420 104// TODO(fbarchard): review w and h requirement. dw and dh may be enough. 105LIBYUV_API 106int MJPGToI420(const uint8* sample, 107 size_t sample_size, 108 uint8* y, 109 int y_stride, 110 uint8* u, 111 int u_stride, 112 uint8* v, 113 int v_stride, 114 int w, 115 int h, 116 int dw, 117 int dh) { 118 if (sample_size == kUnknownDataSize) { 119 // ERROR: MJPEG frame size unknown 120 return -1; 121 } 122 123 // TODO(fbarchard): Port MJpeg to C. 124 MJpegDecoder mjpeg_decoder; 125 LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size); 126 if (ret && 127 (mjpeg_decoder.GetWidth() != w || mjpeg_decoder.GetHeight() != h)) { 128 // ERROR: MJPEG frame has unexpected dimensions 129 mjpeg_decoder.UnloadFrame(); 130 return 1; // runtime failure 131 } 132 if (ret) { 133 I420Buffers bufs = {y, y_stride, u, u_stride, v, v_stride, dw, dh}; 134 // YUV420 135 if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr && 136 mjpeg_decoder.GetNumComponents() == 3 && 137 mjpeg_decoder.GetVertSampFactor(0) == 2 && 138 mjpeg_decoder.GetHorizSampFactor(0) == 2 && 139 mjpeg_decoder.GetVertSampFactor(1) == 1 && 140 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 141 mjpeg_decoder.GetVertSampFactor(2) == 1 && 142 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 143 ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh); 144 // YUV422 145 } else if (mjpeg_decoder.GetColorSpace() == 146 MJpegDecoder::kColorSpaceYCbCr && 147 mjpeg_decoder.GetNumComponents() == 3 && 148 mjpeg_decoder.GetVertSampFactor(0) == 1 && 149 mjpeg_decoder.GetHorizSampFactor(0) == 2 && 150 mjpeg_decoder.GetVertSampFactor(1) == 1 && 151 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 152 mjpeg_decoder.GetVertSampFactor(2) == 1 && 153 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 154 ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh); 155 // YUV444 156 } else if (mjpeg_decoder.GetColorSpace() == 157 MJpegDecoder::kColorSpaceYCbCr && 158 mjpeg_decoder.GetNumComponents() == 3 && 159 mjpeg_decoder.GetVertSampFactor(0) == 1 && 160 mjpeg_decoder.GetHorizSampFactor(0) == 1 && 161 mjpeg_decoder.GetVertSampFactor(1) == 1 && 162 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 163 mjpeg_decoder.GetVertSampFactor(2) == 1 && 164 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 165 ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh); 166 // YUV400 167 } else if (mjpeg_decoder.GetColorSpace() == 168 MJpegDecoder::kColorSpaceGrayscale && 169 mjpeg_decoder.GetNumComponents() == 1 && 170 mjpeg_decoder.GetVertSampFactor(0) == 1 && 171 mjpeg_decoder.GetHorizSampFactor(0) == 1) { 172 ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh); 173 } else { 174 // TODO(fbarchard): Implement conversion for any other colorspace/sample 175 // factors that occur in practice. 176 // ERROR: Unable to convert MJPEG frame because format is not supported 177 mjpeg_decoder.UnloadFrame(); 178 return 1; 179 } 180 } 181 return ret ? 0 : 1; 182} 183 184#ifdef HAVE_JPEG 185struct ARGBBuffers { 186 uint8* argb; 187 int argb_stride; 188 int w; 189 int h; 190}; 191 192static void JpegI420ToARGB(void* opaque, 193 const uint8* const* data, 194 const int* strides, 195 int rows) { 196 ARGBBuffers* dest = (ARGBBuffers*)(opaque); 197 I420ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2], 198 dest->argb, dest->argb_stride, dest->w, rows); 199 dest->argb += rows * dest->argb_stride; 200 dest->h -= rows; 201} 202 203static void JpegI422ToARGB(void* opaque, 204 const uint8* const* data, 205 const int* strides, 206 int rows) { 207 ARGBBuffers* dest = (ARGBBuffers*)(opaque); 208 I422ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2], 209 dest->argb, dest->argb_stride, dest->w, rows); 210 dest->argb += rows * dest->argb_stride; 211 dest->h -= rows; 212} 213 214static void JpegI444ToARGB(void* opaque, 215 const uint8* const* data, 216 const int* strides, 217 int rows) { 218 ARGBBuffers* dest = (ARGBBuffers*)(opaque); 219 I444ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2], 220 dest->argb, dest->argb_stride, dest->w, rows); 221 dest->argb += rows * dest->argb_stride; 222 dest->h -= rows; 223} 224 225static void JpegI400ToARGB(void* opaque, 226 const uint8* const* data, 227 const int* strides, 228 int rows) { 229 ARGBBuffers* dest = (ARGBBuffers*)(opaque); 230 I400ToARGB(data[0], strides[0], dest->argb, dest->argb_stride, dest->w, rows); 231 dest->argb += rows * dest->argb_stride; 232 dest->h -= rows; 233} 234 235// MJPG (Motion JPeg) to ARGB 236// TODO(fbarchard): review w and h requirement. dw and dh may be enough. 237LIBYUV_API 238int MJPGToARGB(const uint8* sample, 239 size_t sample_size, 240 uint8* argb, 241 int argb_stride, 242 int w, 243 int h, 244 int dw, 245 int dh) { 246 if (sample_size == kUnknownDataSize) { 247 // ERROR: MJPEG frame size unknown 248 return -1; 249 } 250 251 // TODO(fbarchard): Port MJpeg to C. 252 MJpegDecoder mjpeg_decoder; 253 LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size); 254 if (ret && 255 (mjpeg_decoder.GetWidth() != w || mjpeg_decoder.GetHeight() != h)) { 256 // ERROR: MJPEG frame has unexpected dimensions 257 mjpeg_decoder.UnloadFrame(); 258 return 1; // runtime failure 259 } 260 if (ret) { 261 ARGBBuffers bufs = {argb, argb_stride, dw, dh}; 262 // YUV420 263 if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr && 264 mjpeg_decoder.GetNumComponents() == 3 && 265 mjpeg_decoder.GetVertSampFactor(0) == 2 && 266 mjpeg_decoder.GetHorizSampFactor(0) == 2 && 267 mjpeg_decoder.GetVertSampFactor(1) == 1 && 268 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 269 mjpeg_decoder.GetVertSampFactor(2) == 1 && 270 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 271 ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dw, dh); 272 // YUV422 273 } else if (mjpeg_decoder.GetColorSpace() == 274 MJpegDecoder::kColorSpaceYCbCr && 275 mjpeg_decoder.GetNumComponents() == 3 && 276 mjpeg_decoder.GetVertSampFactor(0) == 1 && 277 mjpeg_decoder.GetHorizSampFactor(0) == 2 && 278 mjpeg_decoder.GetVertSampFactor(1) == 1 && 279 mjpeg_decoder.GetHorizSampFactor(1) == 1 && 280 mjpeg_decoder.GetVertSampFactor(2) == 1 && 281 mjpeg_decoder.GetHorizSampFactor(2) == 1) { 282 ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dw, dh); 283 // YUV444 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) == 1 && 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(&JpegI444ToARGB, &bufs, dw, dh); 294 // YUV400 295 } else if (mjpeg_decoder.GetColorSpace() == 296 MJpegDecoder::kColorSpaceGrayscale && 297 mjpeg_decoder.GetNumComponents() == 1 && 298 mjpeg_decoder.GetVertSampFactor(0) == 1 && 299 mjpeg_decoder.GetHorizSampFactor(0) == 1) { 300 ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dw, dh); 301 } else { 302 // TODO(fbarchard): Implement conversion for any other colorspace/sample 303 // factors that occur in practice. 304 // ERROR: Unable to convert MJPEG frame because format is not supported 305 mjpeg_decoder.UnloadFrame(); 306 return 1; 307 } 308 } 309 return ret ? 0 : 1; 310} 311#endif 312 313#endif 314 315#ifdef __cplusplus 316} // extern "C" 317} // namespace libyuv 318#endif 319