/* GStreamer * Copyright (C) <1999> Erik Walthinsen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "gstswfdec.h" #include /* elementfactory information */ static GstElementDetails gst_swfdec_details = { "SWF video decoder", "Codec/Video/Decoder", "LGPL", "Uses libswfdec to decode Flash video streams", VERSION, "David Schleef ", "(C) 2002", }; /* Swfdec signals and args */ enum { /* FILL ME */ LAST_SIGNAL }; enum { ARG_0, ARG_FRAME_RATE, /* FILL ME */ }; GST_PAD_TEMPLATE_FACTORY (video_template_factory, "video_%02d", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_CAPS_NEW ( "swfdec_src", "video/raw", "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R','G','B',' ')), "width", GST_PROPS_INT_RANGE (16, 4096), "height", GST_PROPS_INT_RANGE (16, 4096) ) ); GST_PAD_TEMPLATE_FACTORY (audio_template_factory, "audio_%02d", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_CAPS_NEW ( "swfdec_audiosrc", "audio/raw", "format", GST_PROPS_STRING("int"), "law", GST_PROPS_INT(0), "endianness", GST_PROPS_INT(G_BYTE_ORDER), "signed", GST_PROPS_BOOLEAN(TRUE), "width", GST_PROPS_INT(16), "depth", GST_PROPS_INT(16), "rate", GST_PROPS_INT(44100), "channels", GST_PROPS_INT(1) ) ); GST_PAD_TEMPLATE_FACTORY (sink_template_factory, "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_CAPS_NEW ( "swfdec_sink", "application/x-shockwave-flash", "format", GST_PROPS_STRING("SWF") ) ); static void gst_swfdec_class_init (GstSwfdecClass *klass); static void gst_swfdec_init (GstSwfdec *swfdec); static void gst_swfdec_dispose (GObject *object); static void gst_swfdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gst_swfdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); #if 0 static GstPad * gst_swfdec_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *template); #endif #if 0 static gboolean gst_swfdec_src_event (GstPad *pad, GstEvent *event); static gboolean gst_swfdec_src_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value); static gboolean gst_swfdec_convert_sink (GstPad *pad, GstFormat src_format, gint64 src_value, GstFormat *dest_format, gint64 *dest_value); static gboolean gst_swfdec_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value, GstFormat *dest_format, gint64 *dest_value); #endif static GstElementStateReturn gst_swfdec_change_state (GstElement *element); #if 0 static void gst_swfdec_chain (GstPad *pad, GstBuffer *buffer); #endif static GstElementClass *parent_class = NULL; /*static guint gst_swfdec_signals[LAST_SIGNAL] = { 0 };*/ GType gst_swfdec_get_type (void) { static GType swfdec_type = 0; if (!swfdec_type) { static const GTypeInfo swfdec_info = { sizeof(GstSwfdecClass), NULL, NULL, (GClassInitFunc)gst_swfdec_class_init, NULL, NULL, sizeof(GstSwfdec), 0, (GInstanceInitFunc)gst_swfdec_init, }; swfdec_type = g_type_register_static(GST_TYPE_ELEMENT, "GstSwfdec", &swfdec_info, 0); } return swfdec_type; } static void gst_swfdec_class_init(GstSwfdecClass *klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; gobject_class = (GObjectClass*)klass; gstelement_class = (GstElementClass*)klass; parent_class = g_type_class_ref(GST_TYPE_ELEMENT); g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FRAME_RATE, g_param_spec_float ("frame_rate","frame_rate","frame_rate", 0.0, 1000.0, 0.0, G_PARAM_READABLE)); gobject_class->set_property = gst_swfdec_set_property; gobject_class->get_property = gst_swfdec_get_property; gobject_class->dispose = gst_swfdec_dispose; gstelement_class->change_state = gst_swfdec_change_state; //gstelement_class->request_new_pad = gst_swfdec_request_new_pad; } #if 0 static void gst_swfdec_vo_frame_draw (vo_frame_t * frame) { gst_swfdec_vo_instance_t *_instance; gst_swfdec_vo_frame_t *_frame; GstSwfdec *swfdec; gint64 pts = -1; g_return_if_fail (frame != NULL); g_return_if_fail (((gst_swfdec_vo_frame_t *)frame)->buffer != NULL); _frame = (gst_swfdec_vo_frame_t *)frame; _instance = (gst_swfdec_vo_instance_t *)frame->instance; swfdec = GST_SWFDEC (_instance->swfdec); /* we have to be carefull here. we do swf_close in the READY state * but it can send a few frames still. We have to make sure we are playing * when we send frames. we do have to free those last frames though */ if (GST_STATE (GST_ELEMENT (swfdec)) != GST_STATE_PLAYING) { gst_buffer_unref (_frame->buffer); /* pretend we have sent the frame */ _frame->sent = TRUE; return; } if (swfdec->frame_rate_code != swfdec->decoder->frame_rate_code) { swfdec->frame_rate_code = swfdec->decoder->frame_rate_code; g_object_notify (G_OBJECT (swfdec), "frame_rate"); } pts = swfdec->next_time - 3 * (GST_SECOND / video_rates[swfdec->decoder->frame_rate_code]); GST_BUFFER_TIMESTAMP (_frame->buffer) = pts; GST_DEBUG (0, "out: %lld %d %lld", GST_BUFFER_TIMESTAMP (_frame->buffer), swfdec->decoder->frame_rate_code, (long long)(GST_SECOND / video_rates[swfdec->decoder->frame_rate_code])); swfdec->next_time += (GST_SECOND / video_rates[swfdec->decoder->frame_rate_code]) + swfdec->adjust; GST_BUFFER_FLAG_SET (_frame->buffer, GST_BUFFER_READONLY); swfdec->frames_per_PTS++; swfdec->first = FALSE; _frame->sent = TRUE; swfdec->total_frames++; gst_pad_push (swfdec->videopad, _frame->buffer); } #endif #if 0 static int gst_swfdec_setup (GstSwfdec *swfdec, int width, int height) { g_return_val_if_fail (swfdec != NULL, -1); GST_INFO (GST_CAT_PLUGIN_INFO, "VO: setup w=%d h=%d", width, height); swfdec->width = width; swfdec->height = height; swfdec->total_frames = 0; gst_pad_try_set_caps (swfdec->videopad, gst_caps_new ( "swfdec_caps", "video/raw", gst_props_new ( "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R','G','B',' ')), "width", GST_PROPS_INT (width), "height", GST_PROPS_INT (height), NULL))); return 0; } #endif #if 0 static void gst_swfdec_close (GstSwfdec *swfdec) { GST_INFO (GST_CAT_PLUGIN_INFO, "VO: close"); /* FIXME */ } #endif #if 0 static vo_frame_t * gst_swfdec_vo_get_frame (vo_instance_t * instance, int flags) { gst_swfdec_vo_instance_t * _instance; gst_swfdec_vo_frame_t *frame; size_t size0; uint8_t *data = NULL; GstSwfdec *swfdec; g_return_val_if_fail (instance != NULL, NULL); GST_INFO (GST_CAT_PLUGIN_INFO, "VO: get_frame"); _instance = (gst_swfdec_vo_instance_t *)instance; swfdec = _instance->swfdec; if (flags & VO_PREDICTION_FLAG) { _instance->prediction_index ^= 1; frame = &_instance->frames[_instance->prediction_index]; } else { frame = &_instance->frames[2]; } /* we are reusing this frame */ if (frame->buffer != NULL) { /* if the frame wasn't sent, we have to unref twice */ if (!frame->sent) gst_buffer_unref (frame->buffer); gst_buffer_unref (frame->buffer); frame->buffer = NULL; } size0 = swfdec->width * swfdec->height / 4; if (swfdec->pool) { frame->buffer = gst_buffer_new_from_pool (swfdec->pool, 0, 0); } else { size_t size = 6 * size0; size_t offset; GstBuffer *parent; parent = gst_buffer_new (); GST_BUFFER_SIZE(parent) = size + 0x10; GST_BUFFER_DATA(parent) = data = g_new(uint8_t, size + 0x10); offset = 0x10 - (((unsigned long)data) & 0xf); frame->buffer = gst_buffer_create_sub(parent, offset, size); gst_buffer_unref(parent); } data = GST_BUFFER_DATA(frame->buffer); /* need ref=2 */ /* 1 - unref when reusing this frame */ /* 2 - unref when other elements done with buffer */ gst_buffer_ref (frame->buffer); frame->vo.base[0] = data; frame->vo.base[1] = data + 4 * size0; frame->vo.base[2] = data + 5 * size0; /*printf("base[0]=%p\n", frame->vo.base[0]); */ frame->sent = FALSE; return (vo_frame_t *)frame; } #endif #if 0 static void gst_swfdec_vo_open (GstSwfdec *swfdec) { gst_swfdec_vo_instance_t * instance; gint i,j; GST_INFO (GST_CAT_PLUGIN_INFO, "VO: open"); instance = g_new (gst_swfdec_vo_instance_t, 1); instance->vo.setup = gst_swfdec_vo_setup; instance->vo.close = gst_swfdec_vo_close; instance->vo.get_frame = gst_swfdec_vo_get_frame; instance->swfdec = swfdec; for (i=0; iframes[j].vo.base[j] = NULL; } instance->frames[i].vo.copy = NULL; instance->frames[i].vo.field = NULL; instance->frames[i].vo.draw = gst_swfdec_vo_frame_draw; instance->frames[i].vo.instance = (vo_instance_t *)instance; instance->frames[i].buffer = NULL; } swfdec->vo = (vo_instance_t *) instance; } #endif #if 0 static void gst_swfdec_vo_destroy (GstSwfdec *swfdec) { gst_swfdec_vo_instance_t * instance; gint i; GST_INFO (GST_CAT_PLUGIN_INFO, "VO: destroy"); instance = (gst_swfdec_vo_instance_t *) swfdec->vo; for (i=0; iframes[i].buffer) { if (!instance->frames[i].sent) { gst_buffer_unref (instance->frames[i].buffer); } gst_buffer_unref (instance->frames[i].buffer); } } g_free (instance); swfdec->vo = NULL; } #endif #if 0 static GstPadLinkReturn gst_swfdec_connect(GstPad *pad, GstCaps *caps) { return GST_PAD_LINK_DELAYED; } #endif #if 0 static void src_unlinked(GstPad *srcpad, GstPad *sinkpad, GstSwfdec *plugin) { GST_DEBUG(GST_CAT_PADS, "removing pad %s:%s", GST_DEBUG_PAD_NAME(srcpad)); gst_element_remove_pad(GST_ELEMENT(plugin), srcpad); if(plugin->videopad == srcpad) plugin->videopad = NULL; if(plugin->audiopad == srcpad) plugin->audiopad = NULL; } #endif #if 0 static GstPad * gst_swfdec_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *template) { gchar *name; GstPad *srcpad; GstSwfdec *plugin; plugin = GST_SWFDEC(element); g_return_val_if_fail(plugin != NULL, NULL); g_return_val_if_fail(GST_IS_SWFDEC(plugin), NULL); if(templ->direction != GST_PAD_SRC){ g_warning("swfdec: request new pad that is not SRC pad.\n"); return NULL; } //printf("requesting pad %s %d\n",template,templ->name); #if 0 if(strcmp("audio", template) == 0){ g_print("swfdec adding pad audio_00\n"); srcpad = gst_pad_new_from_template(templ, "audio_00"); gst_element_add_pad(GST_ELEMENT(plugin), srcpad); g_signal_connect(G_OBJECT(srcpad), "unlinked", G_CALLBACK(src_unlinked), plugin); gst_pad_set_link_function(srcpad, gst_swfdec_connect); plugin->audiopad = srcpad; }else if(strcmp("video", template) == 0){ #endif if(1){ g_print("swfdec adding pad video_00\n"); srcpad = gst_pad_new_from_template(templ, "video_00"); gst_element_add_pad(GST_ELEMENT(plugin), srcpad); g_signal_connect(G_OBJECT(srcpad), "unlinked", G_CALLBACK(src_unlinked), plugin); gst_pad_set_link_function(srcpad, gst_swfdec_connect); plugin->videopad = srcpad; }else{ g_warning("swfdec: request new pad with bad template\n"); return NULL; } return srcpad; } #endif static void gst_swfdec_loop(GstElement *element) { GstSwfdec *swfdec; GstBuffer *buf = NULL; int ret; g_return_if_fail(element != NULL); g_return_if_fail(GST_IS_SWFDEC(element)); swfdec = GST_SWFDEC(element); if(!swfdec->videopad){ printf("creating videopad\n"); swfdec->videopad = gst_pad_new_from_template( GST_PAD_TEMPLATE_GET(video_template_factory), "video_00"); printf("videopad=%p\n",swfdec->videopad); swfdec->audiopad = gst_pad_new_from_template( GST_PAD_TEMPLATE_GET(audio_template_factory), "audio_00"); printf("setting caps\n"); #if 0 gst_pad_try_set_caps(swfdec->videopad, gst_pad_get_pad_template_caps(swfdec->videopad)); #endif gst_pad_try_set_caps (swfdec->videopad, gst_caps_new ( "swfdec_caps", "video/raw", gst_props_new ( "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R','G','B',' ')), "width", GST_PROPS_INT (640), "height", GST_PROPS_INT (480), NULL))); gst_pad_try_set_caps (swfdec->audiopad, gst_caps_new ( "swfdec_caps", "audio/raw", gst_props_new( "format", GST_PROPS_STRING("int"), "law", GST_PROPS_INT(0), "endianness", GST_PROPS_INT(G_BYTE_ORDER), "signed", GST_PROPS_BOOLEAN(TRUE), "width", GST_PROPS_INT(16), "depth", GST_PROPS_INT(16), "rate", GST_PROPS_INT (44100), "channels", GST_PROPS_INT (1), NULL))); printf("adding pad\n"); gst_element_add_pad(element, swfdec->videopad); gst_element_add_pad(element, swfdec->audiopad); } ret = swf_parse(swfdec->state); if(ret==SWF_NEEDBITS){ buf = gst_pad_pull(swfdec->sinkpad); if(GST_IS_EVENT(buf)){ switch (GST_EVENT_TYPE (buf)) { case GST_EVENT_EOS: printf("got eos\n"); break; default: printf("got event\n"); break; } }else{ if(!GST_BUFFER_DATA(buf)){ printf("expected non-null buffer\n"); } ret = swf_addbits(swfdec->state,GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf)); } } if(ret==SWF_CHANGE){ swfdec->width = swfdec->state->width; swfdec->height = swfdec->state->height; swfdec->interval = GST_SECOND / swfdec->state->rate; #if G_BYTE_ORDER == 4321 #define RED_MASK 0xff0000 #define GREEN_MASK 0x00ff00 #define BLUE_MASK 0x0000ff #else #define RED_MASK 0x0000ff #define GREEN_MASK 0x00ff00 #define BLUE_MASK 0xff0000 #endif #if 1 gst_pad_try_set_caps(swfdec->videopad, gst_caps_new( "swfdec_caps", "video/raw", gst_props_new( "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')), "width", GST_PROPS_INT(swfdec->width), "height", GST_PROPS_INT(swfdec->height), "bpp", GST_PROPS_INT (24), "depth", GST_PROPS_INT (24), "endianness", GST_PROPS_INT (G_BYTE_ORDER), "red_mask", GST_PROPS_INT (RED_MASK), "green_mask", GST_PROPS_INT (GREEN_MASK), "blue_mask", GST_PROPS_INT (BLUE_MASK), NULL))); #else gst_pad_try_set_caps(swfdec->videopad, gst_caps_new( "swfdec_caps", "video/raw", gst_props_new( "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')), "width", GST_PROPS_INT(swfdec->width), "height", GST_PROPS_INT(swfdec->height), "bpp", GST_PROPS_INT (16), "depth", GST_PROPS_INT (16), "endianness", GST_PROPS_INT (G_BYTE_ORDER), NULL))); #endif gst_pad_try_set_caps(swfdec->audiopad, gst_caps_new( "swfdec_caps", "audio/raw", gst_props_new( "format", GST_PROPS_STRING("int"), "law", GST_PROPS_INT(0), "endianness", GST_PROPS_INT(G_BYTE_ORDER), "signed", GST_PROPS_BOOLEAN(TRUE), "width", GST_PROPS_INT(16), "depth", GST_PROPS_INT(16), "rate", GST_PROPS_INT (44100), "channels", GST_PROPS_INT (1), NULL))); return; } if(ret==SWF_IMAGE){ GstBuffer *newbuf = NULL; int newsize = swfdec->state->width * swfdec->state->height * 3; /* video stuff */ if(swfdec->pool){ newbuf = gst_buffer_new_from_pool(swfdec->pool, 0, 0); } if(!newbuf){ newbuf = gst_buffer_new(); GST_BUFFER_SIZE(newbuf) = newsize; GST_BUFFER_DATA(newbuf) = g_malloc(newsize); } g_return_if_fail(GST_BUFFER_DATA(newbuf) != NULL); memcpy(GST_BUFFER_DATA(newbuf),swfdec->state->buffer,newsize); swfdec->timestamp += swfdec->interval; GST_BUFFER_TIMESTAMP(newbuf) = swfdec->timestamp; gst_pad_push(swfdec->videopad, newbuf); /* audio stuff */ newbuf = gst_buffer_new(); newsize = 2*44100.0/swfdec->state->rate; GST_BUFFER_SIZE(newbuf) = newsize; GST_BUFFER_DATA(newbuf) = g_malloc(newsize); memcpy(GST_BUFFER_DATA(newbuf),swfdec->state->sound_buffer, newsize); GST_BUFFER_TIMESTAMP(newbuf) = swfdec->timestamp; gst_pad_push(swfdec->audiopad, newbuf); } if(ret==SWF_EOF){ gst_pad_push(swfdec->videopad, GST_BUFFER (gst_event_new (GST_EVENT_EOS))); gst_pad_push(swfdec->audiopad, GST_BUFFER (gst_event_new (GST_EVENT_EOS))); } } static void gst_swfdec_init (GstSwfdec *swfdec) { /* create the sink and src pads */ swfdec->sinkpad = gst_pad_new_from_template ( GST_PAD_TEMPLATE_GET (sink_template_factory), "sink"); gst_element_add_pad (GST_ELEMENT (swfdec), swfdec->sinkpad); //gst_pad_set_chain_function (swfdec->sinkpad, gst_swfdec_chain); //gst_pad_set_convert_function (swfdec->sinkpad, gst_swfdec_convert_sink); #if 0 swfdec->videopad = gst_pad_new_from_template ( GST_PAD_TEMPLATE_GET (video_template_factory), "video_00"); gst_element_add_pad (GST_ELEMENT (swfdec), swfdec->videopad); #endif //gst_pad_set_event_function (swfdec->videopad, GST_DEBUG_FUNCPTR (gst_swfdec_src_event)); //gst_pad_set_query_function (swfdec->videopad, GST_DEBUG_FUNCPTR (gst_swfdec_src_query)); //gst_pad_set_convert_function (swfdec->videopad, gst_swfdec_convert_src); #if 0 swfdec->audiopad = gst_pad_new_from_template ( GST_PAD_TEMPLATE_GET (audio_template_factory), "audio_00"); gst_element_add_pad (GST_ELEMENT (swfdec), swfdec->audiopad); #endif gst_element_set_loop_function(GST_ELEMENT(swfdec), gst_swfdec_loop); /* initialize the swfdec decoder state */ swfdec->state = swf_init(); g_return_if_fail(swfdec->state != NULL); swfdec->state->colorspace = SWF_COLORSPACE_RGB888; GST_FLAG_SET (GST_ELEMENT (swfdec), GST_ELEMENT_EVENT_AWARE); } static void gst_swfdec_dispose (GObject *object) { //GstSwfdec *swfdec = GST_SWFDEC (object); /* FIXME */ //swf_state_free(swfdec->state); G_OBJECT_CLASS (parent_class)->dispose (object); } #if 0 static void gst_swfdec_chain (GstPad *pad, GstBuffer *buf) { GstSwfdec *swfdec = GST_SWFDEC (gst_pad_get_parent (pad)); guint32 size; guchar *data; gint ret; gint64 pts; GST_DEBUG (0, "SWFDEC: chain called"); if (GST_IS_EVENT (buf)) { GstEvent *event = GST_EVENT (buf); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_DISCONTINUOUS: { //gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value; //swfdec->decoder->is_sequence_needed = 1; GST_DEBUG (GST_CAT_EVENT, "swfdec: discont\n"); swfdec->first = TRUE; swfdec->timestamp = 0; gst_pad_event_default (pad, event); return; } case GST_EVENT_EOS: if (!swfdec->closed) { /* close flushes the last few frames */ //swf_close (swfdec->state); swfdec->closed = TRUE; } default: gst_pad_event_default (pad, event); return; } } size = GST_BUFFER_SIZE (buf); data = GST_BUFFER_DATA (buf); pts = GST_BUFFER_TIMESTAMP (buf); GST_DEBUG (GST_CAT_CLOCK, "swfdec: pts %llu\n", pts); swfdec->timestamp += swfdec->interval; /* fprintf(stderr, "SWFDEC: in timestamp=%llu\n",GST_BUFFER_TIMESTAMP(buf)); */ /* fprintf(stderr, "SWFDEC: have buffer of %d bytes\n",size); */ ret = swf_addbits(swfdec->state, data, size); if(ret==SWF_IMAGE){ } /*fprintf(stderr, "SWFDEC: decoded %d frames\n", num_frames);*/ gst_buffer_unref(buf); } #endif #if 0 static gboolean gst_swfdec_convert_sink (GstPad *pad, GstFormat src_format, gint64 src_value, GstFormat *dest_format, gint64 *dest_value) { gboolean res = TRUE; GstSwfdec *swfdec; swfdec = GST_SWFDEC (gst_pad_get_parent (pad)); switch (src_format) { case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_format = GST_FORMAT_TIME; case GST_FORMAT_TIME: default: res = FALSE; } break; case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_format = GST_FORMAT_BYTES; case GST_FORMAT_BYTES: default: res = FALSE; } break; default: res = FALSE; } return res; } #endif #if 0 static gboolean gst_swfdec_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value, GstFormat *dest_format, gint64 *dest_value) { gboolean res = TRUE; GstSwfdec *swfdec; swfdec = GST_SWFDEC (gst_pad_get_parent (pad)); switch (src_format) { case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_format = GST_FORMAT_TIME; case GST_FORMAT_TIME: default: res = FALSE; } break; case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_format = GST_FORMAT_BYTES; case GST_FORMAT_BYTES: *dest_value = src_value * 6 * (swfdec->width * swfdec->height >> 2) * video_rates[swfdec->decoder->frame_rate_code] / GST_SECOND; break; case GST_FORMAT_UNITS: *dest_value = src_value * video_rates[swfdec->decoder->frame_rate_code] / GST_SECOND; break; default: res = FALSE; } break; case GST_FORMAT_UNITS: switch (*dest_format) { case GST_FORMAT_DEFAULT: *dest_format = GST_FORMAT_TIME; case GST_FORMAT_TIME: if (video_rates[swfdec->decoder->frame_rate_code] != 0.0) { *dest_value = src_value * GST_SECOND / video_rates[swfdec->decoder->frame_rate_code]; } else res = FALSE; break; case GST_FORMAT_BYTES: *dest_value = src_value * 6 * (swfdec->width * swfdec->height >> 2); break; case GST_FORMAT_UNITS: *dest_value = src_value; break; default: res = FALSE; } break; default: res = FALSE; } return res; } #endif #if 0 static gboolean gst_swfdec_src_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value) { gboolean res = TRUE; GstSwfdec *swfdec; static const GstFormat formats[] = { GST_FORMAT_TIME, GST_FORMAT_BYTES }; #define MAX_SEEK_FORMATS 1 /* we can only do time seeking for now */ gint i; swfdec = GST_SWFDEC (gst_pad_get_parent (pad)); switch (type) { case GST_QUERY_TOTAL: { switch (*format) { case GST_FORMAT_DEFAULT: *format = GST_FORMAT_TIME; /* fallthrough */ case GST_FORMAT_TIME: case GST_FORMAT_BYTES: case GST_FORMAT_UNITS: { res = FALSE; for (i = 0; i < MAX_SEEK_FORMATS && !res; i++) { GstFormat peer_format; gint64 peer_value; peer_format = formats[i]; /* do the probe */ if (gst_pad_query (GST_PAD_PEER (swfdec->sinkpad), GST_QUERY_TOTAL, &peer_format, &peer_value)) { GstFormat conv_format; /* convert to TIME */ conv_format = GST_FORMAT_TIME; res = gst_pad_convert (swfdec->sinkpad, peer_format, peer_value, &conv_format, value); /* and to final format */ res &= gst_pad_convert (pad, GST_FORMAT_TIME, *value, format, value); } } break; } default: res = FALSE; break; } break; } case GST_QUERY_POSITION: { switch (*format) { case GST_FORMAT_DEFAULT: *format = GST_FORMAT_TIME; /* fallthrough */ default: res = gst_pad_convert (pad, GST_FORMAT_TIME, swfdec->next_time, format, value); break; } break; } default: res = FALSE; break; } return res; } #endif #if 0 static gboolean gst_swfdec_src_event (GstPad *pad, GstEvent *event) { gboolean res = TRUE; GstSwfdec *swfdec; static const GstFormat formats[] = { GST_FORMAT_TIME, GST_FORMAT_BYTES }; #define MAX_SEEK_FORMATS 1 /* we can only do time seeking for now */ gint i; swfdec = GST_SWFDEC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { /* the all-formats seek logic */ case GST_EVENT_SEEK: { gint64 src_offset; gboolean flush; GstFormat format; format = GST_FORMAT_TIME; /* first bring the src_format to TIME */ if (!gst_pad_convert (pad, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event), &format, &src_offset)) { /* didn't work, probably unsupported seek format then */ res = FALSE; break; } /* shave off the flush flag, we'll need it later */ flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH; /* assume the worst */ res = FALSE; /* while we did not exhaust our seek formats without result */ for (i = 0; i < MAX_SEEK_FORMATS && !res; i++) { gint64 desired_offset; format = formats[i]; /* try to convert requested format to one we can seek with on the sinkpad */ if (gst_pad_convert (swfdec->sinkpad, GST_FORMAT_TIME, src_offset, &format, &desired_offset)) { GstEvent *seek_event; /* conversion succeeded, create the seek */ seek_event = gst_event_new_seek (formats[i] | GST_SEEK_METHOD_SET | flush, desired_offset); /* do the seekk */ if (gst_pad_send_event (GST_PAD_PEER (swfdec->sinkpad), seek_event)) { /* seek worked, we're done, loop will exit */ res = TRUE; } } /* at this point, either the seek worked or res == FALSE */ } break; } default: res = FALSE; break; } gst_event_unref (event); return res; } #endif static GstElementStateReturn gst_swfdec_change_state (GstElement *element) { GstSwfdec *swfdec = GST_SWFDEC (element); switch (GST_STATE_TRANSITION (element)) { case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: { //gst_swfdec_vo_open (swfdec); //swf_init (swfdec->decoder, swfdec->accel, swfdec->vo); //swfdec->decoder->is_sequence_needed = 1; //swfdec->decoder->frame_rate_code = 0; swfdec->timestamp = 0; swfdec->pool = NULL; swfdec->closed = FALSE; /* reset the initial video state */ swfdec->format = -1; swfdec->width = -1; swfdec->height = -1; swfdec->first = TRUE; break; } case GST_STATE_PAUSED_TO_PLAYING: /* try to get a bufferpool */ #if 0 swfdec->pool = gst_pad_get_bufferpool (swfdec->videopad); if (swfdec->pool) GST_INFO (GST_CAT_PLUGIN_INFO, "got pool %p", swfdec->pool); #endif break; case GST_STATE_PLAYING_TO_PAUSED: /* need to clear things we get from other plugins, since we could be reconnected */ if (swfdec->pool) { gst_buffer_pool_unref (swfdec->pool); swfdec->pool = NULL; } break; case GST_STATE_PAUSED_TO_READY: /* if we are not closed by an EOS event do so now, this cen send a few frames but * we are prepared to not really send them (see above) */ if (!swfdec->closed) { /*swf_close (swfdec->decoder); */ swfdec->closed = TRUE; } //gst_swfdec_vo_destroy (swfdec); break; case GST_STATE_READY_TO_NULL: break; default: break; } GST_ELEMENT_CLASS (parent_class)->change_state (element); return GST_STATE_SUCCESS; } static void gst_swfdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstSwfdec *src; /* it's not null if we got it, but it might not be ours */ g_return_if_fail (GST_IS_SWFDEC (object)); src = GST_SWFDEC (object); switch (prop_id) { default: break; } } static void gst_swfdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GstSwfdec *swfdec; /* it's not null if we got it, but it might not be ours */ g_return_if_fail (GST_IS_SWFDEC (object)); swfdec = GST_SWFDEC (object); switch (prop_id) { case ARG_FRAME_RATE: g_value_set_float (value, swfdec->frame_rate); break; default: break; } } static GstCaps * swf_type_find(GstBuffer *buf, gpointer private) { gchar *data = GST_BUFFER_DATA(buf); if((data[0] != 'F' && data[0] != 'C') || data[1] != 'W' || data[2] != 'S')return NULL; return gst_caps_new("swf_type_find","application/x-shockwave-flash", NULL); } static GstTypeDefinition swftype_definition = { "swfdecode/x-shockwave-flash", "application/x-shockwave-flash", ".swf .swfl", swf_type_find }; static gboolean plugin_init (GModule *module, GstPlugin *plugin) { GstElementFactory *factory; GstTypeFactory *type; /* create an elementfactory for the swfdec element */ factory = gst_element_factory_new("swfdec",GST_TYPE_SWFDEC, &gst_swfdec_details); g_return_val_if_fail(factory != NULL, FALSE); gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_PRIMARY); gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (video_template_factory)); gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (audio_template_factory)); gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_template_factory)); gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); type = gst_type_factory_new(&swftype_definition); gst_plugin_add_feature(plugin, GST_PLUGIN_FEATURE(type)); return TRUE; } GstPluginDesc plugin_desc = { GST_VERSION_MAJOR, GST_VERSION_MINOR, "swfdec", plugin_init };