1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-11-29 05:57:37 +02:00

avcodec: Add avcodec_encode_audio2() as replacement for avcodec_encode_audio()

This allows audio encoders to optionally take an AVFrame as input and write
encoded output to an AVPacket.

This also adds AVCodec.encode2() which will also be usable by video and
subtitle encoders once support is implemented in the public functions.
This commit is contained in:
Justin Ruggles
2011-12-18 13:20:15 -05:00
parent 5ee5fa021f
commit b2c75b6e63
5 changed files with 329 additions and 20 deletions

View File

@@ -25,6 +25,7 @@
* utils.
*/
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/crc.h"
#include "libavutil/mathematics.h"
@@ -101,6 +102,16 @@ void avcodec_init(void)
dsputil_static_init();
}
static av_always_inline int codec_is_encoder(AVCodec *codec)
{
return codec && (codec->encode || codec->encode2);
}
static av_always_inline int codec_is_decoder(AVCodec *codec)
{
return codec && codec->decode;
}
void avcodec_register(AVCodec *codec)
{
AVCodec **p;
@@ -690,7 +701,7 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVD
/* if the decoder init function was already called previously,
free the already allocated subtitle_header before overwriting it */
if (codec->decode)
if (codec_is_decoder(codec))
av_freep(&avctx->subtitle_header);
#define SANE_NB_CHANNELS 128U
@@ -738,7 +749,7 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVD
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (avctx->codec->encode) {
if (codec_is_encoder(avctx->codec)) {
int i;
if (avctx->codec->sample_fmts) {
for (i = 0; avctx->codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++)
@@ -812,21 +823,222 @@ free_and_end:
goto end;
}
int attribute_align_arg avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size,
const short *samples)
int ff_alloc_packet(AVPacket *avpkt, int size)
{
if(buf_size < FF_MIN_BUFFER_SIZE && 0){
av_log(avctx, AV_LOG_ERROR, "buffer smaller than minimum size\n");
return -1;
}
if((avctx->codec->capabilities & CODEC_CAP_DELAY) || samples){
int ret = avctx->codec->encode(avctx, buf, buf_size, samples);
avctx->frame_number++;
return ret;
}else
if (size > INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE)
return AVERROR(EINVAL);
if (avpkt->data) {
uint8_t *pkt_data;
int pkt_size;
if (avpkt->size < size)
return AVERROR(EINVAL);
pkt_data = avpkt->data;
pkt_size = avpkt->size;
av_init_packet(avpkt);
avpkt->data = pkt_data;
avpkt->size = pkt_size;
return 0;
} else {
return av_new_packet(avpkt, size);
}
}
int attribute_align_arg avcodec_encode_audio2(AVCodecContext *avctx,
AVPacket *avpkt,
const AVFrame *frame,
int *got_packet_ptr)
{
int ret;
int user_packet = !!avpkt->data;
int nb_samples;
if (!(avctx->codec->capabilities & CODEC_CAP_DELAY) && !frame) {
av_init_packet(avpkt);
avpkt->size = 0;
return 0;
}
/* check for valid frame size */
if (frame) {
nb_samples = frame->nb_samples;
if (avctx->codec->capabilities & CODEC_CAP_SMALL_LAST_FRAME) {
if (nb_samples > avctx->frame_size)
return AVERROR(EINVAL);
} else if (!(avctx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)) {
if (nb_samples != avctx->frame_size)
return AVERROR(EINVAL);
}
} else {
nb_samples = avctx->frame_size;
}
if (avctx->codec->encode2) {
*got_packet_ptr = 0;
ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr);
if (!ret && *got_packet_ptr &&
!(avctx->codec->capabilities & CODEC_CAP_DELAY)) {
avpkt->pts = frame->pts;
avpkt->duration = av_rescale_q(frame->nb_samples,
(AVRational){ 1, avctx->sample_rate },
avctx->time_base);
}
} else {
/* for compatibility with encoders not supporting encode2(), we need to
allocate a packet buffer if the user has not provided one or check
the size otherwise */
int fs_tmp = 0;
int buf_size = avpkt->size;
if (!user_packet) {
if (avctx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) {
av_assert0(av_get_bits_per_sample(avctx->codec_id) != 0);
buf_size = nb_samples * avctx->channels *
av_get_bits_per_sample(avctx->codec_id) / 8;
} else {
/* this is a guess as to the required size.
if an encoder needs more than this, it should probably
implement encode2() */
buf_size = 2 * avctx->frame_size * avctx->channels *
av_get_bytes_per_sample(avctx->sample_fmt);
buf_size += FF_MIN_BUFFER_SIZE;
}
}
if ((ret = ff_alloc_packet(avpkt, buf_size)))
return ret;
/* Encoders using AVCodec.encode() that support
CODEC_CAP_SMALL_LAST_FRAME require avctx->frame_size to be set to
the smaller size when encoding the last frame.
This code can be removed once all encoders supporting
CODEC_CAP_SMALL_LAST_FRAME use encode2() */
if ((avctx->codec->capabilities & CODEC_CAP_SMALL_LAST_FRAME) &&
nb_samples < avctx->frame_size) {
fs_tmp = avctx->frame_size;
avctx->frame_size = nb_samples;
}
/* encode the frame */
ret = avctx->codec->encode(avctx, avpkt->data, avpkt->size,
frame ? frame->data[0] : NULL);
if (ret >= 0) {
if (!ret) {
/* no output. if the packet data was allocated by libavcodec,
free it */
if (!user_packet)
av_freep(&avpkt->data);
} else {
if (avctx->coded_frame)
avpkt->pts = avctx->coded_frame->pts;
/* Set duration for final small packet. This can be removed
once all encoders supporting CODEC_CAP_SMALL_LAST_FRAME use
encode2() */
if (fs_tmp) {
avpkt->duration = av_rescale_q(avctx->frame_size,
(AVRational){ 1, avctx->sample_rate },
avctx->time_base);
}
}
avpkt->size = ret;
*got_packet_ptr = (ret > 0);
ret = 0;
}
if (fs_tmp)
avctx->frame_size = fs_tmp;
}
if (!ret)
avctx->frame_number++;
/* NOTE: if we add any audio encoders which output non-keyframe packets,
this needs to be moved to the encoders, but for now we can do it
here to simplify things */
avpkt->flags |= AV_PKT_FLAG_KEY;
return ret;
}
#if FF_API_OLD_DECODE_AUDIO
int attribute_align_arg avcodec_encode_audio(AVCodecContext *avctx,
uint8_t *buf, int buf_size,
const short *samples)
{
AVPacket pkt;
AVFrame frame0;
AVFrame *frame;
int ret, samples_size, got_packet;
av_init_packet(&pkt);
pkt.data = buf;
pkt.size = buf_size;
if (samples) {
frame = &frame0;
avcodec_get_frame_defaults(frame);
if (avctx->frame_size) {
frame->nb_samples = avctx->frame_size;
} else {
/* if frame_size is not set, the number of samples must be
calculated from the buffer size */
int64_t nb_samples;
if (!av_get_bits_per_sample(avctx->codec_id)) {
av_log(avctx, AV_LOG_ERROR, "avcodec_encode_audio() does not "
"support this codec\n");
return AVERROR(EINVAL);
}
nb_samples = (int64_t)buf_size * 8 /
(av_get_bits_per_sample(avctx->codec_id) *
avctx->channels);
if (nb_samples >= INT_MAX)
return AVERROR(EINVAL);
frame->nb_samples = nb_samples;
}
/* it is assumed that the samples buffer is large enough based on the
relevant parameters */
samples_size = av_samples_get_buffer_size(NULL, avctx->channels,
frame->nb_samples,
avctx->sample_fmt, 1);
if ((ret = avcodec_fill_audio_frame(frame, avctx->channels,
avctx->sample_fmt,
samples, samples_size, 1)))
return ret;
/* fabricate frame pts from sample count.
this is needed because the avcodec_encode_audio() API does not have
a way for the user to provide pts */
frame->pts = av_rescale_q(avctx->internal->sample_count,
(AVRational){ 1, avctx->sample_rate },
avctx->time_base);
avctx->internal->sample_count += frame->nb_samples;
} else {
frame = NULL;
}
got_packet = 0;
ret = avcodec_encode_audio2(avctx, &pkt, frame, &got_packet);
if (!ret && got_packet && avctx->coded_frame) {
avctx->coded_frame->pts = pkt.pts;
avctx->coded_frame->key_frame = !!(pkt.flags & AV_PKT_FLAG_KEY);
}
/* free any side data since we cannot return it */
if (pkt.side_data_elems > 0) {
int i;
for (i = 0; i < pkt.side_data_elems; i++)
av_free(pkt.side_data[i].data);
av_freep(&pkt.side_data);
pkt.side_data_elems = 0;
}
if (frame && frame->extended_data != frame->data)
av_free(frame->extended_data);
return ret ? ret : pkt.size;
}
#endif
int attribute_align_arg avcodec_encode_video(AVCodecContext *avctx, uint8_t *buf, int buf_size,
const AVFrame *pict)
{
@@ -1077,7 +1289,7 @@ av_cold int avcodec_close(AVCodecContext *avctx)
av_opt_free(avctx->priv_data);
av_opt_free(avctx);
av_freep(&avctx->priv_data);
if(avctx->codec && avctx->codec->encode)
if (codec_is_encoder(avctx->codec))
av_freep(&avctx->extradata);
avctx->codec = NULL;
avctx->active_thread_type = 0;
@@ -1095,7 +1307,7 @@ AVCodec *avcodec_find_encoder(enum CodecID id)
AVCodec *p, *experimental=NULL;
p = first_avcodec;
while (p) {
if (p->encode != NULL && p->id == id) {
if (codec_is_encoder(p) && p->id == id) {
if (p->capabilities & CODEC_CAP_EXPERIMENTAL && !experimental) {
experimental = p;
} else
@@ -1113,7 +1325,7 @@ AVCodec *avcodec_find_encoder_by_name(const char *name)
return NULL;
p = first_avcodec;
while (p) {
if (p->encode != NULL && strcmp(name,p->name) == 0)
if (codec_is_encoder(p) && strcmp(name,p->name) == 0)
return p;
p = p->next;
}
@@ -1125,7 +1337,7 @@ AVCodec *avcodec_find_decoder(enum CodecID id)
AVCodec *p;
p = first_avcodec;
while (p) {
if (p->decode != NULL && p->id == id)
if (codec_is_decoder(p) && p->id == id)
return p;
p = p->next;
}
@@ -1139,7 +1351,7 @@ AVCodec *avcodec_find_decoder_by_name(const char *name)
return NULL;
p = first_avcodec;
while (p) {
if (p->decode != NULL && strcmp(name,p->name) == 0)
if (codec_is_decoder(p) && strcmp(name,p->name) == 0)
return p;
p = p->next;
}