blob: 0ab7d399d6750aa37835b25dedf3a77d547ab83e
1 | /* |
2 | * Copyright (C) 2003 The FFmpeg project |
3 | * |
4 | * This file is part of FFmpeg. |
5 | * |
6 | * FFmpeg is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * FFmpeg is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with FFmpeg; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | */ |
20 | |
21 | /** |
22 | * @file |
23 | * id RoQ Video Decoder by Dr. Tim Ferguson |
24 | * For more information about the id RoQ format, visit: |
25 | * http://www.csse.monash.edu.au/~timf/ |
26 | */ |
27 | |
28 | #include "libavutil/avassert.h" |
29 | #include "libavutil/imgutils.h" |
30 | |
31 | #include "avcodec.h" |
32 | #include "bytestream.h" |
33 | #include "internal.h" |
34 | #include "roqvideo.h" |
35 | |
36 | static void roqvideo_decode_frame(RoqContext *ri) |
37 | { |
38 | unsigned int chunk_id = 0, chunk_arg = 0; |
39 | unsigned long chunk_size = 0; |
40 | int i, j, k, nv1, nv2, vqflg = 0, vqflg_pos = -1; |
41 | int vqid, xpos, ypos, xp, yp, x, y, mx, my; |
42 | int frame_stats[2][4] = {{0},{0}}; |
43 | roq_qcell *qcell; |
44 | int64_t chunk_start; |
45 | |
46 | while (bytestream2_get_bytes_left(&ri->gb) >= 8) { |
47 | chunk_id = bytestream2_get_le16(&ri->gb); |
48 | chunk_size = bytestream2_get_le32(&ri->gb); |
49 | chunk_arg = bytestream2_get_le16(&ri->gb); |
50 | |
51 | if(chunk_id == RoQ_QUAD_VQ) |
52 | break; |
53 | if(chunk_id == RoQ_QUAD_CODEBOOK) { |
54 | if((nv1 = chunk_arg >> 8) == 0) |
55 | nv1 = 256; |
56 | if((nv2 = chunk_arg & 0xff) == 0 && nv1 * 6 < chunk_size) |
57 | nv2 = 256; |
58 | for(i = 0; i < nv1; i++) { |
59 | ri->cb2x2[i].y[0] = bytestream2_get_byte(&ri->gb); |
60 | ri->cb2x2[i].y[1] = bytestream2_get_byte(&ri->gb); |
61 | ri->cb2x2[i].y[2] = bytestream2_get_byte(&ri->gb); |
62 | ri->cb2x2[i].y[3] = bytestream2_get_byte(&ri->gb); |
63 | ri->cb2x2[i].u = bytestream2_get_byte(&ri->gb); |
64 | ri->cb2x2[i].v = bytestream2_get_byte(&ri->gb); |
65 | } |
66 | for(i = 0; i < nv2; i++) |
67 | for(j = 0; j < 4; j++) |
68 | ri->cb4x4[i].idx[j] = bytestream2_get_byte(&ri->gb); |
69 | } |
70 | } |
71 | |
72 | chunk_start = bytestream2_tell(&ri->gb); |
73 | xpos = ypos = 0; |
74 | |
75 | if (chunk_size > bytestream2_get_bytes_left(&ri->gb)) { |
76 | av_log(ri->avctx, AV_LOG_ERROR, "Chunk does not fit in input buffer\n"); |
77 | chunk_size = bytestream2_get_bytes_left(&ri->gb); |
78 | } |
79 | |
80 | while (bytestream2_tell(&ri->gb) < chunk_start + chunk_size) { |
81 | for (yp = ypos; yp < ypos + 16; yp += 8) |
82 | for (xp = xpos; xp < xpos + 16; xp += 8) { |
83 | if (bytestream2_tell(&ri->gb) >= chunk_start + chunk_size) { |
84 | av_log(ri->avctx, AV_LOG_VERBOSE, "Chunk is too short\n"); |
85 | return; |
86 | } |
87 | if (vqflg_pos < 0) { |
88 | vqflg = bytestream2_get_le16(&ri->gb); |
89 | vqflg_pos = 7; |
90 | } |
91 | vqid = (vqflg >> (vqflg_pos * 2)) & 0x3; |
92 | frame_stats[0][vqid]++; |
93 | vqflg_pos--; |
94 | |
95 | switch(vqid) { |
96 | case RoQ_ID_MOT: |
97 | break; |
98 | case RoQ_ID_FCC: { |
99 | int byte = bytestream2_get_byte(&ri->gb); |
100 | mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8)); |
101 | my = 8 - (byte & 0xf) - ((signed char) chunk_arg); |
102 | ff_apply_motion_8x8(ri, xp, yp, mx, my); |
103 | break; |
104 | } |
105 | case RoQ_ID_SLD: |
106 | qcell = ri->cb4x4 + bytestream2_get_byte(&ri->gb); |
107 | ff_apply_vector_4x4(ri, xp, yp, ri->cb2x2 + qcell->idx[0]); |
108 | ff_apply_vector_4x4(ri, xp + 4, yp, ri->cb2x2 + qcell->idx[1]); |
109 | ff_apply_vector_4x4(ri, xp, yp + 4, ri->cb2x2 + qcell->idx[2]); |
110 | ff_apply_vector_4x4(ri, xp + 4, yp + 4, ri->cb2x2 + qcell->idx[3]); |
111 | break; |
112 | case RoQ_ID_CCC: |
113 | for (k = 0; k < 4; k++) { |
114 | x = xp; y = yp; |
115 | if(k & 0x01) x += 4; |
116 | if(k & 0x02) y += 4; |
117 | |
118 | if (bytestream2_tell(&ri->gb) >= chunk_start + chunk_size) { |
119 | av_log(ri->avctx, AV_LOG_VERBOSE, "Chunk is too short\n"); |
120 | return; |
121 | } |
122 | if (vqflg_pos < 0) { |
123 | vqflg = bytestream2_get_le16(&ri->gb); |
124 | vqflg_pos = 7; |
125 | } |
126 | vqid = (vqflg >> (vqflg_pos * 2)) & 0x3; |
127 | frame_stats[1][vqid]++; |
128 | vqflg_pos--; |
129 | switch(vqid) { |
130 | case RoQ_ID_MOT: |
131 | break; |
132 | case RoQ_ID_FCC: { |
133 | int byte = bytestream2_get_byte(&ri->gb); |
134 | mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8)); |
135 | my = 8 - (byte & 0xf) - ((signed char) chunk_arg); |
136 | ff_apply_motion_4x4(ri, x, y, mx, my); |
137 | break; |
138 | } |
139 | case RoQ_ID_SLD: |
140 | qcell = ri->cb4x4 + bytestream2_get_byte(&ri->gb); |
141 | ff_apply_vector_2x2(ri, x, y, ri->cb2x2 + qcell->idx[0]); |
142 | ff_apply_vector_2x2(ri, x + 2, y, ri->cb2x2 + qcell->idx[1]); |
143 | ff_apply_vector_2x2(ri, x, y + 2, ri->cb2x2 + qcell->idx[2]); |
144 | ff_apply_vector_2x2(ri, x + 2, y + 2, ri->cb2x2 + qcell->idx[3]); |
145 | break; |
146 | case RoQ_ID_CCC: |
147 | ff_apply_vector_2x2(ri, x, y, ri->cb2x2 + bytestream2_get_byte(&ri->gb)); |
148 | ff_apply_vector_2x2(ri, x + 2, y, ri->cb2x2 + bytestream2_get_byte(&ri->gb)); |
149 | ff_apply_vector_2x2(ri, x, y + 2, ri->cb2x2 + bytestream2_get_byte(&ri->gb)); |
150 | ff_apply_vector_2x2(ri, x + 2, y + 2, ri->cb2x2 + bytestream2_get_byte(&ri->gb)); |
151 | break; |
152 | } |
153 | } |
154 | break; |
155 | default: |
156 | av_assert2(0); |
157 | } |
158 | } |
159 | |
160 | xpos += 16; |
161 | if (xpos >= ri->width) { |
162 | xpos -= ri->width; |
163 | ypos += 16; |
164 | } |
165 | if(ypos >= ri->height) |
166 | break; |
167 | } |
168 | } |
169 | |
170 | |
171 | static av_cold int roq_decode_init(AVCodecContext *avctx) |
172 | { |
173 | RoqContext *s = avctx->priv_data; |
174 | |
175 | s->avctx = avctx; |
176 | |
177 | if (avctx->width % 16 || avctx->height % 16) { |
178 | avpriv_request_sample(avctx, "Dimensions not being a multiple of 16"); |
179 | return AVERROR_PATCHWELCOME; |
180 | } |
181 | |
182 | s->width = avctx->width; |
183 | s->height = avctx->height; |
184 | |
185 | s->last_frame = av_frame_alloc(); |
186 | s->current_frame = av_frame_alloc(); |
187 | if (!s->current_frame || !s->last_frame) { |
188 | av_frame_free(&s->current_frame); |
189 | av_frame_free(&s->last_frame); |
190 | return AVERROR(ENOMEM); |
191 | } |
192 | |
193 | avctx->pix_fmt = AV_PIX_FMT_YUVJ444P; |
194 | avctx->color_range = AVCOL_RANGE_JPEG; |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static int roq_decode_frame(AVCodecContext *avctx, |
200 | void *data, int *got_frame, |
201 | AVPacket *avpkt) |
202 | { |
203 | const uint8_t *buf = avpkt->data; |
204 | int buf_size = avpkt->size; |
205 | RoqContext *s = avctx->priv_data; |
206 | int copy = !s->current_frame->data[0] && s->last_frame->data[0]; |
207 | int ret; |
208 | |
209 | if ((ret = ff_reget_buffer(avctx, s->current_frame)) < 0) |
210 | return ret; |
211 | |
212 | if (copy) { |
213 | ret = av_frame_copy(s->current_frame, s->last_frame); |
214 | if (ret < 0) |
215 | return ret; |
216 | } |
217 | |
218 | bytestream2_init(&s->gb, buf, buf_size); |
219 | roqvideo_decode_frame(s); |
220 | |
221 | if ((ret = av_frame_ref(data, s->current_frame)) < 0) |
222 | return ret; |
223 | *got_frame = 1; |
224 | |
225 | /* shuffle frames */ |
226 | FFSWAP(AVFrame *, s->current_frame, s->last_frame); |
227 | |
228 | return buf_size; |
229 | } |
230 | |
231 | static av_cold int roq_decode_end(AVCodecContext *avctx) |
232 | { |
233 | RoqContext *s = avctx->priv_data; |
234 | |
235 | av_frame_free(&s->current_frame); |
236 | av_frame_free(&s->last_frame); |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | AVCodec ff_roq_decoder = { |
242 | .name = "roqvideo", |
243 | .long_name = NULL_IF_CONFIG_SMALL("id RoQ video"), |
244 | .type = AVMEDIA_TYPE_VIDEO, |
245 | .id = AV_CODEC_ID_ROQ, |
246 | .priv_data_size = sizeof(RoqContext), |
247 | .init = roq_decode_init, |
248 | .close = roq_decode_end, |
249 | .decode = roq_decode_frame, |
250 | .capabilities = AV_CODEC_CAP_DR1, |
251 | }; |
252 |