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.
This commit is contained in:
Mark Nauwelaerts 2008-06-29 19:52:51 +00:00
parent cb0b3da393
commit ae82126a56
5 changed files with 286 additions and 3 deletions

View file

@ -1,3 +1,16 @@
2008-06-29 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
* 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 <mark.nauwelaerts@collabora.co.uk> 2008-06-28 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
* tests/check/elements/avimux.c: (check_avimux_pad): * tests/check/elements/avimux.c: (check_avimux_pad):

View file

@ -45,4 +45,33 @@ typedef struct _gst_riff_avih {
guint32 length; guint32 length;
} gst_riff_avih; } 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__ */ #endif /* __GST_AVI_H__ */

View file

@ -1196,6 +1196,127 @@ gst_avi_demux_read_subindexes_pull (GstAviDemux * avi,
GST_DEBUG_OBJECT (avi, "index %s", ((*index) ? "!= 0" : "== 0")); GST_DEBUG_OBJECT (avi, "index %s", ((*index) ? "!= 0" : "== 0"));
} }
/*
* 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: * gst_avi_demux_parse_stream:
* @avi: calling element (used for debugging/errors). * @avi: calling element (used for debugging/errors).
@ -1223,7 +1344,8 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
GstCaps *caps = NULL; GstCaps *caps = NULL;
GstPad *pad; GstPad *pad;
GstElement *element; 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); element = GST_ELEMENT_CAST (avi);
@ -1294,6 +1416,30 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
got_strf = TRUE; got_strf = TRUE;
break; 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: case GST_RIFF_TAG_strd:
if (stream->initdata) if (stream->initdata)
gst_buffer_unref (stream->initdata); gst_buffer_unref (stream->initdata);
@ -1362,6 +1508,21 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
if (!caps) { if (!caps) {
caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc", caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
GST_TYPE_FOURCC, fourcc, NULL); 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; tag_name = GST_TAG_VIDEO_CODEC;
avi->num_v_streams++; avi->num_v_streams++;
@ -1483,6 +1644,7 @@ fail:
gst_buffer_unref (buf); gst_buffer_unref (buf);
if (sub) if (sub)
gst_buffer_unref (sub); gst_buffer_unref (sub);
g_free (vprp);
g_free (codec_name); g_free (codec_name);
g_free (stream->strh); g_free (stream->strh);
g_free (stream->strf.data); g_free (stream->strf.data);

View file

@ -337,6 +337,7 @@ gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free)
} }
memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids)); memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids));
memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp));
} else { } else {
GstAviAudioPad *audpad = (GstAviAudioPad *) avipad; GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
@ -425,9 +426,10 @@ gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps)
GstAviCollectData *collect_pad; GstAviCollectData *collect_pad;
GstStructure *structure; GstStructure *structure;
const gchar *mimetype; const gchar *mimetype;
const GValue *fps; const GValue *fps, *par;
const GValue *codec_data; const GValue *codec_data;
gint width, height; gint width, height;
gint par_n, par_d;
avimux = GST_AVI_MUX (gst_pad_get_parent (pad)); 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.rate = gst_value_get_fraction_numerator (fps);
avipad->parent.hdr.scale = gst_value_get_fraction_denominator (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 initialization data, if any */
codec_data = gst_structure_get_value (structure, "codec_data"); codec_data = gst_structure_get_value (structure, "codec_data");
if (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) size += avimux->codec_data_size + 100 + sizeof (gst_riff_avih)
+ (g_slist_length (avimux->sinkpads) * (100 + sizeof (gst_riff_strh_full) + (g_slist_length (avimux->sinkpads) * (100 + sizeof (gst_riff_strh_full)
+ sizeof (gst_riff_strf_vids) + sizeof (gst_riff_strf_vids)
+ sizeof (gst_riff_vprp)
+ sizeof (gst_riff_strf_auds) + sizeof (gst_riff_strf_auds)
+ ODML_SUPERINDEX_SIZE)); + ODML_SUPERINDEX_SIZE));
buffer = gst_buffer_new_and_alloc (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; GstAviPad *avipad = (GstAviPad *) node->data;
GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad; GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
GstAviAudioPad *audpad = (GstAviAudioPad *) 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 (avipad->is_video) {
if (vidpad->vids_codec_data) if (vidpad->vids_codec_data)
codec_size = GST_BUFFER_SIZE (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) strl_size = sizeof (gst_riff_strh_full) + sizeof (gst_riff_strf_vids)
+ codec_size + 4 * 5 + ODML_SUPERINDEX_SIZE; + 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 { } else {
if (audpad->auds_codec_data) if (audpad->auds_codec_data)
codec_size = GST_BUFFER_SIZE (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; buffdata += codec_size;
highmark += 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 { } else {
/* the audio header */ /* the audio header */
memcpy (buffdata + 0, "strf", 4); memcpy (buffdata + 0, "strf", 4);

View file

@ -89,6 +89,8 @@ typedef struct _GstAviVideoPad {
gst_riff_strf_vids vids; gst_riff_strf_vids vids;
/* extra data */ /* extra data */
GstBuffer *vids_codec_data; GstBuffer *vids_codec_data;
/* ODML video properties */
gst_riff_vprp vprp;
} GstAviVideoPad; } GstAviVideoPad;