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.
---
libavcodec/avcodec.h | 63 ++++++++++++++-
libavcodec/internal.h | 25 ++++++
libavcodec/pcm.c | 1 +
libavcodec/utils.c | 219 +++++++++++++++++++++++++++++++++++++++++++++----
libavcodec/version.h | 3 +
5 files changed, 293 insertions(+), 18 deletions(-)
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 7ee4049..1c9ee23 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -741,6 +741,11 @@ typedef struct RcOverride{
* Encoders:
* The encoder needs to be fed with NULL data at the end of encoding until the
* encoder no longer returns data.
+ *
+ * NOTE: For encoders implementing the AVCodec.encode2() function, setting this
+ * flag also means that the encoder must set the pts and duration for
+ * each output packet. If this flag is not set, the pts and duration will
+ * be determined by libavcodec from the input frame.
*/
#define CODEC_CAP_DELAY 0x0020
/**
@@ -793,6 +798,10 @@ typedef struct RcOverride{
* Codec supports avctx->thread_count == 0 (auto).
*/
#define CODEC_CAP_AUTO_THREADS 0x8000
+/**
+ * Audio encoder supports receiving a different number of samples in each call.
+ */
+#define CODEC_CAP_VARIABLE_FRAME_SIZE 0x10000
//The following defines may change, don't expect compatibility if you use them.
#define MB_TYPE_INTRA4x4 0x0001
@@ -3246,6 +3255,16 @@ typedef struct AVCodec {
* Initialize codec static data, called from avcodec_register().
*/
void (*init_static_data)(struct AVCodec *codec);
+
+ /**
+ * Encode data to an AVPacket.
+ *
+ * @param avctx codec context
+ * @param avpkt output AVPacket
+ * @param data[in] AVFrame containing the raw data to be encoded
+ * @return 0 on success, negative error code on failure
+ */
+ int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame
*frame);
} AVCodec;
/**
@@ -4208,9 +4227,12 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx,
AVSubtitle *sub,
*/
void avsubtitle_free(AVSubtitle *sub);
+#if FF_API_OLD_ENCODE_AUDIO
/**
* Encode an audio frame from samples into buf.
*
+ * @deprecated Use avcodec_encode_audio2 instead.
+ *
* @note The output buffer should be at least FF_MIN_BUFFER_SIZE bytes large.
* However, for codecs with avctx->frame_size equal to 0 (e.g. PCM) the user
* will know how much space is needed because it depends on the value passed
@@ -4230,8 +4252,45 @@ void avsubtitle_free(AVSubtitle *sub);
* @return On error a negative value is returned, on success zero or the number
* of bytes used to encode the data read from the input buffer.
*/
-int avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size,
- const short *samples);
+int attribute_deprecated avcodec_encode_audio(AVCodecContext *avctx,
+ uint8_t *buf, int buf_size,
+ const short *samples);
+#endif
+
+/**
+ * Encode a frame of audio.
+ *
+ * Takes input samples from frame and writes the next output packet, if
+ * available, to avpkt. The output packet does not necessarily contain data for
+ * the most recent frame, as encoders can delay, split, and combine input
frames
+ * internally as needed.
+ *
+ * @param avctx codec context
+ * @param avpkt output AVPacket.
+ * The user can supply an output buffer by setting
+ * avpkt->data and avpkt->size prior to calling the
+ * function, but if the size of the user-provided data is not
+ * large enough, encoding will fail. All other AVPacket fields
+ * will be reset by the encoder using av_init_packet(). If
+ * avpkt->data is NULL, the encoder will allocate it.
+ * The encoder will set avpkt->size to the size of the
+ * output packet, or to 0 if no packet is being output.
+ * @param[in] frame AVFrame containing the raw audio data to be encoded.
+ * May be NULL when flushing an encoder that has the
+ * CODEC_CAP_DELAY capability set.
+ * There are 2 codec capabilities that affect the allowed
+ * values of frame->nb_samples.
+ * If CODEC_CAP_SMALL_LAST_FRAME is set, then only the final
+ * frame may be smaller than avctx->frame_size, and all other
+ * frames must be equal to avctx->frame_size.
+ * If CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame
+ * can have any number of samples.
+ * If neither is set, frame->nb_samples must be equal to
+ * avctx->frame_size for all frames.
+ * @return 0 on success, negative error code on failure
+ */
+int avcodec_encode_audio2(AVCodecContext *avctx, AVPacket *avpkt,
+ const AVFrame *frame);
/**
* Fill audio frame data and linesize.
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index b5a0454..f09199e 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -61,6 +61,14 @@ typedef struct AVCodecInternal {
* should be freed from the original context only.
*/
int is_copy;
+
+#if FF_API_OLD_DECODE_AUDIO
+ /**
+ * Internal sample count used by avcodec_encode_audio() to fabricate pts.
+ * Can be removed along with avcodec_encode_audio().
+ */
+ int sample_count;
+#endif
} AVCodecInternal;
struct AVCodecDefault {
@@ -94,4 +102,21 @@ unsigned int avpriv_toupper4(unsigned int x);
int avpriv_lock_avformat(void);
int avpriv_unlock_avformat(void);
+/**
+ * Check AVPacket size and/or allocate data.
+ *
+ * Encoders supporting AVCodec.encode2() can use this as a convenience to
+ * ensure the output packet data is large enough, whether provided by the user
+ * or allocated in this function.
+ *
+ * @param avpkt the AVPacket
+ * If avpkt->data is already set, avpkt->size is checked
+ * to ensure it is large enough.
+ * If avpkt->data is NULL, a new buffer is allocated.
+ * All other AVPacket fields will be reset with
av_init_packet().
+ * @param size the minimum required packet size
+ * @return 0 on success, negative error code on failure
+ */
+int ff_alloc_packet(AVPacket *avpkt, int size);
+
#endif /* AVCODEC_INTERNAL_H */
diff --git a/libavcodec/pcm.c b/libavcodec/pcm.c
index 76d5c10..24ff0f9 100644
--- a/libavcodec/pcm.c
+++ b/libavcodec/pcm.c
@@ -474,6 +474,7 @@ AVCodec ff_ ## name_ ## _encoder = { \
.init = pcm_encode_init, \
.encode = pcm_encode_frame, \
.close = pcm_encode_close, \
+ .capabilities = CODEC_CAP_VARIABLE_FRAME_SIZE, \
.sample_fmts = (const enum
AVSampleFormat[]){sample_fmt_,AV_SAMPLE_FMT_NONE}, \
.long_name = NULL_IF_CONFIG_SMALL(long_name_), \
}
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 9b27fcb..4b64799 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -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;
@@ -687,7 +698,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
@@ -735,7 +746,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++)
@@ -809,20 +820,196 @@ 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 (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 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) {
+ ret = avctx->codec->encode2(avctx, avpkt, frame);
+ if (!(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;
+
+ if (avctx->frame_size && 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, otherwise set it to NULL */
+ if (!user_packet)
+ av_freep(&avpkt->data);
+ avpkt->data = NULL;
+ } else {
+ if (avctx->coded_frame)
+ avpkt->pts = avctx->coded_frame->pts;
+ }
+ avpkt->size = ret;
+ ret = 0;
+ }
+
+ if (fs_tmp)
+ avctx->frame_size = fs_tmp;
}
- if((avctx->codec->capabilities & CODEC_CAP_DELAY) || samples){
- int ret = avctx->codec->encode(avctx, buf, buf_size, samples);
+ 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 frame;
+ int ret, samples_size;
+
+ av_init_packet(&pkt);
+ pkt.data = buf;
+ pkt.size = buf_size;
+
+ if (!samples) {
+ ret = avcodec_encode_audio2(avctx, &pkt, NULL);
+ return ret ? ret : pkt.size;
+ }
+
+ 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;
- }else
- return 0;
+
+ /* 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;
+
+ ret = avcodec_encode_audio2(avctx, &pkt, &frame);
+ if (!ret) {
+ if (avctx->coded_frame) {
+ avctx->coded_frame->pts = pkt.pts;
+ avctx->coded_frame->key_frame = !!(pkt.flags & AV_PKT_FLAG_KEY);
+ }
+ }
+
+ if (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)
@@ -1072,7 +1259,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;
@@ -1090,7 +1277,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
@@ -1108,7 +1295,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;
}
@@ -1120,7 +1307,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;
}
@@ -1134,7 +1321,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;
}
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 1317ef0..42cc536 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -119,5 +119,8 @@
#ifndef FF_API_AVFRAME_AGE
#define FF_API_AVFRAME_AGE (LIBAVCODEC_VERSION_MAJOR < 54)
#endif
+#ifndef FF_API_OLD_ENCODE_AUDIO
+#define FF_API_OLD_ENCODE_AUDIO (LIBAVCODEC_VERSION_MAJOR < 54)
+#endif
#endif /* AVCODEC_VERSION_H */
--
1.7.1
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel