Implement audio support in avcodec_default_get_buffer().
Deprecate avcodec_decode_audio3().

Note: This patch does not work by itself. All audio decoders must be converted
to use the new interface.
---
 doc/APIchanges        |    7 ++
 libavcodec/avcodec.h  |  120 ++++++++++++++++++++++--
 libavcodec/internal.h |   17 ++++
 libavcodec/utils.c    |  240 +++++++++++++++++++++++++++++++++++++++++++++----
 libavcodec/version.h  |    5 +-
 5 files changed, 359 insertions(+), 30 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 9bcee66..2763c75 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -13,6 +13,13 @@ libavutil:   2011-04-18
 
 API changes, most recent first:
 
+2011-xx-xx - xxxxxxx - lavc 53.23.0
+  Add nb_samples and extended_data fields to AVFrame.
+  Deprecate AVCODEC_MAX_AUDIO_FRAME_SIZE.
+  Deprecate avcodec_decode_audio3() in favor of avcodec_decode_audio4().
+  avcodec_decode_audio4() writes output samples to an AVFrame, which allows
+  audio decoders to use get_buffer().
+
 2011-xx-xx - xxxxxxx - lavu 51.18.0
   Add av_samples_check_size(), av_samples_fill_arrays(), av_samples_alloc(),
   and av_samples_free() to samplefmt.h.
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 1936b37..8e5c6f2 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -449,8 +449,10 @@ enum CodecID {
 #define CH_LAYOUT_STEREO_DOWNMIX AV_CH_LAYOUT_STEREO_DOWNMIX
 #endif
 
+#if FF_API_OLD_DECODE_AUDIO
 /* in bytes */
 #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
+#endif
 
 /**
  * Required number of additionally allocated bytes at the end of the input 
bitstream for decoding.
@@ -902,10 +904,10 @@ typedef struct AVFrame {
 #define AV_NUM_DATA_POINTERS 8
 #endif
     /**
-     * pointer to the picture planes.
+     * pointer to the picture/channel planes.
      * This might be different from the first allocated byte
-     * - encoding:
-     * - decoding:
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
      */
     uint8_t *data[AV_NUM_DATA_POINTERS];
     int linesize[4];
@@ -962,7 +964,7 @@ typedef struct AVFrame {
      * buffer age (1->was last buffer and dint change, 2->..., ...).
      * Set to INT_MAX if the buffer has not been used yet.
      * - encoding: unused
-     * - decoding: MUST be set by get_buffer().
+     * - decoding: MUST be set by get_buffer() for video.
      */
     int age;
 
@@ -1159,6 +1161,32 @@ typedef struct AVFrame {
      * - decoding: Set by libavcodec.
      */
     void *thread_opaque;
+
+    /**
+     * number of audio samples (per channel) described by this frame
+     * - encoding: unused
+     * - decoding: Set by libavcodec
+     */
+    int nb_samples;
+
+    /**
+     * pointers to the data planes/channels.
+     *
+     * For video, this should simply point to data[].
+     *
+     * For planar audio, each channel has a separate data pointer, and
+     * linesize[0] contains the size of each channel buffer.
+     * For packed audio, there is just one data pointer, and linesize[0]
+     * contains the total size of the buffer for all channels.
+     *
+     * Note: Both data and extended_data will be always be set, but for
+     * planar audio with more channels that can fit in data, extended_data
+     * must be used in order to access all channels.
+     *
+     * encoding: unused
+     * decoding: set by libavcodec in AVCodecContext.get_buffer()
+     */
+    uint8_t **extended_data;
 } AVFrame;
 
 struct AVCodecInternal;
@@ -1514,15 +1542,49 @@ typedef struct AVCodecContext {
 
     /**
      * Called at the beginning of each frame to get a buffer for it.
-     * If pic.reference is set then the frame will be read later by libavcodec.
-     * avcodec_align_dimensions2() should be used to find the required width 
and
-     * height, as they normally need to be rounded up to the next multiple of 
16.
+     *
      * if CODEC_CAP_DR1 is not set then get_buffer() must call
      * avcodec_default_get_buffer() instead of providing buffers allocated by
      * some other means.
+     *
+     * AVFrame.data[] should be 32- or 16-byte-aligned unless the CPU doesn't
+     * need it. avcodec_default_get_buffer() aligns the output buffer properly,
+     * but get_buffer() is overridden then alignment considerations should be
+     * taken into account.
+     *
+     * @defgroup get_buffer_video Video
+     * @{
+     *
+     * If pic.reference is set then the frame will be read later by libavcodec.
+     * avcodec_align_dimensions2() should be used to find the required width 
and
+     * height, as they normally need to be rounded up to the next multiple of 
16.
+     *
      * If frame multithreading is used and thread_safe_callbacks is set,
-     * it may be called from a different thread, but not from more than one at 
once.
-     * Does not need to be reentrant.
+     * it may be called from a different thread, but not from more than one at
+     * once. Does not need to be reentrant.
+     *
+     * @see release_buffer(), reget_buffer()
+     * @see avcodec_align_dimensions2()
+     * @}
+     *
+     * @defgroup get_buffer_audio Audio
+     * @{
+     *
+     * Decoders cannot use the buffer after returning from
+     * avcodec_decode_audio4(), so they will not call release_buffer(), as it
+     * is assumed to be released immediately upon return.
+     *
+     * As a convenience, av_samples_get_buffer_size() and
+     * av_samples_fill_arrays() in libavutil may be used by custom get_buffer()
+     * functions to find the required data size and to fill data pointers and
+     * linesize. In AVFrame.linesize, only linesize[0] needs to be set for
+     * audio since all planes must be the same size.
+     *
+     * @see av_samples_get_buffer_size(), av_samples_fill_arrays()
+     * @}
+     *
+     * @see avcodec_default_get_buffer()
+     *
      * - encoding: unused
      * - decoding: Set by libavcodec, user can override.
      */
@@ -3851,7 +3913,12 @@ int avcodec_open(AVCodecContext *avctx, AVCodec *codec);
  */
 int avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVDictionary 
**options);
 
+#if FF_API_OLD_DECODE_AUDIO
 /**
+ * Wrapper function which calls avcodec_decode_audio4.
+ *
+ * @deprecated Use avcodec_decode_audio4 instead.
+ *
  * Decode the audio frame of size avpkt->size from avpkt->data into samples.
  * Some decoders may support multiple frames in a single AVPacket, such
  * decoders would then just decode the first frame. In this case,
@@ -3894,9 +3961,42 @@ int avcodec_open2(AVCodecContext *avctx, AVCodec *codec, 
AVDictionary **options)
  * @return On error a negative value is returned, otherwise the number of bytes
  * used or zero if no frame data was decompressed (used) from the input 
AVPacket.
  */
-int avcodec_decode_audio3(AVCodecContext *avctx, int16_t *samples,
+attribute_deprecated int avcodec_decode_audio3(AVCodecContext *avctx, int16_t 
*samples,
                          int *frame_size_ptr,
                          AVPacket *avpkt);
+#endif
+
+/**
+ * Decode the audio frame of size avpkt->size from avpkt->data into frame.
+ *
+ * Some decoders may support multiple frames in a single AVPacket. Such
+ * decoders would then just decode the first frame. In this case,
+ * avcodec_decode_audio4 has to be called again with an AVPacket containing
+ * the remaining data in order to decode the second frame, etc...
+ * Even if no frames are returned, the packet needs to be fed to the decoder
+ * with remaining data until it is completely consumed or an error occurs.
+ *
+ * @warning The input buffer, avpkt->data must be FF_INPUT_BUFFER_PADDING_SIZE
+ *          larger than the actual read bytes because some optimized bitstream
+ *          readers read 32 or 64 bits at once and could read over the end.
+ *
+ * @note You might have to align the input buffer. The alignment requirements
+ *       depend on the CPU and the decoder.
+ *
+ * @param      avctx the codec context
+ * @param[out] frame The AVFrame in which to store decoded audio samples.
+ *
+ * @param[out] got_frame_ptr Zero if no frame could be decoded, otherwise it is
+ *                           non-zero.
+ * @param[in]  avpkt The input AVPacket containing the input buffer.
+ *                   At least avpkt->data and avpkt->size should be set. Some
+ *                   decoders might also require additional fields to be set.
+ * @return A negative error code is returned if an error occurred during
+ *         decoding, otherwise the number of bytes consumed from the input
+ *         AVPacket is returned.
+ */
+int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame,
+                          int *got_frame_ptr, AVPacket *avpkt);
 
 /**
  * Decode the video frame of size avpkt->size from avpkt->data into picture.
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 18e851c..ba55ff5 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -37,6 +37,9 @@ typedef struct InternalBuffer {
     int width;
     int height;
     enum PixelFormat pix_fmt;
+    uint8_t **extended_data;
+    int audio_data_size;
+    int nb_channels;
 } InternalBuffer;
 
 typedef struct AVCodecInternal {
@@ -52,6 +55,20 @@ typedef struct AVCodecInternal {
      */
     InternalBuffer *buffer;
 
+#if FF_API_OLD_DECODE_AUDIO
+    /**
+     * User-allocated audio decoder output buffer.
+     * Used for backwards compatibility with avcodec_decode_audio3().
+     */
+    uint8_t *user_buffer;
+
+    /**
+     * Allocated size, in bytes, of AVCodecContext.user_buffer.
+     * Used for backwards compatibility with avcodec_decode_audio3().
+     */
+    int user_buffer_size;
+#endif
+
     /**
      * Whether the parent AVCodecContext is a copy of the context which had
      * init() called on it.
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 3e182a1..1773f66 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -40,11 +40,14 @@
 #include "thread.h"
 #include "audioconvert.h"
 #include "internal.h"
+#include "mathops.h"
 #include <stdlib.h>
 #include <stdarg.h>
 #include <limits.h>
 #include <float.h>
 
+#define SANE_NB_CHANNELS 128U
+
 static int volatile entangled_thread_counter=0;
 static int (*ff_lockmgr_cb)(void **mutex, enum AVLockOp op);
 static void *codec_mutex;
@@ -235,7 +238,138 @@ void avcodec_align_dimensions(AVCodecContext *s, int 
*width, int *height){
     *width=FFALIGN(*width, align);
 }
 
-int avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic){
+static int audio_get_buffer(AVCodecContext *avctx, AVFrame *frame)
+{
+    AVCodecInternal *avci = avctx->internal;
+    InternalBuffer *buf;
+    int buf_size, ret, i, use_extended_data;
+
+    buf_size = av_samples_get_buffer_size(NULL, avctx->channels,
+                                          frame->nb_samples, avctx->sample_fmt,
+                                          32);
+    if (buf_size < 0)
+        return AVERROR(EINVAL);
+
+    use_extended_data = av_sample_fmt_is_planar(avctx->sample_fmt) &&
+                        avctx->channels > AV_NUM_DATA_POINTERS;
+
+#if FF_API_OLD_DECODE_AUDIO
+    /* Check for use of user_buffer for backwards compatibility with
+       avcodec_decode_audio3() */
+    if (avci->user_buffer) {
+        if (avci->user_buffer_size < buf_size) {
+            av_log(avctx, AV_LOG_ERROR, "AVCodecContext.user_buffer_size is "
+                   "too small for the requested frame. (%d < %d)\n",
+                   avci->user_buffer_size, buf_size);
+            return AVERROR(EINVAL);
+        }
+        if (frame->extended_data && frame->extended_data != frame->data)
+            av_freep(&frame->extended_data);
+        if (use_extended_data) {
+            frame->extended_data = av_mallocz(avctx->channels *
+                                              sizeof(*frame->extended_data));
+            if (!frame->extended_data)
+                return AVERROR(ENOMEM);
+        } else {
+            frame->extended_data = frame->data;
+        }
+        ret = av_samples_fill_arrays(frame->extended_data, &frame->linesize[0],
+                                     avci->user_buffer, avctx->channels,
+                                     frame->nb_samples, avctx->sample_fmt, 32);
+        if (ret)
+            return ret;
+
+        if (use_extended_data) {
+            for (i = 0; i < AV_NUM_DATA_POINTERS; i++)
+                frame->data[i] = frame->extended_data[i];
+        }
+
+        frame->type = FF_BUFFER_TYPE_USER;
+
+        if (avctx->pkt) frame->pkt_pts = avctx->pkt->pts;
+        else            frame->pkt_pts = AV_NOPTS_VALUE;
+        frame->reordered_opaque = avctx->reordered_opaque;
+
+        if (avctx->debug & FF_DEBUG_BUFFERS) {
+            av_log(avctx, AV_LOG_DEBUG, "default_get_buffer called on "
+                   "AVFrame %p, AVCodecContext.user_buffer used\n", frame);
+        }
+        return 0;
+    }
+#endif
+
+    if (!avci->buffer) {
+        avci->buffer = av_mallocz(sizeof(InternalBuffer));
+        if (!avci->buffer)
+            return AVERROR(ENOMEM);
+    }
+    buf = avci->buffer;
+
+    if (buf->extended_data) {
+        /* if current buffer is too small, free it */
+        if (buf->extended_data[0] && buf_size > buf->audio_data_size) {
+            av_free(buf->extended_data[0]);
+            if (buf->extended_data != buf->data)
+                av_free(&buf->extended_data);
+            buf->extended_data = NULL;
+            buf->data[0] = NULL;
+        }
+        /* if number of channels has changed, reset and/or free extended data
+           pointers but leave data buffer in buf->data[0] for reuse */
+        if (buf->nb_channels != avctx->channels) {
+            if (buf->extended_data != buf->data)
+                av_free(buf->extended_data);
+            buf->extended_data = NULL;
+        }
+    }
+
+    if (!buf->extended_data) {
+        if (use_extended_data) {
+            buf->extended_data = av_mallocz(avctx->channels *
+                                            sizeof(*buf->extended_data));
+            if (!buf->extended_data)
+                return AVERROR(ENOMEM);
+        } else {
+            buf->extended_data = buf->data;
+        }
+        if (buf->data[0]) {
+            ret = av_samples_fill_arrays(buf->extended_data, &buf->linesize[0],
+                                         buf->data[0], avctx->channels,
+                                         frame->nb_samples, avctx->sample_fmt,
+                                         32);
+        } else {
+            ret = av_samples_alloc(buf->extended_data, &buf->linesize[0],
+                                   avctx->channels, frame->nb_samples,
+                                   avctx->sample_fmt, 32);
+        }
+        if (ret)
+            return ret;
+
+        if (use_extended_data) {
+            for (i = 0; i < AV_NUM_DATA_POINTERS; i++)
+                buf->data[i] = buf->extended_data[i];
+        }
+        buf->audio_data_size = buf_size;
+        buf->nb_channels     = avctx->channels;
+    }
+    frame->type          = FF_BUFFER_TYPE_INTERNAL;
+    frame->extended_data = buf->extended_data;
+    frame->linesize[0]   = buf->linesize[0];
+    memcpy(frame->data, buf->data, sizeof(frame->data));
+
+    if (avctx->pkt) frame->pkt_pts = avctx->pkt->pts;
+    else            frame->pkt_pts = AV_NOPTS_VALUE;
+    frame->reordered_opaque = avctx->reordered_opaque;
+
+    if (avctx->debug & FF_DEBUG_BUFFERS)
+        av_log(avctx, AV_LOG_DEBUG, "default_get_buffer called on frame %p, "
+               "internal audio buffer used\n", frame);
+
+    return 0;
+}
+
+static int video_get_buffer(AVCodecContext *s, AVFrame *pic)
+{
     int i;
     int w= s->width;
     int h= s->height;
@@ -352,6 +486,7 @@ int avcodec_default_get_buffer(AVCodecContext *s, AVFrame 
*pic){
         pic->data[i]= buf->data[i];
         pic->linesize[i]= buf->linesize[i];
     }
+    pic->extended_data = pic->data;
     avci->buffer_count++;
 
     if(s->pkt) pic->pkt_pts= s->pkt->pts;
@@ -365,11 +500,25 @@ int avcodec_default_get_buffer(AVCodecContext *s, AVFrame 
*pic){
     return 0;
 }
 
+int avcodec_default_get_buffer(AVCodecContext *avctx, AVFrame *frame)
+{
+    switch (avctx->codec_type) {
+    case AVMEDIA_TYPE_VIDEO:
+        return video_get_buffer(avctx, frame);
+    case AVMEDIA_TYPE_AUDIO:
+        return audio_get_buffer(avctx, frame);
+    default:
+        return -1;
+    }
+}
+
 void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic){
     int i;
     InternalBuffer *buf, *last;
     AVCodecInternal *avci = s->internal;
 
+    assert(s->codec_type == AVMEDIA_TYPE_VIDEO);
+
     assert(pic->type==FF_BUFFER_TYPE_INTERNAL);
     assert(avci->buffer_count);
 
@@ -402,6 +551,8 @@ int avcodec_default_reget_buffer(AVCodecContext *s, AVFrame 
*pic){
     AVFrame temp_pic;
     int i;
 
+    assert(s->codec_type == AVMEDIA_TYPE_VIDEO);
+
     /* If no picture return a new buffer */
     if(pic->data[0] == NULL) {
         /* We will copy from buffer, so must be readable */
@@ -553,7 +704,6 @@ int attribute_align_arg avcodec_open2(AVCodecContext 
*avctx, AVCodec *codec, AVD
     if (codec->decode)
         av_freep(&avctx->subtitle_header);
 
-#define SANE_NB_CHANNELS 128U
     if (avctx->channels > SANE_NB_CHANNELS) {
         ret = AVERROR(EINVAL);
         goto free_and_end;
@@ -751,11 +901,42 @@ int attribute_align_arg 
avcodec_decode_video2(AVCodecContext *avctx, AVFrame *pi
     return ret;
 }
 
+#if FF_API_OLD_DECODE_AUDIO
 int attribute_align_arg avcodec_decode_audio3(AVCodecContext *avctx, int16_t 
*samples,
                          int *frame_size_ptr,
                          AVPacket *avpkt)
 {
-    int ret;
+    AVCodecInternal *avci = avctx->internal;
+    AVFrame frame;
+    int ret, got_frame = 0;
+
+    if (avctx->get_buffer != avcodec_default_get_buffer) {
+        av_log(avctx, AV_LOG_ERROR, "A custom get_buffer() cannot be used with 
"
+               "avcodec_decode_audio3()\n");
+        return AVERROR(EINVAL);
+    }
+    avci->user_buffer      = (uint8_t *)samples;
+    avci->user_buffer_size = *frame_size_ptr;
+
+    ret = avcodec_decode_audio4(avctx, &frame, &got_frame, avpkt);
+
+    if (ret >= 0 && got_frame) {
+        *frame_size_ptr = frame.linesize[0];
+    } else {
+        *frame_size_ptr = 0;
+    }
+    return ret;
+}
+#endif
+
+int attribute_align_arg avcodec_decode_audio4(AVCodecContext *avctx,
+                                              AVFrame *frame,
+                                              int *got_frame_ptr,
+                                              AVPacket *avpkt)
+{
+    int ret = 0;
+
+    *got_frame_ptr = 0;
 
     avctx->pkt = avpkt;
 
@@ -765,22 +946,11 @@ int attribute_align_arg 
avcodec_decode_audio3(AVCodecContext *avctx, int16_t *sa
     }
 
     if((avctx->codec->capabilities & CODEC_CAP_DELAY) || avpkt->size){
-        //FIXME remove the check below _after_ ensuring that all audio check 
that the available space is enough
-        if(*frame_size_ptr < AVCODEC_MAX_AUDIO_FRAME_SIZE){
-            av_log(avctx, AV_LOG_ERROR, "buffer smaller than 
AVCODEC_MAX_AUDIO_FRAME_SIZE\n");
-            return -1;
-        }
-        if(*frame_size_ptr < FF_MIN_BUFFER_SIZE ||
-        *frame_size_ptr < avctx->channels * avctx->frame_size * 
sizeof(int16_t)){
-            av_log(avctx, AV_LOG_ERROR, "buffer %d too small\n", 
*frame_size_ptr);
-            return -1;
+        ret = avctx->codec->decode(avctx, frame, got_frame_ptr, avpkt);
+        if (ret >= 0 && *got_frame_ptr) {
+            avctx->frame_number++;
+            frame->pkt_dts = avpkt->dts;
         }
-
-        ret = avctx->codec->decode(avctx, samples, frame_size_ptr, avpkt);
-        avctx->frame_number++;
-    }else{
-        ret= 0;
-        *frame_size_ptr=0;
     }
     return ret;
 }
@@ -1105,7 +1275,8 @@ void avcodec_flush_buffers(AVCodecContext *avctx)
         avctx->codec->flush(avctx);
 }
 
-void avcodec_default_free_buffers(AVCodecContext *s){
+static void video_free_buffers(AVCodecContext *s)
+{
     AVCodecInternal *avci = s->internal;
     int i, j;
 
@@ -1127,6 +1298,37 @@ void avcodec_default_free_buffers(AVCodecContext *s){
     avci->buffer_count=0;
 }
 
+static void audio_free_buffers(AVCodecContext *avctx)
+{
+    AVCodecInternal *avci = avctx->internal;
+    InternalBuffer *buf;
+
+    if (!avci->buffer)
+        return;
+    buf = avci->buffer;
+
+    if (buf->extended_data) {
+        av_free(buf->extended_data[0]);
+        if (buf->extended_data != buf->data)
+            av_free(buf->extended_data);
+    }
+    av_freep(&avci->buffer);
+}
+
+void avcodec_default_free_buffers(AVCodecContext *avctx)
+{
+    switch (avctx->codec_type) {
+    case AVMEDIA_TYPE_VIDEO:
+        video_free_buffers(avctx);
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        audio_free_buffers(avctx);
+        break;
+    default:
+        break;
+    }
+}
+
 #if FF_API_OLD_FF_PICT_TYPES
 char av_get_pict_type_char(int pict_type){
     return av_get_picture_type_char(pict_type);
diff --git a/libavcodec/version.h b/libavcodec/version.h
index b1f814a..3698897 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -21,7 +21,7 @@
 #define AVCODEC_VERSION_H
 
 #define LIBAVCODEC_VERSION_MAJOR 53
-#define LIBAVCODEC_VERSION_MINOR 22
+#define LIBAVCODEC_VERSION_MINOR 23
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
@@ -110,5 +110,8 @@
 #ifndef FF_API_DATA_POINTERS
 #define FF_API_DATA_POINTERS (LIBAVCODEC_VERSION_MAJOR < 54)
 #endif
+#ifndef FF_API_OLD_DECODE_AUDIO
+#define FF_API_OLD_DECODE_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

Reply via email to