xref: /third_party/ffmpeg/libavcodec/cyuv.c (revision cabdff1a)
1/*
2 * Creative YUV (CYUV) Video Decoder
3 *   by Mike Melanson (melanson@pcisys.net)
4 * based on "Creative YUV (CYUV) stream format for AVI":
5 *   http://www.csse.monash.edu.au/~timf/videocodec/cyuv.txt
6 *
7 * Copyright (C) 2003 The FFmpeg project
8 *
9 * This file is part of FFmpeg.
10 *
11 * FFmpeg is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * FFmpeg is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with FFmpeg; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26/**
27 * @file
28 * Creative YUV (CYUV) Video Decoder.
29 */
30
31#include "config_components.h"
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "avcodec.h"
38#include "codec_internal.h"
39#include "internal.h"
40#include "libavutil/internal.h"
41
42
43typedef struct CyuvDecodeContext {
44    AVCodecContext *avctx;
45    int width, height;
46} CyuvDecodeContext;
47
48static av_cold int cyuv_decode_init(AVCodecContext *avctx)
49{
50    CyuvDecodeContext *s = avctx->priv_data;
51
52    s->avctx = avctx;
53    s->width = avctx->width;
54    /* width needs to be divisible by 4 for this codec to work */
55    if (s->width & 0x3)
56        return AVERROR_INVALIDDATA;
57    s->height = avctx->height;
58
59    return 0;
60}
61
62static int cyuv_decode_frame(AVCodecContext *avctx, AVFrame *frame,
63                             int *got_frame, AVPacket *avpkt)
64{
65    const uint8_t *buf = avpkt->data;
66    int buf_size = avpkt->size;
67    CyuvDecodeContext *s=avctx->priv_data;
68
69    unsigned char *y_plane;
70    unsigned char *u_plane;
71    unsigned char *v_plane;
72    int y_ptr;
73    int u_ptr;
74    int v_ptr;
75
76    /* prediction error tables (make it clear that they are signed values) */
77    const signed char *y_table = (const signed char*)buf +  0;
78    const signed char *u_table = (const signed char*)buf + 16;
79    const signed char *v_table = (const signed char*)buf + 32;
80
81    unsigned char y_pred, u_pred, v_pred;
82    int stream_ptr;
83    unsigned char cur_byte;
84    int pixel_groups;
85    int rawsize = s->height * FFALIGN(s->width,2) * 2;
86    int ret;
87
88    if (avctx->codec_id == AV_CODEC_ID_AURA) {
89        y_table = u_table;
90        u_table = v_table;
91    }
92    /* sanity check the buffer size: A buffer has 3x16-bytes tables
93     * followed by (height) lines each with 3 bytes to represent groups
94     * of 4 pixels. Thus, the total size of the buffer ought to be:
95     *    (3 * 16) + height * (width * 3 / 4) */
96    if (buf_size == 48 + s->height * (s->width * 3 / 4)) {
97        avctx->pix_fmt = AV_PIX_FMT_YUV411P;
98    } else if(buf_size == rawsize ) {
99        avctx->pix_fmt = AV_PIX_FMT_UYVY422;
100    } else {
101        av_log(avctx, AV_LOG_ERROR, "got a buffer with %d bytes when %d were expected\n",
102               buf_size, 48 + s->height * (s->width * 3 / 4));
103        return AVERROR_INVALIDDATA;
104    }
105
106    /* pixel data starts 48 bytes in, after 3x16-byte tables */
107    stream_ptr = 48;
108
109    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
110        return ret;
111
112    y_plane = frame->data[0];
113    u_plane = frame->data[1];
114    v_plane = frame->data[2];
115
116    if (buf_size == rawsize) {
117        int linesize = FFALIGN(s->width,2) * 2;
118        y_plane += frame->linesize[0] * s->height;
119        for (stream_ptr = 0; stream_ptr < rawsize; stream_ptr += linesize) {
120            y_plane -= frame->linesize[0];
121            memcpy(y_plane, buf+stream_ptr, linesize);
122        }
123    } else {
124
125    /* iterate through each line in the height */
126    for (y_ptr = 0, u_ptr = 0, v_ptr = 0;
127         y_ptr < (s->height * frame->linesize[0]);
128         y_ptr += frame->linesize[0] - s->width,
129         u_ptr += frame->linesize[1] - s->width / 4,
130         v_ptr += frame->linesize[2] - s->width / 4) {
131
132        /* reset predictors */
133        cur_byte = buf[stream_ptr++];
134        u_plane[u_ptr++] = u_pred = cur_byte & 0xF0;
135        y_plane[y_ptr++] = y_pred = (cur_byte & 0x0F) << 4;
136
137        cur_byte = buf[stream_ptr++];
138        v_plane[v_ptr++] = v_pred = cur_byte & 0xF0;
139        y_pred += y_table[cur_byte & 0x0F];
140        y_plane[y_ptr++] = y_pred;
141
142        cur_byte = buf[stream_ptr++];
143        y_pred += y_table[cur_byte & 0x0F];
144        y_plane[y_ptr++] = y_pred;
145        y_pred += y_table[(cur_byte & 0xF0) >> 4];
146        y_plane[y_ptr++] = y_pred;
147
148        /* iterate through the remaining pixel groups (4 pixels/group) */
149        pixel_groups = s->width / 4 - 1;
150        while (pixel_groups--) {
151
152            cur_byte = buf[stream_ptr++];
153            u_pred += u_table[(cur_byte & 0xF0) >> 4];
154            u_plane[u_ptr++] = u_pred;
155            y_pred += y_table[cur_byte & 0x0F];
156            y_plane[y_ptr++] = y_pred;
157
158            cur_byte = buf[stream_ptr++];
159            v_pred += v_table[(cur_byte & 0xF0) >> 4];
160            v_plane[v_ptr++] = v_pred;
161            y_pred += y_table[cur_byte & 0x0F];
162            y_plane[y_ptr++] = y_pred;
163
164            cur_byte = buf[stream_ptr++];
165            y_pred += y_table[cur_byte & 0x0F];
166            y_plane[y_ptr++] = y_pred;
167            y_pred += y_table[(cur_byte & 0xF0) >> 4];
168            y_plane[y_ptr++] = y_pred;
169
170        }
171    }
172    }
173
174    *got_frame = 1;
175
176    return buf_size;
177}
178
179#if CONFIG_AURA_DECODER
180const FFCodec ff_aura_decoder = {
181    .p.name         = "aura",
182    .p.long_name    = NULL_IF_CONFIG_SMALL("Auravision AURA"),
183    .p.type         = AVMEDIA_TYPE_VIDEO,
184    .p.id           = AV_CODEC_ID_AURA,
185    .priv_data_size = sizeof(CyuvDecodeContext),
186    .init           = cyuv_decode_init,
187    FF_CODEC_DECODE_CB(cyuv_decode_frame),
188    .p.capabilities = AV_CODEC_CAP_DR1,
189    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
190};
191#endif
192
193#if CONFIG_CYUV_DECODER
194const FFCodec ff_cyuv_decoder = {
195    .p.name         = "cyuv",
196    .p.long_name    = NULL_IF_CONFIG_SMALL("Creative YUV (CYUV)"),
197    .p.type         = AVMEDIA_TYPE_VIDEO,
198    .p.id           = AV_CODEC_ID_CYUV,
199    .priv_data_size = sizeof(CyuvDecodeContext),
200    .init           = cyuv_decode_init,
201    FF_CODEC_DECODE_CB(cyuv_decode_frame),
202    .p.capabilities = AV_CODEC_CAP_DR1,
203    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
204};
205#endif
206