/* XMMS2 - X Music Multiplexer System * Copyright (C) 2003-2008 XMMS2 Team * * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! * * 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. */ /** * FLV Demuxer * Jai Menon */ #include "xmms/xmms_xformplugin.h" #include "xmms/xmms_log.h" #include "xmms/xmms_medialib.h" #include #include /* * Defines */ #define GET_16(buf, val) \ g_assert (sizeof (val) == 2); \ memcpy (&val, buf, 2); \ buf += 2; \ bytes_left -= 2; \ val = GUINT16_TO_LE (val); #define GET_32(buf, val) \ g_assert (sizeof (val) == 4); \ memcpy (&val, buf, 4); \ buf += 4; \ bytes_left -= 4; \ val = GUINT32_TO_LE (val); #define GET_STR(buf, str, len) \ strncpy ((gchar *) str, (gchar *)buf, len); \ str[len] = '\0'; \ bytes_left -= len; \ buf += len; #define SKIP(len) \ bytes_left -= len; \ buf += len; #define MIN_FLV_SIZE 24 #define MIN_FLV_HEADER 13 #define BUFFERSIZE 4096 /* Private Data Type */ typedef struct _flv_tag_St { guchar tag_type; guchar data_size[3]; guchar timestamp[3]; guchar timestamp_ext; guchar stream_id[3]; } flv_tag_t ; typedef struct xmms_flv_data_St { guchar stream_type; flv_tag_t tag_header; gint bytes_left; } xmms_flv_data_t; /* * Function prototypes */ static gboolean xmms_flv_plugin_setup (xmms_xform_plugin_t *xform_plugin); static gint xmms_flv_read (xmms_xform_t *xform, gpointer buf, gint len, xmms_error_t *error); static void xmms_flv_destroy (xmms_xform_t *xform); static gboolean xmms_flv_init (xmms_xform_t *xform); static gint64 xmms_flv_seek (xmms_xform_t *xform, gint64 samples, xmms_xform_seek_mode_t whence, xmms_error_t *error); /* * Plugin header */ XMMS_XFORM_PLUGIN ("FLV", "FLV Demuxer", XMMS_VERSION, "FLV Demuxer", xmms_flv_plugin_setup); static gboolean xmms_flv_plugin_setup (xmms_xform_plugin_t *xform_plugin) { xmms_xform_methods_t methods; XMMS_XFORM_METHODS_INIT (methods); methods.init = xmms_flv_init; methods.destroy = xmms_flv_destroy; methods.read = xmms_flv_read; methods.seek = xmms_flv_seek; xmms_xform_plugin_methods_set (xform_plugin, &methods); xmms_xform_plugin_indata_add (xform_plugin, XMMS_STREAM_TYPE_MIMETYPE, "video/x-flv", NULL); xmms_magic_add ("FLV Header", "video/x-flv", "0 string FLV", NULL); xmms_magic_extension_add ("video/x-flv", "*.flv"); return TRUE; } static gboolean xmms_flv_init (xmms_xform_t *xform) { xmms_error_t error; xmms_flv_data_t *data = NULL; gint ret = 0; guchar header[MIN_FLV_HEADER+1]; g_return_val_if_fail (xform, FALSE); data = g_new0 (xmms_flv_data_t, 1); data->bytes_left = 0; xmms_xform_private_data_set (xform, data); /* Read header */ ret = xmms_xform_read (xform, header, MIN_FLV_HEADER, &error); if( ret < MIN_FLV_HEADER ) return FALSE; data->stream_type = header[4]; if( !((data->stream_type & 0x05) || (data->stream_type & 0x04)) ) { xmms_log_error(" Only video stream.......no audio stream found.....!! "); return FALSE; } /* TODO: read in metadata tag and also peek at 1st audio tag to get stream format As of now just skip the headers and let read method analyse each tag including metadata and video skipping them as and when encountered */ printf("\n\nInit called.....\n"); xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, "audio/mpeg", NULL); printf("\n\nOutput type set.....\n"); return TRUE; } static gint xmms_flv_read (xmms_xform_t *xform, gpointer buf, gint len, xmms_error_t *error) { gint ret; xmms_flv_data_t *data; guchar sound_info; gint bytes_written = 0; g_return_val_if_fail (xform, -1); data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, -1); printf ("\n\n Read called.....\n"); while ( bytes_written < len ) { if (data->bytes_left == 0) { ret = xmms_xform_read (xform, &data->tag_header, sizeof(flv_tag_t), error); if (ret <= 0) return ret; if (ret < sizeof(flv_tag_t)) { xmms_log_error("Couldn't read FLV tag header......"); return -1; } gint dsize = ( ( data->tag_header.data_size[0] << 16 ) + (data->tag_header.data_size[1] << 8 ) + data->tag_header.data_size[2] ); if (data->tag_header.tag_type == 0x08) { ret = xmms_xform_read (xform, &sound_info, 1, error); if (ret <= 0) { return ret; } dsize--; if (bytes_written + dsize <= len) { ret = xmms_xform_read (xform, buf, dsize, error); if (ret <= 0) return ret; if (ret < dsize) xmms_xform_read (xform, buf, dsize-ret, error); bytes_written += dsize; } else { gint left = len - bytes_written; ret = xmms_xform_read (xform, buf, left, error); if (ret <= 0) return ret; data->bytes_left = dsize - left; bytes_written = len; } } else xmms_xform_seek (xform, dsize, XMMS_XFORM_SEEK_CUR, error); xmms_xform_seek (xform, 4, XMMS_XFORM_SEEK_CUR, error); } else { ret = xmms_xform_read (xform, buf, data->bytes_left, error); bytes_written += data->bytes_left; data->bytes_left = 0; } } printf ("\n\nSuccessfull read.....\n"); return len; } static gint64 xmms_flv_seek (xmms_xform_t *xform, gint64 samples, xmms_xform_seek_mode_t whence, xmms_error_t *error) { return (xmms_xform_seek (xform, samples, whence, error)); } static void xmms_flv_destroy (xmms_xform_t *xform) { g_return_if_fail (xform); g_free (xmms_xform_private_data_get (xform)); }