gstreamer/gst/hdvparse/gsthdvparse.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/")