Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1539,8 +1539,8 @@ Parses a filter description string and applies it to the filter graph.
Parameters:

- `filterDescription` (`string`): The filter description (e.g., `'negate'`, `'scale=640:480'`)
- `inputs` (`FilterInOut`): Input filter endpoints
- `outputs` (`FilterInOut`): Output filter endpoints
- `inputs` (`FilterInOut` | `undefined`): Input filter endpoints (optional)
- `outputs` (`FilterInOut` | `undefined`): Output filter endpoints (optional)

**Returns**: `void`

Expand Down
15 changes: 12 additions & 3 deletions binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3721,11 +3721,20 @@ bare_ffmpeg_filter_graph_parse(
js_env_t *env,
js_receiver_t,
js_arraybuffer_span_of_t<bare_ffmpeg_filter_graph_t, 1> graph,
js_arraybuffer_span_of_t<bare_ffmpeg_filter_inout_t, 1> inputs,
js_arraybuffer_span_of_t<bare_ffmpeg_filter_inout_t, 1> outputs,
std::optional<js_arraybuffer_span_of_t<bare_ffmpeg_filter_inout_t, 1>> inputs,
std::optional<js_arraybuffer_span_of_t<bare_ffmpeg_filter_inout_t, 1>> outputs,
std::string filter_description
) {
int err = avfilter_graph_parse(graph->handle, filter_description.c_str(), inputs->handle, outputs->handle, nullptr);
AVFilterInOut **inputs_ptr = inputs ? &inputs.value()->handle : nullptr;
AVFilterInOut **outputs_ptr = outputs ? &outputs.value()->handle : nullptr;

int err = avfilter_graph_parse_ptr(
graph->handle,
filter_description.c_str(),
inputs_ptr,
outputs_ptr,
nullptr
);
if (err < 0) {
err = js_throw_error(env, nullptr, av_err2str(err));
assert(err == 0);
Expand Down
4 changes: 2 additions & 2 deletions lib/filter-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ module.exports = class FilterGraph {
parse(filterDescription, inputs, outputs) {
binding.parseFilterGraph(
this._handle,
inputs._handle,
outputs._handle,
inputs._handle || undefined,
outputs._handle || undefined,
filterDescription
)
}
Expand Down
102 changes: 90 additions & 12 deletions test/filter-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,79 @@ test('FilterGraph should expose a parse method', (t) => {
const bufferSinkContext = new ffmpeg.FilterContext()
using graph = initGraph(bufferContext, bufferSinkContext)

const inputs = initInputs(bufferSinkContext)
const outputs = initOutputs(bufferContext)
using inputs = initInputs(bufferSinkContext)
using outputs = initOutputs(bufferContext)

t.execution(() => {
graph.parse('negate', inputs, outputs)
})
})

test('FilterGraph.parse should handle multiple inputs', (t) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('FilterGraph.parse should handle multiple inputs', (t) => {
test('FilterGraph should split one input into multiple outputs', (t) => {

According to my understanding. I could be wrong tho 😅

const bufferContext = new ffmpeg.FilterContext()
const sinkContext0 = new ffmpeg.FilterContext()
const sinkContext1 = new ffmpeg.FilterContext()
using graph = new ffmpeg.FilterGraph()
const buffer = new ffmpeg.Filter('buffer')
const sink0 = new ffmpeg.Filter('buffersink')
const sink1 = new ffmpeg.Filter('buffersink')
const args = `video_size=1x1:pix_fmt=${ffmpeg.constants.pixelFormats.RGB24}:time_base=1/30:pixel_aspect=1/1`

graph.createFilter(bufferContext, buffer, 'in', args)
graph.createFilter(sinkContext0, sink0, 'out0')
graph.createFilter(sinkContext1, sink1, 'out1')

using inputs = createFilterInOutChain([
[sinkContext0, 'out0'],
[sinkContext1, 'out1']
])
using outputs = initOutputs(bufferContext)

t.execution(() => {
graph.parse('split=2[out0][out1]', inputs, outputs)
})
Comment on lines +92 to +100

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According what was added in lib/filter-graph.js. It does not look like we have a test that cover that case (having inputs or ouputs with an undefined _handle).


t.execution(() => {
graph.configure()
})
})

test('FilterGraph.parse should handle multiple outputs', (t) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

const bufferContext0 = new ffmpeg.FilterContext()
const bufferContext1 = new ffmpeg.FilterContext()
const sinkContext = new ffmpeg.FilterContext()
using graph = new ffmpeg.FilterGraph()
const buffer0 = new ffmpeg.Filter('buffer')
const buffer1 = new ffmpeg.Filter('buffer')
const sink = new ffmpeg.Filter('buffersink')
const args = `video_size=1x1:pix_fmt=${ffmpeg.constants.pixelFormats.RGB24}:time_base=1/30:pixel_aspect=1/1`

graph.createFilter(bufferContext0, buffer0, 'in0', args)
graph.createFilter(bufferContext1, buffer1, 'in1', args)
graph.createFilter(sinkContext, sink, 'out')

using inputs = initInputs(sinkContext)
using outputs = createFilterInOutChain([
[bufferContext0, 'in0'],
[bufferContext1, 'in1']
])

t.execution(() => {
graph.parse('[in0][in1]hstack=inputs=2[out]', inputs, outputs)
})

t.execution(() => {
graph.configure()
})
})

test('FilterGraph.parse should throw an error if inputs are not valid', (t) => {
const bufferContext = new ffmpeg.FilterContext()
const bufferSinkContext = new ffmpeg.FilterContext()
using graph = initGraph(bufferContext, bufferSinkContext)

const inputs = initInputs(bufferSinkContext)
const outputs = initOutputs(bufferContext)
using inputs = initInputs(bufferSinkContext)
using outputs = initOutputs(bufferContext)

t.exception(() => {
graph.parse('foo', inputs, outputs)
Expand All @@ -93,8 +151,8 @@ test('FilterGraph should expose a configure method', (t) => {
const bufferSinkContext = new ffmpeg.FilterContext()
using graph = initGraph(bufferContext, bufferSinkContext)

const inputs = initInputs(bufferSinkContext)
const outputs = initOutputs(bufferContext)
using inputs = initInputs(bufferSinkContext)
using outputs = initOutputs(bufferContext)

graph.parse('negate', inputs, outputs)

Expand All @@ -108,8 +166,8 @@ test('FilterGraph.configure should throw when parameters are not valid', (t) =>
const bufferSinkContext = new ffmpeg.FilterContext()
using graph = initGraph(bufferContext, bufferSinkContext)

const inputs = initInputs(bufferSinkContext)
const outputs = new ffmpeg.FilterInOut()
using inputs = initInputs(bufferSinkContext)
using outputs = new ffmpeg.FilterInOut()

graph.parse('negate', inputs, outputs)

Expand All @@ -123,8 +181,8 @@ test('FilterGraph should expose a pushFrame method', (t) => {
const bufferContext = new ffmpeg.FilterContext()
const bufferSinkContext = new ffmpeg.FilterContext()
using graph = initGraph(bufferContext, bufferSinkContext)
const inputs = initInputs(bufferSinkContext)
const outputs = initOutputs(bufferContext)
using inputs = initInputs(bufferSinkContext)
using outputs = initOutputs(bufferContext)

graph.parse('negate', inputs, outputs)
graph.configure()
Expand All @@ -137,8 +195,8 @@ test('FilterGraph should expose a pullFrame method', (t) => {
const bufferContext = new ffmpeg.FilterContext()
const bufferSinkContext = new ffmpeg.FilterContext()
using graph = initGraph(bufferContext, bufferSinkContext)
const inputs = initInputs(bufferSinkContext)
const outputs = initOutputs(bufferContext)
using inputs = initInputs(bufferSinkContext)
using outputs = initOutputs(bufferContext)

graph.parse('negate', inputs, outputs)
graph.configure()
Expand Down Expand Up @@ -184,6 +242,26 @@ function initOutputs(ctx) {
return outputs
}

function createFilterInOutChain(entries) {
const [[firstCtx, firstName], ...rest] = entries
const head = new ffmpeg.FilterInOut()
head.name = firstName
head.filterContext = firstCtx
head.padIdx = 0

let current = head
for (const [ctx, name] of rest) {
const next = new ffmpeg.FilterInOut()
next.name = name
next.filterContext = ctx
next.padIdx = 0
current.next = next
current = next
}

return head
}

function createFrame(width = 1, height = 1) {
const frame = new ffmpeg.Frame()
frame.width = width
Expand Down