video.c 10.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * This file is part of Libav.
 *
 * Libav 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,
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "libavutil/imgutils.h"

#include "avfilter.h"
#include "internal.h"
23
#include "video.h"
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38
#ifdef DEBUG
static char *ff_get_ref_perms_string(char *buf, size_t buf_size, int perms)
{
    snprintf(buf, buf_size, "%s%s%s%s%s%s",
             perms & AV_PERM_READ      ? "r" : "",
             perms & AV_PERM_WRITE     ? "w" : "",
             perms & AV_PERM_PRESERVE  ? "p" : "",
             perms & AV_PERM_REUSE     ? "u" : "",
             perms & AV_PERM_REUSE2    ? "U" : "",
             perms & AV_PERM_NEG_LINESIZES ? "n" : "");
    return buf;
}
#endif

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
static void ff_dlog_ref(void *ctx, AVFilterBufferRef *ref, int end)
{
    av_unused char buf[16];
    av_dlog(ctx,
            "ref[%p buf:%p refcount:%d perms:%s data:%p linesize[%d, %d, %d, %d] pts:%"PRId64" pos:%"PRId64,
            ref, ref->buf, ref->buf->refcount, ff_get_ref_perms_string(buf, sizeof(buf), ref->perms), ref->data[0],
            ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3],
            ref->pts, ref->pos);

    if (ref->video) {
        av_dlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c",
                ref->video->pixel_aspect.num, ref->video->pixel_aspect.den,
                ref->video->w, ref->video->h,
                !ref->video->interlaced     ? 'P' :         /* Progressive  */
                ref->video->top_field_first ? 'T' : 'B',    /* Top / Bottom */
                ref->video->key_frame,
                av_get_picture_type_char(ref->video->pict_type));
    }
    if (ref->audio) {
        av_dlog(ctx, " cl:%"PRId64"d n:%d r:%d p:%d",
                ref->audio->channel_layout,
                ref->audio->nb_samples,
                ref->audio->sample_rate,
                ref->audio->planar);
    }

    av_dlog(ctx, "]%s", end ? "\n" : "");
}

68
AVFilterBufferRef *ff_null_get_video_buffer(AVFilterLink *link, int perms, int w, int h)
69
{
70
    return ff_get_video_buffer(link->dst->outputs[0], perms, w, h);
71 72 73 74 75
}

/* TODO: set the buffer's priv member to a context structure for the whole
 * filter chain.  This will allow for a buffer pool instead of the constant
 * alloc & free cycle currently implemented. */
76
AVFilterBufferRef *ff_default_get_video_buffer(AVFilterLink *link, int perms, int w, int h)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
{
    int linesize[4];
    uint8_t *data[4];
    AVFilterBufferRef *picref = NULL;

    // +2 is needed for swscaler, +16 to be SIMD-friendly
    if (av_image_alloc(data, linesize, w, h, link->format, 16) < 0)
        return NULL;

    picref = avfilter_get_video_buffer_ref_from_arrays(data, linesize,
                                                       perms, w, h, link->format);
    if (!picref) {
        av_free(data[0]);
        return NULL;
    }

    return picref;
}

AVFilterBufferRef *
avfilter_get_video_buffer_ref_from_arrays(uint8_t *data[4], int linesize[4], int perms,
                                          int w, int h, enum PixelFormat format)
{
    AVFilterBuffer *pic = av_mallocz(sizeof(AVFilterBuffer));
    AVFilterBufferRef *picref = av_mallocz(sizeof(AVFilterBufferRef));

    if (!pic || !picref)
        goto fail;

    picref->buf = pic;
    picref->buf->free = ff_avfilter_default_free_buffer;
    if (!(picref->video = av_mallocz(sizeof(AVFilterBufferRefVideoProps))))
        goto fail;

    pic->w = picref->video->w = w;
    pic->h = picref->video->h = h;

    /* make sure the buffer gets read permission or it's useless for output */
    picref->perms = perms | AV_PERM_READ;

    pic->refcount = 1;
    picref->type = AVMEDIA_TYPE_VIDEO;
    pic->format = picref->format = format;

    memcpy(pic->data,        data,          4*sizeof(data[0]));
    memcpy(pic->linesize,    linesize,      4*sizeof(linesize[0]));
    memcpy(picref->data,     pic->data,     sizeof(picref->data));
    memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize));

    pic->   extended_data = pic->data;
    picref->extended_data = picref->data;

129 130
    picref->pts = AV_NOPTS_VALUE;

131 132 133 134 135 136 137 138 139 140
    return picref;

fail:
    if (picref && picref->video)
        av_free(picref->video);
    av_free(picref);
    av_free(pic);
    return NULL;
}

141
AVFilterBufferRef *ff_get_video_buffer(AVFilterLink *link, int perms, int w, int h)
142 143 144 145 146 147 148 149 150 151 152
{
    AVFilterBufferRef *ret = NULL;

    av_unused char buf[16];
    FF_DPRINTF_START(NULL, get_video_buffer); ff_dlog_link(NULL, link, 0);
    av_dlog(NULL, " perms:%s w:%d h:%d\n", ff_get_ref_perms_string(buf, sizeof(buf), perms), w, h);

    if (link->dstpad->get_video_buffer)
        ret = link->dstpad->get_video_buffer(link, perms, w, h);

    if (!ret)
153
        ret = ff_default_get_video_buffer(link, perms, w, h);
154 155 156 157 158 159 160 161 162

    if (ret)
        ret->type = AVMEDIA_TYPE_VIDEO;

    FF_DPRINTF_START(NULL, get_video_buffer); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " returning "); ff_dlog_ref(NULL, ret, 1);

    return ret;
}

