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"
7ffe76e5
 #include "libavutil/imgutils.h"
94d3d6a4
 #include "bytestream.h"
 #include "avcodec.h"
594d4d5d
 #include "internal.h"
94d3d6a4
 
 typedef struct DPXContext {
     AVFrame picture;
 } DPXContext;
 
 
 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;
     DPXContext *const s = avctx->priv_data;
     AVFrame *picture  = data;
     AVFrame *const p = &s->picture;
5cc5d9d5
     uint8_t *ptr[AV_NUM_DATA_POINTERS];
94d3d6a4
 
965efc16
     unsigned int offset;
     int magic_num, endian;
07f22d0b
     int x, y, i, ret;
05b73154
     int w, h, bits_per_color, descriptor, elements, packing, total_size;
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;
     }
94d3d6a4
     // Need to end in 0x304 offset from start of file
     buf = avpkt->data + 0x304;
     w = read32(&buf, endian);
     h = read32(&buf, endian);
07f22d0b
     if ((ret = av_image_check_size(w, h, 0, avctx)) < 0)
         return ret;
f2dc82b9
 
     if (w != avctx->width || h != avctx->height)
         avcodec_set_dimensions(avctx, w, h);
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++;
     packing = *((uint16_t*)buf);
94d3d6a4
 
05b73154
     buf += 824;
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
 
94d3d6a4
     switch (descriptor) {
         case 51: // RGBA
             elements = 4;
             break;
         case 50: // RGB
             elements = 3;
             break;
         default:
             av_log(avctx, AV_LOG_ERROR, "Unsupported descriptor %d\n", descriptor);
0c19b23b
             return AVERROR_INVALIDDATA;
94d3d6a4
     }
 
     switch (bits_per_color) {
         case 8:
             if (elements == 4) {
716d413c
                 avctx->pix_fmt = AV_PIX_FMT_RGBA;
94d3d6a4
             } else {
716d413c
                 avctx->pix_fmt = AV_PIX_FMT_RGB24;
94d3d6a4
             }
05b73154
             total_size = avctx->width * avctx->height * elements;
94d3d6a4
             break;
         case 10:
05b73154
             if (!packing) {
                 av_log(avctx, AV_LOG_ERROR, "Packing to 32bit required\n");
                 return -1;
             }
ac627b3d
             avctx->pix_fmt = AV_PIX_FMT_GBRP10;
151067bb
             total_size = (avctx->width * avctx->height * elements + 2) / 3 * 4;
94d3d6a4
             break;
         case 12:
05b73154
             if (!packing) {
                 av_log(avctx, AV_LOG_ERROR, "Packing to 16bit required\n");
                 return -1;
             }
5cc5d9d5
             if (endian) {
05b73154
                 avctx->pix_fmt = AV_PIX_FMT_GBRP12BE;
5cc5d9d5
             } else {
05b73154
                 avctx->pix_fmt = AV_PIX_FMT_GBRP12LE;
5cc5d9d5
             }
05b73154
             total_size = 2 * avctx->width * avctx->height * elements;
5cc5d9d5
             break;
94d3d6a4
         case 16:
             if (endian) {
ac627b3d
                 avctx->pix_fmt = elements == 4 ? AV_PIX_FMT_RGBA64BE : AV_PIX_FMT_RGB48BE;
94d3d6a4
             } else {
ac627b3d
                 avctx->pix_fmt = elements == 4 ? AV_PIX_FMT_RGBA64LE : AV_PIX_FMT_RGB48LE;
94d3d6a4
             }
05b73154
             total_size = 2 * avctx->width * avctx->height * elements;
94d3d6a4
             break;
         default:
             av_log(avctx, AV_LOG_ERROR, "Unsupported color depth : %d\n", bits_per_color);
0c19b23b
             return AVERROR_INVALIDDATA;
94d3d6a4
     }
 
     if (s->picture.data[0])
         avctx->release_buffer(avctx, &s->picture);
0c19b23b
     if ((ret = ff_get_buffer(avctx, p)) < 0) {
94d3d6a4
         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
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
 
cb85779d
     if (total_size + (int64_t)offset > avpkt->size) {
83613154
         av_log(avctx, AV_LOG_ERROR, "Overread buffer. Invalid header?\n");
0c19b23b
         return AVERROR_INVALIDDATA;
83613154
     }
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
             }
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++) {
                 *dst[2] = *((uint16_t*)buf);
                 *dst[2] = (*dst[2] >> 4) | (*dst[2] << 12);
                 dst[2]++;
                 buf += 2;
                 *dst[0] = *((uint16_t*)buf);
                 *dst[0] = (*dst[0] >> 4) | (*dst[0] << 12);
                 dst[0]++;
                 buf += 2;
                 *dst[1] = *((uint16_t*)buf);
                 *dst[1] = (*dst[1] >> 4) | (*dst[1] << 12);
                 dst[1]++;
                 buf += 2;
                 // For 12 bit, ignore alpha
                 if (elements == 4)
                     buf += 2;
94d3d6a4
             }
05b73154
             for (i = 0; i < 3; i++)
                 ptr[i] += p->linesize[i];
         }
         break;
     case 16:
         elements *= 2;
     case 8:
         for (x = 0; x < avctx->height; x++) {
             memcpy(ptr[0], buf, elements*avctx->width);
             ptr[0] += p->linesize[0];
             buf += elements*avctx->width;
         }
         break;
94d3d6a4
     }
 
     *picture   = s->picture;
df9b9567
     *got_frame = 1;
94d3d6a4
 
     return buf_size;
 }
 
 static av_cold int decode_init(AVCodecContext *avctx)
 {
     DPXContext *s = avctx->priv_data;
     avcodec_get_frame_defaults(&s->picture);
     avctx->coded_frame = &s->picture;
     return 0;
 }
 
 static av_cold int decode_end(AVCodecContext *avctx)
 {
     DPXContext *s = avctx->priv_data;
     if (s->picture.data[0])
         avctx->release_buffer(avctx, &s->picture);
 
     return 0;
 }
 
e7e2df27
 AVCodec ff_dpx_decoder = {
ec6402b7
     .name           = "dpx",
     .type           = AVMEDIA_TYPE_VIDEO,
36ef5369
     .id             = AV_CODEC_ID_DPX,
ec6402b7
     .priv_data_size = sizeof(DPXContext),
     .init           = decode_init,
     .close          = decode_end,
     .decode         = decode_frame,
00c3b67b
     .long_name      = NULL_IF_CONFIG_SMALL("DPX image"),
f174fbac
     .capabilities   = CODEC_CAP_DR1,
94d3d6a4
 };