| /* |
| * 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., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, 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_static_metadata (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/") |