mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
ogg muxing of vorbis and theora now has pages ordered correctly again, even with delays.
Original commit message from CVS: ogg muxing of vorbis and theora now has pages ordered correctly again, even with delays. * ext/ogg/README: updated with some examples * ext/theora/theoraenc.c: (granulepos_to_timestamp), (granulepos_add), (theora_buffer_from_packet): * ext/vorbis/vorbisenc.c: (granulepos_to_timestamp_offset), (granulepos_to_timestamp), (gst_vorbisenc_buffer_from_packet), (gst_vorbisenc_chain): implement strategy from ext/ogg/README * ext/ogg/gstoggmux.c: (gst_ogg_mux_buffer_from_page), (gst_ogg_mux_push_buffer), (gst_ogg_mux_dequeue_page), (gst_ogg_mux_pad_queue_page), (gst_ogg_mux_compare_pads), (gst_ogg_mux_queue_pads), (gst_ogg_mux_collected): Fix muxer so that oggz-validate is happy with all streams; except for no eos mark, and the BOS page ordering * tests/check/pipelines/theoraenc.c: (check_buffer_is_header), (check_buffer_granulepos): * tests/check/pipelines/vorbisenc.c: (check_buffer_granulepos): update tests to check for OFFSET being set as requested fixed type of granulepos, it's not a ClockTime
This commit is contained in:
parent
3b285cc760
commit
c9bc7a4844
7 changed files with 256 additions and 106 deletions
22
ChangeLog
22
ChangeLog
|
@ -1,3 +1,25 @@
|
|||
2006-03-05 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
|
||||
* ext/ogg/README:
|
||||
updated with some examples
|
||||
* ext/theora/theoraenc.c: (granulepos_to_timestamp),
|
||||
(granulepos_add), (theora_buffer_from_packet):
|
||||
* ext/vorbis/vorbisenc.c: (granulepos_to_timestamp_offset),
|
||||
(granulepos_to_timestamp), (gst_vorbisenc_buffer_from_packet),
|
||||
(gst_vorbisenc_chain):
|
||||
implement strategy from ext/ogg/README
|
||||
* ext/ogg/gstoggmux.c: (gst_ogg_mux_buffer_from_page),
|
||||
(gst_ogg_mux_push_buffer), (gst_ogg_mux_dequeue_page),
|
||||
(gst_ogg_mux_pad_queue_page), (gst_ogg_mux_compare_pads),
|
||||
(gst_ogg_mux_queue_pads), (gst_ogg_mux_collected):
|
||||
Fix muxer so that oggz-validate is happy with all streams;
|
||||
except for no eos mark, and the BOS page ordering
|
||||
* tests/check/pipelines/theoraenc.c: (check_buffer_is_header),
|
||||
(check_buffer_granulepos):
|
||||
* tests/check/pipelines/vorbisenc.c: (check_buffer_granulepos):
|
||||
update tests to check for OFFSET being set as requested
|
||||
fixed type of granulepos, it's not a ClockTime
|
||||
|
||||
2006-03-05 Julien MOUTTE <julien@moutte.net>
|
||||
|
||||
* sys/xvimage/xvimagesink.c: (gst_xvimagesink_xvimage_new),
|
||||
|
|
|
@ -341,4 +341,27 @@ In practice
|
|||
TODO
|
||||
----
|
||||
- decide on a proper mechanism for communicating extra per-buffer fields
|
||||
- the ogg muxer sets timestamp and duration on outgoing ogg pages based on
|
||||
timestamp/duration of incoming ogg packets.
|
||||
Note that:
|
||||
- since the ogg muxer *has* to output pages sorted by gp time, representing
|
||||
end time of the page, this means that the buffer's timestamps are not
|
||||
necessarily monotonically increasing
|
||||
- timestamp + duration of buffers don't match up; the duration represents
|
||||
the length of the ogg page *for that stream*. Hence, for a normal
|
||||
two-stream file, the sum of all durations is twice the length of the
|
||||
muxed file.
|
||||
|
||||
TESTING
|
||||
-------
|
||||
Proper muxing can be tested by generating test files with command lines like:
|
||||
- video and audio start from 0:
|
||||
gst-launch -v videotestsrc ! theoraenc ! oggmux audiotestsrc ! audioconvert ! vorbisenc ! identity ! oggmux0. oggmux0. ! filesink location=test.ogg
|
||||
|
||||
- video starts after audio:
|
||||
gst-launch -v videotestsrc timestamp-offset=500000000 ! theoraenc ! oggmux audiotestsrc ! audioconvert ! vorbisenc ! identity ! oggmux0. oggmux0. ! filesink location=test.ogg
|
||||
|
||||
- audio starts after video:
|
||||
gst-launch -v videotestsrc ! theoraenc ! oggmux audiotestsrc timestamp-offset=500000000 ! audioconvert ! vorbisenc ! identity ! oggmux0. oggmux0. ! filesink location=test.ogg
|
||||
|
||||
The resulting files can be verified with oggz-validate for correctness.
|
||||
|
|
|
@ -73,10 +73,13 @@ typedef struct
|
|||
guint64 duration; /* duration of current page */
|
||||
gboolean eos;
|
||||
gint64 offset;
|
||||
GstClockTime timestamp; /* start timestamp of last complete packet on
|
||||
this page */
|
||||
GstClockTime timestamp_end; /* end timestamp of last complete packet on this
|
||||
page == granulepos time. */
|
||||
GstClockTime timestamp; /* timestamp of the first packet on the next
|
||||
* page to be dequeued */
|
||||
GstClockTime timestamp_end; /* end timestamp of last complete packet on
|
||||
the next page to be dequeued */
|
||||
GstClockTime gp_time; /* time corresponding to the gp value of the
|
||||
last complete packet on the next page to be
|
||||
dequeued */
|
||||
|
||||
GstOggPadState state; /* state of the pad */
|
||||
|
||||
|
@ -489,10 +492,6 @@ gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
|
|||
memcpy (GST_BUFFER_DATA (buffer) + page->header_len,
|
||||
page->body, page->body_len);
|
||||
|
||||
/* next_ts was the timestamp of the first buffer put in this page */
|
||||
GST_BUFFER_TIMESTAMP (buffer) = mux->next_ts;
|
||||
GST_BUFFER_OFFSET (buffer) = mux->offset;
|
||||
mux->offset += GST_BUFFER_SIZE (buffer);
|
||||
/* Here we set granulepos as our OFFSET_END to give easy direct access to
|
||||
* this value later. Before we push it, we reset this to OFFSET + SIZE
|
||||
* (see gst_ogg_mux_push_buffer). */
|
||||
|
@ -500,14 +499,19 @@ gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
|
|||
if (delta)
|
||||
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
|
||||
GST_LOG_OBJECT (mux, GST_GP_FORMAT
|
||||
" created buffer %p from ogg page", ogg_page_granulepos (page));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer)
|
||||
{
|
||||
GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET (buffer) +
|
||||
GST_BUFFER_SIZE (buffer);
|
||||
/* fix up OFFSET and OFFSET_END again */
|
||||
GST_BUFFER_OFFSET (buffer) = mux->offset;
|
||||
mux->offset += GST_BUFFER_SIZE (buffer);
|
||||
GST_BUFFER_OFFSET_END (buffer) = mux->offset;
|
||||
|
||||
return gst_pad_push (mux->srcpad, buffer);
|
||||
}
|
||||
|
@ -584,18 +588,18 @@ gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
|
|||
if (buf) {
|
||||
/* if no oldest buffer yet, take this one */
|
||||
if (oldest == GST_CLOCK_TIME_NONE) {
|
||||
GST_LOG_OBJECT (mux, "no oldest yet, taking from pad %"
|
||||
GST_PTR_FORMAT " with timestamp %" GST_TIME_FORMAT,
|
||||
pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
||||
oldest = GST_BUFFER_END_TIME (buf);
|
||||
GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %"
|
||||
GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
|
||||
buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
|
||||
oldest = GST_BUFFER_OFFSET (buf);
|
||||
opad = pad;
|
||||
} else {
|
||||
/* if we have an oldest, compare with this one */
|
||||
if (GST_BUFFER_END_TIME (buf) < oldest) {
|
||||
GST_LOG_OBJECT (mux, "older buffer, taking from pad %"
|
||||
GST_PTR_FORMAT " with timestamp %" GST_TIME_FORMAT,
|
||||
pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
||||
oldest = GST_BUFFER_END_TIME (buf);
|
||||
if (GST_BUFFER_OFFSET (buf) < oldest) {
|
||||
GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %"
|
||||
GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
|
||||
buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
|
||||
oldest = GST_BUFFER_OFFSET (buf);
|
||||
opad = pad;
|
||||
}
|
||||
}
|
||||
|
@ -607,8 +611,9 @@ gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
|
|||
g_assert (opad);
|
||||
buf = g_queue_pop_head (opad->pagebuffers);
|
||||
GST_LOG_OBJECT (opad->collect.pad,
|
||||
GST_GP_FORMAT " pushing oldest page (end time %" GST_TIME_FORMAT ")",
|
||||
GST_BUFFER_OFFSET_END (buf), GST_TIME_ARGS (GST_BUFFER_END_TIME (buf)));
|
||||
GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %"
|
||||
GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf,
|
||||
GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
|
||||
*flowret = gst_ogg_mux_push_buffer (mux, buf);
|
||||
ret = TRUE;
|
||||
}
|
||||
|
@ -616,26 +621,41 @@ gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* put the given page on a per-pad queue, timestamping it correctly.
|
||||
* after that, dequeue and push as many pages as possible
|
||||
* before calling me, make sure that the the pad's timestamp matches
|
||||
* the page's granulepos */
|
||||
/* put the given ogg page on a per-pad queue, timestamping it correctly.
|
||||
* after that, dequeue and push as many pages as possible.
|
||||
* Caller should make sure:
|
||||
* pad->timestamp was set with the timestamp of the first packet put
|
||||
* on the page
|
||||
* pad->timestamp_end was set with the timestamp + duration of the last packet
|
||||
* put on the page
|
||||
* pad->gp_time was set with the time matching the gp of the last
|
||||
* packet put on the page
|
||||
*
|
||||
* will also reset timestamp and timestamp_end, so caller func can restart
|
||||
* counting.
|
||||
*/
|
||||
static GstFlowReturn
|
||||
gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPad * pad, ogg_page * page,
|
||||
gboolean delta)
|
||||
{
|
||||
GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
|
||||
GstFlowReturn ret;
|
||||
GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
|
||||
|
||||
/* take the timestamp of the last completed packet on this page */
|
||||
/* take the timestamp of the first packet on this page */
|
||||
GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
|
||||
GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp;
|
||||
/* take the gp time of the last completed packet on this page */
|
||||
GST_BUFFER_OFFSET (buffer) = pad->gp_time;
|
||||
|
||||
/* the next page will start where the current page's end time leaves off */
|
||||
pad->timestamp = pad->timestamp_end;
|
||||
|
||||
g_queue_push_tail (pad->pagebuffers, buffer);
|
||||
GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT " queued buffer page (time %"
|
||||
GST_TIME_FORMAT "), %d page buffers queued", ogg_page_granulepos (page),
|
||||
GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
|
||||
" queued buffer page %p (gp time %"
|
||||
GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT
|
||||
"), %d page buffers queued", ogg_page_granulepos (page),
|
||||
buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)),
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
|
||||
g_queue_get_length (pad->pagebuffers));
|
||||
|
||||
|
@ -648,43 +668,49 @@ gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPad * pad, ogg_page * page,
|
|||
}
|
||||
|
||||
/*
|
||||
* Given two pads, compare the buffers queued on it and return 0 if they have
|
||||
* an equal priority, 1 if the new pad is better, -1 if the old pad is better
|
||||
* Given two pads, compare the buffers queued on it.
|
||||
* Returns:
|
||||
* 0 if they have an equal priority
|
||||
* -1 if the first is better
|
||||
* 1 if the second is better
|
||||
* Priority decided by: a) validity, b) older timestamp, c) smaller number
|
||||
* of muxed pages
|
||||
*/
|
||||
static gint
|
||||
gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPad * old, GstOggPad * new)
|
||||
gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPad * first,
|
||||
GstOggPad * second)
|
||||
{
|
||||
guint64 oldtime, newtime;
|
||||
guint64 firsttime, secondtime;
|
||||
|
||||
/* if the old pad doesn't contain anything or is even NULL, return
|
||||
* the new pad as best candidate and vice versa */
|
||||
if (old == NULL || old->buffer == NULL)
|
||||
/* if the first pad doesn't contain anything or is even NULL, return
|
||||
* the second pad as best candidate and vice versa */
|
||||
if (first == NULL || first->buffer == NULL)
|
||||
return 1;
|
||||
if (new == NULL || new->buffer == NULL)
|
||||
if (second == NULL || second->buffer == NULL)
|
||||
return -1;
|
||||
|
||||
/* no timestamp on old buffer, it must go first */
|
||||
oldtime = GST_BUFFER_TIMESTAMP (old->buffer);
|
||||
if (oldtime == GST_CLOCK_TIME_NONE)
|
||||
/* no timestamp on first buffer, it must go first */
|
||||
firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
|
||||
if (firsttime == GST_CLOCK_TIME_NONE)
|
||||
return -1;
|
||||
|
||||
/* no timestamp on new buffer, it must go first */
|
||||
newtime = GST_BUFFER_TIMESTAMP (new->buffer);
|
||||
if (newtime == GST_CLOCK_TIME_NONE)
|
||||
/* no timestamp on second buffer, it must go first */
|
||||
secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
|
||||
if (secondtime == GST_CLOCK_TIME_NONE)
|
||||
return 1;
|
||||
|
||||
/* old buffer has higher timestamp, new one should go first */
|
||||
if (newtime < oldtime)
|
||||
/* first buffer has higher timestamp, second one should go first */
|
||||
if (secondtime < firsttime)
|
||||
return 1;
|
||||
/* new buffer has higher timestamp, old one should go first */
|
||||
else if (newtime > oldtime)
|
||||
/* second buffer has higher timestamp, first one should go first */
|
||||
else if (secondtime > firsttime)
|
||||
return -1;
|
||||
else {
|
||||
/* buffers with equal timestamps, prefer the pad that has the
|
||||
* least number of pages muxed */
|
||||
if (new->pageno < old->pageno)
|
||||
if (second->pageno < first->pageno)
|
||||
return 1;
|
||||
else if (new->pageno > old->pageno)
|
||||
else if (second->pageno > first->pageno)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -712,8 +738,7 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
|
|||
|
||||
walk = g_slist_next (walk);
|
||||
|
||||
GST_LOG_OBJECT (ogg_mux, "looking at pad %" GST_PTR_FORMAT " (oggpad %p)",
|
||||
data->pad, pad);
|
||||
GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
|
||||
|
||||
/* try to get a new buffer for this pad if needed and possible */
|
||||
if (pad->buffer == NULL) {
|
||||
|
@ -721,7 +746,7 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
|
|||
gboolean incaps;
|
||||
|
||||
buf = gst_collect_pads_pop (ogg_mux->collect, data);
|
||||
GST_LOG_OBJECT (ogg_mux, "popping buffer %" GST_PTR_FORMAT, buf);
|
||||
GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
|
||||
|
||||
/* On EOS we get a NULL buffer */
|
||||
if (buf != NULL) {
|
||||
|
@ -743,7 +768,7 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (ogg_mux, "EOS on pad");
|
||||
GST_DEBUG_OBJECT (data->pad, "EOS on pad");
|
||||
pad->eos = TRUE;
|
||||
}
|
||||
|
||||
|
@ -754,14 +779,12 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
|
|||
* pull on */
|
||||
if (pad->buffer) {
|
||||
if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
|
||||
GST_LOG_OBJECT (ogg_mux, "best pad now %" GST_PTR_FORMAT
|
||||
" (oggpad %p)", data->pad, pad);
|
||||
GST_LOG_OBJECT (data->pad, "new best pad");
|
||||
|
||||
bestpad = pad;
|
||||
}
|
||||
} else if (!pad->eos) {
|
||||
GST_LOG_OBJECT (ogg_mux, "hungry pad %" GST_PTR_FORMAT
|
||||
" (oggpad %p)", data->pad, pad);
|
||||
GST_LOG_OBJECT (data->pad, "hungry pad");
|
||||
still_hungry = pad;
|
||||
}
|
||||
}
|
||||
|
@ -1079,7 +1102,7 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
gboolean delta_unit;
|
||||
GstFlowReturn ret;
|
||||
gint64 granulepos = 0;
|
||||
GstClockTime timestamp, timestamp_end;
|
||||
GstClockTime timestamp, gp_time;
|
||||
|
||||
GST_LOG_OBJECT (ogg_mux, "collected");
|
||||
|
||||
|
@ -1098,8 +1121,9 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
return GST_FLOW_WRONG_STATE;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT " (oggpad %p)"
|
||||
" pulling %" GST_PTR_FORMAT, best->collect.pad, best, ogg_mux->pulling);
|
||||
GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT
|
||||
", currently pulling from %" GST_PTR_FORMAT, best->collect.pad,
|
||||
ogg_mux->pulling);
|
||||
|
||||
/* if we were already pulling from one pad, but the new "best" buffer is
|
||||
* from another pad, we need to check if we have reason to flush a page
|
||||
|
@ -1121,6 +1145,8 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
GST_BUFFER_OFFSET_END (pad->buffer), pad->stream.packetno);
|
||||
|
||||
while (ogg_stream_flush (&pad->stream, &page)) {
|
||||
/* end time of this page is the timestamp of the next buffer */
|
||||
ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
|
||||
/* Place page into the per-pad queue */
|
||||
ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
|
||||
pad->first_delta);
|
||||
|
@ -1137,12 +1163,13 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
/* if we don't know which pad to pull on, use the best one */
|
||||
if (ogg_mux->pulling == NULL) {
|
||||
ogg_mux->pulling = best;
|
||||
GST_LOG_OBJECT (ogg_mux, "pulling now %" GST_PTR_FORMAT " (oggpad %p)",
|
||||
ogg_mux->pulling->collect.pad, ogg_mux->pulling);
|
||||
GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from best pad");
|
||||
|
||||
/* remember timestamp of first buffer for this new pad */
|
||||
/* remember timestamp and gp time of first buffer for this new pad */
|
||||
if (ogg_mux->pulling != NULL) {
|
||||
ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
|
||||
GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "updated times, next ts %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (ogg_mux->next_ts));
|
||||
} else {
|
||||
/* no pad to pull on, send EOS */
|
||||
gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
|
||||
|
@ -1165,8 +1192,7 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
gint64 duration;
|
||||
gboolean force_flush;
|
||||
|
||||
GST_LOG_OBJECT (ogg_mux, "pulling now %" GST_PTR_FORMAT " (oggpad %p)",
|
||||
ogg_mux->pulling->collect.pad, ogg_mux->pulling);
|
||||
GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from pad");
|
||||
|
||||
/* now see if we have a buffer */
|
||||
buf = pad->buffer;
|
||||
|
@ -1179,6 +1205,14 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
duration = GST_BUFFER_DURATION (buf);
|
||||
|
||||
/* if the current "next timestamp" on the pad is unset, then this is the
|
||||
* first packet on the new page. Update our pad's page timestamp */
|
||||
if (ogg_mux->pulling->timestamp == GST_CLOCK_TIME_NONE) {
|
||||
ogg_mux->pulling->timestamp = GST_BUFFER_TIMESTAMP (buf);
|
||||
GST_LOG_OBJECT (ogg_mux->pulling->collect.pad,
|
||||
"updated pad timestamp to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
||||
}
|
||||
/* create a packet from the buffer */
|
||||
packet.packet = GST_BUFFER_DATA (buf);
|
||||
packet.bytes = GST_BUFFER_SIZE (buf);
|
||||
|
@ -1206,12 +1240,14 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
}
|
||||
}
|
||||
|
||||
/* flush the currently built page if neccesary */
|
||||
/* flush the currently built page if necessary */
|
||||
if (force_flush) {
|
||||
GST_LOG_OBJECT (pad->collect.pad,
|
||||
GST_GP_FORMAT " forcing flush because of keyframe",
|
||||
GST_GP_FORMAT " forced flush of page before this packet",
|
||||
GST_BUFFER_OFFSET_END (pad->buffer));
|
||||
while (ogg_stream_flush (&pad->stream, &page)) {
|
||||
/* end time of this page is the timestamp of the next buffer */
|
||||
ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
|
||||
ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
|
||||
pad->first_delta);
|
||||
|
||||
|
@ -1261,14 +1297,15 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
|
||||
ogg_stream_packetin (&pad->stream, &packet);
|
||||
|
||||
gp_time = GST_BUFFER_OFFSET (pad->buffer);
|
||||
granulepos = GST_BUFFER_OFFSET_END (pad->buffer);
|
||||
timestamp = GST_BUFFER_TIMESTAMP (pad->buffer);
|
||||
timestamp_end = GST_BUFFER_END_TIME (pad->buffer);
|
||||
|
||||
GST_LOG_OBJECT (pad->collect.pad,
|
||||
GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", time %"
|
||||
GST_TIME_FORMAT ") packetin'd",
|
||||
granulepos, packet.packetno, GST_TIME_ARGS (timestamp));
|
||||
GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", gp time %"
|
||||
GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT " packetin'd",
|
||||
granulepos, packet.packetno, GST_TIME_ARGS (gp_time),
|
||||
GST_TIME_ARGS (timestamp));
|
||||
/* don't need the old buffer anymore */
|
||||
gst_buffer_unref (pad->buffer);
|
||||
/* store new readahead buffer */
|
||||
|
@ -1290,24 +1327,25 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
pad->stream.pageno);
|
||||
|
||||
if (ogg_page_granulepos (&page) == granulepos) {
|
||||
/* the packet we streamed in finishes on the page,
|
||||
/* the packet we streamed in finishes on the current page,
|
||||
* because the page's granulepos is the granulepos of the last
|
||||
* packet completed on that page,
|
||||
* so update the timestamp that we will give to the page */
|
||||
GST_LOG_OBJECT (pad->collect.pad,
|
||||
GST_GP_FORMAT
|
||||
" packet finishes on new page, updating timestamp to %"
|
||||
GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (timestamp));
|
||||
pad->timestamp = timestamp;
|
||||
pad->timestamp_end = timestamp_end;
|
||||
" packet finishes on current page, updating gp time to %"
|
||||
GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (gp_time));
|
||||
pad->gp_time = gp_time;
|
||||
} else {
|
||||
GST_LOG_OBJECT (pad->collect.pad,
|
||||
GST_GP_FORMAT
|
||||
" packet spans across new page, keeping old timestamp %"
|
||||
GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (pad->timestamp));
|
||||
" packet spans beyond current page, keeping old gp time %"
|
||||
GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (pad->gp_time));
|
||||
}
|
||||
|
||||
/* push the page */
|
||||
/* end time of this page is the timestamp of the next buffer */
|
||||
pad->timestamp_end = timestamp;
|
||||
ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta);
|
||||
pad->pageno++;
|
||||
/* mark next pages as delta */
|
||||
|
@ -1318,10 +1356,9 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
while (ogg_stream_pageout (&pad->stream, &page) > 0) {
|
||||
if (ogg_page_granulepos (&page) == granulepos) {
|
||||
/* the page has taken up the new packet completely, which means
|
||||
* the packet ends the page and we can update the timestamp
|
||||
* the packet ends the page and we can update the gp time
|
||||
* before pushing out */
|
||||
pad->timestamp = timestamp;
|
||||
pad->timestamp_end = timestamp_end;
|
||||
pad->gp_time = gp_time;
|
||||
}
|
||||
|
||||
/* we have a complete page now, we can push the page
|
||||
|
@ -1339,15 +1376,14 @@ gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
|
|||
ogg_mux->pulling = NULL;
|
||||
}
|
||||
|
||||
/* Update the timestamp, if necessary, since any future page will have at
|
||||
* least this timestamp.
|
||||
/* Update the gp time, if necessary, since any future page will have at
|
||||
* least this gp time.
|
||||
*/
|
||||
if (pad->timestamp < timestamp_end) {
|
||||
pad->timestamp = timestamp_end;
|
||||
pad->timestamp_end = timestamp_end;
|
||||
GST_LOG_OBJECT (ogg_mux, "Updated timestamp of pad %" GST_PTR_FORMAT
|
||||
" (oggpad %p) to %" GST_TIME_FORMAT, pad->collect.pad, pad,
|
||||
GST_TIME_ARGS (timestamp_end));
|
||||
if (pad->gp_time < gp_time) {
|
||||
pad->gp_time = gp_time;
|
||||
GST_LOG_OBJECT (pad->collect.pad,
|
||||
"Updated running gp time of pad %" GST_PTR_FORMAT
|
||||
" to %" GST_TIME_FORMAT, pad->collect.pad, GST_TIME_ARGS (gp_time));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,25 @@ enum
|
|||
/* FILL ME */
|
||||
};
|
||||
|
||||
/* this function does a straight granulepos -> timestamp conversion */
|
||||
static GstClockTime
|
||||
granulepos_to_timestamp (GstTheoraEnc * theoraenc, ogg_int64_t granulepos)
|
||||
{
|
||||
guint64 iframe, pframe;
|
||||
int shift = theoraenc->granule_shift;
|
||||
|
||||
if (granulepos < 0)
|
||||
return GST_CLOCK_TIME_NONE;
|
||||
|
||||
iframe = granulepos >> shift;
|
||||
pframe = granulepos - (iframe << shift);
|
||||
|
||||
/* num and den are 32 bit, so we can safely multiply with GST_SECOND */
|
||||
return gst_util_uint64_scale ((guint64) (iframe + pframe),
|
||||
GST_SECOND * theoraenc->info.fps_denominator,
|
||||
theoraenc->info.fps_numerator);
|
||||
}
|
||||
|
||||
static GstElementDetails theora_enc_details = {
|
||||
"TheoraEnc",
|
||||
"Codec/Encoder/Video",
|
||||
|
@ -365,7 +384,7 @@ theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
|
|||
static guint64
|
||||
granulepos_add (guint64 granulepos, guint64 addend, gint shift)
|
||||
{
|
||||
GstClockTime iframe, pframe;
|
||||
guint64 iframe, pframe;
|
||||
|
||||
iframe = granulepos >> shift;
|
||||
pframe = granulepos - (iframe << shift);
|
||||
|
@ -388,10 +407,14 @@ theora_buffer_from_packet (GstTheoraEnc * enc, ogg_packet * packet,
|
|||
goto no_buffer;
|
||||
|
||||
memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
|
||||
GST_BUFFER_OFFSET (buf) = enc->bytes_out;
|
||||
/* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its
|
||||
* time representation */
|
||||
GST_BUFFER_OFFSET_END (buf) =
|
||||
granulepos_add (packet->granulepos, enc->granulepos_offset,
|
||||
enc->granule_shift);
|
||||
GST_BUFFER_OFFSET (buf) = granulepos_to_timestamp (enc,
|
||||
GST_BUFFER_OFFSET_END (buf));
|
||||
|
||||
GST_BUFFER_TIMESTAMP (buf) = timestamp + enc->timestamp_offset;
|
||||
GST_BUFFER_DURATION (buf) = duration;
|
||||
|
||||
|
|
|
@ -93,8 +93,11 @@ enum
|
|||
|
||||
static GstFlowReturn gst_vorbisenc_output_buffers (GstVorbisEnc * vorbisenc);
|
||||
|
||||
/* this function takes into account the granulepos_offset and the subgranule
|
||||
* time offset */
|
||||
static GstClockTime
|
||||
granulepos_to_clocktime (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos)
|
||||
granulepos_to_timestamp_offset (GstVorbisEnc * vorbisenc,
|
||||
ogg_int64_t granulepos)
|
||||
{
|
||||
if (granulepos >= 0)
|
||||
return gst_util_uint64_scale ((guint64) granulepos
|
||||
|
@ -103,6 +106,16 @@ granulepos_to_clocktime (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos)
|
|||
return GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
/* this function does a straight granulepos -> timestamp conversion */
|
||||
static GstClockTime
|
||||
granulepos_to_timestamp (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos)
|
||||
{
|
||||
if (granulepos >= 0)
|
||||
return gst_util_uint64_scale ((guint64) granulepos,
|
||||
GST_SECOND, vorbisenc->frequency);
|
||||
return GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const GstFormat *
|
||||
gst_vorbisenc_get_formats (GstPad * pad)
|
||||
|
@ -807,18 +820,23 @@ gst_vorbisenc_buffer_from_packet (GstVorbisEnc * vorbisenc, ogg_packet * packet)
|
|||
|
||||
outbuf = gst_buffer_new_and_alloc (packet->bytes);
|
||||
memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
|
||||
GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
|
||||
/* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its
|
||||
* time representation */
|
||||
GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos +
|
||||
vorbisenc->granulepos_offset;
|
||||
GST_BUFFER_OFFSET (outbuf) = granulepos_to_timestamp (vorbisenc,
|
||||
GST_BUFFER_OFFSET_END (outbuf));
|
||||
GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts;
|
||||
|
||||
/* need to pass in an unadjusted granulepos here */
|
||||
vorbisenc->next_ts = granulepos_to_clocktime (vorbisenc, packet->granulepos);
|
||||
|
||||
/* update the next timestamp, taking granulepos_offset and subgranule offset
|
||||
* into account */
|
||||
vorbisenc->next_ts =
|
||||
granulepos_to_timestamp_offset (vorbisenc, packet->granulepos);
|
||||
GST_BUFFER_DURATION (outbuf) =
|
||||
vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
|
||||
|
||||
GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf));
|
||||
GST_LOG_OBJECT (vorbisenc, "encoded buffer of %d bytes",
|
||||
GST_BUFFER_SIZE (outbuf));
|
||||
return outbuf;
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1022,7 @@ gst_vorbisenc_chain (GstPad * pad, GstBuffer * buffer)
|
|||
(GST_BUFFER_TIMESTAMP (buffer), vorbisenc->frequency, GST_SECOND);
|
||||
vorbisenc->subgranule_offset = 0;
|
||||
vorbisenc->subgranule_offset =
|
||||
vorbisenc->next_ts - granulepos_to_clocktime (vorbisenc, 0);
|
||||
vorbisenc->next_ts - granulepos_to_timestamp_offset (vorbisenc, 0);
|
||||
|
||||
vorbisenc->header_sent = TRUE;
|
||||
}
|
||||
|
|
|
@ -154,12 +154,24 @@ check_buffer_duration (GstBuffer * buffer, GstClockTime duration)
|
|||
}
|
||||
|
||||
static void
|
||||
check_buffer_granulepos (GstBuffer * buffer, GstClockTime granulepos)
|
||||
check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
|
||||
{
|
||||
GstClockTime clocktime;
|
||||
|
||||
fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
|
||||
"expected granulepos %" G_GUINT64_FORMAT
|
||||
", but got granulepos %" G_GUINT64_FORMAT,
|
||||
granulepos, GST_BUFFER_OFFSET_END (buffer));
|
||||
|
||||
/* contrary to what we record as TIMESTAMP, we can use OFFSET to check
|
||||
* the granulepos correctly here */
|
||||
clocktime = gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
|
||||
FRAMERATE);
|
||||
|
||||
fail_unless (clocktime == GST_BUFFER_OFFSET (buffer),
|
||||
"expected OFFSET set to clocktime %" GST_TIME_FORMAT
|
||||
", but got %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (clocktime), GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)));
|
||||
}
|
||||
|
||||
/* this check is here to check that the granulepos we derive from the
|
||||
|
@ -172,7 +184,7 @@ static void
|
|||
check_buffer_granulepos_from_starttime (GstBuffer * buffer,
|
||||
GstClockTime starttime)
|
||||
{
|
||||
GstClockTime granulepos, expected, framecount;
|
||||
gint64 granulepos, expected, framecount;
|
||||
|
||||
granulepos = GST_BUFFER_OFFSET_END (buffer);
|
||||
framecount = granulepos >> GRANULEPOS_SHIFT;
|
||||
|
@ -239,7 +251,8 @@ GST_START_TEST (test_granulepos_offset)
|
|||
gst_buffer_unref (buffer);
|
||||
|
||||
{
|
||||
GstClockTime next_timestamp, last_granulepos;
|
||||
GstClockTime next_timestamp;
|
||||
gint64 last_granulepos;
|
||||
|
||||
/* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
|
||||
* match the timestamp of the end of the last sample in the output buffer.
|
||||
|
@ -332,7 +345,8 @@ GST_START_TEST (test_continuity)
|
|||
gst_buffer_unref (buffer);
|
||||
|
||||
{
|
||||
GstClockTime next_timestamp, last_granulepos;
|
||||
GstClockTime next_timestamp;
|
||||
gint64 last_granulepos;
|
||||
|
||||
/* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
|
||||
* match the timestamp of the end of the last sample in the output buffer.
|
||||
|
|
|
@ -124,12 +124,24 @@ check_buffer_duration (GstBuffer * buffer, GstClockTime duration)
|
|||
}
|
||||
|
||||
static void
|
||||
check_buffer_granulepos (GstBuffer * buffer, GstClockTime granulepos)
|
||||
check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
|
||||
{
|
||||
GstClockTime clocktime;
|
||||
|
||||
fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
|
||||
"expected granulepos %" G_GUINT64_FORMAT
|
||||
", but got granulepos %" G_GUINT64_FORMAT,
|
||||
granulepos, GST_BUFFER_OFFSET_END (buffer));
|
||||
|
||||
/* contrary to what we record as TIMESTAMP, we can use OFFSET to check
|
||||
* the granulepos correctly here */
|
||||
clocktime = gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), 44100,
|
||||
GST_SECOND);
|
||||
|
||||
fail_unless (clocktime == GST_BUFFER_OFFSET (buffer),
|
||||
"expected OFFSET set to clocktime %" GST_TIME_FORMAT
|
||||
", but got %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (clocktime), GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)));
|
||||
}
|
||||
|
||||
/* this check is here to check that the granulepos we derive from the timestamp
|
||||
|
@ -140,7 +152,7 @@ check_buffer_granulepos (GstBuffer * buffer, GstClockTime granulepos)
|
|||
static void
|
||||
check_buffer_granulepos_from_endtime (GstBuffer * buffer, GstClockTime endtime)
|
||||
{
|
||||
GstClockTime granulepos, expected;
|
||||
gint64 granulepos, expected;
|
||||
|
||||
granulepos = GST_BUFFER_OFFSET_END (buffer);
|
||||
expected = gst_util_uint64_scale (endtime, 44100, GST_SECOND);
|
||||
|
@ -202,7 +214,8 @@ GST_START_TEST (test_granulepos_offset)
|
|||
gst_buffer_unref (buffer);
|
||||
|
||||
{
|
||||
GstClockTime next_timestamp, last_granulepos;
|
||||
GstClockTime next_timestamp;
|
||||
gint64 last_granulepos;
|
||||
|
||||
/* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
|
||||
* match the timestamp of the end of the last sample in the output buffer.
|
||||
|
@ -291,7 +304,8 @@ GST_START_TEST (test_timestamps)
|
|||
gst_buffer_unref (buffer);
|
||||
|
||||
{
|
||||
GstClockTime next_timestamp, last_granulepos;
|
||||
GstClockTime next_timestamp;
|
||||
gint64 last_granulepos;
|
||||
|
||||
/* first buffer has timestamp 0 */
|
||||
buffer = get_buffer (bin, pad);
|
||||
|
|
Loading…
Reference in a new issue