mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 18:50:48 +00:00
890 lines
27 KiB
C
890 lines
27 KiB
C
/*
|
|
* 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 <math.h>
|
|
|
|
#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,
|
|
};
|
|
|
|
|
|
|
|
#define CLOCK_BASE 9LL
|
|
#define CLOCK_FREQ (CLOCK_BASE * 10000)
|
|
|
|
#define MPEGTIME_TO_GSTTIME(time) (gst_util_uint64_scale ((time), \
|
|
GST_MSECOND/10, CLOCK_BASE))
|
|
#define GSTTIME_TO_MPEGTIME(time) (gst_util_uint64_scale ((time), \
|
|
CLOCK_BASE, GST_MSECOND/10))
|
|
|
|
/* If set to 1, then extra validation will be applied to check
|
|
* for complete spec compliance wherever applicable. */
|
|
#define VALIDATE 0
|
|
|
|
/* 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)))
|
|
|
|
/* 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 ("hdv/aux-v;hdv/aux-a")
|
|
);
|
|
|
|
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS
|
|
("hdv/aux-v,parsed=(boolean)True;hdv/aux-a,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);
|
|
static GstCaps *gst_hdvparse_transform_caps (GstBaseTransform * trans,
|
|
GstPadDirection dir, GstCaps * incaps);
|
|
|
|
/* GObject vmethod implementations */
|
|
|
|
static void
|
|
gst_hdvparse_base_init (gpointer klass)
|
|
{
|
|
|
|
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_simple (element_class, "HDVParser",
|
|
"Data/Parser",
|
|
"HDV private stream Parser", "Edward Hervey <bilboed@bilboed.com>");
|
|
}
|
|
|
|
/* 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);
|
|
GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
|
|
GST_DEBUG_FUNCPTR (gst_hdvparse_transform_caps);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_hdvparse_transform_caps (GstBaseTransform * trans, GstPadDirection dir,
|
|
GstCaps * incaps)
|
|
{
|
|
GstCaps *res = NULL;
|
|
GstStructure *st = gst_caps_get_structure (incaps, 0);
|
|
|
|
GST_WARNING_OBJECT (trans, "dir:%d, incaps:%" GST_PTR_FORMAT, dir, incaps);
|
|
|
|
if (dir == GST_PAD_SINK) {
|
|
res = gst_caps_new_simple (gst_structure_get_name (st),
|
|
"parsed", G_TYPE_BOOLEAN, TRUE, NULL);
|
|
} else {
|
|
res = gst_caps_new_simple (gst_structure_get_name (st), NULL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static inline const gchar *
|
|
sfr_to_framerate (guint8 sfr)
|
|
{
|
|
switch (sfr) {
|
|
case 4:
|
|
return "30000/1001";
|
|
case 3:
|
|
return "25/1";
|
|
case 1:
|
|
return "24000/1001";
|
|
default:
|
|
return "RESERVED";
|
|
}
|
|
}
|
|
|
|
static GstFlowReturn
|
|
parse_dv_multi_pack (GstHDVParse * filter, guint8 * data, guint64 size,
|
|
GstStructure * st)
|
|
{
|
|
guint64 offs = 1;
|
|
|
|
while (size / 5) {
|
|
GST_LOG ("DV pack 0x%x", data[offs]);
|
|
switch (data[offs]) {
|
|
case 0x70:{
|
|
guint8 irispos, ae, agc, wbmode, whitebal, focusmode, focuspos;
|
|
|
|
irispos = data[offs + 1] & 0x3f;
|
|
ae = data[offs + 2] >> 4;
|
|
agc = data[offs + 2] & 0xf;
|
|
wbmode = data[offs + 3] >> 5;
|
|
whitebal = data[offs + 3] & 0x1f;
|
|
focusmode = data[offs + 4] >> 7;
|
|
focuspos = data[offs + 4] & 0x7f;
|
|
|
|
GST_LOG (" Consumer Camera 1");
|
|
|
|
GST_LOG (" Iris position %d (0x%x)", irispos, irispos);
|
|
/* Iris position = 2 ^ (IP/8) (for 0 < IP < 0x3C) */
|
|
if (irispos < 0x3c) {
|
|
GST_LOG (" IRIS F%0.2f", powf (2.0, (((float) irispos) / 8.0)));
|
|
gst_structure_set (st, "aperture-fnumber", G_TYPE_FLOAT,
|
|
powf (2.0, (((float) irispos) / 8.0)), NULL);
|
|
} else if (irispos == 0x3d) {
|
|
GST_LOG (" IRIS < 1.0");
|
|
} else if (irispos == 0x3e) {
|
|
GST_LOG (" IRIS closed");
|
|
}
|
|
|
|
/* AE Mode:
|
|
* 0 : Full automatic
|
|
* 1 : Gain Priority mode
|
|
* 2 : Shutter Priority mode
|
|
* 3 : Iris priority mode
|
|
* 4 : Manual
|
|
* ..: Reserved
|
|
* F : No information */
|
|
GST_LOG (" AE Mode: %d (0x%x)", ae, ae);
|
|
|
|
GST_LOG (" AGC: %d (0x%x)", agc, agc);
|
|
if (agc < 0xd) {
|
|
/* This is what the spec says.. but I'm not seeing the same on my camera :( */
|
|
GST_LOG (" Gain:%02.2fdB", (agc * 3.0) - 3.0);
|
|
gst_structure_set (st, "gain", G_TYPE_FLOAT, (agc * 3.0) - 3.0, NULL);
|
|
}
|
|
/* White balance mode
|
|
* 0 : Automatic
|
|
* 1 : hold
|
|
* 2 : one push
|
|
* 3 : pre-set
|
|
* 7 : no-information */
|
|
if (wbmode != 7)
|
|
GST_LOG (" White balance mode : %d (0x%x)", wbmode, wbmode);
|
|
/* White balance
|
|
* 0 : Candle
|
|
* 1 : Incandescent lamp
|
|
* 2 : low color temperature fluorescent lamp
|
|
* 3 : high color temperature fluorescent lamp
|
|
* 4 : sunlight
|
|
* 5 : cloudy weather
|
|
* F : No information
|
|
*/
|
|
if (whitebal != 0xf)
|
|
GST_LOG (" White balance : %d (0x%x)", whitebal, whitebal);
|
|
if (focuspos != 0x7f) {
|
|
GST_LOG (" Focus mode : %s", focusmode ? "MANUAL" : "AUTOMATIC");
|
|
GST_LOG (" Focus position: %d (0x%x)", focuspos, focuspos);
|
|
}
|
|
}
|
|
break;
|
|
case 0x71:{
|
|
guint8 v_pan, h_pan, focal_length, e_zoom;
|
|
gboolean is, zen;
|
|
|
|
v_pan = data[offs + 1] & 0x3f;
|
|
is = data[offs + 2] >> 7;
|
|
h_pan = data[offs + 2] & 0x7f;
|
|
focal_length = data[offs + 3];
|
|
zen = data[offs + 4] >> 7;
|
|
e_zoom = data[offs + 4] & 0x7f;
|
|
|
|
GST_LOG (" Consumer Camera 2");
|
|
if (v_pan != 0x3f)
|
|
GST_LOG (" Vertical Panning : %d (0x%d)", v_pan, v_pan);
|
|
if (h_pan != 0x7f)
|
|
GST_LOG (" Horizontal Panning : %d (0x%d)", h_pan, h_pan);
|
|
GST_LOG (" Stabilizer : %s", is ? "OFF" : "ON");
|
|
if (focal_length != 0xff)
|
|
GST_LOG (" Focal Length : %f mm",
|
|
(focal_length & 0x7f) * pow (10, focal_length & 0x80));
|
|
if (zen == 0)
|
|
GST_LOG (" Electric Zoom %02dd.%03d", e_zoom >> 5, e_zoom & 0x1f);
|
|
}
|
|
break;
|
|
case 0x7f:{
|
|
guint16 speed;
|
|
guint16 speedint;
|
|
|
|
GST_LOG (" Shutter");
|
|
if (data[offs + 1] != 0xff)
|
|
GST_LOG (" Shutter Speed (1) : %d, 0x%x",
|
|
data[offs + 1], data[offs + 1]);
|
|
if (data[offs + 2] != 0xff)
|
|
GST_LOG (" Shutter Speed (1) : %d, 0x%x",
|
|
data[offs + 2], data[offs + 2]);
|
|
|
|
speed = data[offs + 3] | (data[offs + 4] & 0x7f) << 8;
|
|
|
|
/* The shutter speed is 1/(CSS * horizontal scanning period) */
|
|
/* FIXME : 34000 is a value interpolated by observations */
|
|
speedint = (int) (34000.0 / (float) speed);
|
|
/* Only the highest two decimal digits are valid */
|
|
if (speedint > 100)
|
|
speedint = speedint / 10 * 10;
|
|
|
|
GST_LOG (" Shutter speed : 1/%d", speedint);
|
|
gst_structure_set (st, "shutter-speed", GST_TYPE_FRACTION,
|
|
1, speedint, NULL);
|
|
}
|
|
break;
|
|
default:
|
|
GST_MEMDUMP ("Unknown pack", data + offs, 5);
|
|
break;
|
|
}
|
|
size -= 5;
|
|
offs += 5;
|
|
}
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
parse_video_frame (GstHDVParse * filter, guint8 * data, guint64 size,
|
|
GstStructure * st)
|
|
{
|
|
guint32 etn, bitrate;
|
|
guint8 nbframes, data_h, hdr_size, sfr, sdm;
|
|
guint8 aspect, framerate, profile, level, format, chroma;
|
|
guint8 gop_n, gop_m, cgms, recst, abst;
|
|
guint16 vbv_delay, width, height, vbv_buffer;
|
|
guint64 dts;
|
|
gboolean pf, tf, rf;
|
|
|
|
GST_LOG_OBJECT (filter, "Video Frame Pack");
|
|
|
|
/* Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* ---------------------------------
|
|
* 0 | Size (0x39) |
|
|
* ---------------------------------
|
|
* 1 | |
|
|
* 2 | ETN |
|
|
* 3 | |
|
|
* ---------------------------------
|
|
*/
|
|
|
|
if (data[0] != 0x39) {
|
|
GST_WARNING ("Invalid size for Video frame");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
etn = data[3] << 16 | data[2] << 8 | data[1];
|
|
|
|
GST_LOG_OBJECT (filter, " ETN : %" G_GUINT32_FORMAT, etn);
|
|
|
|
/* Pack-V Information
|
|
* ---------------------------------
|
|
* 4 | Number of Video Frames |
|
|
* ---------------------------------
|
|
* 5 | 0 | 0 | 0 | 0 | DATA-H |
|
|
* ---------------------------------
|
|
* 6 | VBV |
|
|
* 7 | DELAY |
|
|
* ---------------------------------
|
|
* 8 | HEADER SIZE |
|
|
* ---------------------------------
|
|
* 9 | |
|
|
* 10 | DTS |
|
|
* 11 | |
|
|
* 12 | |
|
|
* ----------------------------- |
|
|
* 13 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
|
|
* ---------------------------------
|
|
* 14 |PF |TF |RF | 0 | SFR |
|
|
* ---------------------------------
|
|
*/
|
|
|
|
nbframes = data[4];
|
|
|
|
if (VALIDATE && (data[5] >> 4))
|
|
return GST_FLOW_ERROR;
|
|
data_h = data[5] & 0xf;
|
|
|
|
vbv_delay = data[6] | data[7] << 8;
|
|
|
|
hdr_size = data[8];
|
|
|
|
dts = data[9] | data[10] << 8 | data[11] << 16 | data[12] << 24;
|
|
dts |= (guint64) (data[13] & 0x1) << 32;
|
|
if (G_UNLIKELY (VALIDATE && (data[13] & 0xfe))) {
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
pf = data[14] & 0x80;
|
|
tf = data[14] & 0x40;
|
|
rf = data[14] & 0x20;
|
|
if (G_UNLIKELY (VALIDATE && (data[14] & 0x10)))
|
|
return GST_FLOW_ERROR;
|
|
|
|
sfr = data[14] & 0x07;
|
|
|
|
GST_LOG_OBJECT (filter, " Pack-V Information");
|
|
GST_LOG_OBJECT (filter, " Number of Video Frames : %d", nbframes);
|
|
GST_LOG_OBJECT (filter, " Leading PES-V picture type %s (0x%x)",
|
|
(data_h == 0x1) ? "I-picture" : "other", data_h);
|
|
GST_LOG_OBJECT (filter, " VBV Delay of first frame: %" G_GUINT32_FORMAT,
|
|
vbv_delay);
|
|
GST_LOG_OBJECT (filter, " Header Size:%d", hdr_size);
|
|
GST_LOG_OBJECT (filter, " DTS: %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT ")",
|
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (dts)), dts);
|
|
GST_LOG_OBJECT (filter, " Video source : %s %s %s (0x%x 0x%x 0x%x)",
|
|
pf ? "Progressive" : "Interlaced",
|
|
tf ? "TFF" : "", rf ? "RFF" : "", pf, tf, rf);
|
|
GST_LOG_OBJECT (filter, " Source Frame Rate : %s (0x%x)",
|
|
sfr_to_framerate (sfr), sfr);
|
|
|
|
gst_structure_set (st, "DTS", G_TYPE_UINT64, MPEGTIME_TO_GSTTIME (dts),
|
|
"interlaced", G_TYPE_BOOLEAN, !pf, NULL);
|
|
|
|
/* Search Data Mode
|
|
* ---------------------------------
|
|
* 15 | Search Data Mode |
|
|
* ---------------------------------
|
|
*/
|
|
sdm = data[15];
|
|
GST_LOG_OBJECT (filter, " Search Data Mode : 0x%x", sdm);
|
|
GST_LOG_OBJECT (filter, " %s %s %s",
|
|
sdm & 0x2 ? "8x-Base" : "",
|
|
sdm & 0x4 ? "8x-Helper" : "", sdm & 0x10 ? "24x" : "");
|
|
|
|
/* Video Mode
|
|
* ---------------------------------
|
|
* 16 | Horizontal size |
|
|
* ----------------- |
|
|
* 17 | 0 | 0 | 0 | 0 | |
|
|
* ---------------------------------
|
|
* 18 | Vertical size |
|
|
* ----------------- |
|
|
* 19 | 0 | 0 | 0 | 0 | |
|
|
* ---------------------------------
|
|
* 20 | Aspect ratio | Frame Rate |
|
|
* ---------------------------------
|
|
* 21 | |
|
|
* 22 | bitrate |
|
|
* ------------------------- |
|
|
* 23 | 0 | 0 | 0 | 0 | 0 | 0 | |
|
|
* ---------------------------------
|
|
* 24 | VBV Buffer size |
|
|
* ------------------------- |
|
|
* 25 | 0 | 0 | 0 | 0 | 0 | 0 | |
|
|
* ---------------------------------
|
|
* 26 | 0 | Profile | Level |
|
|
* ---------------------------------
|
|
* 27 | 0 | Format |Chroma | 0 | 0 |
|
|
* ---------------------------------
|
|
* 28 | GOP N | GOP M |
|
|
* ---------------------------------
|
|
*/
|
|
width = data[16] | (data[17] & 0xf) << 8;
|
|
height = data[18] | (data[19] & 0xf) << 8;
|
|
if (VALIDATE && ((data[17] & 0xf0) || data[19] & 0xf0))
|
|
return GST_FLOW_ERROR;
|
|
aspect = data[20] >> 4;
|
|
framerate = data[20] & 0xf;
|
|
bitrate = data[21] | data[22] << 8 | (data[23] & 0x3) << 16;
|
|
if (VALIDATE && (data[23] & 0xfc))
|
|
return GST_FLOW_ERROR;
|
|
vbv_buffer = data[24] | (data[25] & 0x3) << 8;
|
|
if (VALIDATE && (data[25] & 0xfc))
|
|
return GST_FLOW_ERROR;
|
|
profile = (data[26] >> 4) & 0x7;
|
|
level = data[26] & 0xf;
|
|
format = (data[27] >> 4) & 0x7;
|
|
chroma = (data[27] >> 2) & 0x3;
|
|
gop_n = data[28] >> 3;
|
|
gop_m = data[28] & 0x7;
|
|
|
|
GST_LOG_OBJECT (filter, " Video Mode");
|
|
GST_LOG_OBJECT (filter, " width:%d, height:%d", width, height);
|
|
GST_LOG_OBJECT (filter, " Aspect Ratio : %s (0x%x)",
|
|
(aspect == 0x3) ? "16/9" : "RESERVED", aspect);
|
|
GST_LOG_OBJECT (filter, " Framerate: %s (0x%x)",
|
|
sfr_to_framerate (framerate), framerate);
|
|
GST_LOG_OBJECT (filter, " Bitrate: %d bit/s", bitrate * 400);
|
|
GST_LOG_OBJECT (filter, " VBV buffer Size : %d bits",
|
|
vbv_buffer * 16 * 1024);
|
|
GST_LOG_OBJECT (filter, " MPEG Profile : %s (0x%x)",
|
|
(profile == 0x4) ? "Main" : "RESERVED", profile);
|
|
GST_LOG_OBJECT (filter, " MPEG Level : %s (0x%x)",
|
|
(level == 0x6) ? "High-1440" : "RESERVED", level);
|
|
GST_LOG_OBJECT (filter, " Video format : %s (0x%x)",
|
|
(format == 0) ? "Component" : "Reserved", format);
|
|
GST_LOG_OBJECT (filter, " Chroma : %s (0x%x)",
|
|
(chroma == 0x1) ? "4:2:0" : "RESERVED", chroma);
|
|
GST_LOG_OBJECT (filter, " GOP N/M : %d / %d", gop_n, gop_m);
|
|
|
|
/* data availability
|
|
* ---------------------------------
|
|
* 29 | 0 | 0 | 0 | 0 | 0 |PE2|PE1|PE0|
|
|
* ---------------------------------
|
|
* PE0 : HD2 TTC is valid
|
|
* PE1 : REC DATE is valid
|
|
* PE2 : REC TIME is valid
|
|
*/
|
|
if (data[29] & 0x1) {
|
|
guint8 fr, sec, min, hr;
|
|
gboolean bf, df;
|
|
gchar *ttcs;
|
|
|
|
/* HD2 TTC
|
|
* ---------------------------------
|
|
* 30 |BF |DF |Tens Fr|Units of Frames|
|
|
* ---------------------------------
|
|
* 31 | 1 |Tens second|Units of Second|
|
|
* ---------------------------------
|
|
* 32 | 1 |Tens minute|Units of Minute|
|
|
* ---------------------------------
|
|
* 33 | 1 | 1 |Tens Hr|Units of Hours |
|
|
* ---------------------------------
|
|
*/
|
|
bf = data[30] >> 7;
|
|
df = (data[30] >> 6) & 0x1;
|
|
fr = BCD (data[30] & 0x3f);
|
|
sec = BCD (data[31] & 0x7f);
|
|
min = BCD (data[32] & 0x7f);
|
|
hr = BCD (data[33] & 0x3f);
|
|
GST_LOG_OBJECT (filter, " HD2 Title Time Code");
|
|
GST_LOG_OBJECT (filter, " BF:%d, Drop Frame:%d", bf, df);
|
|
ttcs = g_strdup_printf ("%02d:%02d:%02d.%02d", hr, min, sec, fr);
|
|
GST_LOG_OBJECT (filter, " Timecode %s", ttcs);
|
|
/* FIXME : Use framerate information from above to convert to GstClockTime */
|
|
gst_structure_set (st, "title-time-code", G_TYPE_STRING, ttcs, NULL);
|
|
g_free (ttcs);
|
|
|
|
}
|
|
|
|
if (data[29] & 0x2) {
|
|
gboolean ds, tm;
|
|
guint8 tz, day, dow, month, year;
|
|
GDate *date;
|
|
|
|
/* REC DATE
|
|
* ---------------------------------
|
|
* 34 |DS |TM |Tens TZ|Units of TimeZn|
|
|
* ---------------------------------
|
|
* 35 | 1 | 1 |Tens dy| Units of Days |
|
|
* ---------------------------------
|
|
* 36 | Week |TMN|Units of Months|
|
|
* ---------------------------------
|
|
* 37 | Tens of Years |Units of Years |
|
|
* ---------------------------------
|
|
*/
|
|
ds = data[34] >> 7;
|
|
tm = (data[34] >> 6) & 0x1;
|
|
tz = BCD (data[34] & 0x3f);
|
|
day = BCD (data[35] & 0x3f);
|
|
dow = data[36] >> 5;
|
|
month = BCD (data[36] & 0x1f);
|
|
year = BCD (data[37]);
|
|
|
|
GST_LOG_OBJECT (filter, " REC DATE");
|
|
GST_LOG_OBJECT (filter, " ds:%d, tm:%d", ds, tm);
|
|
GST_LOG_OBJECT (filter, " Timezone: %d", tz);
|
|
GST_LOG_OBJECT (filter, " Date: %d %02d/%02d/%04d", dow, day, month, year);
|
|
date = g_date_new_dmy (day, month, year);
|
|
gst_structure_set (st, "date", GST_TYPE_DATE, date,
|
|
"timezone", G_TYPE_INT, tz,
|
|
"daylight-saving", G_TYPE_BOOLEAN, ds, NULL);
|
|
g_date_free (date);
|
|
}
|
|
|
|
if (data[29] & 0x4) {
|
|
guint8 fr, sec, min, hr;
|
|
gchar *times;
|
|
|
|
/* REC TIME
|
|
* ---------------------------------
|
|
* 38 | 1 | 1 |Tens Fr|Units of Frames|
|
|
* ---------------------------------
|
|
* 39 | 1 |Tens second|Units of Second|
|
|
* ---------------------------------
|
|
* 40 | 1 |Tens minute|Units of Minute|
|
|
* ---------------------------------
|
|
* 41 | 1 | 1 |Tens Hr|Units of Hours |
|
|
* ---------------------------------
|
|
*/
|
|
fr = BCD (data[38] & 0x3f);
|
|
sec = BCD (data[39] & 0x7f);
|
|
min = BCD (data[40] & 0x7f);
|
|
hr = BCD (data[41] & 0x3f);
|
|
times = g_strdup_printf ("%02d:%02d:%02d", hr, min, sec);
|
|
GST_LOG_OBJECT (filter, " REC TIME %02d:%02d:%02d.%02d", hr, min, sec, fr);
|
|
gst_structure_set (st, "time", G_TYPE_STRING, times, NULL);
|
|
g_free (times);
|
|
}
|
|
|
|
/* MISC
|
|
* ---------------------------------
|
|
* 42 | CGMS |REC|ABS| 0 | 0 | 0 | 0 |
|
|
* ---------------------------------
|
|
*/
|
|
cgms = data[42] >> 6;
|
|
recst = (data[42] >> 5) & 0x1;
|
|
abst = (data[42] >> 4) & 0x1;
|
|
|
|
GST_LOG_OBJECT (filter, " CGMS:0x%x", cgms);
|
|
GST_LOG_OBJECT (filter, " Recording Start Point : %s",
|
|
(recst == 0) ? "PRESENT" : "ABSENT");
|
|
GST_LOG_OBJECT (filter, " ABST : %s",
|
|
(abst == 0) ? "DISCONTINUITY" : "NO DISCONTINUITY");
|
|
|
|
gst_structure_set (st, "recording-start-point", G_TYPE_BOOLEAN, !recst, NULL);
|
|
|
|
/* Extended DV Pack #1
|
|
* 43 - 47
|
|
*/
|
|
GST_LOG_OBJECT (filter, " Extended DV Pack #1 : 0x%x", data[43]);
|
|
|
|
/* Extended DV Pack #1
|
|
* 48 - 52
|
|
*/
|
|
GST_LOG_OBJECT (filter, " Extended DV Pack #2 : 0x%x", data[48]);
|
|
|
|
/* Extended DV Pack #1
|
|
* 53 - 57
|
|
*/
|
|
GST_LOG_OBJECT (filter, " Extended DV Pack #3 : 0x%x", data[53]);
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
}
|
|
|
|
static GstFlowReturn
|
|
parse_audio_frame (GstHDVParse * filter, guint8 * data, guint64 size,
|
|
GstStructure * st)
|
|
{
|
|
guint32 etn;
|
|
guint8 nbmute, nbaau;
|
|
guint64 pts;
|
|
guint16 audio_comp;
|
|
guint8 bitrate, fs, compress, channel;
|
|
guint8 option, cgms;
|
|
gboolean acly, recst;
|
|
|
|
GST_LOG_OBJECT (filter, "Audio Frame Pack");
|
|
|
|
/* Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* ---------------------------------
|
|
* 0 | Size (0x0f) |
|
|
* ---------------------------------
|
|
* 1 | |
|
|
* 2 | ETN |
|
|
* 3 | |
|
|
* ---------------------------------
|
|
* 4 |Nb Audio Mute | Number of AAU |
|
|
* ---------------------------------
|
|
*/
|
|
|
|
if (data[0] != 0x0f) {
|
|
GST_WARNING ("Invalid size for audio frame");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
etn = data[3] << 16 | data[2] << 8 | data[1];
|
|
|
|
GST_LOG_OBJECT (filter, " ETN : %" G_GUINT32_FORMAT, etn);
|
|
|
|
/* Pack-A Information
|
|
* ---------------------------------
|
|
* 4 |Nb Audio Mute | Number of AAU |
|
|
* ---------------------------------
|
|
* 5 | |
|
|
* 6 | PTS |
|
|
* 7 | |
|
|
* 8 | |
|
|
* ----------------------------- |
|
|
* 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
|
|
* ---------------------------------
|
|
* 10 | Audio |
|
|
* 11 | Compensation |
|
|
* ---------------------------------
|
|
*/
|
|
|
|
/* Number of Audio Mute Frames */
|
|
nbmute = data[4] >> 4;
|
|
/* Number of AAU */
|
|
nbaau = data[4] & 0x0f;
|
|
/* PTS of the first AAU immediatly following */
|
|
pts = (data[5] | data[6] << 8 | data[7] << 16 | data[8] << 24);
|
|
pts |= (guint64) (data[9] & 0x1) << 32;
|
|
if (G_UNLIKELY (VALIDATE && (data[9] & 0xfe))) {
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
/* Amount of compensation */
|
|
audio_comp = data[10] | data[11] << 8;
|
|
|
|
GST_LOG_OBJECT (filter, " Pack-A Information");
|
|
GST_LOG_OBJECT (filter, " Nb Audio Mute Frames : %d", nbmute);
|
|
GST_LOG_OBJECT (filter, " Nb AAU : %d", nbaau);
|
|
GST_LOG_OBJECT (filter,
|
|
" PTS : %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT ")",
|
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pts)), pts);
|
|
GST_LOG_OBJECT (filter, " Audio Compensation : %" G_GUINT32_FORMAT,
|
|
audio_comp);
|
|
|
|
/* Audio Mode
|
|
* ---------------------------------
|
|
* 12 | Bitrate Index | 0 |Samplerate |
|
|
* ---------------------------------
|
|
* 13 | Compression | Channels |
|
|
* ---------------------------------
|
|
* 14 | X | Anciliary Option |
|
|
* ---------------------------------
|
|
*
|
|
* X : Anciliary data present
|
|
*/
|
|
|
|
bitrate = data[12] >> 4;
|
|
fs = data[12] & 0x7;
|
|
if (G_UNLIKELY (VALIDATE && (data[12] & 0x08)))
|
|
return GST_FLOW_ERROR;
|
|
|
|
compress = data[13] >> 4;
|
|
channel = data[13] & 0xf;
|
|
acly = data[14] & 0x80;
|
|
option = data[14] & 0x7f;
|
|
|
|
GST_LOG_OBJECT (filter, " Audio Mode");
|
|
GST_LOG_OBJECT (filter, " Bitrate : %s (0x%x)",
|
|
(bitrate == 0xe) ? "384kbps" : "RESERVED", bitrate);
|
|
GST_LOG_OBJECT (filter, " Samplerate : %s (0x%x)",
|
|
(fs == 0x1) ? "48 kHz" : "RESERVED", fs);
|
|
GST_LOG_OBJECT (filter, " Compression : %s (0x%x)",
|
|
(compress == 0x2) ? "MPEG-1 Layer II" : "RESERVED", compress);
|
|
GST_LOG_OBJECT (filter, " Channels : %s (0x%x)",
|
|
(channel == 0) ? "Stereo" : "RESERVED", channel);
|
|
GST_LOG_OBJECT (filter, " Anciliary data %s %s (0x%x)",
|
|
acly ? "PRESENT" : "ABSENT",
|
|
(option == 0xc) ? "IEC 13818-3" : "ABSENT/RESERVED", option);
|
|
/*
|
|
* ---------------------------------
|
|
* 15 | CGMS | R | 0 | 0 | 0 | 0 | 0 |
|
|
* ---------------------------------
|
|
*
|
|
* R : Recording Start Point
|
|
*/
|
|
|
|
cgms = data[15] & 0xc0;
|
|
recst = data[15] & 0x20;
|
|
|
|
GST_LOG_OBJECT (filter, " Misc");
|
|
GST_LOG_OBJECT (filter, " CGMS : 0x%x", cgms);
|
|
GST_LOG_OBJECT (filter, " Recording Start Point %s",
|
|
(recst) ? "ABSENT" : "PRESENT");
|
|
|
|
gst_structure_set (st, "PTS", G_TYPE_UINT64, MPEGTIME_TO_GSTTIME (pts),
|
|
"recording-start-point", G_TYPE_BOOLEAN, !recst, NULL);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_hdvparse_parse (GstHDVParse * filter, GstBuffer * buf)
|
|
{
|
|
GstFlowReturn res = GST_FLOW_OK;
|
|
guint8 *data = GST_BUFFER_DATA (buf);
|
|
guint64 offs = 0;
|
|
guint64 insize = GST_BUFFER_SIZE (buf);
|
|
GstStructure *st;
|
|
|
|
/* Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* ---------------------------------
|
|
* 0 | 0 | KEYWORD |
|
|
* (1) | LENGTH |
|
|
* ....
|
|
*
|
|
* KEYWORD :
|
|
* 0x00 - 0x3F : Constant length (5 bytes)
|
|
* 0x40 - 0x7F : Variable length (LENGTH + 1)
|
|
*
|
|
* LENGTH : if present, size of fields 1-N
|
|
*
|
|
* Known keyword values:
|
|
* 0x00-0x07 : AUX-V
|
|
* 0x08-0x3E : RESERVED
|
|
* 0x3F : AUX-N NO-INFO
|
|
* 0x40-0x43 : AUX-A
|
|
* 0x44-0x47 : AUX-V
|
|
* 0x48-0x4F : AUX-N
|
|
* 0x50-0x53 : AUX-SYS
|
|
* 0x54-0x7E : RESERVED
|
|
* 0x7F : AUX-N NULL PACK
|
|
*/
|
|
|
|
st = gst_structure_empty_new ("hdv-aux");
|
|
|
|
while (res == GST_FLOW_OK && (offs < insize)) {
|
|
guint8 kw = data[offs] & 0x7f;
|
|
guint8 size;
|
|
|
|
/* Variable size packs */
|
|
if (kw >= 0x40) {
|
|
size = data[offs + 1];
|
|
} else
|
|
size = 4;
|
|
|
|
/* Size validation */
|
|
GST_DEBUG ("kw:0x%x, insize:%" G_GUINT64_FORMAT ", offs:%" G_GUINT64_FORMAT
|
|
", size:%d", kw, insize, offs, size);
|
|
if (insize < offs + size) {
|
|
res = GST_FLOW_ERROR;
|
|
goto beach;
|
|
}
|
|
|
|
switch (kw) {
|
|
case 0x01:
|
|
GST_LOG ("BINARY GROUP");
|
|
offs += size + 1;
|
|
break;
|
|
case 0x07:
|
|
GST_LOG ("ETN pack");
|
|
break;
|
|
case 0x40:
|
|
GST_LOG ("Audio frame pack");
|
|
res = parse_audio_frame (filter, data + offs + 1, size, st);
|
|
offs += size + 2;
|
|
break;
|
|
case 0x3f:
|
|
GST_LOG ("NO INFO pack");
|
|
offs += size + 1;
|
|
break;
|
|
case 0x44:
|
|
GST_LOG ("Video frame pack");
|
|
res = parse_video_frame (filter, data + offs + 1, size, st);
|
|
offs += size + 2;
|
|
break;
|
|
case 0x48:
|
|
case 0x49:
|
|
case 0x4A:
|
|
case 0x4B:
|
|
GST_LOG ("DV multi-pack");
|
|
res = parse_dv_multi_pack (filter, data + offs + 1, size, st);
|
|
offs += size + 2;
|
|
break;
|
|
default:
|
|
GST_WARNING_OBJECT (filter, "Unknown AUX pack data of type 0x%x", kw);
|
|
res = GST_FLOW_ERROR;
|
|
}
|
|
}
|
|
|
|
beach:
|
|
if (gst_structure_n_fields (st)) {
|
|
GstMessage *msg;
|
|
/* Emit the element message */
|
|
msg = gst_message_new_element (GST_OBJECT (filter), st);
|
|
gst_element_post_message (GST_ELEMENT (filter), msg);
|
|
} else
|
|
gst_structure_free (st);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
/* GstBaseTransform vmethod implementations */
|
|
|
|
static GstFlowReturn
|
|
gst_hdvparse_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
|
|
{
|
|
GstHDVParse *filter = GST_HDVPARSE (base);
|
|
|
|
return gst_hdvparse_parse (filter, outbuf);
|
|
}
|
|
|
|
|
|
/* 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)
|
|
{
|
|
return gst_element_register (HDVParse, "hdvparse", GST_RANK_NONE,
|
|
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/")
|