summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Penquerc'h <vincent.penquerch@collabora.co.uk>2016-09-26 16:25:14 +0100
committerVincent Penquerc'h <vincent.penquerch@collabora.co.uk>2016-10-05 14:49:52 +0100
commit1a4ba790446ef0e043bec9cd9d78aa296235d47b (patch)
tree9a87a980a411e8ed26ce1e58506f328440c0c0ce
parenta7b75cb1ccf9fb5bfcd6ecef0251d39ef870c482 (diff)
downloadgstreamer-plugins-base-1a4ba790446ef0e043bec9cd9d78aa296235d47b.tar.gz
vorbisenc: strip after-eos samples from the end of the eos buffer
https://bugzilla.gnome.org/show_bug.cgi?id=768763
-rw-r--r--ext/vorbis/gstvorbisenc.c204
-rw-r--r--ext/vorbis/gstvorbisenc.h5
2 files changed, 209 insertions, 0 deletions
diff --git a/ext/vorbis/gstvorbisenc.c b/ext/vorbis/gstvorbisenc.c
index 57f193f8e..a5a838e09 100644
--- a/ext/vorbis/gstvorbisenc.c
+++ b/ext/vorbis/gstvorbisenc.c
@@ -222,6 +222,7 @@ gst_vorbis_enc_start (GstAudioEncoder * enc)
GST_DEBUG_OBJECT (enc, "start");
vorbisenc->tags = gst_tag_list_new_empty ();
vorbisenc->header_sent = FALSE;
+ vorbisenc->last_size = 0;
return TRUE;
}
@@ -539,12 +540,200 @@ gst_vorbis_enc_flush (GstAudioEncoder * enc)
vorbisenc->header_sent = FALSE;
}
+/* copied and adapted from ext/ogg/gstoggstream.c */
+static gint64
+packet_duration_vorbis (GstVorbisEnc * enc, ogg_packet * packet)
+{
+ int mode;
+ int size;
+ int duration;
+
+ if (packet->bytes == 0 || packet->packet[0] & 1)
+ return 0;
+
+ mode = (packet->packet[0] >> 1) & ((1 << enc->vorbis_log2_num_modes) - 1);
+ size = enc->vorbis_mode_sizes[mode] ? enc->long_size : enc->short_size;
+
+ if (enc->last_size == 0) {
+ duration = 0;
+ } else {
+ duration = enc->last_size / 4 + size / 4;
+ }
+ enc->last_size = size;
+
+ GST_DEBUG_OBJECT (enc, "duration %d", (int) duration);
+
+ return duration;
+}
+
+/* copied and adapted from ext/ogg/gstoggstream.c */
+static void
+parse_vorbis_header_packet (GstVorbisEnc * enc, ogg_packet * packet)
+{
+ /*
+ * on the first (b_o_s) packet, determine the long and short sizes,
+ * and then calculate l/2, l/4 - s/4, 3 * l/4 - s/4, l/2 - s/2 and s/2
+ */
+
+ enc->long_size = 1 << (packet->packet[28] >> 4);
+ enc->short_size = 1 << (packet->packet[28] & 0xF);
+}
+
+/* copied and adapted from ext/ogg/gstoggstream.c */
+static void
+parse_vorbis_codebooks_packet (GstVorbisEnc * enc, ogg_packet * op)
+{
+ /*
+ * the code pages, a whole bunch of other fairly useless stuff, AND,
+ * RIGHT AT THE END (of a bunch of variable-length compressed rubbish that
+ * basically has only one actual set of values that everyone uses BUT YOU
+ * CAN'T BE SURE OF THAT, OH NO YOU CAN'T) is the only piece of data that's
+ * actually useful to us - the packet modes (because it's inconceivable to
+ * think people might want _just that_ and nothing else, you know, for
+ * seeking and stuff).
+ *
+ * Fortunately, because of the mandate that non-used bits must be zero
+ * at the end of the packet, we might be able to sneakily work backwards
+ * and find out the information we need (namely a mapping of modes to
+ * packet sizes)
+ */
+ unsigned char *current_pos = &op->packet[op->bytes - 1];
+ int offset;
+ int size;
+ int size_check;
+ int *mode_size_ptr;
+ int i;
+ int ii;
+
+ /*
+ * This is the format of the mode data at the end of the packet for all
+ * Vorbis Version 1 :
+ *
+ * [ 6:number_of_modes ]
+ * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ]
+ * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ]
+ * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ]
+ * [ 1:framing(1) ]
+ *
+ * e.g.:
+ *
+ * <-
+ * 0 0 0 0 0 1 0 0
+ * 0 0 1 0 0 0 0 0
+ * 0 0 1 0 0 0 0 0
+ * 0 0 1|0 0 0 0 0
+ * 0 0 0 0|0|0 0 0
+ * 0 0 0 0 0 0 0 0
+ * 0 0 0 0|0 0 0 0
+ * 0 0 0 0 0 0 0 0
+ * 0 0 0 0|0 0 0 0
+ * 0 0 0|1|0 0 0 0 |
+ * 0 0 0 0 0 0 0 0 V
+ * 0 0 0|0 0 0 0 0
+ * 0 0 0 0 0 0 0 0
+ * 0 0 1|0 0 0 0 0
+ * 0 0|1|0 0 0 0 0
+ *
+ *
+ * i.e. each entry is an important bit, 32 bits of 0, 8 bits of blah, a
+ * bit of 1.
+ * Let's find our last 1 bit first.
+ *
+ */
+
+ size = 0;
+
+ offset = 8;
+ while (!((1 << --offset) & *current_pos)) {
+ if (offset == 0) {
+ offset = 8;
+ current_pos -= 1;
+ }
+ }
+
+ while (1) {
+
+ /*
+ * from current_pos-5:(offset+1) to current_pos-1:(offset+1) should
+ * be zero
+ */
+ offset = (offset + 7) % 8;
+ if (offset == 7)
+ current_pos -= 1;
+
+ if (((current_pos[-5] & ~((1 << (offset + 1)) - 1)) != 0)
+ ||
+ current_pos[-4] != 0
+ ||
+ current_pos[-3] != 0
+ ||
+ current_pos[-2] != 0
+ || ((current_pos[-1] & ((1 << (offset + 1)) - 1)) != 0)
+ ) {
+ break;
+ }
+
+ size += 1;
+
+ current_pos -= 5;
+
+ }
+
+ /* Give ourselves a chance to recover if we went back too far by using
+ * the size check. */
+ for (ii = 0; ii < 2; ii++) {
+ if (offset > 4) {
+ size_check = (current_pos[0] >> (offset - 5)) & 0x3F;
+ } else {
+ /* mask part of byte from current_pos */
+ size_check = (current_pos[0] & ((1 << (offset + 1)) - 1));
+ /* shift to appropriate position */
+ size_check <<= (5 - offset);
+ /* or in part of byte from current_pos - 1 */
+ size_check |= (current_pos[-1] & ~((1 << (offset + 3)) - 1)) >>
+ (offset + 3);
+ }
+
+ size_check += 1;
+ if (size_check == size) {
+ break;
+ }
+ offset = (offset + 1) % 8;
+ if (offset == 0)
+ current_pos += 1;
+ current_pos += 5;
+ size -= 1;
+ }
+
+ /* Store mode size information in our info struct */
+ i = -1;
+ while ((1 << (++i)) < size);
+ enc->vorbis_log2_num_modes = i;
+
+ mode_size_ptr = enc->vorbis_mode_sizes;
+
+ for (i = 0; i < size; i++) {
+ offset = (offset + 1) % 8;
+ if (offset == 0)
+ current_pos += 1;
+ *mode_size_ptr++ = (current_pos[0] >> offset) & 0x1;
+ current_pos += 5;
+ }
+
+}
+
static GstBuffer *
gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc,
ogg_packet * packet)
{
GstBuffer *outbuf;
+ if (packet->bytes > 0 && packet->packet[0] == '\001') {
+ parse_vorbis_header_packet (vorbisenc, packet);
+ } else if (packet->bytes > 0 && packet->packet[0] == '\003') {
+ parse_vorbis_codebooks_packet (vorbisenc, packet);
+ }
+
outbuf =
gst_audio_encoder_allocate_output_buffer (GST_AUDIO_ENCODER (vorbisenc),
packet->bytes);
@@ -759,6 +948,7 @@ static GstFlowReturn
gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc)
{
GstFlowReturn ret;
+ gint64 duration;
/* vorbis does some data preanalysis, then divides up blocks for
more involved (potentially parallel) processing. Get a single
@@ -780,6 +970,20 @@ gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc)
gst_audio_encoder_allocate_output_buffer (GST_AUDIO_ENCODER
(vorbisenc), op.bytes);
gst_buffer_fill (buf, 0, op.packet, op.bytes);
+
+ /* we have to call this every packet, not just on e_o_s, since
+ each packet's duration depends on the previous one's */
+ duration = packet_duration_vorbis (vorbisenc, &op);
+ if (op.e_o_s) {
+ gint64 samples = op.granulepos - vorbisenc->samples_out;
+ if (samples < duration) {
+ gint64 trim_end = duration - samples;
+ GST_DEBUG_OBJECT (vorbisenc,
+ "Adding trim-end %" G_GUINT64_FORMAT, trim_end);
+ gst_buffer_add_audio_clipping_meta (buf, GST_FORMAT_DEFAULT, 0,
+ trim_end);
+ }
+ }
/* tracking granulepos should tell us samples accounted for */
ret =
gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER
diff --git a/ext/vorbis/gstvorbisenc.h b/ext/vorbis/gstvorbisenc.h
index 5eaa11b93..8ae22baa1 100644
--- a/ext/vorbis/gstvorbisenc.h
+++ b/ext/vorbis/gstvorbisenc.h
@@ -79,6 +79,11 @@ struct _GstVorbisEnc {
gboolean setup;
gboolean header_sent;
gchar *last_message;
+
+ int long_size, short_size;
+ int last_size;
+ int vorbis_log2_num_modes;
+ int vorbis_mode_sizes[256];
};
struct _GstVorbisEncClass {