From 9e4b3ccf30b09f28b1842494f81389a6ea2aa8fa Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 25 Sep 2007 16:11:58 +0200 Subject: [PATCH] rework video handling in particular: - Video output handling has been changed. Codecs are now assigned a format that the decoders have to decode to (I420 for H263/VP6, RGB for screen video). The decoded video is then manually colorspace converted by Swfdec. (NB: This is currently only implemented using FFmpeg.) - Decoded video images are returned in a much more verbose way. - Using these new features, VP6 alpha was implemented. Another thing this patch should ease is transition to colorspace conversion by Cairo (or at least by GL). --- libswfdec/Makefile.am | 1 + libswfdec/swfdec_codec_ffmpeg.c | 105 ++++++++++++++++++++++------------- libswfdec/swfdec_codec_gst.c | 104 +++++++++++++++++++++------------- libswfdec/swfdec_codec_screen.c | 46 +++++++-------- libswfdec/swfdec_codec_video.c | 109 +++++++++++++++++++++++++++++------- libswfdec/swfdec_codec_video.h | 32 +++++++++-- libswfdec/swfdec_codec_vp6_alpha.c | 111 +++++++++++++++++++++++++++++++++++++ libswfdec/swfdec_internal.h | 2 + libswfdec/swfdec_net_stream.c | 4 +- 9 files changed, 382 insertions(+), 132 deletions(-) create mode 100644 libswfdec/swfdec_codec_vp6_alpha.c diff --git a/libswfdec/Makefile.am b/libswfdec/Makefile.am index ca6d565f..a94cd84d 100644 --- a/libswfdec/Makefile.am +++ b/libswfdec/Makefile.am @@ -56,6 +56,7 @@ libswfdec_@SWFDEC_MAJORMINOR@_la_SOURCES = \ $(CODECS) \ swfdec_codec_screen.c \ swfdec_codec_video.c \ + swfdec_codec_vp6_alpha.c \ swfdec_color.c \ swfdec_color_as.c \ swfdec_debug.c \ diff --git a/libswfdec/swfdec_codec_ffmpeg.c b/libswfdec/swfdec_codec_ffmpeg.c index 3e8b64c4..9f5d872f 100644 --- a/libswfdec/swfdec_codec_ffmpeg.c +++ b/libswfdec/swfdec_codec_ffmpeg.c @@ -208,27 +208,36 @@ typedef struct { SwfdecVideoDecoder decoder; AVCodecContext * ctx; /* out context (d'oh) */ AVFrame * frame; /* the frame we use for decoding */ - struct SwsContext * sws; /* the format conversion */ - int sws_width; /* width used in resampler */ - int sws_height; /* height used in resampler */ + enum PixelFormat format; /* format we must output */ } SwfdecVideoDecoderFFMpeg; +static enum PixelFormat +swfdec_video_decoder_ffmpeg_get_format (SwfdecVideoCodec codec) +{ + switch (swfdec_video_codec_get_format (codec)) { + case SWFDEC_VIDEO_FORMAT_RGBA: + return PIX_FMT_RGB32; + case SWFDEC_VIDEO_FORMAT_I420: + return PIX_FMT_YUV420P; + } + g_assert_not_reached (); + return PIX_FMT_RGB32; +} + #define ALIGNMENT 31 -static SwfdecBuffer * +static gboolean swfdec_video_decoder_ffmpeg_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer, - guint *width, guint *height, guint *rowstride) + SwfdecVideoImage *image) { SwfdecVideoDecoderFFMpeg *codec = (SwfdecVideoDecoderFFMpeg *) dec; int got_image = 0; - SwfdecBuffer *ret; - AVPicture picture; guchar *tmp, *aligned; /* fullfill alignment and padding requirements */ tmp = g_try_malloc (buffer->length + ALIGNMENT + FF_INPUT_BUFFER_PADDING_SIZE); if (tmp == NULL) { SWFDEC_WARNING ("Could not allocate temporary memory"); - return NULL; + return FALSE; } aligned = (guchar *) (((uintptr_t) tmp + ALIGNMENT) & ~ALIGNMENT); memcpy (aligned, buffer->data, buffer->length); @@ -237,38 +246,28 @@ swfdec_video_decoder_ffmpeg_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffe aligned, buffer->length) < 0) { g_free (tmp); SWFDEC_WARNING ("error decoding frame"); - return NULL; + return FALSE; } g_free (tmp); if (got_image == 0) { SWFDEC_WARNING ("did not get an image from decoding"); - return NULL; - } - if (codec->sws && - (codec->sws_width != codec->ctx->width || - codec->sws_height != codec->ctx->height)) { - sws_freeContext (codec->sws); - codec->sws = NULL; + return FALSE; } - if (codec->sws == NULL) { - codec->sws = sws_getContext (codec->ctx->width, codec->ctx->height, codec->ctx->pix_fmt, - codec->ctx->width, codec->ctx->height, PIX_FMT_RGB32, 0, NULL, NULL, NULL); - if (codec->sws == NULL) { - SWFDEC_ERROR ("Could not get conversion context"); - return NULL; - } - codec->sws_width = codec->ctx->width; - codec->sws_height = codec->ctx->height; + if (codec->ctx->pix_fmt != codec->format) { + SWFDEC_WARNING ("decoded to wrong format, expected %u, but got %u", + codec->format, codec->ctx->pix_fmt); + return FALSE; } - ret = swfdec_buffer_new_and_alloc (codec->ctx->width * codec->ctx->height * 4); - avpicture_fill (&picture, ret->data, PIX_FMT_RGB32, codec->ctx->width, - codec->ctx->height); - sws_scale (codec->sws, codec->frame->data, codec->frame->linesize, 0, codec->ctx->height, - picture.data, picture.linesize); - *width = codec->ctx->width; - *height = codec->ctx->height; - *rowstride = codec->ctx->width * 4; - return ret; + image->width = codec->ctx->width; + image->height = codec->ctx->height; + image->mask = NULL; + image->plane[0] = codec->frame->data[0]; + image->plane[1] = codec->frame->data[1]; + image->plane[2] = codec->frame->data[2]; + image->rowstride[0] = codec->frame->linesize[0]; + image->rowstride[1] = codec->frame->linesize[1]; + image->rowstride[2] = codec->frame->linesize[2]; + return TRUE; } static void @@ -276,9 +275,6 @@ swfdec_video_decoder_ffmpeg_free (SwfdecVideoDecoder *dec) { SwfdecVideoDecoderFFMpeg *codec = (SwfdecVideoDecoderFFMpeg *) dec; - if (codec->sws) { - sws_freeContext (codec->sws); - }; avcodec_close (codec->ctx); av_free (codec->ctx); av_free (codec->frame); @@ -314,7 +310,42 @@ swfdec_video_decoder_ffmpeg_new (SwfdecVideoCodec type) codec->decoder.free = swfdec_video_decoder_ffmpeg_free; codec->ctx = ctx; codec->frame = avcodec_alloc_frame (); + codec->format = swfdec_video_decoder_ffmpeg_get_format (type); return &codec->decoder; } +guint8 * +swfdec_video_ffmpeg_i420_to_rgb (SwfdecVideoImage *image) +{ + struct SwsContext *sws; + AVPicture src, dst; + guint8 *data; + + sws = sws_getContext (image->width, image->height, PIX_FMT_YUV420P, + image->width, image->height, PIX_FMT_RGB32, 0, NULL, NULL, NULL); + if (sws == NULL) { + SWFDEC_ERROR ("Could not get conversion context"); + return NULL; + } + data = g_try_malloc (image->width * image->height * 4); + if (data == NULL) { + SWFDEC_ERROR ("Out of memory"); + sws_freeContext (sws); + return NULL; + } + src.data[0] = (unsigned char *) image->plane[0]; + src.data[1] = (unsigned char *) image->plane[1]; + src.data[2] = (unsigned char *) image->plane[2]; + src.data[3] = NULL; + src.linesize[0] = image->rowstride[0]; + src.linesize[1] = image->rowstride[1]; + src.linesize[2] = image->rowstride[2]; + src.linesize[3] = 0; + avpicture_fill (&dst, data, PIX_FMT_RGB32, image->width, image->height); + sws_scale (sws, src.data, src.linesize, 0, image->height, + dst.data, dst.linesize); + sws_freeContext (sws); + return data; +} + diff --git a/libswfdec/swfdec_codec_gst.c b/libswfdec/swfdec_codec_gst.c index 0369a411..0b7760a6 100644 --- a/libswfdec/swfdec_codec_gst.c +++ b/libswfdec/swfdec_codec_gst.c @@ -298,7 +298,7 @@ typedef struct _SwfdecGstVideo SwfdecGstVideo; struct _SwfdecGstVideo { SwfdecVideoDecoder decoder; - GMutex * mutex; /* mutex that blocks everything below */ + GMutex * mutex; /* mutex that blocks everything below (NB: locked by default) */ GCond * cond; /* cond used to signal when stuff below changes */ volatile int refcount; /* refcount (d'oh) */ @@ -335,7 +335,6 @@ swfdec_video_decoder_gst_free (SwfdecVideoDecoder *dec) SwfdecGstVideo *player = (SwfdecGstVideo *) dec; GstElement *pipeline; - g_mutex_lock (player->mutex); pipeline = player->pipeline; player->pipeline = NULL; g_cond_signal (player->cond); @@ -346,13 +345,12 @@ swfdec_video_decoder_gst_free (SwfdecVideoDecoder *dec) swfdec_gst_video_unref (player, NULL); } -static SwfdecBuffer * +static gboolean swfdec_video_decoder_gst_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer, - guint *width, guint *height, guint *rowstride) + SwfdecVideoImage *image) { SwfdecGstVideo *player = (SwfdecGstVideo *) dec; - g_mutex_lock (player->mutex); while (player->in != NULL && !player->error) { swfdec_cond_wait (player->cond, player->mutex); } @@ -361,17 +359,26 @@ swfdec_video_decoder_gst_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer, while (player->out == NULL && !player->error) { swfdec_cond_wait (player->cond, player->mutex); } - if (player->error) { - g_mutex_unlock (player->mutex); - return NULL; + if (player->error) + return FALSE; + image->width = player->width; + image->height = player->height; + image->mask = NULL; + switch (swfdec_video_codec_get_format (dec->codec)) { + case SWFDEC_VIDEO_FORMAT_RGBA: + image->plane[0] = player->out->data; + image->rowstride[0] = player->width * 4; + break; + case SWFDEC_VIDEO_FORMAT_I420: + image->plane[0] = player->out->data; + image->rowstride[0] = player->width; + image->plane[1] = image->plane[0] + player->width * player->height; + image->rowstride[1] = (player->width + 1) / 2; + image->plane[2] = image->plane[1] + image->rowstride[1] * ((player->height + 1) / 2); + image->rowstride[2] = image->rowstride[1]; + break; } - buffer = player->out; - player->out = NULL; - *width = player->width; - *height = player->height; - *rowstride = player->width * 4; - g_mutex_unlock (player->mutex); - return buffer; + return TRUE; } static void @@ -430,8 +437,15 @@ swfdec_video_decoder_gst_fakesink_handoff (GstElement *fakesrc, GstBuffer *buf, g_mutex_unlock (player->mutex); return; } - player->out = swfdec_buffer_new_for_data ( - g_memdup (buf->data, buf->size), buf->size); + if (player->out == NULL || player->out->length < buf->size) { + if (player->out) + swfdec_buffer_unref (player->out); + player->out = swfdec_buffer_new_for_data ( + g_memdup (buf->data, buf->size), buf->size); + } else { + memcpy (player->out->data, buf->data, buf->size); + player->out->length = buf->size; + } player->out_next = FALSE; g_cond_signal (player->cond); g_mutex_unlock (player->mutex); @@ -440,22 +454,46 @@ swfdec_video_decoder_gst_fakesink_handoff (GstElement *fakesrc, GstBuffer *buf, static void swfdec_video_decoder_gst_link (GstElement *src, GstPad *pad, GstElement *sink) { - if (!gst_element_link (src, sink)) { + GstCaps *caps; + + caps = g_object_get_data (G_OBJECT (sink), "swfdec-caps"); + g_assert (caps); + if (!gst_element_link_filtered (src, sink, caps)) { SWFDEC_ERROR ("no delayed link"); } + gst_caps_unref (caps); +} + +static GstCaps * +swfdec_video_decoder_get_sink_caps (SwfdecVideoCodec codec) +{ + switch (swfdec_video_codec_get_format (codec)) { + case SWFDEC_VIDEO_FORMAT_RGBA: +#if G_BYTE_ORDER == G_BIG_ENDIAN + return gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, " + "red_mask=16711680, green_mask=65280, blue_mask=255"); +#else + return gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, " + "red_mask=65280, green_mask=16711680, blue_mask=-16777216"); +#endif + case SWFDEC_VIDEO_FORMAT_I420: + return gst_caps_from_string ("video/x-raw-yuv, format=(fourcc)I420"); + } + g_assert_not_reached (); + return NULL; } SwfdecVideoDecoder * -swfdec_video_decoder_gst_new (SwfdecVideoCodec type) +swfdec_video_decoder_gst_new (SwfdecVideoCodec codec) { SwfdecGstVideo *player; - GstElement *fakesrc, *fakesink, *decoder, *csp; + GstElement *fakesrc, *fakesink, *decoder; GstCaps *caps; if (!gst_init_check (NULL, NULL, NULL)) return NULL; - switch (type) { + switch (codec) { case SWFDEC_VIDEO_CODEC_H263: caps = gst_caps_from_string ("video/x-flash-video"); break; @@ -498,6 +536,9 @@ swfdec_video_decoder_gst_new (SwfdecVideoCodec type) g_object_set (fakesink, "signal-handoffs", TRUE, NULL); g_signal_connect (fakesink, "handoff", G_CALLBACK (swfdec_video_decoder_gst_fakesink_handoff), player); + caps = swfdec_video_decoder_get_sink_caps (codec); + g_object_set_data_full (G_OBJECT (fakesink), "swfdec-caps", caps, + (GDestroyNotify) gst_caps_unref); g_atomic_int_inc (&player->refcount); g_object_weak_ref (G_OBJECT (fakesink), swfdec_gst_video_unref, player); gst_bin_add (GST_BIN (player->pipeline), fakesink); @@ -508,31 +549,14 @@ swfdec_video_decoder_gst_new (SwfdecVideoCodec type) return NULL; } gst_bin_add (GST_BIN (player->pipeline), decoder); - csp = gst_element_factory_make ("ffmpegcolorspace", NULL); - if (csp == NULL) { - SWFDEC_ERROR ("failed to create colorspace"); - swfdec_video_decoder_gst_free (&player->decoder); - return NULL; - } - gst_bin_add (GST_BIN (player->pipeline), csp); g_signal_connect (decoder, "pad-added", - G_CALLBACK (swfdec_video_decoder_gst_link), csp); + G_CALLBACK (swfdec_video_decoder_gst_link), fakesink); -#if G_BYTE_ORDER == G_BIG_ENDIAN - caps = gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, " - "red_mask=16711680, green_mask=65280, blue_mask=255"); -#else - caps = gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, " - "red_mask=65280, green_mask=16711680, blue_mask=-16777216"); -#endif - g_assert (caps); - if (!gst_element_link_filtered (fakesrc, decoder, player->srccaps) || - !gst_element_link_filtered (csp, fakesink, caps)) { + if (!gst_element_link_filtered (fakesrc, decoder, player->srccaps)) { SWFDEC_ERROR ("linking failed"); swfdec_video_decoder_gst_free (&player->decoder); return NULL; } - gst_caps_unref (caps); if (gst_element_set_state (player->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { SWFDEC_ERROR ("failed to change sate"); swfdec_video_decoder_gst_free (&player->decoder); diff --git a/libswfdec/swfdec_codec_screen.c b/libswfdec/swfdec_codec_screen.c index cb8d6a96..233af0d6 100644 --- a/libswfdec/swfdec_codec_screen.c +++ b/libswfdec/swfdec_codec_screen.c @@ -36,15 +36,14 @@ struct _SwfdecCodecScreen { SwfdecVideoDecoder decoder; /* the decoder */ gulong width; /* width of last image */ gulong height; /* height of last image */ - SwfdecBuffer * buffer; /* buffer containing last decoded image */ + guint8 * data; /* contains decoded image */ }; -static SwfdecBuffer * +static gboolean swfdec_video_decoder_screen_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer, - guint *width, guint *height, guint *rowstride) + SwfdecVideoImage *image) { SwfdecCodecScreen *screen = (SwfdecCodecScreen *) dec; - SwfdecBuffer *ret; SwfdecBits bits; gulong i, j, w, h, bw, bh, stride; @@ -56,7 +55,12 @@ swfdec_video_decoder_screen_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffe if (screen->width == 0 || screen->height == 0) { if (w == 0 || h == 0) { SWFDEC_ERROR ("width or height is 0: %lux%lu", w, h); - return NULL; + return FALSE; + } + screen->data = g_try_malloc (w * h * 4); + if (screen->data == NULL) { + SWFDEC_ERROR ("could not allocate %lu bytes", w * h * 4); + return FALSE; } screen->width = w; screen->height = h; @@ -64,21 +68,7 @@ swfdec_video_decoder_screen_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffe SWFDEC_ERROR ("width or height differ from original: was %lux%lu, is %lux%lu", screen->width, screen->height, w, h); /* FIXME: this is what ffmpeg does, should we be more forgiving? */ - return NULL; - } - if (screen->buffer && screen->buffer->ref_count == 1) { - g_assert (screen->buffer->length == w * h * 4); - swfdec_buffer_ref (screen->buffer); - ret = screen->buffer; - } else { - ret = swfdec_buffer_new_and_alloc (w * h * 4); - if (screen->buffer) { - g_assert (screen->buffer->length == w * h * 4); - memcpy (ret->data, screen->buffer->data, screen->buffer->length); - swfdec_buffer_unref (screen->buffer); - } - swfdec_buffer_ref (ret); - screen->buffer = ret; + return FALSE; } stride = w * 4; SWFDEC_LOG ("size: %lu x %lu - block size %lu x %lu\n", w, h, bw, bh); @@ -96,7 +86,7 @@ swfdec_video_decoder_screen_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffe continue; } /* convert format and write out data */ - out = ret->data + stride * (h - j - 1) + i * 4; + out = screen->data + stride * (h - j - 1) + i * 4; in = buf->data; for (y = 0; y < MIN (bh, h - j); y++) { for (x = 0; x < MIN (bw, w - i); x++) { @@ -109,10 +99,12 @@ swfdec_video_decoder_screen_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffe swfdec_buffer_unref (buf); } } - *width = screen->width; - *height = screen->height; - *rowstride = stride; - return ret; + image->width = screen->width; + image->height = screen->height; + image->plane[0] = screen->data; + image->rowstride[0] = stride; + image->mask = NULL; + return TRUE; } static void @@ -120,8 +112,8 @@ swfdec_video_decoder_screen_free (SwfdecVideoDecoder *dec) { SwfdecCodecScreen *screen = (SwfdecCodecScreen *) dec; - if (screen->buffer) - swfdec_buffer_unref (screen->buffer); + if (screen->data) + g_free (screen->data); g_free (screen); } diff --git a/libswfdec/swfdec_codec_video.c b/libswfdec/swfdec_codec_video.c index cabe2f31..3b2d9ab7 100644 --- a/libswfdec/swfdec_codec_video.c +++ b/libswfdec/swfdec_codec_video.c @@ -22,39 +22,42 @@ #endif #include "swfdec_codec_video.h" +#include "swfdec_color.h" #include "swfdec_debug.h" #include "swfdec_internal.h" /** * swfdec_video_decoder_new: - * @format: #SwfdecVideoCodec to create the #SwfdecVideoDecoder for + * @codec: #SwfdecVideoCodec to create the #SwfdecVideoDecoder for * - * Creates a new decoder to decode videos of type @format. If no suitable + * Creates a new decoder to decode videos of type @codec. If no suitable * decoder could be created, %NULL is returned. * * Returns: **/ SwfdecVideoDecoder * -swfdec_video_decoder_new (SwfdecVideoCodec format) +swfdec_video_decoder_new (SwfdecVideoCodec codec) { SwfdecVideoDecoder *ret; - ret = swfdec_video_decoder_screen_new (format); + ret = swfdec_video_decoder_screen_new (codec); + if (ret == NULL) + ret = swfdec_video_decoder_vp6_alpha_new (codec); #ifdef HAVE_FFMPEG if (ret == NULL) - ret = swfdec_video_decoder_ffmpeg_new (format); + ret = swfdec_video_decoder_ffmpeg_new (codec); #endif #ifdef HAVE_GST if (ret == NULL) - ret = swfdec_video_decoder_gst_new (format); + ret = swfdec_video_decoder_gst_new (codec); #endif if (ret != NULL) { - ret->format = format; + ret->codec = codec; g_return_val_if_fail (ret->decode, ret); g_return_val_if_fail (ret->free, ret); } else { - SWFDEC_WARNING ("no decoder found for codec %u", (guint) format); + SWFDEC_WARNING ("no decoder found for codec %u", (guint) codec); } return ret; } @@ -73,6 +76,40 @@ swfdec_video_decoder_free (SwfdecVideoDecoder *decoder) decoder->free (decoder); } +#ifdef HAVE_FFMPEG +#define swfdec_video_i420_to_rgb swfdec_video_ffmpeg_i420_to_rgb +#else +static guint8 * +swfdec_video_i420_to_rgb (&image) +{ + SWFDEC_FIXME ("implement I420 => RGB without ffmpeg"); + return NULL; +} +#endif /* HAVE_FFMPEG */ + +/* FIXME: use liboil for this */ +static void +swfdec_video_codec_apply_mask (guint8 *data, guint rowstride, const guint8 *mask, + guint mask_rowstride, guint width, guint height) +{ + const guint8 *in; + guint8 *out; + guint x, y; + + data += SWFDEC_COLOR_INDEX_ALPHA; + for (y = 0; y < height; y++) { + in = mask; + out = data; + for (x = 0; x < width; x++) { + *out = *in; + out += 4; + in++; + } + mask += mask_rowstride; + data += rowstride; + } +} + /** * swfdec_video_decoder_decode: * @decoder: a #SwfdecVideoDecoder @@ -85,34 +122,66 @@ swfdec_video_decoder_free (SwfdecVideoDecoder *decoder) cairo_surface_t * swfdec_video_decoder_decode (SwfdecVideoDecoder *decoder, SwfdecBuffer *buffer) { + SwfdecVideoImage image; static const cairo_user_data_key_t key; cairo_surface_t *surface; - guint width, height, rowstride; + guint8 *data; g_return_val_if_fail (decoder != NULL, NULL); g_return_val_if_fail (buffer != NULL, NULL); - buffer = decoder->decode (decoder, buffer, &width, &height, &rowstride); - if (buffer == NULL) { + if (!decoder->decode (decoder, buffer, &image)) { SWFDEC_ERROR ("failed to decode video"); return NULL; } - if (width == 0 || height == 0 || rowstride < width * 4) { - SWFDEC_ERROR ("invalid image size"); - swfdec_buffer_unref (buffer); - return NULL; + g_assert (image.width != 0 && image.height != 0); + /* FIXME: use cairo for all of this when cairo accelerates it */ + if (swfdec_video_codec_get_format (decoder->codec) == SWFDEC_VIDEO_FORMAT_I420) { + data = swfdec_video_i420_to_rgb (&image); + if (data == NULL) { + SWFDEC_ERROR ("I420 => RGB conversion failed"); + return NULL; + } + } else { + data = g_memdup (image.plane[0], image.rowstride[0] * image.height); + } + if (image.mask) { + swfdec_video_codec_apply_mask (data, image.width * 4, image.mask, + image.mask_rowstride, image.width, image.height); } - surface = cairo_image_surface_create_for_data (buffer->data, CAIRO_FORMAT_RGB24, - width, height, rowstride); + surface = cairo_image_surface_create_for_data (data, + image.mask ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, + image.width, image.height, image.width * 4); if (cairo_surface_status (surface)) { SWFDEC_ERROR ("failed to create surface: %s", cairo_status_to_string (cairo_surface_status (surface))); cairo_surface_destroy (surface); - swfdec_buffer_unref (buffer); return NULL; } - cairo_surface_set_user_data (surface, &key, buffer, - (cairo_destroy_func_t) swfdec_buffer_unref); + cairo_surface_set_user_data (surface, &key, data, + (cairo_destroy_func_t) g_free); return surface; } +/** + * swfdec_video_codec_get_format: + * @codec: codec to check + * + * Returns the output format used for this codec. Video codecs must use these + * codecs when decoding video. + * + * Returns: the output format to use for this format + **/ +SwfdecVideoFormat +swfdec_video_codec_get_format (SwfdecVideoCodec codec) +{ + switch (codec) { + case SWFDEC_VIDEO_CODEC_H263: + case SWFDEC_VIDEO_CODEC_VP6: + case SWFDEC_VIDEO_CODEC_VP6_ALPHA: + return SWFDEC_VIDEO_FORMAT_I420; + default: + return SWFDEC_VIDEO_FORMAT_RGBA; + } +} + diff --git a/libswfdec/swfdec_codec_video.h b/libswfdec/swfdec_codec_video.h index a7c7e7c4..c5606c10 100644 --- a/libswfdec/swfdec_codec_video.h +++ b/libswfdec/swfdec_codec_video.h @@ -33,20 +33,40 @@ typedef enum { SWFDEC_VIDEO_CODEC_SCREEN2 = 6 } SwfdecVideoCodec; +typedef enum { + SWFDEC_VIDEO_FORMAT_RGBA, + SWFDEC_VIDEO_FORMAT_I420 +} SwfdecVideoFormat; + +typedef struct { + guint width; /* width of image in pixels */ + guint height; /* height of image in pixels */ + const guint8 * plane[3]; /* planes of the image, not all might be used */ + const guint8 * mask; /* A8 mask or NULL if none */ + guint rowstride[3]; /* rowstrides of the planes */ + guint mask_rowstride; /* rowstride of mask plane */ +} SwfdecVideoImage; + typedef struct _SwfdecVideoDecoder SwfdecVideoDecoder; typedef SwfdecVideoDecoder * (SwfdecVideoDecoderNewFunc) (SwfdecVideoCodec format); +/* notes about the decode function: + * - the data must be in the format specified by swfdec_video_codec_get_format() + * - the data returned in the image belongs to the decoder and must be valid + * until the next function is called on the decoder. + * - you need to explicitly set mask to %NULL. + */ struct _SwfdecVideoDecoder { - SwfdecVideoCodec format; - SwfdecBuffer * (* decode) (SwfdecVideoDecoder * decoder, + SwfdecVideoCodec codec; + gboolean (* decode) (SwfdecVideoDecoder * decoder, SwfdecBuffer * buffer, - guint * width, - guint * height, - guint * rowstride); + SwfdecVideoImage * result); void (* free) (SwfdecVideoDecoder * decoder); }; -SwfdecVideoDecoder * swfdec_video_decoder_new (SwfdecVideoCodec format); +SwfdecVideoFormat swfdec_video_codec_get_format (SwfdecVideoCodec codec); + +SwfdecVideoDecoder * swfdec_video_decoder_new (SwfdecVideoCodec codec); void swfdec_video_decoder_free (SwfdecVideoDecoder * decoder); cairo_surface_t * swfdec_video_decoder_decode (SwfdecVideoDecoder * decoder, diff --git a/libswfdec/swfdec_codec_vp6_alpha.c b/libswfdec/swfdec_codec_vp6_alpha.c new file mode 100644 index 00000000..2d6ca9de --- /dev/null +++ b/libswfdec/swfdec_codec_vp6_alpha.c @@ -0,0 +1,111 @@ +/* Swfdec + * Copyright (C) 2007 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "swfdec_codec_video.h" +#include "swfdec_bits.h" +#include "swfdec_debug.h" +#include "swfdec_internal.h" + +typedef struct _SwfdecCodecVp6Alpha SwfdecCodecVp6Alpha; + +struct _SwfdecCodecVp6Alpha { + SwfdecVideoDecoder decoder; /* the decoder */ + SwfdecVideoDecoder * image; /* the image decoder */ + SwfdecVideoDecoder * alpha; /* the alpha decoder */ +}; + +static gboolean +swfdec_video_decoder_vp6_alpha_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer, + SwfdecVideoImage *image) +{ + SwfdecCodecVp6Alpha *vp6 = (SwfdecCodecVp6Alpha *) dec; + SwfdecBuffer *tmp; + SwfdecVideoImage alpha; + SwfdecBits bits; + guint size; + + swfdec_bits_init (&bits, buffer); + size = swfdec_bits_get_bu24 (&bits); + tmp = swfdec_bits_get_buffer (&bits, size); + if (tmp == NULL) + return FALSE; + if (!vp6->image->decode (vp6->image, tmp, image)) { + swfdec_buffer_unref (tmp); + return FALSE; + } + swfdec_buffer_unref (tmp); + tmp = swfdec_bits_get_buffer (&bits, -1); + if (tmp == NULL) + return FALSE; + if (!vp6->alpha->decode (vp6->alpha, tmp, &alpha)) { + swfdec_buffer_unref (tmp); + return FALSE; + } + swfdec_buffer_unref (tmp); + if (alpha.width != image->width || alpha.height != image->height) { + SWFDEC_ERROR ("size of mask doesn't match image: %ux%u vs %ux%u", + alpha.width, alpha.height, image->width, image->height); + return FALSE; + } + image->mask = alpha.plane[0]; + image->mask_rowstride = alpha.rowstride[0]; + return TRUE; +} + +static void +swfdec_video_decoder_vp6_alpha_free (SwfdecVideoDecoder *dec) +{ + SwfdecCodecVp6Alpha *vp6 = (SwfdecCodecVp6Alpha *) dec; + + if (vp6->image) + swfdec_video_decoder_free (vp6->image); + if (vp6->alpha) + swfdec_video_decoder_free (vp6->alpha); + g_free (vp6); +} + +SwfdecVideoDecoder * +swfdec_video_decoder_vp6_alpha_new (SwfdecVideoCodec type) +{ + SwfdecCodecVp6Alpha *vp6; + + if (type != SWFDEC_VIDEO_CODEC_VP6_ALPHA) + return NULL; + + vp6 = g_new0 (SwfdecCodecVp6Alpha, 1); + vp6->decoder.decode = swfdec_video_decoder_vp6_alpha_decode; + vp6->decoder.free = swfdec_video_decoder_vp6_alpha_free; + vp6->image = swfdec_video_decoder_new (SWFDEC_VIDEO_CODEC_VP6); + vp6->alpha = swfdec_video_decoder_new (SWFDEC_VIDEO_CODEC_VP6); + if (vp6->alpha == NULL || vp6->image == NULL) { + swfdec_video_decoder_vp6_alpha_free (&vp6->decoder); + return NULL; + } + + return &vp6->decoder; +} + diff --git a/libswfdec/swfdec_internal.h b/libswfdec/swfdec_internal.h index 0c6248c9..3f0952d9 100644 --- a/libswfdec/swfdec_internal.h +++ b/libswfdec/swfdec_internal.h @@ -53,8 +53,10 @@ SwfdecAudioDecoder * swfdec_audio_decoder_gst_new (SwfdecAudioCodec type, /* video codecs */ SwfdecVideoDecoder * swfdec_video_decoder_screen_new (SwfdecVideoCodec format); +SwfdecVideoDecoder * swfdec_video_decoder_vp6_alpha_new (SwfdecVideoCodec format); #ifdef HAVE_FFMPEG SwfdecVideoDecoder * swfdec_video_decoder_ffmpeg_new (SwfdecVideoCodec format); +guint8 * swfdec_video_ffmpeg_i420_to_rgb (SwfdecVideoImage * image); #endif #ifdef HAVE_GST SwfdecVideoDecoder * swfdec_video_decoder_gst_new (SwfdecVideoCodec format); diff --git a/libswfdec/swfdec_net_stream.c b/libswfdec/swfdec_net_stream.c index 82022464..3ffe057c 100644 --- a/libswfdec/swfdec_net_stream.c +++ b/libswfdec/swfdec_net_stream.c @@ -59,8 +59,8 @@ swfdec_net_stream_decode_video (SwfdecNetStream *stream, SwfdecBuffer *buffer) if (decoder == NULL) return NULL; - if (decoder->format == SWFDEC_VIDEO_CODEC_VP6 || - decoder->format == SWFDEC_VIDEO_CODEC_VP6_ALPHA) { + if (decoder->codec == SWFDEC_VIDEO_CODEC_VP6 || + decoder->codec == SWFDEC_VIDEO_CODEC_VP6_ALPHA) { guint wsub, hsub; SwfdecBuffer *tmp; wsub = *buffer->data; -- 2.11.4.GIT