copy/paste caused some issues here
[swfdec.git] / libswfdec / swfdec_codec_video.c
blobe088d001b123e620236a29376d73848ef4ec099a
1 /* Swfdec
2 * Copyright (C) 2007 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <liboil/liboil.h>
25 #include "swfdec_codec_video.h"
26 #include "swfdec_color.h"
27 #include "swfdec_debug.h"
28 #include "swfdec_internal.h"
30 /**
31 * swfdec_video_decoder_new:
32 * @codec: #SwfdecVideoCodec to create the #SwfdecVideoDecoder for
34 * Creates a new decoder to decode videos of type @codec. If no suitable
35 * decoder could be created, %NULL is returned.
37 * Returns:
38 **/
39 SwfdecVideoDecoder *
40 swfdec_video_decoder_new (SwfdecVideoCodec codec)
42 SwfdecVideoDecoder *ret;
44 ret = swfdec_video_decoder_screen_new (codec);
45 if (ret == NULL)
46 ret = swfdec_video_decoder_vp6_alpha_new (codec);
47 #ifdef HAVE_FFMPEG
48 if (ret == NULL)
49 ret = swfdec_video_decoder_ffmpeg_new (codec);
50 #endif
51 #ifdef HAVE_GST
52 if (ret == NULL)
53 ret = swfdec_video_decoder_gst_new (codec);
54 #endif
56 if (ret != NULL) {
57 ret->codec = codec;
58 g_return_val_if_fail (ret->decode, ret);
59 g_return_val_if_fail (ret->free, ret);
60 } else {
61 SWFDEC_WARNING ("no decoder found for codec %u", (guint) codec);
63 return ret;
66 /**
67 * swfdec_video_decoder_free:
68 * @decoder: a #SwfdecVideoDecoder
70 * Frees the given @decoder and all associated ressources.
71 **/
72 void
73 swfdec_video_decoder_free (SwfdecVideoDecoder *decoder)
75 g_return_if_fail (decoder);
77 decoder->free (decoder);
80 #define oil_argb(a,r,g,b) (((a) << 24) | ((r) << 16) | ((g) << 8) | b)
81 static gint16 jfif_matrix[24] = {
82 0, 0, -8192, -8192,
83 16384, 0, 0, 0,
84 0, 16384, 16384, 16384,
85 0, 0, -5638, 29032,
86 0, 22970, -11700, 0,
87 0, 0, 0, 0
90 static void
91 yuv_mux (guint32 *dest, const guint8 *src_y, const guint8 *src_u, const guint8 *src_v,
92 int n)
94 int i;
95 for (i = 0; i < n; i++) {
96 dest[i] = oil_argb(255, src_y[i], src_u[i], src_v[i]);
100 static void
101 upsample (guint8 *d, guint8 *s, int n)
103 int i;
105 d[0] = s[0];
107 for (i = 0; i < n-3; i+=2) {
108 d[i + 1] = (3*s[i/2] + s[i/2+1] + 2)>>2;
109 d[i + 2] = (s[i/2] + 3*s[i/2+1] + 2)>>2;
112 if (n&1) {
113 i = n-3;
114 d[n-2] = s[n/2];
115 d[n-1] = s[n/2];
116 } else {
117 d[n-1] = s[n/2-1];
121 static guint8 *
122 swfdec_video_i420_to_rgb (SwfdecVideoImage *image)
124 guint32 *tmp;
125 guint8 *tmp_u;
126 guint8 *tmp_v;
127 guint8 *tmp1;
128 guint32 *argb_image;
129 const guint8 *yp, *up, *vp;
130 guint32 *argbp;
131 int j;
132 guint halfwidth;
133 int halfheight;
135 halfwidth = (image->width + 1)>>1;
136 tmp = g_malloc (4 * image->width * image->height);
137 tmp_u = g_malloc (image->width);
138 tmp_v = g_malloc (image->width);
139 tmp1 = g_malloc (halfwidth);
140 argb_image = g_malloc (4 * image->width * image->height);
142 yp = image->plane[0];
143 up = image->plane[1];
144 vp = image->plane[2];
145 argbp = argb_image;
146 halfheight = (image->height+1)>>1;
147 for(j=0;(guint)j<image->height;j++){
148 guint32 weight = 192 - 128*(j&1);
150 oil_merge_linear_u8(tmp1,
151 up + image->rowstride[1] * CLAMP((j-1)/2,0,halfheight-1),
152 up + image->rowstride[1] * CLAMP((j+1)/2,0,halfheight-1),
153 &weight, halfwidth);
154 upsample (tmp_u, tmp1, image->width);
155 oil_merge_linear_u8(tmp1,
156 vp + image->rowstride[2] * CLAMP((j-1)/2,0,halfheight-1),
157 vp + image->rowstride[2] * CLAMP((j+1)/2,0,halfheight-1),
158 &weight, halfwidth);
159 upsample (tmp_v, tmp1, image->width);
161 yuv_mux (tmp, yp, tmp_u, tmp_v, image->width);
162 oil_colorspace_argb(argbp, tmp, jfif_matrix, image->width);
163 yp += image->rowstride[0];
164 argbp += image->width;
166 g_free(tmp);
167 g_free(tmp_u);
168 g_free(tmp_v);
169 g_free(tmp1);
170 return (unsigned char *)argb_image;
173 /* FIXME: use liboil for this */
174 static void
175 swfdec_video_codec_apply_mask (guint8 *data, guint rowstride, const guint8 *mask,
176 guint mask_rowstride, guint width, guint height)
178 const guint8 *in;
179 guint8 *out;
180 guint x, y;
182 data += SWFDEC_COLOR_INDEX_ALPHA;
183 for (y = 0; y < height; y++) {
184 in = mask;
185 out = data;
186 for (x = 0; x < width; x++) {
187 *out = *in;
188 out += 4;
189 in++;
191 mask += mask_rowstride;
192 data += rowstride;
197 * swfdec_video_decoder_decode:
198 * @decoder: a #SwfdecVideoDecoder
199 * @buffer: buffer to decode
201 * Decodes the given buffer into an image surface.
203 * Returns: a new cairo image surface or %NULL on error.
205 cairo_surface_t *
206 swfdec_video_decoder_decode (SwfdecVideoDecoder *decoder, SwfdecBuffer *buffer)
208 SwfdecVideoImage image;
209 static const cairo_user_data_key_t key;
210 cairo_surface_t *surface;
211 guint8 *data;
212 guint rowstride;
214 g_return_val_if_fail (decoder != NULL, NULL);
215 g_return_val_if_fail (buffer != NULL, NULL);
217 if (!decoder->decode (decoder, buffer, &image)) {
218 SWFDEC_ERROR ("failed to decode video");
219 return NULL;
221 g_assert (image.width != 0 && image.height != 0);
222 /* FIXME: use cairo for all of this when cairo accelerates it */
223 if (swfdec_video_codec_get_format (decoder->codec) == SWFDEC_VIDEO_FORMAT_I420) {
224 data = swfdec_video_i420_to_rgb (&image);
225 if (data == NULL) {
226 SWFDEC_ERROR ("I420 => RGB conversion failed");
227 return NULL;
229 rowstride = image.width * 4;
230 } else {
231 rowstride = image.rowstride[0];
232 data = g_memdup (image.plane[0], rowstride * image.height);
234 if (image.mask) {
235 swfdec_video_codec_apply_mask (data, image.width * 4, image.mask,
236 image.mask_rowstride, image.width, image.height);
238 surface = cairo_image_surface_create_for_data (data,
239 image.mask ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
240 image.width, image.height, rowstride);
241 if (cairo_surface_status (surface)) {
242 SWFDEC_ERROR ("failed to create surface: %s",
243 cairo_status_to_string (cairo_surface_status (surface)));
244 cairo_surface_destroy (surface);
245 return NULL;
247 cairo_surface_set_user_data (surface, &key, data,
248 (cairo_destroy_func_t) g_free);
249 return surface;
253 * swfdec_video_codec_get_format:
254 * @codec: codec to check
256 * Returns the output format used for this codec. Video codecs must use these
257 * codecs when decoding video.
259 * Returns: the output format to use for this format
261 SwfdecVideoFormat
262 swfdec_video_codec_get_format (SwfdecVideoCodec codec)
264 switch (codec) {
265 case SWFDEC_VIDEO_CODEC_H263:
266 case SWFDEC_VIDEO_CODEC_VP6:
267 case SWFDEC_VIDEO_CODEC_VP6_ALPHA:
268 return SWFDEC_VIDEO_FORMAT_I420;
269 default:
270 return SWFDEC_VIDEO_FORMAT_RGBA;