libavcodec/dpx.c
94d3d6a4
 /*
  * DPX (.dpx) image decoder
  * Copyright (c) 2009 Jimmy Christensen
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 #include "libavutil/intreadwrite.h"
d90ba411
 #include "libavutil/intfloat.h"
7ffe76e5
 #include "libavutil/imgutils.h"
94d3d6a4
 #include "bytestream.h"
 #include "avcodec.h"
594d4d5d
 #include "internal.h"
94d3d6a4
 
8cb7d205
 static unsigned int read16(const uint8_t **ptr, int is_big)
 {
     unsigned int temp;
     if (is_big) {
         temp = AV_RB16(*ptr);
     } else {
         temp = AV_RL16(*ptr);
     }
     *ptr += 2;
     return temp;
 }
 
94d3d6a4
 static unsigned int read32(const uint8_t **ptr, int is_big)
 {
     unsigned int temp;
     if (is_big) {
         temp = AV_RB32(*ptr);
     } else {
         temp = AV_RL32(*ptr);
     }
     *ptr += 4;
     return temp;
 }
 
05b73154
 static uint16_t read10in32(const uint8_t **ptr, uint32_t * lbuf,
                                   int * n_datum, int is_big)
 {
     if (*n_datum)
         (*n_datum)--;
     else {
         *lbuf = read32(ptr, is_big);
         *n_datum = 2;
     }
 
     *lbuf = (*lbuf << 10) | (*lbuf >> 22);
 
     return *lbuf & 0x3FF;
 }
 
94d3d6a4
 static int decode_frame(AVCodecContext *avctx,
                         void *data,
df9b9567
                         int *got_frame,
94d3d6a4
                         AVPacket *avpkt)
 {
     const uint8_t *buf = avpkt->data;
     int buf_size       = avpkt->size;
759001c5
     AVFrame *const p = data;
5cc5d9d5
     uint8_t *ptr[AV_NUM_DATA_POINTERS];
94d3d6a4
 
965efc16
     unsigned int offset;
     int magic_num, endian;
4ba45c18
     int x, y, stride, i, ret;
     int w, h, bits_per_color, descriptor, elements, packing;
     int encoding, need_align = 0;
94d3d6a4
 
05b73154
     unsigned int rgbBuffer = 0;
     int n_datum = 0;
94d3d6a4
 
1b5282a2
     if (avpkt->size <= 1634) {
83613154
         av_log(avctx, AV_LOG_ERROR, "Packet too small for DPX header\n");
         return AVERROR_INVALIDDATA;
     }
 
94d3d6a4
     magic_num = AV_RB32(buf);
     buf += 4;
 
     /* Check if the files "magic number" is "SDPX" which means it uses
      * big-endian or XPDS which is for little-endian files */
     if (magic_num == AV_RL32("SDPX")) {
         endian = 0;
     } else if (magic_num == AV_RB32("SDPX")) {
         endian = 1;
     } else {
         av_log(avctx, AV_LOG_ERROR, "DPX marker not found\n");
0c19b23b
         return AVERROR_INVALIDDATA;
94d3d6a4
     }
 
     offset = read32(&buf, endian);
83613154
     if (avpkt->size <= offset) {
         av_log(avctx, AV_LOG_ERROR, "Invalid data start offset\n");
         return AVERROR_INVALIDDATA;
     }
7cdef77b
 
     // Check encryption
     buf = avpkt->data + 660;
     ret = read32(&buf, endian);
     if (ret != 0xFFFFFFFF) {
         avpriv_report_missing_feature(avctx, "Encryption");
         av_log(avctx, AV_LOG_WARNING, "The image is encrypted and may "
                "not properly decode.\n");
     }
 
94d3d6a4
     // Need to end in 0x304 offset from start of file
     buf = avpkt->data + 0x304;
     w = read32(&buf, endian);
     h = read32(&buf, endian);
f2dc82b9
 
4a356512
     if ((ret = ff_set_dimensions(avctx, w, h)) < 0)
         return ret;
94d3d6a4
 
     // Need to end in 0x320 to read the descriptor
     buf += 20;
     descriptor = buf[0];
 
     // Need to end in 0x323 to read the bits per color
     buf += 3;
c3abb551
     avctx->bits_per_raw_sample =
94d3d6a4
     bits_per_color = buf[0];
05b73154
     buf++;
fb726ef0
     packing = read16(&buf, endian);
23f105a8
     encoding = read16(&buf, endian);
94d3d6a4
 
a927276f
     if (packing > 1) {
0841652b
         avpriv_report_missing_feature(avctx, "Packing %d", packing);
a927276f
         return AVERROR_PATCHWELCOME;
     }
23f105a8
     if (encoding) {
0841652b
         avpriv_report_missing_feature(avctx, "Encoding %d", encoding);
23f105a8
         return AVERROR_PATCHWELCOME;
     }
 
     buf += 820;
