convert_jpeg.cc 11.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS. All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "libyuv/convert.h"
12
#include "libyuv/convert_argb.h"
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

#ifdef HAVE_JPEG
#include "libyuv/mjpeg_decoder.h"
#endif

#ifdef __cplusplus
namespace libyuv {
extern "C" {
#endif

#ifdef HAVE_JPEG
struct I420Buffers {
  uint8* y;
  int y_stride;
  uint8* u;
  int u_stride;
  uint8* v;
  int v_stride;
  int w;
  int h;
};

static void JpegCopyI420(void* opaque,
                         const uint8* const* data,
                         const int* strides,
                         int rows) {
39
  I420Buffers* dest = (I420Buffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
40 41 42
  I420Copy(data[0], strides[0], data[1], strides[1], data[2], strides[2],
           dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
           dest->v_stride, dest->w, rows);
43 44 45 46 47 48 49 50 51 52
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

static void JpegI422ToI420(void* opaque,
                           const uint8* const* data,
                           const int* strides,
                           int rows) {
53
  I420Buffers* dest = (I420Buffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
54 55 56
  I422ToI420(data[0], strides[0], data[1], strides[1], data[2], strides[2],
             dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
             dest->v_stride, dest->w, rows);
57 58 59 60 61 62 63 64 65 66
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

static void JpegI444ToI420(void* opaque,
                           const uint8* const* data,
                           const int* strides,
                           int rows) {
67
  I420Buffers* dest = (I420Buffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
68 69 70
  I444ToI420(data[0], strides[0], data[1], strides[1], data[2], strides[2],
             dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
             dest->v_stride, dest->w, rows);
71 72 73 74 75 76 77 78 79 80
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

static void JpegI400ToI420(void* opaque,
                           const uint8* const* data,
                           const int* strides,
                           int rows) {
81
  I420Buffers* dest = (I420Buffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
82 83
  I400ToI420(data[0], strides[0], dest->y, dest->y_stride, dest->u,
             dest->u_stride, dest->v, dest->v_stride, dest->w, rows);
84 85 86 87 88 89 90 91
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

// Query size of MJPG in pixels.
LIBYUV_API
Frank Barchard's avatar
Frank Barchard committed
92
int 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 96 97 98 99 100 101 102 103 104 105 106 107
  if (ret) {
    *width = mjpeg_decoder.GetWidth();
    *height = mjpeg_decoder.GetHeight();
  }
  mjpeg_decoder.UnloadFrame();
  return ret ? 0 : -1;  // -1 for runtime failure.
}

// MJPG (Motion JPeg) to I420
// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
LIBYUV_API
int MJPGToI420(const uint8* sample,
               size_t sample_size,
Frank Barchard's avatar
Frank Barchard committed
108 109 110 111 112 113 114 115 116 117
               uint8* y,
               int y_stride,
               uint8* u,
               int u_stride,
               uint8* v,
               int v_stride,
               int w,
               int h,
               int dw,
               int dh) {
118 119 120 121 122 123 124
  if (sample_size == kUnknownDataSize) {
    // ERROR: MJPEG frame size unknown
    return -1;
  }

  // TODO(fbarchard): Port MJpeg to C.
  MJpegDecoder mjpeg_decoder;
125
  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
Frank Barchard's avatar
Frank Barchard committed
126 127
  if (ret &&
      (mjpeg_decoder.GetWidth() != w || mjpeg_decoder.GetHeight() != h)) {
128 129 130 131 132
    // ERROR: MJPEG frame has unexpected dimensions
    mjpeg_decoder.UnloadFrame();
    return 1;  // runtime failure
  }
  if (ret) {
Frank Barchard's avatar
Frank Barchard committed
133
    I420Buffers bufs = {y, y_stride, u, u_stride, v, v_stride, dw, dh};
134
    // YUV420
Frank Barchard's avatar
Frank Barchard committed
135
    if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr &&
136 137 138 139 140 141 142 143
        mjpeg_decoder.GetNumComponents() == 3 &&
        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh);
Frank Barchard's avatar
Frank Barchard committed
144
      // YUV422
145 146 147 148 149 150 151 152 153 154
    } else if (mjpeg_decoder.GetColorSpace() ==
                   MJpegDecoder::kColorSpaceYCbCr &&
               mjpeg_decoder.GetNumComponents() == 3 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh);
Frank Barchard's avatar
Frank Barchard committed
155
      // YUV444
156 157 158 159 160 161 162 163 164 165
    } else if (mjpeg_decoder.GetColorSpace() ==
                   MJpegDecoder::kColorSpaceYCbCr &&
               mjpeg_decoder.GetNumComponents() == 3 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh);
Frank Barchard's avatar
Frank Barchard committed
166
      // YUV400
167 168 169 170 171 172 173 174
    } else if (mjpeg_decoder.GetColorSpace() ==
                   MJpegDecoder::kColorSpaceGrayscale &&
               mjpeg_decoder.GetNumComponents() == 1 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh);
    } else {
      // TODO(fbarchard): Implement conversion for any other colorspace/sample
Frank Barchard's avatar
Frank Barchard committed
175
      // factors that occur in practice.
176 177 178 179 180
      // ERROR: Unable to convert MJPEG frame because format is not supported
      mjpeg_decoder.UnloadFrame();
      return 1;
    }
  }
181
  return ret ? 0 : 1;
182
}
183 184 185 186 187 188 189 190 191 192

#ifdef HAVE_JPEG
struct ARGBBuffers {
  uint8* argb;
  int argb_stride;
  int w;
  int h;
};

static void JpegI420ToARGB(void* opaque,
Frank Barchard's avatar
Frank Barchard committed
193 194 195
                           const uint8* const* data,
                           const int* strides,
                           int rows) {
196
  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
197 198
  I420ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
             dest->argb, dest->argb_stride, dest->w, rows);
199 200 201 202 203 204 205 206
  dest->argb += rows * dest->argb_stride;
  dest->h -= rows;
}

static void JpegI422ToARGB(void* opaque,
                           const uint8* const* data,
                           const int* strides,
                           int rows) {
207
  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
208 209
  I422ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
             dest->argb, dest->argb_stride, dest->w, rows);
210 211 212 213 214 215 216 217
  dest->argb += rows * dest->argb_stride;
  dest->h -= rows;
}

static void JpegI444ToARGB(void* opaque,
                           const uint8* const* data,
                           const int* strides,
                           int rows) {
218
  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
219 220
  I444ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
             dest->argb, dest->argb_stride, dest->w, rows);
221 222 223 224 225 226 227 228
  dest->argb += rows * dest->argb_stride;
  dest->h -= rows;
}

static void JpegI400ToARGB(void* opaque,
                           const uint8* const* data,
                           const int* strides,
                           int rows) {
229
  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
Frank Barchard's avatar
Frank Barchard committed
230
  I400ToARGB(data[0], strides[0], dest->argb, dest->argb_stride, dest->w, rows);
231 232 233 234 235 236 237 238 239
  dest->argb += rows * dest->argb_stride;
  dest->h -= rows;
}

// MJPG (Motion JPeg) to ARGB
// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
LIBYUV_API
int MJPGToARGB(const uint8* sample,
               size_t sample_size,
Frank Barchard's avatar
Frank Barchard committed
240 241 242 243 244 245
               uint8* argb,
               int argb_stride,
               int w,
               int h,
               int dw,
               int dh) {
246 247 248 249 250 251 252
  if (sample_size == kUnknownDataSize) {
    // ERROR: MJPEG frame size unknown
    return -1;
  }

  // TODO(fbarchard): Port MJpeg to C.
  MJpegDecoder mjpeg_decoder;
253
  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
Frank Barchard's avatar
Frank Barchard committed
254 255
  if (ret &&
      (mjpeg_decoder.GetWidth() != w || mjpeg_decoder.GetHeight() != h)) {
256 257 258 259 260
    // ERROR: MJPEG frame has unexpected dimensions
    mjpeg_decoder.UnloadFrame();
    return 1;  // runtime failure
  }
  if (ret) {
Frank Barchard's avatar
Frank Barchard committed
261
    ARGBBuffers bufs = {argb, argb_stride, dw, dh};
262
    // YUV420
Frank Barchard's avatar
Frank Barchard committed
263
    if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr &&
264 265 266 267 268 269 270 271
        mjpeg_decoder.GetNumComponents() == 3 &&
        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dw, dh);
Frank Barchard's avatar
Frank Barchard committed
272
      // YUV422
273 274 275 276 277 278 279 280 281 282
    } else if (mjpeg_decoder.GetColorSpace() ==
                   MJpegDecoder::kColorSpaceYCbCr &&
               mjpeg_decoder.GetNumComponents() == 3 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dw, dh);
Frank Barchard's avatar
Frank Barchard committed
283
      // YUV444
284 285 286 287 288 289 290 291 292 293
    } else if (mjpeg_decoder.GetColorSpace() ==
                   MJpegDecoder::kColorSpaceYCbCr &&
               mjpeg_decoder.GetNumComponents() == 3 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToARGB, &bufs, dw, dh);
Frank Barchard's avatar
Frank Barchard committed
294
      // YUV400
295 296 297 298 299 300 301 302
    } else if (mjpeg_decoder.GetColorSpace() ==
                   MJpegDecoder::kColorSpaceGrayscale &&
               mjpeg_decoder.GetNumComponents() == 1 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dw, dh);
    } else {
      // TODO(fbarchard): Implement conversion for any other colorspace/sample
Frank Barchard's avatar
Frank Barchard committed
303
      // factors that occur in practice.
304 305 306 307 308
      // ERROR: Unable to convert MJPEG frame because format is not supported
      mjpeg_decoder.UnloadFrame();
      return 1;
    }
  }
309
  return ret ? 0 : 1;
310 311 312
}
#endif

313 314 315 316 317 318
#endif

#ifdef __cplusplus
}  // extern "C"
}  // namespace libyuv
#endif