vorbisenc: strip after-eos samples from the end of the eos buffer

https://bugzilla.gnome.org/show_bug.cgi?id=768763
This commit is contained in:
Vincent Penquerc'h 2016-09-26 16:25:14 +01:00
parent a7b75cb1cc
commit 1a4ba79044
2 changed files with 209 additions and 0 deletions

View file

@ -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

View file

@ -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 {