2 * Electronic Arts CMV Video Decoder
3 * Copyright (c) 2007-2008 Peter Ross
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 * Electronic Arts CMV Video Decoder
25 * by Peter Ross (pross@xvid.org)
27 * Technical details here:
28 * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_CMV
31 #include "libavutil/common.h"
32 #include "libavutil/intreadwrite.h"
33 #include "libavutil/imgutils.h"
37 typedef struct CmvContext
{
38 AVCodecContext
*avctx
;
39 AVFrame
*last_frame
; ///< last
40 AVFrame
*last2_frame
; ///< second-last
42 unsigned int palette
[AVPALETTE_COUNT
];
45 static av_cold
int cmv_decode_init(AVCodecContext
*avctx
){
46 CmvContext
*s
= avctx
->priv_data
;
49 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
51 s
->last_frame
= av_frame_alloc();
52 s
->last2_frame
= av_frame_alloc();
53 if (!s
->last_frame
|| !s
->last2_frame
) {
54 av_frame_free(&s
->last_frame
);
55 av_frame_free(&s
->last2_frame
);
56 return AVERROR(ENOMEM
);
62 static void cmv_decode_intra(CmvContext
* s
, AVFrame
*frame
,
63 const uint8_t *buf
, const uint8_t *buf_end
)
65 unsigned char *dst
= frame
->data
[0];
68 for (i
=0; i
< s
->avctx
->height
&& buf_end
- buf
>= s
->avctx
->width
; i
++) {
69 memcpy(dst
, buf
, s
->avctx
->width
);
70 dst
+= frame
->linesize
[0];
71 buf
+= s
->avctx
->width
;
75 static void cmv_motcomp(unsigned char *dst
, int dst_stride
,
76 const unsigned char *src
, int src_stride
,
78 int xoffset
, int yoffset
,
79 int width
, int height
){
85 if (i
+xoffset
>=0 && i
+xoffset
<width
&&
86 j
+yoffset
>=0 && j
+yoffset
<height
) {
87 dst
[j
*dst_stride
+ i
] = src
[(j
+yoffset
)*src_stride
+ i
+xoffset
];
89 dst
[j
*dst_stride
+ i
] = 0;
94 static void cmv_decode_inter(CmvContext
*s
, AVFrame
*frame
, const uint8_t *buf
,
95 const uint8_t *buf_end
)
97 const uint8_t *raw
= buf
+ (s
->avctx
->width
*s
->avctx
->height
/16);
101 for(y
=0; y
<s
->avctx
->height
/4; y
++)
102 for(x
=0; x
<s
->avctx
->width
/4 && buf_end
- buf
> i
; x
++) {
104 unsigned char *dst
= frame
->data
[0] + (y
*4)*frame
->linesize
[0] + x
*4;
105 if (raw
+16<buf_end
&& *raw
==0xFF) { /* intra */
108 memcpy(dst
+ frame
->linesize
[0], raw
+4, 4);
109 memcpy(dst
+ 2 * frame
->linesize
[0], raw
+8, 4);
110 memcpy(dst
+ 3 * frame
->linesize
[0], raw
+12, 4);
112 }else if(raw
<buf_end
) { /* inter using second-last frame as reference */
113 int xoffset
= (*raw
& 0xF) - 7;
114 int yoffset
= ((*raw
>> 4)) - 7;
115 if (s
->last2_frame
->data
[0])
116 cmv_motcomp(frame
->data
[0], frame
->linesize
[0],
117 s
->last2_frame
->data
[0], s
->last2_frame
->linesize
[0],
118 x
*4, y
*4, xoffset
, yoffset
, s
->avctx
->width
, s
->avctx
->height
);
121 }else{ /* inter using last frame as reference */
122 int xoffset
= (buf
[i
] & 0xF) - 7;
123 int yoffset
= ((buf
[i
] >> 4)) - 7;
124 if (s
->last_frame
->data
[0])
125 cmv_motcomp(frame
->data
[0], frame
->linesize
[0],
126 s
->last_frame
->data
[0], s
->last_frame
->linesize
[0],
127 x
*4, y
*4, xoffset
, yoffset
, s
->avctx
->width
, s
->avctx
->height
);
133 static int cmv_process_header(CmvContext
*s
, const uint8_t *buf
, const uint8_t *buf_end
)
135 int pal_start
, pal_count
, i
, ret
, fps
;
137 if(buf_end
- buf
< 16) {
138 av_log(s
->avctx
, AV_LOG_WARNING
, "truncated header\n");
139 return AVERROR_INVALIDDATA
;
142 s
->width
= AV_RL16(&buf
[4]);
143 s
->height
= AV_RL16(&buf
[6]);
145 if (s
->width
!= s
->avctx
->width
||
146 s
->height
!= s
->avctx
->height
) {
147 av_frame_unref(s
->last_frame
);
148 av_frame_unref(s
->last2_frame
);
151 ret
= ff_set_dimensions(s
->avctx
, s
->width
, s
->height
);
155 fps
= AV_RL16(&buf
[10]);
157 s
->avctx
->framerate
= (AVRational
){ fps
, 1 };
159 pal_start
= AV_RL16(&buf
[12]);
160 pal_count
= AV_RL16(&buf
[14]);
163 for (i
=pal_start
; i
<pal_start
+pal_count
&& i
<AVPALETTE_COUNT
&& buf_end
- buf
>= 3; i
++) {
164 s
->palette
[i
] = 0xFFU
<< 24 | AV_RB24(buf
);
171 #define EA_PREAMBLE_SIZE 8
172 #define MVIh_TAG MKTAG('M', 'V', 'I', 'h')
174 static int cmv_decode_frame(AVCodecContext
*avctx
,
175 void *data
, int *got_frame
,
178 const uint8_t *buf
= avpkt
->data
;
179 int buf_size
= avpkt
->size
;
180 CmvContext
*s
= avctx
->priv_data
;
181 const uint8_t *buf_end
= buf
+ buf_size
;
182 AVFrame
*frame
= data
;
185 if (buf_end
- buf
< EA_PREAMBLE_SIZE
)
186 return AVERROR_INVALIDDATA
;
188 if (AV_RL32(buf
)==MVIh_TAG
||AV_RB32(buf
)==MVIh_TAG
) {
189 unsigned size
= AV_RL32(buf
+ 4);
190 ret
= cmv_process_header(s
, buf
+EA_PREAMBLE_SIZE
, buf_end
);
193 if (size
> buf_end
- buf
- EA_PREAMBLE_SIZE
)
198 if (av_image_check_size(s
->width
, s
->height
, 0, s
->avctx
))
201 if ((ret
= ff_get_buffer(avctx
, frame
, AV_GET_BUFFER_FLAG_REF
)) < 0)
204 memcpy(frame
->data
[1], s
->palette
, AVPALETTE_SIZE
);
206 buf
+= EA_PREAMBLE_SIZE
;
207 if ((buf
[0]&1)) { // subtype
208 cmv_decode_inter(s
, frame
, buf
+2, buf_end
);
209 frame
->key_frame
= 0;
210 frame
->pict_type
= AV_PICTURE_TYPE_P
;
212 frame
->key_frame
= 1;
213 frame
->pict_type
= AV_PICTURE_TYPE_I
;
214 cmv_decode_intra(s
, frame
, buf
+2, buf_end
);
217 av_frame_unref(s
->last2_frame
);
218 av_frame_move_ref(s
->last2_frame
, s
->last_frame
);
219 if ((ret
= av_frame_ref(s
->last_frame
, frame
)) < 0)
227 static av_cold
int cmv_decode_end(AVCodecContext
*avctx
){
228 CmvContext
*s
= avctx
->priv_data
;
230 av_frame_free(&s
->last_frame
);
231 av_frame_free(&s
->last2_frame
);
236 AVCodec ff_eacmv_decoder
= {
238 .long_name
= NULL_IF_CONFIG_SMALL("Electronic Arts CMV video"),
239 .type
= AVMEDIA_TYPE_VIDEO
,
240 .id
= AV_CODEC_ID_CMV
,
241 .priv_data_size
= sizeof(CmvContext
),
242 .init
= cmv_decode_init
,
243 .close
= cmv_decode_end
,
244 .decode
= cmv_decode_frame
,
245 .capabilities
= CODEC_CAP_DR1
,