1b5282a2
     avctx->sample_aspect_ratio.num = read32(&buf, endian);
     avctx->sample_aspect_ratio.den = read32(&buf, endian);
8b421fad
     if (avctx->sample_aspect_ratio.num > 0 && avctx->sample_aspect_ratio.den > 0)
         av_reduce(&avctx->sample_aspect_ratio.num, &avctx->sample_aspect_ratio.den,
                    avctx->sample_aspect_ratio.num,  avctx->sample_aspect_ratio.den,
                   0x10000);
     else
0abab003
         avctx->sample_aspect_ratio = (AVRational){ 0, 1 };
1b5282a2
 
e0e60c92
     if (offset >= 1724 + 4) {
         buf = avpkt->data + 1724;
         i = read32(&buf, endian);
         if(i) {
d90ba411
             AVRational q = av_d2q(av_int2float(i), 4096);
e0e60c92
             if (q.num > 0 && q.den > 0)
18255441
                 avctx->framerate = q;
e0e60c92
         }
     }
 
94d3d6a4
     switch (descriptor) {
23824b96
     case 6:  // Y
         elements = 1;
         break;
3abbf209
     case 52: // ABGR
fc83c99c
     case 51: // RGBA
2efaaf94
     case 103: // UYVA4444
fc83c99c
         elements = 4;
         break;
     case 50: // RGB
2efaaf94
     case 102: // UYV444
fc83c99c
         elements = 3;
         break;
2efaaf94
     case 100: // UYVY422
         elements = 2;
         break;
fc83c99c
     default:
         avpriv_report_missing_feature(avctx, "Descriptor %d", descriptor);
         return AVERROR_PATCHWELCOME;
94d3d6a4
     }
 
     switch (bits_per_color) {
fc83c99c
     case 8:
4ba45c18
         stride = avctx->width * elements;
fc83c99c
         break;
     case 10:
         if (!packing) {
             av_log(avctx, AV_LOG_ERROR, "Packing to 32bit required\n");
             return -1;
         }
4ba45c18
         stride = (avctx->width * elements + 2) / 3 * 4;
fc83c99c
         break;
     case 12:
         if (!packing) {
             av_log(avctx, AV_LOG_ERROR, "Packing to 16bit required\n");
             return -1;
         }
4ba45c18
         stride = 2 * avctx->width * elements;
fc83c99c
         break;
     case 16:
4ba45c18
         stride = 2 * avctx->width * elements;
fc83c99c
         break;
62078f25
     case 1:
     case 32:
     case 64:
         avpriv_report_missing_feature(avctx, "Depth %d", bits_per_color);
         return AVERROR_PATCHWELCOME;
fc83c99c
     default:
         return AVERROR_INVALIDDATA;
94d3d6a4
     }
 
4ba45c18
     // Table 3c: Runs will always break at scan line boundaries. Packing
     // will always break to the next 32-bit word at scan-line boundaries.
     // Unfortunately, the encoder produced invalid files, so attempt
     // to detect it
     need_align = FFALIGN(stride, 4);
     if (need_align*avctx->height + (int64_t)offset > avpkt->size) {
         // Alignment seems unappliable, try without
         if (stride*avctx->height + (int64_t)offset > avpkt->size) {
             av_log(avctx, AV_LOG_ERROR, "Overread buffer. Invalid header?\n");
             return AVERROR_INVALIDDATA;
         } else {
             av_log(avctx, AV_LOG_INFO, "Decoding DPX without scanline "
                    "alignment.\n");
             need_align = 0;
         }
     } else {
         need_align -= stride;
         stride = FFALIGN(stride, 4);
     }
 
e745dc2d
     switch (1000 * descriptor + 10 * bits_per_color + endian) {
23824b96
     case 6081:
     case 6080:
         avctx->pix_fmt = AV_PIX_FMT_GRAY8;
         break;
e745dc2d
     case 50081:
     case 50080:
         avctx->pix_fmt = AV_PIX_FMT_RGB24;
         break;
3abbf209
     case 52081:
     case 52080:
         avctx->pix_fmt = AV_PIX_FMT_ABGR;
         break;
e745dc2d
     case 51081:
     case 51080:
         avctx->pix_fmt = AV_PIX_FMT_RGBA;
         break;
     case 50100:
     case 51100:
     case 50101:
     case 51101:
         avctx->pix_fmt = AV_PIX_FMT_GBRP10;
         break;
     case 50120:
     case 51120:
     case 50121:
     case 51121:
         avctx->pix_fmt = AV_PIX_FMT_GBRP12;
         break;
23824b96
     case 6161:
         avctx->pix_fmt = AV_PIX_FMT_GRAY16BE;
         break;
     case 6160:
         avctx->pix_fmt = AV_PIX_FMT_GRAY16LE;
         break;
e745dc2d
     case 50161:
         avctx->pix_fmt = AV_PIX_FMT_RGB48BE;
         break;
     case 50160:
         avctx->pix_fmt = AV_PIX_FMT_RGB48LE;
         break;
     case 51161:
         avctx->pix_fmt = AV_PIX_FMT_RGBA64BE;
         break;
     case 51160:
         avctx->pix_fmt = AV_PIX_FMT_RGBA64LE;
         break;
2efaaf94
     case 100081:
         avctx->pix_fmt = AV_PIX_FMT_UYVY422;
         break;
     case 102081:
         avctx->pix_fmt = AV_PIX_FMT_YUV444P;
         break;
     case 103081:
         avctx->pix_fmt = AV_PIX_FMT_YUVA444P;
         break;
e745dc2d
     default:
         av_log(avctx, AV_LOG_ERROR, "Unsupported format\n");
         return AVERROR_PATCHWELCOME;
     }
 