163
int ff_null_start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
164
{
165
    AVFilterBufferRef *buf_out = avfilter_ref_buffer(picref, ~0);
166 167 168
    if (!buf_out)
        return AVERROR(ENOMEM);
    return ff_start_frame(link->dst->outputs[0], buf_out);
169 170
}

171
static int default_start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
172 173 174
{
    AVFilterLink *outlink = NULL;

175
    if (inlink->dst->nb_outputs)
176 177 178
        outlink = inlink->dst->outputs[0];

    if (outlink) {
179
        outlink->out_buf = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
180 181 182
        if (!outlink->out_buf)
            return AVERROR(ENOMEM);

183
        avfilter_copy_buffer_ref_props(outlink->out_buf, picref);
184
        return ff_start_frame(outlink, avfilter_ref_buffer(outlink->out_buf, ~0));
185
    }
186 187 188 189 190 191 192 193
    return 0;
}

static void clear_link(AVFilterLink *link)
{
    avfilter_unref_bufferp(&link->cur_buf);
    avfilter_unref_bufferp(&link->src_buf);
    avfilter_unref_bufferp(&link->out_buf);
194 195 196 197
}

/* XXX: should we do the duplicating of the picture ref here, instead of
 * forcing the source filter to do it? */
198
int ff_start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
199
{
200
    int (*start_frame)(AVFilterLink *, AVFilterBufferRef *);
201
    AVFilterPad *dst = link->dstpad;
202
    int ret, perms = picref->perms;
203 204 205 206

    FF_DPRINTF_START(NULL, start_frame); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " "); ff_dlog_ref(NULL, picref, 1);

    if (!(start_frame = dst->start_frame))
207
        start_frame = default_start_frame;
208 209 210 211 212 213 214 215 216 217

    if (picref->linesize[0] < 0)
        perms |= AV_PERM_NEG_LINESIZES;
    /* prepare to copy the picture if it has insufficient permissions */
    if ((dst->min_perms & perms) != dst->min_perms || dst->rej_perms & perms) {
        av_log(link->dst, AV_LOG_DEBUG,
                "frame copy needed (have perms %x, need %x, reject %x)\n",
                picref->perms,
                link->dstpad->min_perms, link->dstpad->rej_perms);

218
        link->cur_buf = ff_get_video_buffer(link, dst->min_perms, link->w, link->h);
219 220 221 222 223
        if (!link->cur_buf) {
            avfilter_unref_bufferp(&picref);
            return AVERROR(ENOMEM);
        }

224 225 226 227 228 229
        link->src_buf = picref;
        avfilter_copy_buffer_ref_props(link->cur_buf, link->src_buf);
    }
    else
        link->cur_buf = picref;

230 231 232 233 234
    ret = start_frame(link, link->cur_buf);
    if (ret < 0)
        clear_link(link);

    return ret;
235 236
}

237
void ff_null_end_frame(AVFilterLink *link)
238
{
239
    ff_end_frame(link->dst->outputs[0]);
240 241
}

242
static void default_end_frame(AVFilterLink *inlink)
243 244 245
{
    AVFilterLink *outlink = NULL;

246
    if (inlink->dst->nb_outputs)
247 248 249
        outlink = inlink->dst->outputs[0];

    if (outlink) {
250
        ff_end_frame(outlink);
251 252 253
    }
}

254
void ff_end_frame(AVFilterLink *link)
255 256 257 258
{
    void (*end_frame)(AVFilterLink *);

    if (!(end_frame = link->dstpad->end_frame))
259
        end_frame = default_end_frame;
260 261 262

    end_frame(link);

263
    clear_link(link);
264 265
}

266
int ff_null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
267
{
268
    return ff_draw_slice(link->dst->outputs[0], y, h, slice_dir);
269 270
}

271
static int default_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
272 273 274
{
    AVFilterLink *outlink = NULL;

275
    if (inlink->dst->nb_outputs)
276 277 278
        outlink = inlink->dst->outputs[0];

    if (outlink)
279 280
        return ff_draw_slice(outlink, y, h, slice_dir);
    return 0;
281 282
}

283
int ff_draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
284 285
{
    uint8_t *src[4], *dst[4];
286 287
    int i, j, vsub, ret;
    int (*draw_slice)(AVFilterLink *, int, int, int);
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319

    FF_DPRINTF_START(NULL, draw_slice); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " y:%d h:%d dir:%d\n", y, h, slice_dir);

    /* copy the slice if needed for permission reasons */
    if (link->src_buf) {
        vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h;

        for (i = 0; i < 4; i++) {
            if (link->src_buf->data[i]) {
                src[i] = link->src_buf-> data[i] +
                    (y >> (i==1 || i==2 ? vsub : 0)) * link->src_buf-> linesize[i];
                dst[i] = link->cur_buf->data[i] +
                    (y >> (i==1 || i==2 ? vsub : 0)) * link->cur_buf->linesize[i];
            } else
                src[i] = dst[i] = NULL;
        }

        for (i = 0; i < 4; i++) {
            int planew =
                av_image_get_linesize(link->format, link->cur_buf->video->w, i);

            if (!src[i]) continue;

            for (j = 0; j < h >> (i==1 || i==2 ? vsub : 0); j++) {
                memcpy(dst[i], src[i], planew);
                src[i] += link->src_buf->linesize[i];
                dst[i] += link->cur_buf->linesize[i];
            }
        }
    }

    if (!(draw_slice = link->dstpad->draw_slice))
320
        draw_slice = default_draw_slice;
321 322 323 324
    ret = draw_slice(link, y, h, slice_dir);
    if (ret < 0)
        clear_link(link);
    return ret;
325
}