blob: cbc8c138b8c0f999c72caadff897631f742a295f [file] [edit]
/*
* This file is part of FFmpeg.
*
* FFmpeg 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.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Packet source. Heavily based on libavfilter/buffersrc.c
*/
#include <float.h>
#include "libavutil/container_fifo.h"
#include "libavutil/opt.h"
#include "libavcodec/bsf.h"
#include "libavcodec/bsf_internal.h"
#include "libavcodec/packet.h"
#include "libavcodec/packet_internal.h"
typedef struct SourceContext {
const AVClass *class;
AVCodecParameters *par;
AVRational time_base; ///< time_base to set in the output link
unsigned nb_failed_requests;
unsigned warning_limit;
int eof;
int64_t last_pts;
} SourceContext;
int av_bsf_source_parameters_set(AVBitStreamFilterContext *ctx, const AVCodecParameters *par)
{
SourceContext *s = ctx->priv_data;
return avcodec_parameters_copy(s->par, par);
}
static int push_packet(AVBitStreamFilterGraph *graph)
{
int ret;
while (1) {
ret = ff_bsf_graph_run_once(graph);
if (ret == AVERROR(EAGAIN))
break;
if (ret < 0 && ret != FFERROR_SOURCE_EMPTY)
return ret;
}
return 0;
}
int attribute_align_arg av_bsf_source_add_packet(AVBitStreamFilterContext *ctx, AVPacket *pkt, int flags)
{
SourceContext *s = ctx->priv_data;
AVPacket *copy;
int ret;
s->nb_failed_requests = 0;
if (!pkt || AVPACKET_IS_EMPTY(pkt))
return av_bsf_source_close(ctx, s->last_pts, flags);
if (s->eof)
return AVERROR_EOF;
s->last_pts = pkt->pts + pkt->duration;
copy = av_packet_alloc();
if (!copy)
return AVERROR(ENOMEM);
if ((flags & AV_BSF_SOURCE_FLAG_KEEP_REF)) {
ret = av_packet_ref(copy, pkt);
if (ret < 0) {
av_packet_free(&copy);
return ret;
}
} else
av_packet_move_ref(copy, pkt);
ret = ff_bsf_filter_packet(ctx->outputs[0], copy);
if (ret < 0)
return ret;
if ((flags & AV_BSF_SOURCE_FLAG_PUSH)) {
ret = push_packet(ctx->graph);
if (ret < 0)
return ret;
}
BitStreamFilterLinkInternal *const li = ff_link_internal(ctx->outputs[0]);
if (s->warning_limit &&
av_container_fifo_can_read(li->fifo) >= s->warning_limit) {
av_log(s, AV_LOG_WARNING,
"%d buffers queued in %s, something may be wrong.\n",
s->warning_limit,
(char *)av_x_if_null(ctx->name, ctx->filter->name));
s->warning_limit *= 10;
}
return 0;
}
int av_bsf_source_close(AVBitStreamFilterContext *ctx, int64_t pts, unsigned flags)
{
SourceContext *s = ctx->priv_data;
s->eof = 1;
ff_bsf_link_set_in_status(ctx->outputs[0], AVERROR_EOF, pts);
return 0;
}
int av_bsf_source_get_status(AVBitStreamFilterContext *ctx)
{
SourceContext *s = ctx->priv_data;
if (!s->eof && ff_bsf_outlink_get_status(ctx->outputs[0]))
s->eof = 1;
return s->eof ? AVERROR(EOF) : 0;
}
static av_cold int preinit(AVBitStreamFilterContext *ctx)
{
SourceContext *c = ctx->priv_data;
c->par = avcodec_parameters_alloc();
if (!c->par)
return AVERROR(ENOMEM);
return 0;
}
static av_cold int init(AVBitStreamFilterContext *ctx)
{
SourceContext *c = ctx->priv_data;
if (av_q2d(c->time_base) <= 0) {
av_log(ctx, AV_LOG_ERROR, "Invalid time base %d/%d\n", c->time_base.num, c->time_base.den);
return AVERROR(EINVAL);
}
c->warning_limit = 100;
return 0;
}
unsigned ff_bsf_source_get_nb_failed_requests(const AVBitStreamFilterContext *buffer_src)
{
return ((SourceContext *)buffer_src->priv_data)->nb_failed_requests;
}
#define OFFSET(x) offsetof(SourceContext, x)
#define FLAGS (AV_OPT_FLAG_BSF_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
static const AVOption buffer_options[] = {
{ "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, FLAGS },
{ NULL },
};
BSF_DEFINE_CLASS(buffer);
static av_cold void uninit(AVBitStreamFilterContext *ctx)
{
SourceContext *s = ctx->priv_data;
avcodec_parameters_free(&s->par);
}
static int config_props(AVBitStreamFilterLink *link)
{
SourceContext *c = link->src->priv_data;
link->time_base = c->time_base;
return avcodec_parameters_copy(link->par, c->par);
}
static int activate(AVBitStreamFilterContext *ctx)
{
AVBitStreamFilterLink *outlink = ctx->outputs[0];
SourceContext *c = ctx->priv_data;
if (!c->eof && ff_bsf_outlink_get_status(outlink)) {
c->eof = 1;
return 0;
}
if (c->eof) {
ff_bsf_link_set_in_status(outlink, AVERROR_EOF, c->last_pts);
return 0;
}
c->nb_failed_requests++;
return FFERROR_SOURCE_EMPTY;
}
static const AVBitStreamFilterPad source_outputs[] = {
{
.name = "default",
.config_props = config_props,
},
};
const FFBitStreamFilter ff_source_bsf = {
.p.name = "source",
.p.priv_class = &buffer_class,
.priv_data_size = sizeof(SourceContext),
.activate = activate,
.preinit = preinit,
.init2 = init,
.uninit = uninit,
BSFILTER_OUTPUTS(source_outputs),
};