From ae82126a56898c37b4e242cc370dfa28c151c471 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Sun, 29 Jun 2008 19:52:51 +0000 Subject: [PATCH] gst/avi/avi-ids.h: Add vprp chunk related structures. Original commit message from CVS: * gst/avi/avi-ids.h: Add vprp chunk related structures. * gst/avi/gstavidemux.c: (gst_avi_demux_riff_parse_vprp), (gst_avi_demux_parse_stream): Parse optional vprp chunk and add calculated pixel-aspect-ratio to caps. Fixes #539482. * gst/avi/gstavimux.h: * gst/avi/gstavimux.c: (gst_avi_mux_pad_reset), (gst_avi_mux_vidsink_set_caps), (gst_avi_mux_riff_get_avi_header): Add a vprp chunk if non-trival pixel-aspect-ratio provided in caps. --- ChangeLog | 13 ++++ gst/avi/avi-ids.h | 29 +++++++++ gst/avi/gstavidemux.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++- gst/avi/gstavimux.c | 81 ++++++++++++++++++++++++- gst/avi/gstavimux.h | 2 + 5 files changed, 286 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 03cd272..ac48c0d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2008-06-29 Mark Nauwelaerts + + * gst/avi/avi-ids.h: + Add vprp chunk related structures. + * gst/avi/gstavidemux.c: (gst_avi_demux_riff_parse_vprp), + (gst_avi_demux_parse_stream): + Parse optional vprp chunk and add calculated pixel-aspect-ratio + to caps. Fixes #539482. + * gst/avi/gstavimux.h: + * gst/avi/gstavimux.c: (gst_avi_mux_pad_reset), + (gst_avi_mux_vidsink_set_caps), (gst_avi_mux_riff_get_avi_header): + Add a vprp chunk if non-trival pixel-aspect-ratio provided in caps. + 2008-06-28 Mark Nauwelaerts * tests/check/elements/avimux.c: (check_avimux_pad): diff --git a/gst/avi/avi-ids.h b/gst/avi/avi-ids.h index f9e2dd3..8ca550b 100644 --- a/gst/avi/avi-ids.h +++ b/gst/avi/avi-ids.h @@ -45,4 +45,33 @@ typedef struct _gst_riff_avih { guint32 length; } gst_riff_avih; +/* vprp (video properties) ODML header */ +/* see ODML spec for some/more explanation */ +#define GST_RIFF_TAG_vprp GST_MAKE_FOURCC ('v','p','r','p') +#define GST_RIFF_VPRP_VIDEO_FIELDS (2) + +typedef struct _gst_riff_vprp_video_field_desc { + guint32 compressed_bm_height; + guint32 compressed_bm_width; + guint32 valid_bm_height; + guint32 valid_bm_width; + guint32 valid_bm_x_offset; + guint32 valid_bm_y_offset; + guint32 video_x_t_offset; + guint32 video_y_start; +} gst_riff_vprp_video_field_desc; + +typedef struct _gst_riff_vprp { + guint32 format_token; /* whether fields defined by standard */ + guint32 standard; /* video display standard, UNKNOWN, PAL, etc */ + guint32 vert_rate; /* vertical refresh rate */ + guint32 hor_t_total; /* width */ + guint32 vert_lines; /* height */ + guint32 aspect; /* aspect ratio high word:low word */ + guint32 width; /* active width */ + guint32 height; /* active height */ + guint32 fields; /* field count */ + gst_riff_vprp_video_field_desc field_info[GST_RIFF_VPRP_VIDEO_FIELDS]; +} gst_riff_vprp; + #endif /* __GST_AVI_H__ */ diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index 38211be..a48ceee 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -1197,6 +1197,127 @@ gst_avi_demux_read_subindexes_pull (GstAviDemux * avi, } /* + * gst_avi_demux_riff_parse_vprp: + * @element: caller element (used for debugging/error). + * @buf: input data to be used for parsing, stripped from header. + * @vprp: a pointer (returned by this function) to a filled-in vprp + * structure. Caller should free it. + * + * Parses a video stream´s vprp. This function takes ownership of @buf. + * + * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream + * should be skipped on error, but it is not fatal. + */ +static gboolean +gst_avi_demux_riff_parse_vprp (GstElement * element, + GstBuffer * buf, gst_riff_vprp ** _vprp) +{ + gst_riff_vprp *vprp; + gint k; + + g_return_val_if_fail (buf != NULL, FALSE); + g_return_val_if_fail (_vprp != NULL, FALSE); + + if (GST_BUFFER_SIZE (buf) < G_STRUCT_OFFSET (gst_riff_vprp, field_info)) + goto too_small; + + vprp = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + vprp->format_token = GUINT32_FROM_LE (vprp->format_token); + vprp->standard = GUINT32_FROM_LE (vprp->standard); + vprp->vert_rate = GUINT32_FROM_LE (vprp->vert_rate); + vprp->hor_t_total = GUINT32_FROM_LE (vprp->hor_t_total); + vprp->vert_lines = GUINT32_FROM_LE (vprp->vert_lines); + vprp->aspect = GUINT32_FROM_LE (vprp->aspect); + vprp->width = GUINT32_FROM_LE (vprp->width); + vprp->height = GUINT32_FROM_LE (vprp->height); + vprp->fields = GUINT32_FROM_LE (vprp->fields); +#endif + + /* size checking */ + /* calculate fields based on size */ + k = (GST_BUFFER_SIZE (buf) - G_STRUCT_OFFSET (gst_riff_vprp, field_info)) / + vprp->fields; + if (vprp->fields > k) { + GST_WARNING_OBJECT (element, + "vprp header indicated %d fields, only %d available", vprp->fields, k); + vprp->fields = k; + } + if (vprp->fields > GST_RIFF_VPRP_VIDEO_FIELDS) { + GST_WARNING_OBJECT (element, + "vprp header indicated %d fields, at most %d supported", vprp->fields, + GST_RIFF_VPRP_VIDEO_FIELDS); + vprp->fields = GST_RIFF_VPRP_VIDEO_FIELDS; + } +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + for (k = 0; k < vprp->fields; k++) { + gst_riff_vprp_video_field_desc *fd; + + fd = &(vidpad->vprp.field_info[k]); + fd->compressed_bm_height = GUINT32_FROM_LE (fd->compressed_bm_height); + fd->compressed_bm_width = GUINT32_FROM_LE (fd->compressed_bm_width); + fd->valid_bm_height = GUINT32_FROM_LE (fd->valid_bm_height); + fd->valid_bm_width = GUINT16_FROM_LE (fd->valid_bm_width); + fd->valid_bm_x_offset = GUINT16_FROM_LE (fd->valid_bm_x_offset); + fd->valid_bm_y_offset = GUINT32_FROM_LE (fd->valid_bm_y_offset); + fd->video_x_t_offset = GUINT32_FROM_LE (fd->video_x_t_offset); + fd->video_y_start = GUINT32_FROM_LE (fd->video_y_start); + } +#endif + + /* debug */ + GST_INFO_OBJECT (element, "vprp tag found in context vids:"); + GST_INFO_OBJECT (element, " format_token %d", vprp->format_token); + GST_INFO_OBJECT (element, " standard %d", vprp->standard); + GST_INFO_OBJECT (element, " vert_rate %d", vprp->vert_rate); + GST_INFO_OBJECT (element, " hor_t_total %d", vprp->hor_t_total); + GST_INFO_OBJECT (element, " vert_lines %d", vprp->vert_lines); + GST_INFO_OBJECT (element, " aspect %d:%d", vprp->aspect >> 16, + vprp->aspect & 0xffff); + GST_INFO_OBJECT (element, " width %d", vprp->width); + GST_INFO_OBJECT (element, " height %d", vprp->height); + GST_INFO_OBJECT (element, " fields %d", vprp->fields); + for (k = 0; k < vprp->fields; k++) { + gst_riff_vprp_video_field_desc *fd; + + fd = &(vprp->field_info[k]); + GST_INFO_OBJECT (element, " field %u description:", k); + GST_INFO_OBJECT (element, " compressed_bm_height %d", + fd->compressed_bm_height); + GST_INFO_OBJECT (element, " compressed_bm_width %d", + fd->compressed_bm_width); + GST_INFO_OBJECT (element, " valid_bm_height %d", + fd->valid_bm_height); + GST_INFO_OBJECT (element, " valid_bm_width %d", fd->valid_bm_width); + GST_INFO_OBJECT (element, " valid_bm_x_offset %d", + fd->valid_bm_x_offset); + GST_INFO_OBJECT (element, " valid_bm_y_offset %d", + fd->valid_bm_y_offset); + GST_INFO_OBJECT (element, " video_x_t_offset %d", + fd->video_x_t_offset); + GST_INFO_OBJECT (element, " video_y_start %d", fd->video_y_start); + } + + gst_buffer_unref (buf); + + *_vprp = vprp; + + return TRUE; + + /* ERRORS */ +too_small: + { + GST_ERROR_OBJECT (element, + "Too small vprp (%d available, at least %d needed)", + GST_BUFFER_SIZE (buf), + (int) G_STRUCT_OFFSET (gst_riff_vprp, field_info)); + gst_buffer_unref (buf); + return FALSE; + } +} + +/* * gst_avi_demux_parse_stream: * @avi: calling element (used for debugging/errors). * @buf: input buffer used to parse the stream. @@ -1223,7 +1344,8 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) GstCaps *caps = NULL; GstPad *pad; GstElement *element; - gboolean got_strh = FALSE, got_strf = FALSE; + gboolean got_strh = FALSE, got_strf = FALSE, got_vprp = FALSE; + gst_riff_vprp *vprp = NULL; element = GST_ELEMENT_CAST (avi); @@ -1294,6 +1416,30 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) got_strf = TRUE; break; } + case GST_RIFF_TAG_vprp: + { + if (got_vprp) { + GST_WARNING_OBJECT (avi, "Ignoring additional vprp chunk"); + break; + } + if (!got_strh) { + GST_ERROR_OBJECT (avi, "Found vprp chunk before strh chunk"); + goto fail; + } + if (!got_strf) { + GST_ERROR_OBJECT (avi, "Found vprp chunk before strf chunk"); + goto fail; + } + + if (!gst_avi_demux_riff_parse_vprp (element, sub, &vprp)) { + GST_WARNING_OBJECT (avi, "Failed to parse vprp chunk"); + /* not considered fatal */ + g_free (vprp); + vprp = NULL; + } else + got_vprp = TRUE; + break; + } case GST_RIFF_TAG_strd: if (stream->initdata) gst_buffer_unref (stream->initdata); @@ -1362,6 +1508,21 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) if (!caps) { caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc", GST_TYPE_FOURCC, fourcc, NULL); + } else if (got_vprp && vprp) { + guint32 aspect_n, aspect_d; + gint n, d; + + aspect_n = vprp->aspect >> 16; + aspect_d = vprp->aspect & 0xffff; + /* calculate the pixel aspect ratio using w/h and aspect ratio */ + n = aspect_n * stream->strf.vids->height; + d = aspect_d * stream->strf.vids->width; + if (n && d) + gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, + n, d, NULL); + /* very local, not needed elsewhere */ + g_free (vprp); + vprp = NULL; } tag_name = GST_TAG_VIDEO_CODEC; avi->num_v_streams++; @@ -1483,6 +1644,7 @@ fail: gst_buffer_unref (buf); if (sub) gst_buffer_unref (sub); + g_free (vprp); g_free (codec_name); g_free (stream->strh); g_free (stream->strf.data); diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c index 3beff5e..80d8ba8 100644 --- a/gst/avi/gstavimux.c +++ b/gst/avi/gstavimux.c @@ -337,6 +337,7 @@ gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free) } memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids)); + memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp)); } else { GstAviAudioPad *audpad = (GstAviAudioPad *) avipad; @@ -425,9 +426,10 @@ gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps) GstAviCollectData *collect_pad; GstStructure *structure; const gchar *mimetype; - const GValue *fps; + const GValue *fps, *par; const GValue *codec_data; gint width, height; + gint par_n, par_d; avimux = GST_AVI_MUX (gst_pad_get_parent (pad)); @@ -463,6 +465,36 @@ gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps) avipad->parent.hdr.rate = gst_value_get_fraction_numerator (fps); avipad->parent.hdr.scale = gst_value_get_fraction_denominator (fps); + /* (pixel) aspect ratio data, if any */ + par = gst_structure_get_value (structure, "pixel-aspect-ratio"); + /* only use video properties header if there is non-trivial aspect info */ + if (par && GST_VALUE_HOLDS_FRACTION (par) && + ((par_n = gst_value_get_fraction_numerator (par)) != + (par_d = gst_value_get_fraction_denominator (par)))) { + GValue to_ratio = { 0, }; + guint ratio_n, ratio_d; + + /* some fraction voodoo to obtain simplest possible ratio */ + g_value_init (&to_ratio, GST_TYPE_FRACTION); + gst_value_set_fraction (&to_ratio, width * par_n, height * par_d); + ratio_n = gst_value_get_fraction_numerator (&to_ratio); + ratio_d = gst_value_get_fraction_denominator (&to_ratio); + GST_DEBUG_OBJECT (avimux, "generating vprp data with aspect ratio %d/%d", + ratio_n, ratio_d); + /* simply fill in */ + avipad->vprp.vert_rate = avipad->parent.hdr.rate / avipad->parent.hdr.scale; + avipad->vprp.hor_t_total = width; + avipad->vprp.vert_lines = height; + avipad->vprp.aspect = (ratio_n) << 16 | (ratio_d & 0xffff); + avipad->vprp.width = width; + avipad->vprp.height = height; + avipad->vprp.fields = 1; + avipad->vprp.field_info[0].compressed_bm_height = height; + avipad->vprp.field_info[0].compressed_bm_width = width; + avipad->vprp.field_info[0].valid_bm_height = height; + avipad->vprp.field_info[0].valid_bm_width = width; + } + /* codec initialization data, if any */ codec_data = gst_structure_get_value (structure, "codec_data"); if (codec_data) { @@ -957,6 +989,7 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux) size += avimux->codec_data_size + 100 + sizeof (gst_riff_avih) + (g_slist_length (avimux->sinkpads) * (100 + sizeof (gst_riff_strh_full) + sizeof (gst_riff_strf_vids) + + sizeof (gst_riff_vprp) + sizeof (gst_riff_strf_auds) + ODML_SUPERINDEX_SIZE)); buffer = gst_buffer_new_and_alloc (size); @@ -1003,13 +1036,21 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux) GstAviPad *avipad = (GstAviPad *) node->data; GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad; GstAviAudioPad *audpad = (GstAviAudioPad *) avipad; - guint codec_size = 0, strl_size = 0; + guint codec_size = 0, strl_size = 0, vprp_size = 0; if (avipad->is_video) { if (vidpad->vids_codec_data) codec_size = GST_BUFFER_SIZE (vidpad->vids_codec_data); strl_size = sizeof (gst_riff_strh_full) + sizeof (gst_riff_strf_vids) + codec_size + 4 * 5 + ODML_SUPERINDEX_SIZE; + if (vidpad->vprp.aspect) { + /* let's be on the safe side */ + vidpad->vprp.fields = MIN (vidpad->vprp.fields, + GST_RIFF_VPRP_VIDEO_FIELDS); + vprp_size = G_STRUCT_OFFSET (gst_riff_vprp, field_info) + + (vidpad->vprp.fields * sizeof (gst_riff_vprp_video_field_desc)); + strl_size += 4 * 2 + vprp_size; + } } else { if (audpad->auds_codec_data) codec_size = GST_BUFFER_SIZE (audpad->auds_codec_data); @@ -1072,6 +1113,42 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux) buffdata += codec_size; highmark += codec_size; } + + /* add video property data, mainly for aspect ratio, if any */ + if (vprp_size) { + gint f; + + /* the vprp header */ + memcpy (buffdata + 0, "vprp", 4); + GST_WRITE_UINT32_LE (buffdata + 4, vprp_size); + /* the actual data */ + GST_WRITE_UINT32_LE (buffdata + 8, vidpad->vprp.format_token); + GST_WRITE_UINT32_LE (buffdata + 12, vidpad->vprp.standard); + GST_WRITE_UINT32_LE (buffdata + 16, vidpad->vprp.vert_rate); + GST_WRITE_UINT32_LE (buffdata + 20, vidpad->vprp.hor_t_total); + GST_WRITE_UINT32_LE (buffdata + 24, vidpad->vprp.vert_lines); + GST_WRITE_UINT32_LE (buffdata + 28, vidpad->vprp.aspect); + GST_WRITE_UINT32_LE (buffdata + 32, vidpad->vprp.width); + GST_WRITE_UINT32_LE (buffdata + 36, vidpad->vprp.height); + GST_WRITE_UINT32_LE (buffdata + 40, vidpad->vprp.fields); + buffdata += codec_size + 44; + highmark += codec_size + 44; + for (f = 0; f < vidpad->vprp.fields; ++f) { + gst_riff_vprp_video_field_desc *fd; + + fd = &(vidpad->vprp.field_info[f]); + GST_WRITE_UINT32_LE (buffdata + 0, fd->compressed_bm_height); + GST_WRITE_UINT32_LE (buffdata + 4, fd->compressed_bm_width); + GST_WRITE_UINT32_LE (buffdata + 8, fd->valid_bm_height); + GST_WRITE_UINT32_LE (buffdata + 12, fd->valid_bm_width); + GST_WRITE_UINT32_LE (buffdata + 16, fd->valid_bm_x_offset); + GST_WRITE_UINT32_LE (buffdata + 20, fd->valid_bm_y_offset); + GST_WRITE_UINT32_LE (buffdata + 24, fd->video_x_t_offset); + GST_WRITE_UINT32_LE (buffdata + 28, fd->video_y_start); + buffdata += codec_size + 32; + highmark += codec_size + 32; + } + } } else { /* the audio header */ memcpy (buffdata + 0, "strf", 4); diff --git a/gst/avi/gstavimux.h b/gst/avi/gstavimux.h index 0100e03..6fb9a47 100644 --- a/gst/avi/gstavimux.h +++ b/gst/avi/gstavimux.h @@ -89,6 +89,8 @@ typedef struct _GstAviVideoPad { gst_riff_strf_vids vids; /* extra data */ GstBuffer *vids_codec_data; + /* ODML video properties */ + gst_riff_vprp vprp; } GstAviVideoPad; -- 2.7.4