/*
* copyright (c) 2002 Mark Hills <mark@pogo.org.uk>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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_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[] = {
/* variable bitrate by estimate, disable slow rate management */
if (minrate == -1 && maxrate == -1)
if ((ret = vorbis_encode_ctl(vi, OV_ECTL_RATEMANAGE2_SET, NULL)))
- goto error;
+ goto error; /* should not happen */
}
/* cutoff frequency */
if (avctx->cutoff > 0) {
cfreq = avctx->cutoff / 1000.0;
if ((ret = vorbis_encode_ctl(vi, OV_ECTL_LOWPASS_SET, &cfreq)))
- goto error;
+ goto error; /* should not happen */
}
/* impulse block bias */
goto error;
}
+ if (avctx->channels == 3 &&
+ avctx->channel_layout != (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER) ||
+ avctx->channels == 4 &&
+ avctx->channel_layout != AV_CH_LAYOUT_2_2 &&
+ avctx->channel_layout != AV_CH_LAYOUT_QUAD ||
+ avctx->channels == 5 &&
+ avctx->channel_layout != AV_CH_LAYOUT_5POINT0 &&
+ avctx->channel_layout != AV_CH_LAYOUT_5POINT0_BACK ||
+ avctx->channels == 6 &&
+ avctx->channel_layout != AV_CH_LAYOUT_5POINT1 &&
+ avctx->channel_layout != AV_CH_LAYOUT_5POINT1_BACK ||
+ avctx->channels == 7 &&
+ avctx->channel_layout != (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER) ||
+ avctx->channels == 8 &&
+ avctx->channel_layout != AV_CH_LAYOUT_7POINT1) {
+ if (avctx->channel_layout) {
+ char name[32];
+ av_get_channel_layout_string(name, sizeof(name), avctx->channels,
+ avctx->channel_layout);
+ av_log(avctx, AV_LOG_ERROR, "%s not supported by Vorbis: "
+ "output stream will have incorrect "
+ "channel layout.\n", name);
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "No channel layout specified. The encoder "
+ "will use Vorbis channel layout for "
+ "%d channels.\n", avctx->channels);
+ }
+ }
+
if ((ret = vorbis_encode_setup_init(vi)))
goto error;
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) {
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;
- float *audio = data;
- int pkt_size, ret;
+ 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;
for (i = 0; i < samples; i++)
buffer[c][i] = audio[i * channels + co];
}
- if ((ret = vorbis_analysis_wrote(&s->vd, samples)) < 0)
+ 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)
- if ((ret = vorbis_analysis_wrote(&s->vd, 0)) < 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;
}
/* add any available packets to the output packet buffer */
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, "libvorbis: buffer overflow.");
- return -1;
+ av_log(avctx, AV_LOG_ERROR, "packet buffer is too small\n");
+ return AVERROR_BUG;
}
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)
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "error getting available packets\n");
break;
+ }
}
- if (ret < 0)
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "error getting available packets\n");
return vorbis_error_to_averror(ret);
+ }
+
+ /* check for available packets */
+ if (av_fifo_size(s->pkt_fifo) < sizeof(ogg_packet))
+ return 0;
+
+ av_fifo_generic_read(s->pkt_fifo, &op, sizeof(ogg_packet), NULL);
- /* output then next packet from the output buffer, if available */
- pkt_size = 0;
- if (av_fifo_size(s->pkt_fifo) >= sizeof(ogg_packet)) {
- av_fifo_generic_read(s->pkt_fifo, &op, sizeof(ogg_packet), NULL);
- pkt_size = op.bytes;
- // FIXME: we should use the user-supplied pts and duration
- avctx->coded_frame->pts = ff_samples_to_time_base(avctx,
- op.granulepos);
- if (pkt_size > buf_size) {
- av_log(avctx, AV_LOG_ERROR, "libvorbis: buffer overflow.");
- return -1;
+ if ((ret = ff_alloc_packet2(avctx, avpkt, op.bytes)))
+ 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;
}
- av_fifo_generic_read(s->pkt_fifo, packets, pkt_size, NULL);
+ ff_af_queue_remove(&s->afq, duration, &avpkt->pts, &avpkt->duration);
}
- return pkt_size;
+ *got_packet_ptr = 1;
+ return 0;
}
AVCodec ff_libvorbis_encoder = {
.id = 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_FLT,