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  |   47 +++++++++++++++-
 libavcodec/internal.h |   17 ++++++
 libavcodec/utils.c    |  148 ++++++++++++++++++++++++++++++++++++++++++++++---
 libavcodec/version.h  |    3 +
 4 files changed, 204 insertions(+), 11 deletions(-)

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 7577cd5..f620457 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -3170,6 +3170,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 or AVSubtitle containing the raw data to be 
encoded
+     * @return          0 on success, negative error code on failure
+     */
+    int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const void *data);
 } AVCodec;
 
 /**
@@ -4135,6 +4145,8 @@ void avsubtitle_free(AVSubtitle *sub);
 /**
  * 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 supporting CODEC_CAP_VARIABLE_FRAME_SIZE
  * (e.g. PCM and G.722) the user will know how much space is needed because it
@@ -4153,8 +4165,39 @@ 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);
+
+/**
+ * Encode a frame of audio.
+ *
+ * @param avctx     codec context
+ * @param avpkt     output AVPacket.
+ *                  The user can supply an output buffer by setting
+ *                  @a avpkt->data and @a 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
+ *                  @a avpkt->data is NULL, the encoder will allocate it.
+ *                  The encoder will set @a avpkt->size to the size of the
+ *                  output packet.
+ * @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 @a frame->nb_samples.
+ *                  If CODEC_CAP_SMALL_LAST_FRAME is set, then only the final
+ *                  frame may be smaller than @a avctx->frame_size, and all 
other
+ *                  frames must be equal to @a avctx->frame_size.
+ *                  If CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame
+ *                  can have any number of samples.
+ *                  If neither is set, @a frame->nb_samples must be equal to
+ *                  @a 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 fb011c7..5d784d4 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -95,4 +95,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 @a avpkt->data is already set, @a avpkt->size is checked
+ *                to ensure it is large enough.
+ *                If @a 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/utils.c b/libavcodec/utils.c
index c3ed7aa..ecd3a30 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -814,20 +814,150 @@ 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 + FF_INPUT_BUFFER_PADDING_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;
+
+    if (!(avctx->codec->capabilities & CODEC_CAP_DELAY) && !frame) {
+        avpkt->size = 0;
+        return 0;
+    }
+
+    /* check for valid frame size */
+    if (frame) {
+        if (avctx->codec->capabilities & CODEC_CAP_SMALL_LAST_FRAME) {
+            if (frame->nb_samples > avctx->frame_size)
+                return AVERROR(EINVAL);
+        } else if (!(avctx->codec->capabilities & 
CODEC_CAP_VARIABLE_FRAME_SIZE)) {
+            if (frame->nb_samples != avctx->frame_size)
+                return AVERROR(EINVAL);
+        }
+    }
+
+    /* 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 */
+    if (!avctx->codec->encode2) {
+        int buf_size = avpkt->size;
+        if (!user_packet) {
+            /* this is a guess that the encoder will no more than double the
+               size of the input */
+            buf_size = 2 * frame->nb_samples * avctx->channels *
+                       av_get_bytes_per_sample(avctx->sample_fmt);
+        }
+        buf_size = FFMAX(buf_size, FF_MIN_BUFFER_SIZE);
+
+        if (!(ret = ff_alloc_packet(avpkt, buf_size)))
+            return ret;
+    }
+
+    if (avctx->codec->encode2)
+        ret = avctx->codec->encode2(avctx, avpkt, frame);
+    else {
+        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;
+            }
+            avpkt->size = ret;
+            ret = 0;
+        }
     }
-    if((avctx->codec->capabilities & CODEC_CAP_DELAY) || samples){
-        int ret = avctx->codec->encode(avctx, buf, buf_size, samples);
+    if (!ret)
         avctx->frame_number++;
+
+    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 frame_size is not set, CODEC_CAP_VARIABLE_FRAME_SIZE must be
+       supported and the number of samples must be able to be calculated from
+       the buffer size */
+    if (!avctx->frame_size &&
+        (!(avctx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) ||
+         !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);
+    }
+    if (avctx->frame_size) {
+        frame.nb_samples = avctx->frame_size;
+    } else {
+        int64_t 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 = fill_audio_frame(&frame, avctx->channels, avctx->sample_fmt,
+                                samples, samples_size, 1)))
         return ret;
-    }else
-        return 0;
+
+    ret = avcodec_encode_audio2(avctx, &pkt, &frame);
+
+    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)
diff --git a/libavcodec/version.h b/libavcodec/version.h
index d15fb48..f6d5aff 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -116,5 +116,8 @@
 #ifndef FF_API_OLD_DECODE_AUDIO
 #define FF_API_OLD_DECODE_AUDIO (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
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to