/src/ffmpeg/libavcodec/roqvideodec.c
Line | Count | Source |
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 | | |
30 | | #include "avcodec.h" |
31 | | #include "bytestream.h" |
32 | | #include "codec_internal.h" |
33 | | #include "decode.h" |
34 | | #include "roqvideo.h" |
35 | | |
36 | | static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb) |
37 | 127k | { |
38 | 127k | unsigned int chunk_id = 0, chunk_arg = 0; |
39 | 127k | unsigned long chunk_size = 0; |
40 | 127k | int i, j, k, nv1, nv2, vqflg = 0, vqflg_pos = -1; |
41 | 127k | int vqid, xpos, ypos, xp, yp, x, y, mx, my; |
42 | 127k | roq_qcell *qcell; |
43 | 127k | int64_t chunk_start; |
44 | | |
45 | 1.31M | while (bytestream2_get_bytes_left(gb) >= 8) { |
46 | 1.18M | chunk_id = bytestream2_get_le16(gb); |
47 | 1.18M | chunk_size = bytestream2_get_le32(gb); |
48 | 1.18M | chunk_arg = bytestream2_get_le16(gb); |
49 | | |
50 | 1.18M | if(chunk_id == RoQ_QUAD_VQ) |
51 | 456 | break; |
52 | 1.18M | if(chunk_id == RoQ_QUAD_CODEBOOK) { |
53 | 1.27k | if((nv1 = chunk_arg >> 8) == 0) |
54 | 482 | nv1 = 256; |
55 | 1.27k | if((nv2 = chunk_arg & 0xff) == 0 && nv1 * 6 < chunk_size) |
56 | 332 | nv2 = 256; |
57 | 224k | for(i = 0; i < nv1; i++) { |
58 | 222k | ri->cb2x2[i].y[0] = bytestream2_get_byte(gb); |
59 | 222k | ri->cb2x2[i].y[1] = bytestream2_get_byte(gb); |
60 | 222k | ri->cb2x2[i].y[2] = bytestream2_get_byte(gb); |
61 | 222k | ri->cb2x2[i].y[3] = bytestream2_get_byte(gb); |
62 | 222k | ri->cb2x2[i].u = bytestream2_get_byte(gb); |
63 | 222k | ri->cb2x2[i].v = bytestream2_get_byte(gb); |
64 | 222k | } |
65 | 140k | for(i = 0; i < nv2; i++) |
66 | 696k | for(j = 0; j < 4; j++) |
67 | 557k | ri->cb4x4[i].idx[j] = bytestream2_get_byte(gb); |
68 | 1.27k | } |
69 | 1.18M | } |
70 | | |
71 | 127k | chunk_start = bytestream2_tell(gb); |
72 | 127k | xpos = ypos = 0; |
73 | | |
74 | 127k | if (chunk_size > bytestream2_get_bytes_left(gb)) { |
75 | 9.98k | av_log(ri->logctx, AV_LOG_ERROR, "Chunk does not fit in input buffer\n"); |
76 | 9.98k | chunk_size = bytestream2_get_bytes_left(gb); |
77 | 9.98k | } |
78 | | |
79 | 1.78M | while (bytestream2_tell(gb) < chunk_start + chunk_size) { |
80 | 4.97M | for (yp = ypos; yp < ypos + 16; yp += 8) |
81 | 9.95M | for (xp = xpos; xp < xpos + 16; xp += 8) { |
82 | 6.64M | if (bytestream2_tell(gb) >= chunk_start + chunk_size) { |
83 | 4.57k | av_log(ri->logctx, AV_LOG_VERBOSE, "Chunk is too short\n"); |
84 | 4.57k | return; |
85 | 4.57k | } |
86 | 6.63M | if (vqflg_pos < 0) { |
87 | 864k | vqflg = bytestream2_get_le16(gb); |
88 | 864k | vqflg_pos = 7; |
89 | 864k | } |
90 | 6.63M | vqid = (vqflg >> (vqflg_pos * 2)) & 0x3; |
91 | 6.63M | vqflg_pos--; |
92 | | |
93 | 6.63M | switch(vqid) { |
94 | 6.00M | case RoQ_ID_MOT: |
95 | 6.00M | break; |
96 | 232k | case RoQ_ID_FCC: { |
97 | 232k | int byte = bytestream2_get_byte(gb); |
98 | 232k | mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8)); |
99 | 232k | my = 8 - (byte & 0xf) - ((signed char) chunk_arg); |
100 | 232k | ff_apply_motion_8x8(ri, xp, yp, mx, my); |
101 | 232k | break; |
102 | 0 | } |
103 | 217k | case RoQ_ID_SLD: |
104 | 217k | qcell = ri->cb4x4 + bytestream2_get_byte(gb); |
105 | 217k | ff_apply_vector_4x4(ri, xp, yp, ri->cb2x2 + qcell->idx[0]); |
106 | 217k | ff_apply_vector_4x4(ri, xp + 4, yp, ri->cb2x2 + qcell->idx[1]); |
107 | 217k | ff_apply_vector_4x4(ri, xp, yp + 4, ri->cb2x2 + qcell->idx[2]); |
108 | 217k | ff_apply_vector_4x4(ri, xp + 4, yp + 4, ri->cb2x2 + qcell->idx[3]); |
109 | 217k | break; |
110 | 178k | case RoQ_ID_CCC: |
111 | 885k | for (k = 0; k < 4; k++) { |
112 | 709k | x = xp; y = yp; |
113 | 709k | if(k & 0x01) x += 4; |
114 | 709k | if(k & 0x02) y += 4; |
115 | | |
116 | 709k | if (bytestream2_tell(gb) >= chunk_start + chunk_size) { |
117 | 3.05k | av_log(ri->logctx, AV_LOG_VERBOSE, "Chunk is too short\n"); |
118 | 3.05k | return; |
119 | 3.05k | } |
120 | 706k | if (vqflg_pos < 0) { |
121 | 58.8k | vqflg = bytestream2_get_le16(gb); |
122 | 58.8k | vqflg_pos = 7; |
123 | 58.8k | } |
124 | 706k | vqid = (vqflg >> (vqflg_pos * 2)) & 0x3; |
125 | 706k | vqflg_pos--; |
126 | 706k | switch(vqid) { |
127 | 217k | case RoQ_ID_MOT: |
128 | 217k | break; |
129 | 90.8k | case RoQ_ID_FCC: { |
130 | 90.8k | int byte = bytestream2_get_byte(gb); |
131 | 90.8k | mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8)); |
132 | 90.8k | my = 8 - (byte & 0xf) - ((signed char) chunk_arg); |
133 | 90.8k | ff_apply_motion_4x4(ri, x, y, mx, my); |
134 | 90.8k | break; |
135 | 0 | } |
136 | 114k | case RoQ_ID_SLD: |
137 | 114k | qcell = ri->cb4x4 + bytestream2_get_byte(gb); |
138 | 114k | ff_apply_vector_2x2(ri, x, y, ri->cb2x2 + qcell->idx[0]); |
139 | 114k | ff_apply_vector_2x2(ri, x + 2, y, ri->cb2x2 + qcell->idx[1]); |
140 | 114k | ff_apply_vector_2x2(ri, x, y + 2, ri->cb2x2 + qcell->idx[2]); |
141 | 114k | ff_apply_vector_2x2(ri, x + 2, y + 2, ri->cb2x2 + qcell->idx[3]); |
142 | 114k | break; |
143 | 282k | case RoQ_ID_CCC: |
144 | 282k | ff_apply_vector_2x2(ri, x, y, ri->cb2x2 + bytestream2_get_byte(gb)); |
145 | 282k | ff_apply_vector_2x2(ri, x + 2, y, ri->cb2x2 + bytestream2_get_byte(gb)); |
146 | 282k | ff_apply_vector_2x2(ri, x, y + 2, ri->cb2x2 + bytestream2_get_byte(gb)); |
147 | 282k | ff_apply_vector_2x2(ri, x + 2, y + 2, ri->cb2x2 + bytestream2_get_byte(gb)); |
148 | 282k | break; |
149 | 706k | } |
150 | 706k | } |
151 | 175k | break; |
152 | 175k | default: |
153 | 0 | av_assert2(0); |
154 | 6.63M | } |
155 | 6.63M | } |
156 | | |
157 | 1.65M | xpos += 16; |
158 | 1.65M | if (xpos >= ri->width) { |
159 | 275k | xpos -= ri->width; |
160 | 275k | ypos += 16; |
161 | 275k | } |
162 | 1.65M | if(ypos >= ri->height) |
163 | 290 | break; |
164 | 1.65M | } |
165 | 127k | } |
166 | | |
167 | | |
168 | | static av_cold int roq_decode_init(AVCodecContext *avctx) |
169 | 1.03k | { |
170 | 1.03k | RoqContext *s = avctx->priv_data; |
171 | | |
172 | 1.03k | s->logctx = avctx; |
173 | | |
174 | 1.03k | if (avctx->width % 16 || avctx->height % 16) { |
175 | 39 | avpriv_request_sample(avctx, "Dimensions not being a multiple of 16"); |
176 | 39 | return AVERROR_PATCHWELCOME; |
177 | 39 | } |
178 | | |
179 | 1.00k | s->width = avctx->width; |
180 | 1.00k | s->height = avctx->height; |
181 | | |
182 | 1.00k | s->last_frame = av_frame_alloc(); |
183 | 1.00k | s->current_frame = av_frame_alloc(); |
184 | 1.00k | if (!s->current_frame || !s->last_frame) |
185 | 0 | return AVERROR(ENOMEM); |
186 | | |
187 | 1.00k | avctx->pix_fmt = AV_PIX_FMT_YUVJ444P; |
188 | 1.00k | avctx->color_range = AVCOL_RANGE_JPEG; |
189 | | |
190 | 1.00k | return 0; |
191 | 1.00k | } |
192 | | |
193 | | static int roq_decode_frame(AVCodecContext *avctx, AVFrame *rframe, |
194 | | int *got_frame, AVPacket *avpkt) |
195 | 1.21M | { |
196 | 1.21M | const uint8_t *buf = avpkt->data; |
197 | 1.21M | int buf_size = avpkt->size; |
198 | 1.21M | RoqContext *s = avctx->priv_data; |
199 | 1.21M | int copy = !s->current_frame->data[0] && s->last_frame->data[0]; |
200 | 1.21M | GetByteContext gb; |
201 | 1.21M | int ret; |
202 | | |
203 | 1.21M | if ((ret = ff_reget_buffer(avctx, s->current_frame, 0)) < 0) |
204 | 1.09M | return ret; |
205 | | |
206 | 127k | if (copy) { |
207 | 305 | ret = av_frame_copy(s->current_frame, s->last_frame); |
208 | 305 | if (ret < 0) |
209 | 0 | return ret; |
210 | 305 | } |
211 | | |
212 | 127k | bytestream2_init(&gb, buf, buf_size); |
213 | 127k | roqvideo_decode_frame(s, &gb); |
214 | | |
215 | 127k | if ((ret = av_frame_ref(rframe, s->current_frame)) < 0) |
216 | 0 | return ret; |
217 | 127k | *got_frame = 1; |
218 | | |
219 | | /* shuffle frames */ |
220 | 127k | FFSWAP(AVFrame *, s->current_frame, s->last_frame); |
221 | | |
222 | 127k | return buf_size; |
223 | 127k | } |
224 | | |
225 | | static av_cold int roq_decode_end(AVCodecContext *avctx) |
226 | 1.03k | { |
227 | 1.03k | RoqContext *s = avctx->priv_data; |
228 | | |
229 | 1.03k | av_frame_free(&s->current_frame); |
230 | 1.03k | av_frame_free(&s->last_frame); |
231 | | |
232 | 1.03k | return 0; |
233 | 1.03k | } |
234 | | |
235 | | const FFCodec ff_roq_decoder = { |
236 | | .p.name = "roqvideo", |
237 | | CODEC_LONG_NAME("id RoQ video"), |
238 | | .p.type = AVMEDIA_TYPE_VIDEO, |
239 | | .p.id = AV_CODEC_ID_ROQ, |
240 | | .priv_data_size = sizeof(RoqContext), |
241 | | .init = roq_decode_init, |
242 | | .close = roq_decode_end, |
243 | | FF_CODEC_DECODE_CB(roq_decode_frame), |
244 | | .p.capabilities = AV_CODEC_CAP_DR1, |
245 | | .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, |
246 | | }; |