#include <vorbis/vorbisenc.h>
+#include "libavutil/fifo.h"
#include "libavutil/opt.h"
#include "avcodec.h"
+#include "audio_frame_queue.h"
#include "bytestream.h"
#include "internal.h"
#include "vorbis.h"
+#include "vorbis_parser.h"
#undef NDEBUG
#include <assert.h>
vorbis_info vi; /**< vorbis_info used during init */
vorbis_dsp_state vd; /**< DSP state used for analysis */
vorbis_block vb; /**< vorbis_block used for analysis */
- uint8_t buffer[BUFFER_SIZE]; /**< output packet buffer */
- int buffer_index; /**< current buffer position */
+ AVFifoBuffer *pkt_fifo; /**< output packet buffer */
int eof; /**< end-of-file flag */
+ int dsp_initialized; /**< vd has been initialized */
vorbis_comment vc; /**< VorbisComment info */
ogg_packet op; /**< ogg packet */
double iblock; /**< impulse block bias option */
+ VorbisParseContext vp; /**< parse context to get durations */
+ AudioFrameQueue afq; /**< frame queue for timestamps */
} OggVorbisContext;
static const AVOption options[] = {
{ "iblock", "Sets the impulse block bias", offsetof(OggVorbisContext, iblock), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, -15, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
{ NULL }
};
+
+static const AVCodecDefault defaults[] = {
+ { "b", "0" },
+ { NULL },
+};
+
static const AVClass class = { "libvorbis", av_default_item_name, options, LIBAVUTIL_VERSION_INT };
double cfreq;
int ret;
- if (avctx->flags & CODEC_FLAG_QSCALE) {
+ if (avctx->flags & CODEC_FLAG_QSCALE || !avctx->bit_rate) {
/* variable bitrate
* NOTE: we use the oggenc range of -1 to 10 for global_quality for
- * user convenience, but libvorbis uses -0.1 to 1.0
+ * user convenience, but libvorbis uses -0.1 to 1.0.
*/
float q = avctx->global_quality / (float)FF_QP2LAMBDA;
+ /* default to 3 if the user did not set quality or bitrate */
+ if (!(avctx->flags & CODEC_FLAG_QSCALE))
+ q = 3.0;
if ((ret = vorbis_encode_setup_vbr(vi, avctx->channels,
avctx->sample_rate,
q / 10.0)))
OggVorbisContext *s = avctx->priv_data;
/* notify vorbisenc this is EOF */
- vorbis_analysis_wrote(&s->vd, 0);
+ if (s->dsp_initialized)
+ vorbis_analysis_wrote(&s->vd, 0);
vorbis_block_clear(&s->vb);
vorbis_dsp_clear(&s->vd);
vorbis_info_clear(&s->vi);
+ av_fifo_free(s->pkt_fifo);
+ ff_af_queue_close(&s->afq);
+#if FF_API_OLD_ENCODE_AUDIO
av_freep(&avctx->coded_frame);
+#endif
av_freep(&avctx->extradata);
return 0;
vorbis_info_init(&s->vi);
if ((ret = oggvorbis_init_encoder(&s->vi, avctx))) {
- av_log(avctx, AV_LOG_ERROR, "oggvorbis_encode_init: init_encoder failed\n");
+ av_log(avctx, AV_LOG_ERROR, "encoder setup failed\n");
goto error;
}
if ((ret = vorbis_analysis_init(&s->vd, &s->vi))) {
+ av_log(avctx, AV_LOG_ERROR, "analysis init failed\n");
ret = vorbis_error_to_averror(ret);
goto error;
}
+ s->dsp_initialized = 1;
if ((ret = vorbis_block_init(&s->vd, &s->vb))) {
+ av_log(avctx, AV_LOG_ERROR, "dsp init failed\n");
ret = vorbis_error_to_averror(ret);
goto error;
}
offset += header_code.bytes;
assert(offset == avctx->extradata_size);
+ if ((ret = avpriv_vorbis_parse_extradata(avctx, &s->vp)) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "invalid extradata\n");
+ return ret;
+ }
+
vorbis_comment_clear(&s->vc);
avctx->frame_size = OGGVORBIS_FRAME_SIZE;
+ ff_af_queue_init(avctx, &s->afq);
+
+ s->pkt_fifo = av_fifo_alloc(BUFFER_SIZE);
+ if (!s->pkt_fifo) {
+ ret = AVERROR(ENOMEM);
+ goto error;
+ }
+#if FF_API_OLD_ENCODE_AUDIO
avctx->coded_frame = avcodec_alloc_frame();
if (!avctx->coded_frame) {
ret = AVERROR(ENOMEM);
goto error;
}
+#endif
return 0;
error:
return ret;
}
-static int oggvorbis_encode_frame(AVCodecContext *avctx, unsigned char *packets,
- int buf_size, void *data)
+static int oggvorbis_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+ const AVFrame *frame, int *got_packet_ptr)
{
OggVorbisContext *s = avctx->priv_data;
ogg_packet op;
- signed short *audio = data;
- int pkt_size;
+ int ret, duration;
/* send samples to libvorbis */
- if (data) {
- const int samples = avctx->frame_size;
+ if (frame) {
+ const float *audio = (const float *)frame->data[0];
+ const int samples = frame->nb_samples;
float **buffer;
int c, channels = s->vi.channels;
int co = (channels > 8) ? c :
ff_vorbis_encoding_channel_layout_offsets[channels - 1][c];
for (i = 0; i < samples; i++)
- buffer[c][i] = audio[i * channels + co] / 32768.f;
+ buffer[c][i] = audio[i * channels + co];
}
- vorbis_analysis_wrote(&s->vd, samples);
+ if ((ret = vorbis_analysis_wrote(&s->vd, samples)) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "error in vorbis_analysis_wrote()\n");
+ return vorbis_error_to_averror(ret);
+ }
+ if ((ret = ff_af_queue_add(&s->afq, frame) < 0))
+ return ret;
} else {
if (!s->eof)
- vorbis_analysis_wrote(&s->vd, 0);
+ if ((ret = vorbis_analysis_wrote(&s->vd, 0)) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "error in vorbis_analysis_wrote()\n");
+ return vorbis_error_to_averror(ret);
+ }
s->eof = 1;
}
/* retrieve available packets from libvorbis */
- while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
- vorbis_analysis(&s->vb, NULL);
- vorbis_bitrate_addblock(&s->vb);
+ while ((ret = vorbis_analysis_blockout(&s->vd, &s->vb)) == 1) {
+ if ((ret = vorbis_analysis(&s->vb, NULL)) < 0)
+ break;
+ if ((ret = vorbis_bitrate_addblock(&s->vb)) < 0)
+ break;
/* add any available packets to the output packet buffer */
- while (vorbis_bitrate_flushpacket(&s->vd, &op)) {
- /* i'd love to say the following line is a hack, but sadly it's
- * not, apparently the end of stream decision is in libogg. */
- if (op.bytes == 1 && op.e_o_s)
- continue;
- if (s->buffer_index + sizeof(ogg_packet) + op.bytes > BUFFER_SIZE) {
- av_log(avctx, AV_LOG_ERROR, "libvorbis: buffer overflow.");
- return -1;
+ while ((ret = vorbis_bitrate_flushpacket(&s->vd, &op)) == 1) {
+ if (av_fifo_space(s->pkt_fifo) < sizeof(ogg_packet) + op.bytes) {
+ av_log(avctx, AV_LOG_ERROR, "packet buffer is too small");
+ return AVERROR_BUG;
}
- memcpy(s->buffer + s->buffer_index, &op, sizeof(ogg_packet));
- s->buffer_index += sizeof(ogg_packet);
- memcpy(s->buffer + s->buffer_index, op.packet, op.bytes);
- s->buffer_index += op.bytes;
+ av_fifo_generic_write(s->pkt_fifo, &op, sizeof(ogg_packet), NULL);
+ av_fifo_generic_write(s->pkt_fifo, op.packet, op.bytes, NULL);
+ }
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "error getting available packets\n");
+ break;
}
}
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "error getting available packets\n");
+ return vorbis_error_to_averror(ret);
+ }
- /* output then next packet from the output buffer, if available */
- pkt_size = 0;
- if (s->buffer_index) {
- ogg_packet *op2 = (ogg_packet *)s->buffer;
- op2->packet = s->buffer + sizeof(ogg_packet);
-
- pkt_size = op2->bytes;
- // FIXME: we should use the user-supplied pts and duration
- avctx->coded_frame->pts = ff_samples_to_time_base(avctx,
- op2->granulepos);
- if (pkt_size > buf_size) {
- av_log(avctx, AV_LOG_ERROR, "libvorbis: buffer overflow.");
- return -1;
- }
+ /* check for available packets */
+ if (av_fifo_size(s->pkt_fifo) < sizeof(ogg_packet))
+ return 0;
- memcpy(packets, op2->packet, pkt_size);
- s->buffer_index -= pkt_size + sizeof(ogg_packet);
- memmove(s->buffer, s->buffer + pkt_size + sizeof(ogg_packet),
- s->buffer_index);
+ av_fifo_generic_read(s->pkt_fifo, &op, sizeof(ogg_packet), NULL);
+
+ if ((ret = ff_alloc_packet(avpkt, op.bytes))) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting output packet\n");
+ return ret;
+ }
+ av_fifo_generic_read(s->pkt_fifo, avpkt->data, op.bytes, NULL);
+
+ avpkt->pts = ff_samples_to_time_base(avctx, op.granulepos);
+
+ duration = avpriv_vorbis_parse_frame(&s->vp, avpkt->data, avpkt->size);
+ if (duration > 0) {
+ /* we do not know encoder delay until we get the first packet from
+ * libvorbis, so we have to update the AudioFrameQueue counts */
+ if (!avctx->delay) {
+ avctx->delay = duration;
+ s->afq.remaining_delay += duration;
+ s->afq.remaining_samples += duration;
+ }
+ ff_af_queue_remove(&s->afq, duration, &avpkt->pts, &avpkt->duration);
}
- return pkt_size;
+ *got_packet_ptr = 1;
+ return 0;
}
AVCodec ff_libvorbis_encoder = {
.name = "libvorbis",
.type = AVMEDIA_TYPE_AUDIO,
- .id = CODEC_ID_VORBIS,
+ .id = AV_CODEC_ID_VORBIS,
.priv_data_size = sizeof(OggVorbisContext),
.init = oggvorbis_encode_init,
- .encode = oggvorbis_encode_frame,
+ .encode2 = oggvorbis_encode_frame,
.close = oggvorbis_encode_close,
.capabilities = CODEC_CAP_DELAY,
- .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16,
+ .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLT,
AV_SAMPLE_FMT_NONE },
.long_name = NULL_IF_CONFIG_SMALL("libvorbis Vorbis"),
.priv_class = &class,
+ .defaults = defaults,
};