Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
George Kiagiadakis
gst-plugins-good
Commits
890b1752
Commit
890b1752
authored
Mar 29, 2012
by
Alexander Saprykin
Committed by
Stefan Sauer
Mar 29, 2012
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
matroska: add chapter support in GstMatroskaReadCommon
parent
e7807712
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
434 additions
and
11 deletions
+434
-11
gst/matroska/matroska-read-common.c
gst/matroska/matroska-read-common.c
+427
-10
gst/matroska/matroska-read-common.h
gst/matroska/matroska-read-common.h
+7
-1
No files found.
gst/matroska/matroska-read-common.c
View file @
890b1752
...
...
@@ -56,6 +56,10 @@ GST_DEBUG_CATEGORY (matroskareadcommon_debug);
GST_DEBUG_OBJECT (common, "Parsing " element " element " \
" finished with '%s'", gst_flow_get_name (ret))
#define GST_MATROSKA_TOC_UID_CHAPTER "chapter"
#define GST_MATROSKA_TOC_UID_EDITION "edition"
#define GST_MATROSKA_TOC_UID_EMPTY "empty"
static
gboolean
gst_matroska_decompress_data
(
GstMatroskaTrackEncoding
*
enc
,
guint8
**
data_out
,
guint
*
size_out
,
...
...
@@ -694,16 +698,375 @@ gst_matroska_read_common_parse_attachments (GstMatroskaReadCommon * common,
return
ret
;
}
static
void
gst_matroska_read_common_parse_toc_tag
(
GstTocEntry
*
entry
,
GArray
*
edition_targets
,
GArray
*
chapter_targtes
,
GstTagList
*
tags
)
{
gchar
*
uid
;
guint
i
;
guint64
tgt
;
GArray
*
targets
;
GList
*
cur
;
targets
=
(
entry
->
type
==
GST_TOC_ENTRY_TYPE_EDITION
)
?
edition_targets
:
chapter_targtes
;
for
(
i
=
0
;
i
<
targets
->
len
;
++
i
)
{
tgt
=
g_array_index
(
targets
,
guint64
,
i
);
if
(
tgt
==
0
)
gst_tag_list_insert
(
entry
->
tags
,
tags
,
GST_TAG_MERGE_APPEND
);
else
{
uid
=
g_strdup_printf
(
"%"
G_GUINT64_FORMAT
,
tgt
);
if
(
g_strcmp0
(
entry
->
uid
,
uid
)
==
0
)
gst_tag_list_insert
(
entry
->
tags
,
tags
,
GST_TAG_MERGE_APPEND
);
g_free
(
uid
);
}
}
cur
=
entry
->
subentries
;
while
(
cur
!=
NULL
)
{
gst_matroska_read_common_parse_toc_tag
(
cur
->
data
,
edition_targets
,
chapter_targtes
,
tags
);
cur
=
cur
->
next
;
}
}
static
GstFlowReturn
gst_matroska_read_common_parse_metadata_targets
(
GstMatroskaReadCommon
*
common
,
GstEbmlRead
*
ebml
,
GArray
*
edition_targets
,
GArray
*
chapter_targets
)
{
GstFlowReturn
ret
=
GST_FLOW_OK
;
guint32
id
;
guint64
uid
;
DEBUG_ELEMENT_START
(
common
,
ebml
,
"TagTargets"
);
if
((
ret
=
gst_ebml_read_master
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
{
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"TagTargets"
,
ret
);
return
ret
;
}
while
(
ret
==
GST_FLOW_OK
&&
gst_ebml_read_has_remaining
(
ebml
,
1
,
TRUE
))
{
if
((
ret
=
gst_ebml_peek_id
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
break
;
switch
(
id
)
{
case
GST_MATROSKA_ID_TARGETCHAPTERUID
:
if
((
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
uid
))
==
GST_FLOW_OK
)
g_array_append_val
(
chapter_targets
,
uid
);
break
;
case
GST_MATROSKA_ID_TARGETEDITIONUID
:
if
((
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
uid
))
==
GST_FLOW_OK
)
g_array_append_val
(
edition_targets
,
uid
);
break
;
default:
ret
=
gst_matroska_read_common_parse_skip
(
common
,
ebml
,
"TagTargets"
,
id
);
break
;
}
}
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"TagTargets"
,
ret
);
return
ret
;
}
static
void
gst_matroska_read_common_postprocess_toc_entries
(
GList
*
toc_entries
,
guint64
max
,
const
gchar
*
parent_uid
)
{
GstTocEntry
*
cur_info
,
*
prev_info
,
*
next_info
;
GList
*
cur_list
,
*
prev_list
,
*
next_list
;
gchar
*
iter_digit
;
gint
i
=
0
;
gint64
cur_start
,
prev_start
,
stop
;
cur_list
=
toc_entries
;
while
(
cur_list
!=
NULL
)
{
++
i
;
cur_info
=
cur_list
->
data
;
iter_digit
=
g_strdup_printf
(
"%d"
,
i
);
switch
(
cur_info
->
type
)
{
case
GST_TOC_ENTRY_TYPE_EDITION
:
/* in Matroska terms edition has duration of full track */
gst_toc_entry_set_start_stop
(
cur_info
,
0
,
max
);
if
(
cur_info
->
uid
==
NULL
)
cur_info
->
uid
=
g_strconcat
(
parent_uid
,
"/"
,
GST_MATROSKA_TOC_UID_EDITION
,
iter_digit
,
NULL
);
gst_matroska_read_common_postprocess_toc_entries
(
cur_info
->
subentries
,
max
,
cur_info
->
uid
);
break
;
case
GST_TOC_ENTRY_TYPE_CHAPTER
:
prev_list
=
cur_list
->
prev
;
next_list
=
cur_list
->
next
;
if
(
prev_list
!=
NULL
)
prev_info
=
prev_list
->
data
;
else
prev_info
=
NULL
;
if
(
next_list
!=
NULL
)
next_info
=
next_list
->
data
;
else
next_info
=
NULL
;
if
(
cur_info
->
uid
==
NULL
)
cur_info
->
uid
=
g_strconcat
(
parent_uid
,
"/"
,
GST_MATROSKA_TOC_UID_CHAPTER
,
iter_digit
,
NULL
);
/* updated stop time in previous chapter and it's subchapters */
if
(
prev_info
!=
NULL
)
{
gst_toc_entry_get_start_stop
(
prev_info
,
&
prev_start
,
&
stop
);
gst_toc_entry_get_start_stop
(
cur_info
,
&
cur_start
,
&
stop
);
stop
=
cur_start
;
gst_toc_entry_set_start_stop
(
prev_info
,
prev_start
,
stop
);
gst_matroska_read_common_postprocess_toc_entries
(
prev_info
->
subentries
,
cur_start
,
prev_info
->
uid
);
}
/* updated stop time in current chapter and it's subchapters */
if
(
next_info
==
NULL
)
{
gst_toc_entry_get_start_stop
(
cur_info
,
&
cur_start
,
&
stop
);
if
(
stop
==
-
1
)
{
stop
=
max
;
gst_toc_entry_set_start_stop
(
cur_info
,
cur_start
,
stop
);
}
gst_matroska_read_common_postprocess_toc_entries
(
cur_info
->
subentries
,
stop
,
cur_info
->
uid
);
}
break
;
}
cur_list
=
cur_list
->
next
;
g_free
(
iter_digit
);
}
}
static
GstFlowReturn
gst_matroska_read_common_parse_chapter_titles
(
GstMatroskaReadCommon
*
common
,
GstEbmlRead
*
ebml
,
GstTagList
*
titles
)
{
guint32
id
;
gchar
*
title
=
NULL
;
GstFlowReturn
ret
=
GST_FLOW_OK
;
DEBUG_ELEMENT_START
(
common
,
ebml
,
"ChaptersTitles"
);
if
((
ret
=
gst_ebml_read_master
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
{
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"ChaptersTitles"
,
ret
);
return
ret
;
}
while
(
ret
==
GST_FLOW_OK
&&
gst_ebml_read_has_remaining
(
ebml
,
1
,
TRUE
))
{
if
((
ret
=
gst_ebml_peek_id
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
break
;
switch
(
id
)
{
case
GST_MATROSKA_ID_CHAPSTRING
:
ret
=
gst_ebml_read_utf8
(
ebml
,
&
id
,
&
title
);
break
;
default:
ret
=
gst_matroska_read_common_parse_skip
(
common
,
ebml
,
"ChaptersTitles"
,
id
);
break
;
}
}
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"ChaptersTitles"
,
ret
);
if
(
title
!=
NULL
&&
ret
==
GST_FLOW_OK
)
gst_tag_list_add
(
titles
,
GST_TAG_MERGE_APPEND
,
GST_TAG_TITLE
,
title
,
NULL
);
g_free
(
title
);
return
ret
;
}
static
GstFlowReturn
gst_matroska_read_common_parse_chapter_element
(
GstMatroskaReadCommon
*
common
,
GstEbmlRead
*
ebml
,
GstTocEntry
*
toc_entry
)
{
guint32
id
;
guint64
start_time
=
-
1
,
stop_time
=
-
1
;
guint64
is_hidden
=
0
,
is_enabled
=
1
,
uid
=
0
;
GstFlowReturn
ret
=
GST_FLOW_OK
;
GstTocEntry
*
chapter_info
;
GstTagList
*
titles
;
DEBUG_ELEMENT_START
(
common
,
ebml
,
"ChaptersElement"
);
if
((
ret
=
gst_ebml_read_master
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
{
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"ChaptersElement"
,
ret
);
return
ret
;
}
titles
=
gst_tag_list_new
();
chapter_info
=
gst_toc_entry_new
(
GST_TOC_ENTRY_TYPE_CHAPTER
,
GST_MATROSKA_TOC_UID_EMPTY
);
while
(
ret
==
GST_FLOW_OK
&&
gst_ebml_read_has_remaining
(
ebml
,
1
,
TRUE
))
{
if
((
ret
=
gst_ebml_peek_id
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
break
;
switch
(
id
)
{
case
GST_MATROSKA_ID_CHAPTERUID
:
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
uid
);
break
;
case
GST_MATROSKA_ID_CHAPTERTIMESTART
:
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
start_time
);
break
;
case
GST_MATROSKA_ID_CHAPTERTIMESTOP
:
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
stop_time
);
break
;
case
GST_MATROSKA_ID_CHAPTERATOM
:
ret
=
gst_matroska_read_common_parse_chapter_element
(
common
,
ebml
,
chapter_info
);
break
;
case
GST_MATROSKA_ID_CHAPTERDISPLAY
:
ret
=
gst_matroska_read_common_parse_chapter_titles
(
common
,
ebml
,
titles
);
break
;
case
GST_MATROSKA_ID_CHAPTERFLAGHIDDEN
:
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
is_hidden
);
break
;
case
GST_MATROSKA_ID_CHAPTERFLAGENABLED
:
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
is_enabled
);
break
;
default:
ret
=
gst_matroska_read_common_parse_skip
(
common
,
ebml
,
"ChaptersElement"
,
id
);
break
;
}
}
gst_toc_entry_set_start_stop
(
chapter_info
,
start_time
,
stop_time
);
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"ChaptersElement"
,
ret
);
g_free
(
chapter_info
->
uid
);
if
(
uid
!=
0
)
chapter_info
->
uid
=
g_strdup_printf
(
"%"
G_GUINT64_FORMAT
,
uid
);
else
chapter_info
->
uid
=
NULL
;
/* start time is mandatory and has no default value,
* so we should skip chapters without it */
if
(
is_hidden
==
0
&&
is_enabled
>
0
&&
start_time
!=
-
1
&&
ret
==
GST_FLOW_OK
)
{
if
(
!
gst_tag_list_is_empty
(
titles
))
gst_tag_list_insert
(
chapter_info
->
tags
,
titles
,
GST_TAG_MERGE_APPEND
);
toc_entry
->
subentries
=
g_list_append
(
toc_entry
->
subentries
,
chapter_info
);
}
else
gst_toc_entry_free
(
chapter_info
);
gst_tag_list_free
(
titles
);
return
ret
;
}
static
GstFlowReturn
gst_matroska_read_common_parse_chapter_edition
(
GstMatroskaReadCommon
*
common
,
GstEbmlRead
*
ebml
,
GstToc
*
toc
)
{
guint32
id
;
guint64
is_hidden
=
0
,
uid
=
0
;
GstFlowReturn
ret
=
GST_FLOW_OK
;
GstTocEntry
*
edition_info
;
DEBUG_ELEMENT_START
(
common
,
ebml
,
"ChaptersEdition"
);
if
((
ret
=
gst_ebml_read_master
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
{
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"ChaptersEdition"
,
ret
);
return
ret
;
}
edition_info
=
gst_toc_entry_new
(
GST_TOC_ENTRY_TYPE_EDITION
,
GST_MATROSKA_TOC_UID_EMPTY
);
gst_toc_entry_set_start_stop
(
edition_info
,
-
1
,
-
1
);
while
(
ret
==
GST_FLOW_OK
&&
gst_ebml_read_has_remaining
(
ebml
,
1
,
TRUE
))
{
if
((
ret
=
gst_ebml_peek_id
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
break
;
switch
(
id
)
{
case
GST_MATROSKA_ID_EDITIONUID
:
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
uid
);
break
;
case
GST_MATROSKA_ID_CHAPTERATOM
:
ret
=
gst_matroska_read_common_parse_chapter_element
(
common
,
ebml
,
edition_info
);
break
;
case
GST_MATROSKA_ID_EDITIONFLAGHIDDEN
:
ret
=
gst_ebml_read_uint
(
ebml
,
&
id
,
&
is_hidden
);
break
;
default:
ret
=
gst_matroska_read_common_parse_skip
(
common
,
ebml
,
"ChaptersEdition"
,
id
);
break
;
}
}
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"ChaptersEdition"
,
ret
);
g_free
(
edition_info
->
uid
);
if
(
uid
!=
0
)
edition_info
->
uid
=
g_strdup_printf
(
"%"
G_GUINT64_FORMAT
,
uid
);
else
edition_info
->
uid
=
NULL
;
if
(
is_hidden
==
0
&&
edition_info
->
subentries
!=
NULL
&&
ret
==
GST_FLOW_OK
)
toc
->
entries
=
g_list_prepend
(
toc
->
entries
,
edition_info
);
else
{
GST_DEBUG_OBJECT
(
common
,
"Skipping empty or hidden edition in the chapters TOC"
);
gst_toc_entry_free
(
edition_info
);
}
return
ret
;
}
GstFlowReturn
gst_matroska_read_common_parse_chapters
(
GstMatroskaReadCommon
*
common
,
GstEbmlRead
*
ebml
)
{
guint32
id
;
GstFlowReturn
ret
=
GST_FLOW_OK
;
GST_WARNING_OBJECT
(
common
,
"Parsing of chapters not implemented yet"
);
/* TODO: implement parsing of chapters */
GstToc
*
toc
;
DEBUG_ELEMENT_START
(
common
,
ebml
,
"Chapters"
);
...
...
@@ -712,17 +1075,36 @@ gst_matroska_read_common_parse_chapters (GstMatroskaReadCommon * common,
return
ret
;
}
toc
=
gst_toc_new
();
while
(
ret
==
GST_FLOW_OK
&&
gst_ebml_read_has_remaining
(
ebml
,
1
,
TRUE
))
{
if
((
ret
=
gst_ebml_peek_id
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
break
;
switch
(
id
)
{
case
GST_MATROSKA_ID_EDITIONENTRY
:
ret
=
gst_matroska_read_common_parse_chapter_edition
(
common
,
ebml
,
toc
);
break
;
default:
ret
=
gst_ebml_read_skip
(
ebml
);
ret
=
gst_matroska_read_common_parse_skip
(
common
,
ebml
,
"Chapters"
,
id
);
break
;
}
}
if
(
toc
->
entries
!=
NULL
)
{
toc
->
entries
=
g_list_reverse
(
toc
->
entries
);
gst_matroska_read_common_postprocess_toc_entries
(
toc
->
entries
,
common
->
segment
.
duration
,
""
);
common
->
toc
=
toc
;
}
else
gst_toc_free
(
toc
);
common
->
chapters_parsed
=
TRUE
;
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"Chapters"
,
ret
);
return
ret
;
}
...
...
@@ -1470,6 +1852,9 @@ gst_matroska_read_common_parse_metadata_id_tag (GstMatroskaReadCommon * common,
{
guint32
id
;
GstFlowReturn
ret
;
GArray
*
chapter_targets
,
*
edition_targets
;
GstTagList
*
taglist
;
GList
*
cur
;
DEBUG_ELEMENT_START
(
common
,
ebml
,
"Tag"
);
...
...
@@ -1478,6 +1863,10 @@ gst_matroska_read_common_parse_metadata_id_tag (GstMatroskaReadCommon * common,
return
ret
;
}
edition_targets
=
g_array_new
(
FALSE
,
FALSE
,
sizeof
(
guint64
));
chapter_targets
=
g_array_new
(
FALSE
,
FALSE
,
sizeof
(
guint64
));
taglist
=
gst_tag_list_new
();
while
(
ret
==
GST_FLOW_OK
&&
gst_ebml_read_has_remaining
(
ebml
,
1
,
TRUE
))
{
/* read all sub-entries */
...
...
@@ -1487,7 +1876,13 @@ gst_matroska_read_common_parse_metadata_id_tag (GstMatroskaReadCommon * common,
switch
(
id
)
{
case
GST_MATROSKA_ID_SIMPLETAG
:
ret
=
gst_matroska_read_common_parse_metadata_id_simple_tag
(
common
,
ebml
,
p_taglist
);
ebml
,
&
taglist
);
break
;
case
GST_MATROSKA_ID_TARGETS
:
ret
=
gst_matroska_read_common_parse_metadata_targets
(
common
,
ebml
,
edition_targets
,
chapter_targets
);
break
;
default:
...
...
@@ -1498,6 +1893,27 @@ gst_matroska_read_common_parse_metadata_id_tag (GstMatroskaReadCommon * common,
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"Tag"
,
ret
);
/* if tag is chapter/edition specific - try to find that entry */
if
(
G_UNLIKELY
(
chapter_targets
->
len
>
0
||
edition_targets
->
len
>
0
))
{
if
(
common
->
toc
==
NULL
)
GST_WARNING_OBJECT
(
common
,
"Found chapter/edition specific tag, but TOC doesn't present"
);
else
{
cur
=
common
->
toc
->
entries
;
while
(
cur
!=
NULL
)
{
gst_matroska_read_common_parse_toc_tag
(
cur
->
data
,
edition_targets
,
chapter_targets
,
taglist
);
cur
=
cur
->
next
;
}
common
->
toc_updated
=
TRUE
;
}
}
else
gst_tag_list_insert
(
*
p_taglist
,
taglist
,
GST_TAG_MERGE_APPEND
);
gst_tag_list_free
(
taglist
);
g_array_unref
(
chapter_targets
);
g_array_unref
(
edition_targets
);
return
ret
;
}
...
...
@@ -1537,6 +1953,7 @@ gst_matroska_read_common_parse_metadata (GstMatroskaReadCommon * common,
}
taglist
=
gst_tag_list_new
();
common
->
toc_updated
=
FALSE
;
while
(
ret
==
GST_FLOW_OK
&&
gst_ebml_read_has_remaining
(
ebml
,
1
,
TRUE
))
{
if
((
ret
=
gst_ebml_peek_id
(
ebml
,
&
id
))
!=
GST_FLOW_OK
)
...
...
@@ -1552,15 +1969,15 @@ gst_matroska_read_common_parse_metadata (GstMatroskaReadCommon * common,
ret
=
gst_matroska_read_common_parse_skip
(
common
,
ebml
,
"Tags"
,
id
);
break
;
/* FIXME: Use to limit the tags to specific pads */
case
GST_MATROSKA_ID_TARGETS
:
ret
=
gst_ebml_read_skip
(
ebml
);
break
;
}
}
DEBUG_ELEMENT_STOP
(
common
,
ebml
,
"Tags"
,
ret
);
gst_matroska_read_common_found_global_tag
(
common
,
el
,
taglist
);
if
(
G_LIKELY
(
!
gst_tag_list_is_empty
(
taglist
)))
gst_matroska_read_common_found_global_tag
(
common
,
el
,
taglist
);
else
gst_tag_list_free
(
taglist
);
return
ret
;
}
...
...
gst/matroska/matroska-read-common.h
View file @
890b1752
...
...
@@ -59,12 +59,18 @@ typedef struct _GstMatroskaReadCommon {
/* state */
GstMatroskaReadState
state
;
/* did we parse cues/tracks/segmentinfo already? */
gboolean
index_parsed
;
gboolean
segmentinfo_parsed
;
gboolean
attachments_parsed
;
gboolean
chapters_parsed
;
GList
*
tags_parsed
;
/* chapters stuff */
GstToc
*
toc
;
gboolean
toc_updated
;
/* start-of-segment */
guint64
ebml_segment_start
;
...
...
@@ -80,7 +86,7 @@ typedef struct _GstMatroskaReadCommon {
GstTagList
*
global_tags
;
/* pull mode caching */
GstBuffer
*
cached_buffer
;
GstBuffer
*
cached_buffer
;
/* push and pull mode */
guint64
offset
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment