mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-22 15:18:21 +00:00
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:
parent
a7b75cb1cc
commit
1a4ba79044
2 changed files with 209 additions and 0 deletions
|
@ -222,6 +222,7 @@ gst_vorbis_enc_start (GstAudioEncoder * enc)
|
||||||
GST_DEBUG_OBJECT (enc, "start");
|
GST_DEBUG_OBJECT (enc, "start");
|
||||||
vorbisenc->tags = gst_tag_list_new_empty ();
|
vorbisenc->tags = gst_tag_list_new_empty ();
|
||||||
vorbisenc->header_sent = FALSE;
|
vorbisenc->header_sent = FALSE;
|
||||||
|
vorbisenc->last_size = 0;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -539,12 +540,200 @@ gst_vorbis_enc_flush (GstAudioEncoder * enc)
|
||||||
vorbisenc->header_sent = FALSE;
|
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 *
|
static GstBuffer *
|
||||||
gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc,
|
gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc,
|
||||||
ogg_packet * packet)
|
ogg_packet * packet)
|
||||||
{
|
{
|
||||||
GstBuffer *outbuf;
|
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 =
|
outbuf =
|
||||||
gst_audio_encoder_allocate_output_buffer (GST_AUDIO_ENCODER (vorbisenc),
|
gst_audio_encoder_allocate_output_buffer (GST_AUDIO_ENCODER (vorbisenc),
|
||||||
packet->bytes);
|
packet->bytes);
|
||||||
|
@ -759,6 +948,7 @@ static GstFlowReturn
|
||||||
gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc)
|
gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc)
|
||||||
{
|
{
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
|
gint64 duration;
|
||||||
|
|
||||||
/* vorbis does some data preanalysis, then divides up blocks for
|
/* vorbis does some data preanalysis, then divides up blocks for
|
||||||
more involved (potentially parallel) processing. Get a single
|
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
|
gst_audio_encoder_allocate_output_buffer (GST_AUDIO_ENCODER
|
||||||
(vorbisenc), op.bytes);
|
(vorbisenc), op.bytes);
|
||||||
gst_buffer_fill (buf, 0, op.packet, 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 */
|
/* tracking granulepos should tell us samples accounted for */
|
||||||
ret =
|
ret =
|
||||||
gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER
|
gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER
|
||||||
|
|
|
@ -79,6 +79,11 @@ struct _GstVorbisEnc {
|
||||||
gboolean setup;
|
gboolean setup;
|
||||||
gboolean header_sent;
|
gboolean header_sent;
|
||||||
gchar *last_message;
|
gchar *last_message;
|
||||||
|
|
||||||
|
int long_size, short_size;
|
||||||
|
int last_size;
|
||||||
|
int vorbis_log2_num_modes;
|
||||||
|
int vorbis_mode_sizes[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstVorbisEncClass {
|
struct _GstVorbisEncClass {
|
||||||
|
|
Loading…
Reference in a new issue