mirror of https://github.com/HandBrake/HandBrake
285 lines
7.4 KiB
C
285 lines
7.4 KiB
C
/* avfilter.c
|
|
|
|
Copyright (c) 2003-2025 HandBrake Team
|
|
This file is part of the HandBrake source code
|
|
Homepage: <http://handbrake.fr/>.
|
|
It may be used under the terms of the GNU General Public License v2.
|
|
For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
|
|
*/
|
|
|
|
#include "handbrake/common.h"
|
|
#include "handbrake/hbavfilter.h"
|
|
#include "handbrake/avfilter_priv.h"
|
|
|
|
static int avfilter_init(hb_filter_object_t * filter, hb_filter_init_t * init);
|
|
static int avfilter_post_init( hb_filter_object_t * filter, hb_job_t * job );
|
|
static void avfilter_close( hb_filter_object_t * filter );
|
|
static int avfilter_work( hb_filter_object_t * filter,
|
|
hb_buffer_t ** buf_in, hb_buffer_t ** buf_out );
|
|
static hb_filter_info_t * avfilter_info( hb_filter_object_t * filter );
|
|
|
|
hb_filter_object_t hb_filter_avfilter =
|
|
{
|
|
.id = HB_FILTER_AVFILTER,
|
|
.enforce_order = 0,
|
|
.name = "AVFilter",
|
|
.settings = NULL,
|
|
.init = avfilter_init,
|
|
.post_init = avfilter_post_init,
|
|
.work = avfilter_work,
|
|
.close = avfilter_close,
|
|
.info = avfilter_info,
|
|
};
|
|
|
|
int hb_avfilter_null_work( hb_filter_object_t * filter,
|
|
hb_buffer_t ** buf_in, hb_buffer_t ** buf_out )
|
|
{
|
|
hb_log("hb_avfilter_null_work: It is an error to call this function.");
|
|
return HB_WORK_DONE;
|
|
}
|
|
|
|
static int avfilter_init( hb_filter_object_t * filter, hb_filter_init_t * init )
|
|
{
|
|
hb_filter_private_t * pv = NULL;
|
|
|
|
pv = calloc(1, sizeof(struct hb_filter_private_s));
|
|
filter->private_data = pv;
|
|
if (pv == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
pv->input = *init;
|
|
pv->initialized = 1;
|
|
|
|
pv->graph = hb_avfilter_graph_init(filter->settings, init);
|
|
if (pv->graph == NULL)
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
// Retrieve the parameters of the output filter
|
|
hb_avfilter_graph_update_init(pv->graph, init);
|
|
pv->output = *init;
|
|
|
|
hb_buffer_list_clear(&pv->list);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
hb_avfilter_graph_close(&pv->graph);
|
|
free(pv);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// avfilter_post_init is used to finalize filters that are aliases
|
|
// to avfilter (pad, rotate, cropscale, deinterlace, and colorspace).
|
|
// These filters have their own init function that gets called during
|
|
// the initial filter init pass. Then in post_init, the AVFilterGraph
|
|
// that does the real work is initialized.
|
|
static int avfilter_post_init( hb_filter_object_t * filter, hb_job_t * job )
|
|
{
|
|
hb_filter_private_t * pv = filter->private_data;
|
|
|
|
if (pv == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
if (pv->initialized)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
pv->graph = hb_avfilter_graph_init(filter->settings, &pv->input);
|
|
if (pv->graph == NULL)
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
// Retrieve the parameters of the output filter
|
|
pv->output = pv->input;
|
|
hb_avfilter_graph_update_init(pv->graph, &pv->output);
|
|
|
|
hb_buffer_list_clear(&pv->list);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
hb_avfilter_graph_close(&pv->graph);
|
|
free(pv);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static hb_filter_info_t * avfilter_info(hb_filter_object_t * filter)
|
|
{
|
|
hb_filter_private_t * pv = filter->private_data;
|
|
hb_filter_info_t * info;
|
|
|
|
if (global_verbosity_level < 2)
|
|
{
|
|
// Only show this for log levels 2 and above
|
|
return NULL;
|
|
}
|
|
if (pv == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
info = calloc(1, sizeof(hb_filter_info_t));
|
|
if (info == NULL)
|
|
{
|
|
hb_error("avfilter_info: allocation failure");
|
|
return NULL;
|
|
}
|
|
info->output = pv->output;
|
|
info->human_readable_desc = malloc(1024);
|
|
if (info->human_readable_desc == NULL)
|
|
{
|
|
free(info);
|
|
hb_error("avfilter_info: allocation failure");
|
|
return NULL;
|
|
}
|
|
info->human_readable_desc[0] = 0;
|
|
|
|
char * dst = info->human_readable_desc;
|
|
const char * start = hb_avfilter_graph_settings(pv->graph);
|
|
while (start != NULL && *start != 0)
|
|
{
|
|
// Find end of a filter
|
|
char * comma = strchr(start, ',');
|
|
char * quote = strchr(start, '\'');
|
|
if (comma != NULL && quote != NULL && quote < comma)
|
|
{
|
|
// Find end of quote
|
|
//quote = strchr(quote+1, '\'');
|
|
comma = strchr(start, ',');
|
|
}
|
|
// pretty print line
|
|
int name = 1;
|
|
while (*start != 0 && (comma == NULL || start < comma))
|
|
{
|
|
switch (*start)
|
|
{
|
|
case '=':
|
|
if (name)
|
|
{
|
|
*dst++ = ':';
|
|
*dst++ = ' ';
|
|
name = 0;
|
|
}
|
|
else
|
|
{
|
|
*dst++ = '=';
|
|
}
|
|
break;
|
|
|
|
case ':':
|
|
*dst++ = ',';
|
|
*dst++ = ' ';
|
|
break;
|
|
|
|
case '\'':
|
|
case ' ':
|
|
break;
|
|
|
|
default:
|
|
*dst++ = *start;
|
|
|
|
}
|
|
start++;
|
|
}
|
|
if (*start != 0)
|
|
{
|
|
*dst++ = '\n';
|
|
start++;
|
|
}
|
|
}
|
|
*dst = 0;
|
|
return info;
|
|
}
|
|
|
|
static void avfilter_close( hb_filter_object_t * filter )
|
|
{
|
|
hb_filter_private_t * pv = filter->private_data;
|
|
if (pv == NULL)
|
|
{
|
|
// Already closed
|
|
return;
|
|
}
|
|
|
|
hb_buffer_list_close(&pv->list);
|
|
hb_avfilter_graph_close(&pv->graph);
|
|
free(pv);
|
|
filter->private_data = NULL;
|
|
}
|
|
|
|
void hb_avfilter_alias_close( hb_filter_object_t * filter )
|
|
{
|
|
hb_filter_private_t * pv = filter->private_data;
|
|
if (pv == NULL)
|
|
{
|
|
// Already closed
|
|
return;
|
|
}
|
|
|
|
hb_buffer_list_close(&pv->list);
|
|
hb_value_free(&pv->avfilters);
|
|
free(pv);
|
|
filter->private_data = NULL;
|
|
}
|
|
|
|
static hb_buffer_t* filterFrame( hb_filter_private_t * pv, hb_buffer_t ** buf_in )
|
|
{
|
|
hb_buffer_list_t list;
|
|
hb_buffer_t * buf = NULL, * next = NULL;
|
|
|
|
hb_avfilter_add_buf(pv->graph, buf_in);
|
|
buf = hb_avfilter_get_buf(pv->graph);
|
|
|
|
while (buf != NULL)
|
|
{
|
|
hb_buffer_list_append(&pv->list, buf);
|
|
buf = hb_avfilter_get_buf(pv->graph);
|
|
}
|
|
// Delay one frame so we can set the stop time of the output buffer
|
|
hb_buffer_list_clear(&list);
|
|
while (hb_buffer_list_count(&pv->list) > 1)
|
|
{
|
|
buf = hb_buffer_list_rem_head(&pv->list);
|
|
next = hb_buffer_list_head(&pv->list);
|
|
|
|
buf->s.stop = next->s.start;
|
|
buf->s.duration = buf->s.stop - buf->s.start;
|
|
hb_buffer_list_append(&list, buf);
|
|
}
|
|
|
|
return hb_buffer_list_head(&list);
|
|
}
|
|
|
|
static int avfilter_work( hb_filter_object_t * filter,
|
|
hb_buffer_t ** buf_in, hb_buffer_t ** buf_out )
|
|
{
|
|
hb_filter_private_t * pv = filter->private_data;
|
|
hb_buffer_t * in = *buf_in;
|
|
|
|
if (in->s.flags & HB_BUF_FLAG_EOF)
|
|
{
|
|
hb_buffer_t * out = filterFrame(pv, NULL);
|
|
hb_buffer_t * last = hb_buffer_list_tail(&pv->list);
|
|
if (last != NULL && last->s.start != AV_NOPTS_VALUE)
|
|
{
|
|
last->s.stop = last->s.start + last->s.duration;
|
|
}
|
|
hb_buffer_list_prepend(&pv->list, out);
|
|
hb_buffer_list_append(&pv->list, in);
|
|
*buf_out = hb_buffer_list_clear(&pv->list);
|
|
*buf_in = NULL;
|
|
return HB_FILTER_DONE;
|
|
}
|
|
|
|
*buf_out = filterFrame(pv, buf_in);
|
|
|
|
return HB_FILTER_OK;
|
|
}
|