Libav
dpx.c
Go to the documentation of this file.
1 /*
2  * DPX (.dpx) image decoder
3  * Copyright (c) 2009 Jimmy Christensen
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "libavutil/intreadwrite.h"
23 #include "libavutil/imgutils.h"
24 #include "bytestream.h"
25 #include "avcodec.h"
26 #include "internal.h"
27 
28 static unsigned int read32(const uint8_t **ptr, int is_big)
29 {
30  unsigned int temp;
31  if (is_big) {
32  temp = AV_RB32(*ptr);
33  } else {
34  temp = AV_RL32(*ptr);
35  }
36  *ptr += 4;
37  return temp;
38 }
39 
40 static inline unsigned make_16bit(unsigned value)
41 {
42  // mask away invalid bits
43  value &= 0xFFC0;
44  // correctly expand to 16 bits
45  return value + (value >> 10);
46 }
47 
48 static int decode_frame(AVCodecContext *avctx,
49  void *data,
50  int *got_frame,
51  AVPacket *avpkt)
52 {
53  const uint8_t *buf = avpkt->data;
54  const uint8_t *buf_end = avpkt->data + avpkt->size;
55  int buf_size = avpkt->size;
56  AVFrame *const p = data;
57  uint8_t *ptr;
58 
59  unsigned int offset;
60  int magic_num, endian;
61  int x, y, ret;
62  int w, h, stride, bits_per_color, descriptor, elements, target_packet_size, source_packet_size;
63 
64  unsigned int rgbBuffer;
65 
66  if (avpkt->size <= 1634) {
67  av_log(avctx, AV_LOG_ERROR, "Packet too small for DPX header\n");
68  return AVERROR_INVALIDDATA;
69  }
70 
71  magic_num = AV_RB32(buf);
72  buf += 4;
73 
74  /* Check if the files "magic number" is "SDPX" which means it uses
75  * big-endian or XPDS which is for little-endian files */
76  if (magic_num == AV_RL32("SDPX")) {
77  endian = 0;
78  } else if (magic_num == AV_RB32("SDPX")) {
79  endian = 1;
80  } else {
81  av_log(avctx, AV_LOG_ERROR, "DPX marker not found\n");
82  return AVERROR_INVALIDDATA;
83  }
84 
85  offset = read32(&buf, endian);
86  if (avpkt->size <= offset) {
87  av_log(avctx, AV_LOG_ERROR, "Invalid data start offset\n");
88  return AVERROR_INVALIDDATA;
89  }
90  // Need to end in 0x304 offset from start of file
91  buf = avpkt->data + 0x304;
92  w = read32(&buf, endian);
93  h = read32(&buf, endian);
94 
95  // Need to end in 0x320 to read the descriptor
96  buf += 20;
97  descriptor = buf[0];
98 
99  // Need to end in 0x323 to read the bits per color
100  buf += 3;
101  avctx->bits_per_raw_sample =
102  bits_per_color = buf[0];
103 
104  buf += 825;
105  avctx->sample_aspect_ratio.num = read32(&buf, endian);
106  avctx->sample_aspect_ratio.den = read32(&buf, endian);
107 
108  switch (descriptor) {
109  case 51: // RGBA
110  elements = 4;
111  break;
112  case 50: // RGB
113  elements = 3;
114  break;
115  default:
116  av_log(avctx, AV_LOG_ERROR, "Unsupported descriptor %d\n", descriptor);
117  return AVERROR_INVALIDDATA;
118  }
119 
120  switch (bits_per_color) {
121  case 8:
122  if (elements == 4) {
123  avctx->pix_fmt = AV_PIX_FMT_RGBA;
124  } else {
125  avctx->pix_fmt = AV_PIX_FMT_RGB24;
126  }
127  source_packet_size = elements;
128  target_packet_size = elements;
129  break;
130  case 10:
131  avctx->pix_fmt = AV_PIX_FMT_RGB48;
132  target_packet_size = 6;
133  source_packet_size = 4;
134  break;
135  case 12:
136  case 16:
137  if (endian) {
138  avctx->pix_fmt = AV_PIX_FMT_RGB48BE;
139  } else {
140  avctx->pix_fmt = AV_PIX_FMT_RGB48LE;
141  }
142  target_packet_size = 6;
143  source_packet_size = elements * 2;
144  break;
145  default:
146  av_log(avctx, AV_LOG_ERROR, "Unsupported color depth : %d\n", bits_per_color);
147  return AVERROR_INVALIDDATA;
148  }
149 
150  if ((ret = ff_set_dimensions(avctx, w, h)) < 0)
151  return ret;
152 
153  if ((ret = ff_get_buffer(avctx, p, 0)) < 0) {
154  av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
155  return ret;
156  }
157 
158  // Move pointer to offset from start of file
159  buf = avpkt->data + offset;
160 
161  ptr = p->data[0];
162  stride = p->linesize[0];
163 
164  if (source_packet_size*avctx->width*avctx->height > buf_end - buf) {
165  av_log(avctx, AV_LOG_ERROR, "Overread buffer. Invalid header?\n");
166  return AVERROR_INVALIDDATA;
167  }
168  switch (bits_per_color) {
169  case 10:
170  for (x = 0; x < avctx->height; x++) {
171  uint16_t *dst = (uint16_t*)ptr;
172  for (y = 0; y < avctx->width; y++) {
173  rgbBuffer = read32(&buf, endian);
174  // Read out the 10-bit colors and convert to 16-bit
175  *dst++ = make_16bit(rgbBuffer >> 16);
176  *dst++ = make_16bit(rgbBuffer >> 6);
177  *dst++ = make_16bit(rgbBuffer << 4);
178  }
179  ptr += stride;
180  }
181  break;
182  case 8:
183  case 12: // Treat 12-bit as 16-bit
184  case 16:
185  if (source_packet_size == target_packet_size) {
186  for (x = 0; x < avctx->height; x++) {
187  memcpy(ptr, buf, target_packet_size*avctx->width);
188  ptr += stride;
189  buf += source_packet_size*avctx->width;
190  }
191  } else {
192  for (x = 0; x < avctx->height; x++) {
193  uint8_t *dst = ptr;
194  for (y = 0; y < avctx->width; y++) {
195  memcpy(dst, buf, target_packet_size);
196  dst += target_packet_size;
197  buf += source_packet_size;
198  }
199  ptr += stride;
200  }
201  }
202  break;
203  }
204 
205  *got_frame = 1;
206 
207  return buf_size;
208 }
209 
211  .name = "dpx",
212  .long_name = NULL_IF_CONFIG_SMALL("DPX image"),
213  .type = AVMEDIA_TYPE_VIDEO,
214  .id = AV_CODEC_ID_DPX,
215  .decode = decode_frame,
216  .capabilities = CODEC_CAP_DR1,
217 };