9e500efd
     ff_set_sar(avctx, avctx->sample_aspect_ratio);
 
1ec94b0f
     if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
0c19b23b
         return ret;
94d3d6a4
 
     // Move pointer to offset from start of file
     buf =  avpkt->data + offset;
 
5cc5d9d5
     for (i=0; i<AV_NUM_DATA_POINTERS; i++)
         ptr[i] = p->data[i];
94d3d6a4
 
     switch (bits_per_color) {
05b73154
     case 10:
         for (x = 0; x < avctx->height; x++) {
             uint16_t *dst[3] = {(uint16_t*)ptr[0],
                                 (uint16_t*)ptr[1],
                                 (uint16_t*)ptr[2]};
             for (y = 0; y < avctx->width; y++) {
                 *dst[2]++ = read10in32(&buf, &rgbBuffer,
                                        &n_datum, endian);
                 *dst[0]++ = read10in32(&buf, &rgbBuffer,
                                        &n_datum, endian);
                 *dst[1]++ = read10in32(&buf, &rgbBuffer,
                                        &n_datum, endian);
                 // For 10 bit, ignore alpha
                 if (elements == 4)
                     read10in32(&buf, &rgbBuffer,
                                &n_datum, endian);
94d3d6a4
             }
2f284c01
             n_datum = 0;
05b73154
             for (i = 0; i < 3; i++)
                 ptr[i] += p->linesize[i];
         }
         break;
     case 12:
         for (x = 0; x < avctx->height; x++) {
             uint16_t *dst[3] = {(uint16_t*)ptr[0],
                                 (uint16_t*)ptr[1],
                                 (uint16_t*)ptr[2]};
             for (y = 0; y < avctx->width; y++) {
8cb7d205
                 *dst[2] = read16(&buf, endian) >> 4;
05b73154
                 dst[2]++;
8cb7d205
                 *dst[0] = read16(&buf, endian) >> 4;
05b73154
                 dst[0]++;
8cb7d205
                 *dst[1] = read16(&buf, endian) >> 4;
05b73154
                 dst[1]++;
                 // For 12 bit, ignore alpha
                 if (elements == 4)
                     buf += 2;
4ba45c18
                 // Jump to next aligned position
                 buf += need_align;
94d3d6a4
             }
05b73154
             for (i = 0; i < 3; i++)
                 ptr[i] += p->linesize[i];
         }
         break;
     case 16:
         elements *= 2;
     case 8:
2efaaf94
         if (   avctx->pix_fmt == AV_PIX_FMT_YUVA444P
             || avctx->pix_fmt == AV_PIX_FMT_YUV444P) {
             for (x = 0; x < avctx->height; x++) {
                 ptr[0] = p->data[0] + x * p->linesize[0];
                 ptr[1] = p->data[1] + x * p->linesize[1];
                 ptr[2] = p->data[2] + x * p->linesize[2];
                 ptr[3] = p->data[3] + x * p->linesize[3];
                 for (y = 0; y < avctx->width; y++) {
                     *ptr[1]++ = *buf++;
                     *ptr[0]++ = *buf++;
                     *ptr[2]++ = *buf++;
                     if (avctx->pix_fmt == AV_PIX_FMT_YUVA444P)
                         *ptr[3]++ = *buf++;
                 }
             }
         } else {
94ad38e2
         av_image_copy_plane(ptr[0], p->linesize[0],
4ba45c18
                             buf, stride,
94ad38e2
                             elements * avctx->width, avctx->height);
2efaaf94
         }
05b73154
         break;
94d3d6a4
     }
 
df9b9567
     *got_frame = 1;
94d3d6a4
 
     return buf_size;
 }
 
e7e2df27
 AVCodec ff_dpx_decoder = {
ec6402b7
     .name           = "dpx",
ee3d03bf
     .long_name      = NULL_IF_CONFIG_SMALL("DPX (Digital Picture Exchange) image"),
ec6402b7
     .type           = AVMEDIA_TYPE_VIDEO,
36ef5369
     .id             = AV_CODEC_ID_DPX,
ec6402b7
     .decode         = decode_frame,
f174fbac
     .capabilities   = CODEC_CAP_DR1,
94d3d6a4
 };