Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
F
ffmpeg.wasm-core
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Linshizhi
ffmpeg.wasm-core
Commits
e0545262
Commit
e0545262
authored
Jun 03, 2012
by
Nicolas George
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
amerge: accept multiple inputs.
parent
e8e492b3
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
127 additions
and
68 deletions
+127
-68
filters.texi
doc/filters.texi
+12
-10
af_amerge.c
libavfilter/af_amerge.c
+115
-58
No files found.
doc/filters.texi
View file @
e0545262
...
@@ -168,9 +168,16 @@ aformat=sample_fmts\=u8\,s16:channel_layouts\=stereo
...
@@ -168,9 +168,16 @@ aformat=sample_fmts\=u8\,s16:channel_layouts\=stereo
@section amerge
@section amerge
Merge two audio streams into a single multi-channel stream.
Merge two
or more
audio streams into a single multi-channel stream.
This filter does not need any argument.
The filter accepts the following named options:
@table @option
@item inputs
Set the number of inputs. Default is 2.
@end table
If the channel layouts of the inputs are disjoint, and therefore compatible,
If the channel layouts of the inputs are disjoint, and therefore compatible,
the channel layout of the output will be set accordingly and the channels
the channel layout of the output will be set accordingly and the channels
...
@@ -189,7 +196,7 @@ On the other hand, if both input are in stereo, the output channels will be
...
@@ -189,7 +196,7 @@ On the other hand, if both input are in stereo, the output channels will be
in the default order: a1, a2, b1, b2, and the channel layout will be
in the default order: a1, a2, b1, b2, and the channel layout will be
arbitrarily set to 4.0, which may or may not be the expected value.
arbitrarily set to 4.0, which may or may not be the expected value.
Both
inputs must have the same sample rate, and format.
All
inputs must have the same sample rate, and format.
If inputs do not have the same duration, the output will stop with the
If inputs do not have the same duration, the output will stop with the
shortest.
shortest.
...
@@ -199,8 +206,7 @@ Example: merge two mono files into a stereo stream:
...
@@ -199,8 +206,7 @@ Example: merge two mono files into a stereo stream:
amovie=left.wav [l] ; amovie=right.mp3 [r] ; [l] [r] amerge
amovie=left.wav [l] ; amovie=right.mp3 [r] ; [l] [r] amerge
@end example
@end example
If you need to do multiple merges (for instance multiple mono audio streams in
Example: multiple merges:
a single video media), you can do:
@example
@example
ffmpeg -f lavfi -i "
ffmpeg -f lavfi -i "
amovie=input.mkv:si=0 [a0];
amovie=input.mkv:si=0 [a0];
...
@@ -209,11 +215,7 @@ amovie=input.mkv:si=2 [a2];
...
@@ -209,11 +215,7 @@ amovie=input.mkv:si=2 [a2];
amovie=input.mkv:si=3 [a3];
amovie=input.mkv:si=3 [a3];
amovie=input.mkv:si=4 [a4];
amovie=input.mkv:si=4 [a4];
amovie=input.mkv:si=5 [a5];
amovie=input.mkv:si=5 [a5];
[a0][a1] amerge [x0];
[a0][a1][a2][a3][a4][a5] amerge=inputs=6" -c:a pcm_s16le output.mkv
[x0][a2] amerge [x1];
[x1][a3] amerge [x2];
[x2][a4] amerge [x3];
[x3][a5] amerge" -c:a pcm_s16le output.mkv
@end example
@end example
@section amix
@section amix
...
...
libavfilter/af_amerge.c
View file @
e0545262
...
@@ -23,6 +23,8 @@
...
@@ -23,6 +23,8 @@
* Audio merging filter
* Audio merging filter
*/
*/
#include "libavutil/bprint.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h" // only for SWR_CH_MAX
#include "libswresample/swresample.h" // only for SWR_CH_MAX
#include "avfilter.h"
#include "avfilter.h"
#include "audio.h"
#include "audio.h"
...
@@ -30,6 +32,8 @@
...
@@ -30,6 +32,8 @@
#include "internal.h"
#include "internal.h"
typedef
struct
{
typedef
struct
{
const
AVClass
*
class
;
int
nb_inputs
;
int
route
[
SWR_CH_MAX
];
/**< channels routing, see copy_samples */
int
route
[
SWR_CH_MAX
];
/**< channels routing, see copy_samples */
int
bps
;
int
bps
;
struct
amerge_input
{
struct
amerge_input
{
...
@@ -37,27 +41,41 @@ typedef struct {
...
@@ -37,27 +41,41 @@ typedef struct {
int
nb_ch
;
/**< number of channels for the input */
int
nb_ch
;
/**< number of channels for the input */
int
nb_samples
;
int
nb_samples
;
int
pos
;
int
pos
;
}
in
[
2
]
;
}
*
in
;
}
AMergeContext
;
}
AMergeContext
;
#define OFFSET(x) offsetof(AMergeContext, x)
static
const
AVOption
amerge_options
[]
=
{
{
"inputs"
,
"specify the number of inputs"
,
OFFSET
(
nb_inputs
),
AV_OPT_TYPE_INT
,
{
.
dbl
=
2
},
2
,
SWR_CH_MAX
},
};
static
const
AVClass
amerge_class
=
{
.
class_name
=
"AMergeContext"
,
.
item_name
=
av_default_item_name
,
.
option
=
amerge_options
,
};
static
av_cold
void
uninit
(
AVFilterContext
*
ctx
)
static
av_cold
void
uninit
(
AVFilterContext
*
ctx
)
{
{
AMergeContext
*
am
=
ctx
->
priv
;
AMergeContext
*
am
=
ctx
->
priv
;
int
i
;
int
i
;
for
(
i
=
0
;
i
<
2
;
i
++
)
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
ff_bufqueue_discard_all
(
&
am
->
in
[
i
].
queue
);
ff_bufqueue_discard_all
(
&
am
->
in
[
i
].
queue
);
av_freep
(
&
am
->
in
);
}
}
static
int
query_formats
(
AVFilterContext
*
ctx
)
static
int
query_formats
(
AVFilterContext
*
ctx
)
{
{
AMergeContext
*
am
=
ctx
->
priv
;
AMergeContext
*
am
=
ctx
->
priv
;
int64_t
inlayout
[
2
],
outlayout
;
int64_t
inlayout
[
SWR_CH_MAX
],
outlayout
=
0
;
AVFilterFormats
*
formats
;
AVFilterFormats
*
formats
;
AVFilterChannelLayouts
*
layouts
;
AVFilterChannelLayouts
*
layouts
;
int
i
;
int
i
,
overlap
=
0
,
nb_ch
=
0
;
for
(
i
=
0
;
i
<
2
;
i
++
)
{
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
{
if
(
!
ctx
->
inputs
[
i
]
->
in_channel_layouts
||
if
(
!
ctx
->
inputs
[
i
]
->
in_channel_layouts
||
!
ctx
->
inputs
[
i
]
->
in_channel_layouts
->
nb_channel_layouts
)
{
!
ctx
->
inputs
[
i
]
->
in_channel_layouts
->
nb_channel_layouts
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
av_log
(
ctx
,
AV_LOG_ERROR
,
...
@@ -71,33 +89,38 @@ static int query_formats(AVFilterContext *ctx)
...
@@ -71,33 +89,38 @@ static int query_formats(AVFilterContext *ctx)
av_log
(
ctx
,
AV_LOG_INFO
,
"Using
\"
%s
\"
for input %d
\n
"
,
buf
,
i
+
1
);
av_log
(
ctx
,
AV_LOG_INFO
,
"Using
\"
%s
\"
for input %d
\n
"
,
buf
,
i
+
1
);
}
}
am
->
in
[
i
].
nb_ch
=
av_get_channel_layout_nb_channels
(
inlayout
[
i
]);
am
->
in
[
i
].
nb_ch
=
av_get_channel_layout_nb_channels
(
inlayout
[
i
]);
if
(
outlayout
&
inlayout
[
i
])
overlap
++
;
outlayout
|=
inlayout
[
i
];
nb_ch
+=
am
->
in
[
i
].
nb_ch
;
}
}
if
(
am
->
in
[
0
].
nb_ch
+
am
->
in
[
1
].
nb_ch
>
SWR_CH_MAX
)
{
if
(
nb_ch
>
SWR_CH_MAX
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"Too many channels (max %d)
\n
"
,
SWR_CH_MAX
);
av_log
(
ctx
,
AV_LOG_ERROR
,
"Too many channels (max %d)
\n
"
,
SWR_CH_MAX
);
return
AVERROR
(
EINVAL
);
return
AVERROR
(
EINVAL
);
}
}
if
(
inlayout
[
0
]
&
inlayout
[
1
]
)
{
if
(
overlap
)
{
av_log
(
ctx
,
AV_LOG_WARNING
,
av_log
(
ctx
,
AV_LOG_WARNING
,
"Inputs overlap: output layout will be meaningless
\n
"
);
"Inputs overlap: output layout will be meaningless
\n
"
);
for
(
i
=
0
;
i
<
am
->
in
[
0
].
nb_ch
+
am
->
in
[
1
].
nb_ch
;
i
++
)
for
(
i
=
0
;
i
<
nb_ch
;
i
++
)
am
->
route
[
i
]
=
i
;
am
->
route
[
i
]
=
i
;
outlayout
=
av_get_default_channel_layout
(
am
->
in
[
0
].
nb_ch
+
outlayout
=
av_get_default_channel_layout
(
nb_ch
);
am
->
in
[
1
].
nb_ch
);
if
(
!
outlayout
)
if
(
!
outlayout
)
outlayout
=
((
int64_t
)
1
<<
(
am
->
in
[
0
].
nb_ch
+
am
->
in
[
1
].
nb_ch
)
)
-
1
;
outlayout
=
((
int64_t
)
1
<<
nb_ch
)
-
1
;
}
else
{
}
else
{
int
*
route
[
2
]
=
{
am
->
route
,
am
->
route
+
am
->
in
[
0
].
nb_ch
}
;
int
*
route
[
SWR_CH_MAX
]
;
int
c
,
out_ch_number
=
0
;
int
c
,
out_ch_number
=
0
;
outlayout
=
inlayout
[
0
]
|
inlayout
[
1
];
route
[
0
]
=
am
->
route
;
for
(
i
=
1
;
i
<
am
->
nb_inputs
;
i
++
)
route
[
i
]
=
route
[
i
-
1
]
+
am
->
in
[
i
-
1
].
nb_ch
;
for
(
c
=
0
;
c
<
64
;
c
++
)
for
(
c
=
0
;
c
<
64
;
c
++
)
for
(
i
=
0
;
i
<
2
;
i
++
)
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
if
((
inlayout
[
i
]
>>
c
)
&
1
)
if
((
inlayout
[
i
]
>>
c
)
&
1
)
*
(
route
[
i
]
++
)
=
out_ch_number
++
;
*
(
route
[
i
]
++
)
=
out_ch_number
++
;
}
}
formats
=
avfilter_make_format_list
(
ff_packed_sample_fmts
);
formats
=
avfilter_make_format_list
(
ff_packed_sample_fmts
);
avfilter_set_common_sample_formats
(
ctx
,
formats
);
avfilter_set_common_sample_formats
(
ctx
,
formats
);
for
(
i
=
0
;
i
<
2
;
i
++
)
{
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
{
layouts
=
NULL
;
layouts
=
NULL
;
ff_add_channel_layout
(
&
layouts
,
inlayout
[
i
]);
ff_add_channel_layout
(
&
layouts
,
inlayout
[
i
]);
ff_channel_layouts_ref
(
layouts
,
&
ctx
->
inputs
[
i
]
->
out_channel_layouts
);
ff_channel_layouts_ref
(
layouts
,
&
ctx
->
inputs
[
i
]
->
out_channel_layouts
);
...
@@ -113,26 +136,31 @@ static int config_output(AVFilterLink *outlink)
...
@@ -113,26 +136,31 @@ static int config_output(AVFilterLink *outlink)
{
{
AVFilterContext
*
ctx
=
outlink
->
src
;
AVFilterContext
*
ctx
=
outlink
->
src
;
AMergeContext
*
am
=
ctx
->
priv
;
AMergeContext
*
am
=
ctx
->
priv
;
int64_t
layout
;
AVBPrint
bp
;
char
name
[
3
][
256
];
int
i
;
int
i
;
if
(
ctx
->
inputs
[
0
]
->
sample_rate
!=
ctx
->
inputs
[
1
]
->
sample_rate
)
{
for
(
i
=
1
;
i
<
am
->
nb_inputs
;
i
++
)
{
if
(
ctx
->
inputs
[
i
]
->
sample_rate
!=
ctx
->
inputs
[
0
]
->
sample_rate
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
av_log
(
ctx
,
AV_LOG_ERROR
,
"Inputs must have the same sample rate "
"Inputs must have the same sample rate "
"(%"
PRIi64
"
vs %"
PRIi64
")
\n
"
,
"(%"
PRIi64
" for in%d
vs %"
PRIi64
")
\n
"
,
ctx
->
inputs
[
0
]
->
sample_rate
,
ctx
->
inputs
[
1
]
->
sample_rate
);
ctx
->
inputs
[
i
]
->
sample_rate
,
i
,
ctx
->
inputs
[
0
]
->
sample_rate
);
return
AVERROR
(
EINVAL
);
return
AVERROR
(
EINVAL
);
}
}
}
am
->
bps
=
av_get_bytes_per_sample
(
ctx
->
outputs
[
0
]
->
format
);
am
->
bps
=
av_get_bytes_per_sample
(
ctx
->
outputs
[
0
]
->
format
);
outlink
->
sample_rate
=
ctx
->
inputs
[
0
]
->
sample_rate
;
outlink
->
sample_rate
=
ctx
->
inputs
[
0
]
->
sample_rate
;
outlink
->
time_base
=
ctx
->
inputs
[
0
]
->
time_base
;
outlink
->
time_base
=
ctx
->
inputs
[
0
]
->
time_base
;
for
(
i
=
0
;
i
<
3
;
i
++
)
{
layout
=
(
i
<
2
?
ctx
->
inputs
[
i
]
:
ctx
->
outputs
[
0
])
->
channel_layout
;
av_bprint_init
(
&
bp
,
0
,
1
);
av_get_channel_layout_string
(
name
[
i
],
sizeof
(
name
[
i
]),
-
1
,
layout
);
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
{
av_bprintf
(
&
bp
,
"%sin%d:"
,
i
?
" + "
:
""
,
i
);
av_bprint_channel_layout
(
&
bp
,
-
1
,
ctx
->
inputs
[
i
]
->
channel_layout
);
}
}
av_log
(
ctx
,
AV_LOG_INFO
,
av_bprintf
(
&
bp
,
" -> out:"
);
"in1:%s + in2:%s -> out:%s
\n
"
,
name
[
0
],
name
[
1
],
name
[
2
]);
av_bprint_channel_layout
(
&
bp
,
-
1
,
ctx
->
outputs
[
0
]
->
channel_layout
);
av_log
(
ctx
,
AV_LOG_INFO
,
"%s
\n
"
,
bp
.
str
);
return
0
;
return
0
;
}
}
...
@@ -142,7 +170,7 @@ static int request_frame(AVFilterLink *outlink)
...
@@ -142,7 +170,7 @@ static int request_frame(AVFilterLink *outlink)
AMergeContext
*
am
=
ctx
->
priv
;
AMergeContext
*
am
=
ctx
->
priv
;
int
i
,
ret
;
int
i
,
ret
;
for
(
i
=
0
;
i
<
2
;
i
++
)
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
if
(
!
am
->
in
[
i
].
nb_samples
)
if
(
!
am
->
in
[
i
].
nb_samples
)
if
((
ret
=
avfilter_request_frame
(
ctx
->
inputs
[
i
]))
<
0
)
if
((
ret
=
avfilter_request_frame
(
ctx
->
inputs
[
i
]))
<
0
)
return
ret
;
return
ret
;
...
@@ -150,7 +178,8 @@ static int request_frame(AVFilterLink *outlink)
...
@@ -150,7 +178,8 @@ static int request_frame(AVFilterLink *outlink)
}
}
/**
/**
* Copy samples from two input streams to one output stream.
* Copy samples from several input streams to one output stream.
* @param nb_inputs number of inputs
* @param in inputs; used only for the nb_ch field;
* @param in inputs; used only for the nb_ch field;
* @param route routing values;
* @param route routing values;
* input channel i goes to output channel route[i];
* input channel i goes to output channel route[i];
...
@@ -164,21 +193,24 @@ static int request_frame(AVFilterLink *outlink)
...
@@ -164,21 +193,24 @@ static int request_frame(AVFilterLink *outlink)
* @param ns number of samples to copy
* @param ns number of samples to copy
* @param bps bytes per sample
* @param bps bytes per sample
*/
*/
static
inline
void
copy_samples
(
struct
amerge_input
in
[
2
],
int
*
route
,
uint8_t
*
ins
[
2
],
static
inline
void
copy_samples
(
int
nb_inputs
,
struct
amerge_input
in
[],
int
*
route
,
uint8_t
*
ins
[],
uint8_t
**
outs
,
int
ns
,
int
bps
)
uint8_t
**
outs
,
int
ns
,
int
bps
)
{
{
int
*
route_cur
;
int
*
route_cur
;
int
i
,
c
;
int
i
,
c
,
nb_ch
=
0
;
for
(
i
=
0
;
i
<
nb_inputs
;
i
++
)
nb_ch
+=
in
[
i
].
nb_ch
;
while
(
ns
--
)
{
while
(
ns
--
)
{
route_cur
=
route
;
route_cur
=
route
;
for
(
i
=
0
;
i
<
2
;
i
++
)
{
for
(
i
=
0
;
i
<
nb_inputs
;
i
++
)
{
for
(
c
=
0
;
c
<
in
[
i
].
nb_ch
;
c
++
)
{
for
(
c
=
0
;
c
<
in
[
i
].
nb_ch
;
c
++
)
{
memcpy
((
*
outs
)
+
bps
*
*
(
route_cur
++
),
ins
[
i
],
bps
);
memcpy
((
*
outs
)
+
bps
*
*
(
route_cur
++
),
ins
[
i
],
bps
);
ins
[
i
]
+=
bps
;
ins
[
i
]
+=
bps
;
}
}
}
}
*
outs
+=
(
in
[
0
].
nb_ch
+
in
[
1
].
nb_ch
)
*
bps
;
*
outs
+=
nb_ch
*
bps
;
}
}
}
}
...
@@ -187,21 +219,26 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
...
@@ -187,21 +219,26 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
AVFilterContext
*
ctx
=
inlink
->
dst
;
AVFilterContext
*
ctx
=
inlink
->
dst
;
AMergeContext
*
am
=
ctx
->
priv
;
AMergeContext
*
am
=
ctx
->
priv
;
AVFilterLink
*
const
outlink
=
ctx
->
outputs
[
0
];
AVFilterLink
*
const
outlink
=
ctx
->
outputs
[
0
];
int
input_number
=
inlink
==
ctx
->
inputs
[
1
]
;
int
input_number
;
int
nb_samples
,
ns
,
i
;
int
nb_samples
,
ns
,
i
;
AVFilterBufferRef
*
outbuf
,
*
inbuf
[
2
];
AVFilterBufferRef
*
outbuf
,
*
inbuf
[
SWR_CH_MAX
];
uint8_t
*
ins
[
2
],
*
outs
;
uint8_t
*
ins
[
SWR_CH_MAX
],
*
outs
;
for
(
input_number
=
0
;
input_number
<
am
->
nb_inputs
;
input_number
++
)
if
(
inlink
==
ctx
->
inputs
[
input_number
])
break
;
av_assert1
(
input_number
<
am
->
nb_inputs
);
ff_bufqueue_add
(
ctx
,
&
am
->
in
[
input_number
].
queue
,
insamples
);
ff_bufqueue_add
(
ctx
,
&
am
->
in
[
input_number
].
queue
,
insamples
);
am
->
in
[
input_number
].
nb_samples
+=
insamples
->
audio
->
nb_samples
;
am
->
in
[
input_number
].
nb_samples
+=
insamples
->
audio
->
nb_samples
;
if
(
!
am
->
in
[
!
input_number
].
nb_samples
)
nb_samples
=
am
->
in
[
0
].
nb_samples
;
for
(
i
=
1
;
i
<
am
->
nb_inputs
;
i
++
)
nb_samples
=
FFMIN
(
nb_samples
,
am
->
in
[
i
].
nb_samples
);
if
(
!
nb_samples
)
return
;
return
;
nb_samples
=
FFMIN
(
am
->
in
[
0
].
nb_samples
,
am
->
in
[
1
].
nb_samples
);
outbuf
=
ff_get_audio_buffer
(
ctx
->
outputs
[
0
],
AV_PERM_WRITE
,
nb_samples
);
outbuf
=
ff_get_audio_buffer
(
ctx
->
outputs
[
0
],
AV_PERM_WRITE
,
nb_samples
);
outs
=
outbuf
->
data
[
0
];
outs
=
outbuf
->
data
[
0
];
for
(
i
=
0
;
i
<
2
;
i
++
)
{
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
{
inbuf
[
i
]
=
ff_bufqueue_peek
(
&
am
->
in
[
i
].
queue
,
0
);
inbuf
[
i
]
=
ff_bufqueue_peek
(
&
am
->
in
[
i
].
queue
,
0
);
ins
[
i
]
=
inbuf
[
i
]
->
data
[
0
]
+
ins
[
i
]
=
inbuf
[
i
]
->
data
[
0
]
+
am
->
in
[
i
].
pos
*
am
->
in
[
i
].
nb_ch
*
am
->
bps
;
am
->
in
[
i
].
pos
*
am
->
in
[
i
].
nb_ch
*
am
->
bps
;
...
@@ -218,27 +255,27 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
...
@@ -218,27 +255,27 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
while
(
nb_samples
)
{
while
(
nb_samples
)
{
ns
=
nb_samples
;
ns
=
nb_samples
;
for
(
i
=
0
;
i
<
2
;
i
++
)
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
ns
=
FFMIN
(
ns
,
inbuf
[
i
]
->
audio
->
nb_samples
-
am
->
in
[
i
].
pos
);
ns
=
FFMIN
(
ns
,
inbuf
[
i
]
->
audio
->
nb_samples
-
am
->
in
[
i
].
pos
);
/* Unroll the most common sample formats: speed +~350% for the loop,
/* Unroll the most common sample formats: speed +~350% for the loop,
+~13% overall (including two common decoders) */
+~13% overall (including two common decoders) */
switch
(
am
->
bps
)
{
switch
(
am
->
bps
)
{
case
1
:
case
1
:
copy_samples
(
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
1
);
copy_samples
(
am
->
nb_inputs
,
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
1
);
break
;
break
;
case
2
:
case
2
:
copy_samples
(
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
2
);
copy_samples
(
am
->
nb_inputs
,
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
2
);
break
;
break
;
case
4
:
case
4
:
copy_samples
(
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
4
);
copy_samples
(
am
->
nb_inputs
,
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
4
);
break
;
break
;
default:
default:
copy_samples
(
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
am
->
bps
);
copy_samples
(
am
->
nb_inputs
,
am
->
in
,
am
->
route
,
ins
,
&
outs
,
ns
,
am
->
bps
);
break
;
break
;
}
}
nb_samples
-=
ns
;
nb_samples
-=
ns
;
for
(
i
=
0
;
i
<
2
;
i
++
)
{
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
{
am
->
in
[
i
].
nb_samples
-=
ns
;
am
->
in
[
i
].
nb_samples
-=
ns
;
am
->
in
[
i
].
pos
+=
ns
;
am
->
in
[
i
].
pos
+=
ns
;
if
(
am
->
in
[
i
].
pos
==
inbuf
[
i
]
->
audio
->
nb_samples
)
{
if
(
am
->
in
[
i
].
pos
==
inbuf
[
i
]
->
audio
->
nb_samples
)
{
...
@@ -253,25 +290,45 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
...
@@ -253,25 +290,45 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
ff_filter_samples
(
ctx
->
outputs
[
0
],
outbuf
);
ff_filter_samples
(
ctx
->
outputs
[
0
],
outbuf
);
}
}
static
av_cold
int
init
(
AVFilterContext
*
ctx
,
const
char
*
args
,
void
*
opaque
)
{
AMergeContext
*
am
=
ctx
->
priv
;
int
ret
,
i
;
char
name
[
16
];
am
->
class
=
&
amerge_class
;
av_opt_set_defaults
(
am
);
ret
=
av_set_options_string
(
am
,
args
,
"="
,
":"
);
if
(
ret
<
0
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"Error parsing options: '%s'
\n
"
,
args
);
return
ret
;
}
am
->
in
=
av_calloc
(
am
->
nb_inputs
,
sizeof
(
*
am
->
in
));
if
(
!
am
->
in
)
return
AVERROR
(
ENOMEM
);
for
(
i
=
0
;
i
<
am
->
nb_inputs
;
i
++
)
{
AVFilterPad
pad
=
{
.
name
=
name
,
.
type
=
AVMEDIA_TYPE_AUDIO
,
.
filter_samples
=
filter_samples
,
.
min_perms
=
AV_PERM_READ
|
AV_PERM_PRESERVE
,
};
snprintf
(
name
,
sizeof
(
name
),
"in%d"
,
i
);
avfilter_insert_inpad
(
ctx
,
i
,
&
pad
);
}
return
0
;
}
AVFilter
avfilter_af_amerge
=
{
AVFilter
avfilter_af_amerge
=
{
.
name
=
"amerge"
,
.
name
=
"amerge"
,
.
description
=
NULL_IF_CONFIG_SMALL
(
"Merge two audio streams into "
.
description
=
NULL_IF_CONFIG_SMALL
(
"Merge two audio streams into "
"a single multi-channel stream."
),
"a single multi-channel stream."
),
.
priv_size
=
sizeof
(
AMergeContext
),
.
priv_size
=
sizeof
(
AMergeContext
),
.
init
=
init
,
.
uninit
=
uninit
,
.
uninit
=
uninit
,
.
query_formats
=
query_formats
,
.
query_formats
=
query_formats
,
.
inputs
=
(
const
AVFilterPad
[])
{
.
inputs
=
(
const
AVFilterPad
[])
{
{
.
name
=
NULL
}
},
{
.
name
=
"in1"
,
.
type
=
AVMEDIA_TYPE_AUDIO
,
.
filter_samples
=
filter_samples
,
.
min_perms
=
AV_PERM_READ
|
AV_PERM_PRESERVE
,
},
{
.
name
=
"in2"
,
.
type
=
AVMEDIA_TYPE_AUDIO
,
.
filter_samples
=
filter_samples
,
.
min_perms
=
AV_PERM_READ
|
AV_PERM_PRESERVE
,
},
{
.
name
=
NULL
}
},
.
outputs
=
(
const
AVFilterPad
[])
{
.
outputs
=
(
const
AVFilterPad
[])
{
{
.
name
=
"default"
,
{
.
name
=
"default"
,
.
type
=
AVMEDIA_TYPE_AUDIO
,
.
type
=
AVMEDIA_TYPE_AUDIO
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment