mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-02 14:36:41 +00:00
isomp4: Make non-seekable downstream an error in normal mode
When not in fast-start or fragmented mode, we need to be able to rewrite the size of the mdat atom, or else the output just won't be playable - the mdat placeholder with size == 0 will cover the rest of the file, including any moov atom we write out. https://bugzilla.gnome.org/show_bug.cgi?id=708808
This commit is contained in:
parent
cf54d4cc67
commit
3d59b5f814
3 changed files with 53 additions and 26 deletions
|
@ -1691,10 +1691,29 @@ serialize_error:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_qt_mux_downstream_is_seekable (GstQTMux * qtmux)
|
||||||
|
{
|
||||||
|
gboolean seekable = FALSE;
|
||||||
|
GstQuery *query = gst_query_new_seeking (GST_FORMAT_BYTES);
|
||||||
|
|
||||||
|
if (gst_pad_peer_query (qtmux->srcpad, query)) {
|
||||||
|
gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
|
||||||
|
GST_INFO_OBJECT (qtmux, "downstream is %sseekable", seekable ? "" : "not ");
|
||||||
|
} else {
|
||||||
|
/* have to assume seeking is not supported if query not handled downstream */
|
||||||
|
GST_WARNING_OBJECT (qtmux, "downstream did not handle seeking query");
|
||||||
|
seekable = FALSE;
|
||||||
|
}
|
||||||
|
gst_query_unref (query);
|
||||||
|
|
||||||
|
return seekable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_qt_mux_start_file (GstQTMux * qtmux)
|
gst_qt_mux_start_file (GstQTMux * qtmux)
|
||||||
{
|
{
|
||||||
GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
|
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
GstSegment segment;
|
GstSegment segment;
|
||||||
|
@ -1713,30 +1732,15 @@ gst_qt_mux_start_file (GstQTMux * qtmux)
|
||||||
gst_pad_set_caps (qtmux->srcpad, caps);
|
gst_pad_set_caps (qtmux->srcpad, caps);
|
||||||
gst_caps_unref (caps);
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
/* if not streaming, check if downstream is seekable */
|
/* if not streaming or doing fast-start, check if downstream is seekable */
|
||||||
if (!qtmux->streamable) {
|
if (!qtmux->streamable) {
|
||||||
gboolean seekable;
|
if (!gst_qt_mux_downstream_is_seekable (qtmux)) {
|
||||||
GstQuery *query;
|
if (qtmux->fragment_duration == 0) {
|
||||||
|
|
||||||
query = gst_query_new_seeking (GST_FORMAT_BYTES);
|
|
||||||
if (gst_pad_peer_query (qtmux->srcpad, query)) {
|
|
||||||
gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
|
|
||||||
GST_INFO_OBJECT (qtmux, "downstream is %sseekable",
|
|
||||||
seekable ? "" : "not ");
|
|
||||||
} else {
|
|
||||||
/* have to assume seeking is supported if query not handled downstream */
|
|
||||||
GST_WARNING_OBJECT (qtmux, "downstream did not handle seeking query");
|
|
||||||
seekable = FALSE;
|
|
||||||
}
|
|
||||||
gst_query_unref (query);
|
|
||||||
if (!seekable) {
|
|
||||||
if (qtmux_klass->format != GST_QT_MUX_FORMAT_ISML) {
|
|
||||||
if (!qtmux->fast_start) {
|
if (!qtmux->fast_start) {
|
||||||
GST_ELEMENT_WARNING (qtmux, STREAM, FAILED,
|
GST_ELEMENT_ERROR (qtmux, STREAM, MUX,
|
||||||
("Downstream is not seekable and headers can't be rewritten"),
|
("Downstream is not seekable - will not be able to create a playable file"),
|
||||||
(NULL));
|
(NULL));
|
||||||
/* FIXME: Is there something better we can do? */
|
return GST_FLOW_ERROR;
|
||||||
qtmux->streamable = TRUE;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING_OBJECT (qtmux, "downstream is not seekable, but "
|
GST_WARNING_OBJECT (qtmux, "downstream is not seekable, but "
|
||||||
|
|
|
@ -196,6 +196,9 @@ struct _GstQTMux
|
||||||
gchar *fast_start_file_path;
|
gchar *fast_start_file_path;
|
||||||
gchar *moov_recov_file_path;
|
gchar *moov_recov_file_path;
|
||||||
guint32 fragment_duration;
|
guint32 fragment_duration;
|
||||||
|
/* Whether or not to work in 'streamable' mode and not
|
||||||
|
* seek to rewrite headers - only valid for fragmented
|
||||||
|
* mode. */
|
||||||
gboolean streamable;
|
gboolean streamable;
|
||||||
|
|
||||||
/* for request pad naming */
|
/* for request pad naming */
|
||||||
|
|
|
@ -153,8 +153,24 @@ teardown_src_pad (GstPad * srcpad)
|
||||||
gst_object_unref (srcpad);
|
gst_object_unref (srcpad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean downstream_is_seekable;
|
||||||
|
static gboolean
|
||||||
|
qtmux_sinkpad_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
||||||
|
{
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
if (GST_QUERY_TYPE (query) == GST_QUERY_SEEKING) {
|
||||||
|
gst_query_set_seeking (query, GST_FORMAT_BYTES, downstream_is_seekable, 0,
|
||||||
|
-1);
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static GstElement *
|
static GstElement *
|
||||||
setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname)
|
setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
|
||||||
|
gboolean seekable)
|
||||||
{
|
{
|
||||||
GstElement *qtmux;
|
GstElement *qtmux;
|
||||||
|
|
||||||
|
@ -162,6 +178,10 @@ setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname)
|
||||||
qtmux = gst_check_setup_element ("qtmux");
|
qtmux = gst_check_setup_element ("qtmux");
|
||||||
mysrcpad = setup_src_pad (qtmux, srctemplate, sinkname);
|
mysrcpad = setup_src_pad (qtmux, srctemplate, sinkname);
|
||||||
mysinkpad = gst_check_setup_sink_pad (qtmux, &sinktemplate);
|
mysinkpad = gst_check_setup_sink_pad (qtmux, &sinktemplate);
|
||||||
|
|
||||||
|
downstream_is_seekable = seekable;
|
||||||
|
gst_pad_set_query_function (mysinkpad, qtmux_sinkpad_query);
|
||||||
|
|
||||||
gst_pad_set_active (mysrcpad, TRUE);
|
gst_pad_set_active (mysrcpad, TRUE);
|
||||||
gst_pad_set_active (mysinkpad, TRUE);
|
gst_pad_set_active (mysinkpad, TRUE);
|
||||||
|
|
||||||
|
@ -195,7 +215,7 @@ check_qtmux_pad (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
|
||||||
guint8 data2[4] = "moov";
|
guint8 data2[4] = "moov";
|
||||||
GstSegment segment;
|
GstSegment segment;
|
||||||
|
|
||||||
qtmux = setup_qtmux (srctemplate, sinkname);
|
qtmux = setup_qtmux (srctemplate, sinkname, TRUE);
|
||||||
g_object_set (qtmux, "dts-method", dts_method, NULL);
|
g_object_set (qtmux, "dts-method", dts_method, NULL);
|
||||||
fail_unless (gst_element_set_state (qtmux,
|
fail_unless (gst_element_set_state (qtmux,
|
||||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
|
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
|
||||||
|
@ -285,7 +305,7 @@ check_qtmux_pad_fragmented (GstStaticPadTemplate * srctemplate,
|
||||||
guint8 data4[4] = "mfra";
|
guint8 data4[4] = "mfra";
|
||||||
GstSegment segment;
|
GstSegment segment;
|
||||||
|
|
||||||
qtmux = setup_qtmux (srctemplate, sinkname);
|
qtmux = setup_qtmux (srctemplate, sinkname, !streamable);
|
||||||
g_object_set (qtmux, "dts-method", dts_method, NULL);
|
g_object_set (qtmux, "dts-method", dts_method, NULL);
|
||||||
g_object_set (qtmux, "fragment-duration", 2000, NULL);
|
g_object_set (qtmux, "fragment-duration", 2000, NULL);
|
||||||
g_object_set (qtmux, "streamable", streamable, NULL);
|
g_object_set (qtmux, "streamable", streamable, NULL);
|
||||||
|
@ -515,7 +535,7 @@ GST_END_TEST;
|
||||||
|
|
||||||
GST_START_TEST (test_reuse)
|
GST_START_TEST (test_reuse)
|
||||||
{
|
{
|
||||||
GstElement *qtmux = setup_qtmux (&srcvideotemplate, "video_%u");
|
GstElement *qtmux = setup_qtmux (&srcvideotemplate, "video_%u", TRUE);
|
||||||
GstBuffer *inbuffer;
|
GstBuffer *inbuffer;
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
GstSegment segment;
|
GstSegment segment;
|
||||||
|
|
Loading…
Reference in a new issue