2009-05-31 12:15:11 +00:00
|
|
|
/*
|
|
|
|
* GStreamer
|
|
|
|
* Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION:element-HDVParse
|
|
|
|
*
|
|
|
|
* <refsect2>
|
|
|
|
* <title>Example launch line</title>
|
|
|
|
* <para>
|
|
|
|
* <programlisting>
|
|
|
|
* gst-launch -v -m filesrc ! mpegtsdemux ! hdvparse ! fakesink silent=TRUE
|
|
|
|
* </programlisting>
|
|
|
|
* </para>
|
|
|
|
* </refsect2>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <gst/gst.h>
|
|
|
|
#include <gst/base/gstbasetransform.h>
|
|
|
|
|
|
|
|
#include "gsthdvparse.h"
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_hdvparse_debug);
|
|
|
|
#define GST_CAT_DEFAULT gst_hdvparse_debug
|
|
|
|
|
|
|
|
/* Filter signals and args */
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
/* FILL ME */
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
};
|
|
|
|
|
2009-05-31 16:23:08 +00:00
|
|
|
static gchar *aperture_table[] = {
|
|
|
|
"???",
|
|
|
|
"cls",
|
|
|
|
"1.0",
|
|
|
|
"1.2",
|
|
|
|
"1.4",
|
|
|
|
"1.6",
|
|
|
|
"1.7",
|
|
|
|
"1.8",
|
|
|
|
"2.0",
|
|
|
|
"2.2",
|
|
|
|
"2.4",
|
|
|
|
"2.6",
|
|
|
|
"2.8",
|
|
|
|
"3.1",
|
|
|
|
"3.4",
|
|
|
|
"3.7",
|
|
|
|
"4.0",
|
|
|
|
"4.4",
|
|
|
|
"4.8",
|
|
|
|
"5.2",
|
|
|
|
"5.6",
|
|
|
|
"6.2",
|
|
|
|
"6.8",
|
|
|
|
"7.3",
|
|
|
|
"8.0",
|
|
|
|
"8.7",
|
|
|
|
"9.6",
|
|
|
|
"10",
|
|
|
|
"11",
|
|
|
|
"12",
|
|
|
|
"14",
|
|
|
|
"14",
|
|
|
|
"16",
|
|
|
|
"17",
|
|
|
|
"18",
|
|
|
|
"6.7"
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Observations from my HDV Camera (Canon HV20 Pal)
|
|
|
|
* FIXME : replace with with code once we've figured out the algorithm.
|
|
|
|
* Shutter speed 0x4f 0x50
|
|
|
|
* ------------------------------------
|
|
|
|
* 1/6 F3 95
|
|
|
|
* 1/8 90 91
|
|
|
|
* 1/12 FA 8A
|
|
|
|
* 1/15 C8 88
|
|
|
|
* 1/24 7D 85
|
|
|
|
* 1/30 64 84
|
|
|
|
* 1/48 BE 82
|
|
|
|
* 1/60 32 82
|
|
|
|
* 1/100 51 81
|
|
|
|
* 1/250 87 80
|
|
|
|
* 1/500 43 80
|
|
|
|
* 1/1000 22 80
|
|
|
|
* 1/2000 11 80
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
guint vala, valb, shutter;
|
|
|
|
} Shutter_t;
|
|
|
|
|
|
|
|
static Shutter_t shutter_table[] = {
|
|
|
|
{0xf3, 0x95, 6},
|
|
|
|
{0x90, 0x91, 8},
|
|
|
|
{0xfa, 0x8a, 12},
|
|
|
|
{0xc8, 0x88, 15},
|
|
|
|
{0x7d, 0x85, 24},
|
|
|
|
{0x64, 0x84, 30},
|
|
|
|
{0xbe, 0x82, 48},
|
|
|
|
{0x32, 0x82, 60},
|
|
|
|
{0x51, 0x81, 100},
|
|
|
|
{0x87, 0x80, 250},
|
|
|
|
{0x43, 0x80, 500},
|
|
|
|
{0x22, 0x80, 1000},
|
|
|
|
{0x11, 0x80, 2000}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Binary-coded decimal reading macro */
|
|
|
|
#define BCD(c) ( ((((c) >> 4) & 0x0f) * 10) + ((c) & 0x0f) )
|
|
|
|
/* Same as before, but with a mask */
|
|
|
|
#define BCD_M(c, mask) (BCD ((c) & (mask)))
|
|
|
|
|
|
|
|
|
2009-05-31 12:15:11 +00:00
|
|
|
/* the capabilities of the inputs and outputs.
|
|
|
|
*
|
|
|
|
* describe the real formats here.
|
|
|
|
*/
|
|
|
|
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
|
|
|
GST_PAD_SINK,
|
|
|
|
GST_PAD_ALWAYS,
|
|
|
|
GST_STATIC_CAPS ("private/hdv-a1")
|
|
|
|
);
|
|
|
|
|
|
|
|
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|
|
|
GST_PAD_SRC,
|
|
|
|
GST_PAD_ALWAYS,
|
|
|
|
GST_STATIC_CAPS ("private/hdv-a1,parsed=(boolean)True")
|
|
|
|
);
|
|
|
|
|
|
|
|
/* debug category for fltering log messages
|
|
|
|
*
|
|
|
|
* exchange the string 'Template HDVParse' with your description
|
|
|
|
*/
|
|
|
|
#define DEBUG_INIT(bla) \
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_hdvparse_debug, "hdvparse", 0, "HDV private stream parser");
|
|
|
|
|
|
|
|
GST_BOILERPLATE_FULL (GstHDVParse, gst_hdvparse, GstBaseTransform,
|
|
|
|
GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
|
|
|
|
|
|
|
|
static GstFlowReturn gst_hdvparse_transform_ip (GstBaseTransform * base,
|
|
|
|
GstBuffer * outbuf);
|
|
|
|
|
|
|
|
/* GObject vmethod implementations */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_hdvparse_base_init (gpointer klass)
|
|
|
|
{
|
|
|
|
static GstElementDetails element_details = {
|
|
|
|
"HDVParser",
|
|
|
|
"Data/Parser",
|
|
|
|
"HDV private stream Parser",
|
|
|
|
"Edward Hervey <bilboed@bilboed.com>"
|
|
|
|
};
|
|
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
|
|
|
|
|
|
gst_element_class_add_pad_template (element_class,
|
|
|
|
gst_static_pad_template_get (&src_template));
|
|
|
|
gst_element_class_add_pad_template (element_class,
|
|
|
|
gst_static_pad_template_get (&sink_template));
|
|
|
|
gst_element_class_set_details (element_class, &element_details);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize the HDVParse's class */
|
|
|
|
static void
|
|
|
|
gst_hdvparse_class_init (GstHDVParseClass * klass)
|
|
|
|
{
|
|
|
|
GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
|
|
|
|
GST_DEBUG_FUNCPTR (gst_hdvparse_transform_ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize the new element
|
|
|
|
* initialize instance structure
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
gst_hdvparse_init (GstHDVParse * filter, GstHDVParseClass * klass)
|
|
|
|
{
|
|
|
|
GstBaseTransform *transform = GST_BASE_TRANSFORM (filter);
|
|
|
|
|
|
|
|
gst_base_transform_set_in_place (transform, TRUE);
|
|
|
|
gst_base_transform_set_passthrough (transform, TRUE);
|
|
|
|
}
|
|
|
|
|
2009-05-31 16:23:08 +00:00
|
|
|
static guint
|
|
|
|
get_shutter_speed (guint8 vala, guint8 valb)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (shutter_table); i++)
|
|
|
|
if (shutter_table[i].vala == vala && shutter_table[i].valb == valb)
|
|
|
|
return shutter_table[i].shutter;
|
|
|
|
GST_WARNING ("Unknown shutter speed ! vala:0x%02x, valb:0x%02x", vala, valb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-05-31 12:15:11 +00:00
|
|
|
static void
|
|
|
|
gst_hdvparse_parse (GstHDVParse * filter, GstBuffer * buf)
|
|
|
|
{
|
2009-05-31 16:23:08 +00:00
|
|
|
guint8 *data = GST_BUFFER_DATA (buf);
|
|
|
|
guint apertured, shutter;
|
|
|
|
gfloat gain;
|
|
|
|
gboolean dst = FALSE;
|
|
|
|
GstStructure *str;
|
|
|
|
GstMessage *msg;
|
|
|
|
|
|
|
|
GST_MEMDUMP_OBJECT (filter, "BUFFER", data, GST_BUFFER_SIZE (buf));
|
|
|
|
|
|
|
|
str = gst_structure_empty_new ("HDV");
|
|
|
|
|
|
|
|
/* 0x1f - 0x23 : TimeCode */
|
|
|
|
|
|
|
|
if (data[0x1f] != 0xff) {
|
|
|
|
guint8 tframe, tsec, tmin, thour;
|
|
|
|
gchar *timecode = NULL;
|
|
|
|
tframe = BCD (data[0x1f] & 0x3f);
|
|
|
|
tsec = BCD (data[0x20] & 0x7f);
|
|
|
|
tmin = BCD (data[0x21] & 0x7f);
|
|
|
|
thour = BCD (data[0x22] & 0x3f);
|
|
|
|
|
|
|
|
timecode =
|
|
|
|
g_strdup_printf ("%01d:%02d:%02d.%02d", thour, tmin, tsec, tframe);
|
|
|
|
gst_structure_set (str, "timecode", G_TYPE_STRING, timecode, NULL);
|
|
|
|
g_free (timecode);
|
|
|
|
GST_LOG_OBJECT (filter, timecode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 0x23 : Timezone / Dailight Saving Time */
|
|
|
|
/* 0x24 - 0x2a : Original time */
|
|
|
|
if (data[0x23] != 0xff) {
|
|
|
|
GDate *date = NULL;
|
|
|
|
guint tzone = 0;
|
|
|
|
guint day, month, year, hour, min, sec;
|
|
|
|
gchar *datetime;
|
|
|
|
|
|
|
|
tzone = data[0x23];
|
|
|
|
dst = !(tzone & 0x80);
|
|
|
|
tzone =
|
|
|
|
BCD (tzone & 0x1f) > 12 ? BCD (tzone & 0x1f) - 12 : BCD (tzone & 0x1f);
|
|
|
|
GST_LOG_OBJECT (filter, "TimeZone : %d, DST : %d", tzone, dst);
|
|
|
|
|
|
|
|
day = BCD_M (data[0x24], 0x3f);
|
|
|
|
month = BCD_M (data[0x25], 0x1f);
|
|
|
|
year = BCD (data[0x26]);
|
|
|
|
if (year > 90)
|
|
|
|
year += 1900;
|
|
|
|
else
|
|
|
|
year += 2000;
|
|
|
|
/* 0x27: ??? */
|
|
|
|
sec = BCD_M (data[0x28], 0x7f);
|
|
|
|
min = BCD_M (data[0x29], 0x7f);
|
|
|
|
hour = BCD_M (data[0x2a], 0x3f);
|
|
|
|
|
|
|
|
/* FIXME : we need a date/time object ! */
|
|
|
|
date = g_date_new_dmy (day, month, year);
|
|
|
|
datetime =
|
|
|
|
g_strdup_printf ("%02d/%02d/%02d %02d:%02d:%02d", day, month, year,
|
|
|
|
hour, min, sec);
|
|
|
|
gst_structure_set (str, "date", GST_TYPE_DATE, date, "recording-time",
|
|
|
|
G_TYPE_STRING, datetime, NULL);
|
|
|
|
g_free (datetime);
|
|
|
|
GST_LOG_OBJECT (filter, datetime);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 0x2b : Various flags, including scene-change */
|
|
|
|
if (!((data[0x2b] & 0x20) >> 5)) {
|
|
|
|
GST_LOG_OBJECT (filter, "Scene change !");
|
|
|
|
gst_structure_set (str, "scene-change", G_TYPE_BOOLEAN, TRUE, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for partials */
|
|
|
|
if (GST_BUFFER_SIZE (buf) < 0x50) {
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 0x43 : Aperture */
|
|
|
|
apertured = data[0x43] & 0x3f;
|
|
|
|
if (apertured < 35) {
|
|
|
|
GST_LOG_OBJECT (filter, "Aperture : F%s", aperture_table[apertured]);
|
|
|
|
gst_structure_set (str, "aperture", G_TYPE_STRING,
|
|
|
|
aperture_table[apertured], NULL);
|
|
|
|
} else {
|
|
|
|
GST_LOG_OBJECT (filter, "Aperture : %d", apertured);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 0x44 : Gain */
|
|
|
|
gain = ((data[0x44] & 0xf) - 1) * 1.5;
|
|
|
|
GST_LOG_OBJECT (filter, "Gain : %03f db", gain);
|
|
|
|
gst_structure_set (str, "gain", G_TYPE_FLOAT, gain, NULL);
|
|
|
|
|
|
|
|
/* 0x4f - 0x50 : Shutter */
|
|
|
|
shutter = get_shutter_speed (data[0x4f], data[0x50]);
|
|
|
|
GST_LOG_OBJECT (filter, "Shutter speed : 1/%d", shutter);
|
|
|
|
if (shutter)
|
|
|
|
gst_structure_set (str, "shutter-speed", GST_TYPE_FRACTION, 1, shutter,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
beach:
|
|
|
|
msg = gst_message_new_element (GST_OBJECT (filter), str);
|
|
|
|
gst_element_post_message (GST_ELEMENT (filter), msg);
|
2009-05-31 12:15:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* GstBaseTransform vmethod implementations */
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
gst_hdvparse_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
|
|
|
|
{
|
|
|
|
GstHDVParse *filter = GST_HDVPARSE (base);
|
|
|
|
|
|
|
|
gst_hdvparse_parse (filter, outbuf);
|
|
|
|
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* entry point to initialize the plug-in
|
|
|
|
* initialize the plug-in itself
|
|
|
|
* register the element factories and other features
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
|
|
HDVParse_init (GstPlugin * HDVParse)
|
|
|
|
{
|
2009-05-31 16:23:50 +00:00
|
|
|
return gst_element_register (HDVParse, "hdvparse", GST_RANK_NONE,
|
2009-05-31 12:15:11 +00:00
|
|
|
GST_TYPE_HDVPARSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* gstreamer looks for this structure to register HDVParses
|
|
|
|
*
|
|
|
|
* exchange the string 'Template HDVParse' with you HDVParse description
|
|
|
|
*/
|
|
|
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
|
|
|
GST_VERSION_MINOR,
|
|
|
|
"hdvparse",
|
|
|
|
"HDV private stream parser",
|
|
|
|
HDVParse_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
|