Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
audiowmark
Project
Project
Details
Activity
Releases
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
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Stefan Westerfeld
audiowmark
Commits
55bab8bb
Commit
55bab8bb
authored
Jul 09, 2020
by
Stefan Westerfeld
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add hls-prepare command to audiowmark main binary.
Signed-off-by:
Stefan Westerfeld
<
stefan@space.twc.de
>
parent
eeab8b5a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
212 additions
and
200 deletions
+212
-200
audiowmark.cc
src/audiowmark.cc
+11
-4
hls.cc
src/hls.cc
+194
-0
hls.hh
src/hls.hh
+5
-2
testhls.cc
src/testhls.cc
+2
-194
No files found.
src/audiowmark.cc
View file @
55bab8bb
...
...
@@ -446,10 +446,6 @@ parse_shared_options (ArgParser& ap)
{
Params
::
mix
=
false
;
}
if
(
ap
.
parse_opt
(
"--quiet"
)
||
ap
.
parse_opt
(
"-q"
))
{
set_log_level
(
Log
::
WARNING
);
}
if
(
Params
::
have_key
>
1
)
{
error
(
"audiowmark: watermark key can at most be set once (--key / --test-key option)
\n
"
);
...
...
@@ -574,6 +570,10 @@ main (int argc, char **argv)
printf
(
"audiowmark %s
\n
"
,
VERSION
);
return
0
;
}
if
(
ap
.
parse_opt
(
"--quiet"
)
||
ap
.
parse_opt
(
"-q"
))
{
set_log_level
(
Log
::
WARNING
);
}
if
(
ap
.
parse_cmd
(
"hls-add"
))
{
parse_shared_options
(
ap
);
...
...
@@ -583,6 +583,13 @@ main (int argc, char **argv)
if
(
ap
.
parse_args
(
3
,
args
))
return
hls_add
(
args
[
0
],
args
[
1
],
args
[
2
]);
}
else
if
(
ap
.
parse_cmd
(
"hls-prepare"
))
{
ap
.
parse_opt
(
"--bit-rate"
,
Params
::
hls_bit_rate
);
if
(
ap
.
parse_args
(
4
,
args
))
return
hls_prepare
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
]);
}
else
if
(
ap
.
parse_cmd
(
"add"
))
{
parse_shared_options
(
ap
);
...
...
src/hls.cc
View file @
55bab8bb
...
...
@@ -16,17 +16,64 @@
*/
#include <string>
#include <regex>
#include "utils.hh"
#include "mpegts.hh"
#include "sfinputstream.hh"
#include "hlsoutputstream.hh"
#include "sfoutputstream.hh"
#include "wmcommon.hh"
#include "wavdata.hh"
using
std
::
string
;
using
std
::
vector
;
using
std
::
regex
;
using
std
::
map
;
using
std
::
min
;
Error
xsystem
(
const
string
&
cmd
)
{
info
(
"+++ %s
\n
"
,
cmd
.
c_str
());
int
rc
=
system
(
cmd
.
c_str
());
int
exit_status
=
WEXITSTATUS
(
rc
);
if
(
exit_status
!=
0
)
{
error
(
"audiowmark: failed to execute command:
\n
%s
\n
"
,
cmd
.
c_str
());
return
Error
(
string_printf
(
"system failed / exit status %d"
,
exit_status
));
}
return
Error
::
Code
::
NONE
;
}
Error
ff_decode
(
const
string
&
filename
,
WavData
&
out_wav_data
)
{
FILE
*
tmp_file
=
tmpfile
();
ScopedFile
tmp_file_s
(
tmp_file
);
string
tmp_file_name
=
string_printf
(
"/dev/fd/%d"
,
fileno
(
tmp_file
));
FILE
*
input_tmp_file
=
tmpfile
();
ScopedFile
input_tmp_file_s
(
input_tmp_file
);
string
input_tmp_file_name
=
string_printf
(
"/dev/fd/%d"
,
fileno
(
input_tmp_file
));
// write current ts
FILE
*
main
=
fopen
(
filename
.
c_str
(),
"r"
);
ScopedFile
main_s
(
main
);
int
c
;
while
((
c
=
fgetc
(
main
))
>=
0
)
fputc
(
c
,
input_tmp_file
);
fflush
(
input_tmp_file
);
string
cmd
=
string_printf
(
"ffmpeg -v error -y -f mpegts -i %s -f wav %s"
,
input_tmp_file_name
.
c_str
(),
tmp_file_name
.
c_str
());
Error
err
=
xsystem
(
cmd
.
c_str
());
if
(
err
)
return
err
;
err
=
out_wav_data
.
load
(
tmp_file_name
);
return
err
;
}
int
hls_add
(
const
string
&
infile
,
const
string
&
outfile
,
const
string
&
bits
)
{
...
...
@@ -111,4 +158,151 @@ hls_add (const string& infile, const string& outfile, const string& bits)
return
0
;
}
int
hls_prepare
(
const
string
&
in_dir
,
const
string
&
out_dir
,
const
string
&
filename
,
const
string
&
audio_master
)
{
string
in_name
=
in_dir
+
"/"
+
filename
;
FILE
*
in_file
=
fopen
(
in_name
.
c_str
(),
"r"
);
ScopedFile
in_file_s
(
in_file
);
if
(
!
in_file
)
{
error
(
"audiowmark: error opening input playlist %s
\n
"
,
in_name
.
c_str
());
return
1
;
}
string
out_name
=
out_dir
+
"/"
+
filename
;
FILE
*
out_file
=
fopen
(
out_name
.
c_str
(),
"w"
);
ScopedFile
out_file_s
(
out_file
);
if
(
!
out_file
)
{
error
(
"audiowmark: error opening output playlist %s
\n
"
,
out_name
.
c_str
());
return
1
;
}
WavData
audio_master_data
;
Error
err
=
audio_master_data
.
load
(
audio_master
);
if
(
err
)
{
error
(
"audiowmark: failed to load audio master: %s
\n
"
,
audio_master
.
c_str
());
return
1
;
}
info
(
"AAC Bitrate: %d
\n
"
,
Params
::
hls_bit_rate
);
struct
Segment
{
string
name
;
size_t
size
;
map
<
string
,
string
>
vars
;
};
vector
<
Segment
>
segments
;
char
buffer
[
1024
];
int
line
=
1
;
const
regex
blank_re
(
R"(\s*(#.*)?)"
);
while
(
fgets
(
buffer
,
1024
,
in_file
))
{
/* kill newline chars at end */
int
last
=
strlen
(
buffer
)
-
1
;
while
(
last
>
0
&&
(
buffer
[
last
]
==
'\n'
||
buffer
[
last
]
==
'\r'
))
buffer
[
last
--
]
=
0
;
string
s
=
buffer
;
std
::
smatch
match
;
if
(
regex_match
(
s
,
blank_re
))
{
/* blank line or comment */
fprintf
(
out_file
,
"%s
\n
"
,
s
.
c_str
());
}
else
{
fprintf
(
out_file
,
"%s
\n
"
,
s
.
c_str
());
Segment
segment
;
segment
.
name
=
s
;
segments
.
push_back
(
segment
);
}
line
++
;
}
size_t
start_pos
=
0
;
for
(
auto
&
segment
:
segments
)
{
WavData
out
;
Error
err
=
ff_decode
(
in_dir
+
"/"
+
segment
.
name
,
out
);
if
(
err
)
{
error
(
"audiowmark: hls: ff_decode failed: %s
\n
"
,
err
.
message
());
return
1
;
}
printf
(
"%d %zd
\n
"
,
out
.
sample_rate
(),
out
.
n_values
()
/
out
.
n_channels
());
segment
.
size
=
out
.
n_values
()
/
out
.
n_channels
();
/* obtain pts for first frame */
string
cmd
=
string_printf
(
"ffprobe -v 0 -show_entries packet=pts_time %s/%s -of compact=p=0:nk=1 | grep '^[0-9]'"
,
in_dir
.
c_str
(),
segment
.
name
.
c_str
());
FILE
*
pts
=
popen
(
cmd
.
c_str
(),
"r"
);
char
buffer
[
1024
];
if
(
fgets
(
buffer
,
1024
,
pts
))
{
if
(
strlen
(
buffer
)
&&
buffer
[
strlen
(
buffer
)
-
1
]
==
'\n'
)
buffer
[
strlen
(
buffer
)
-
1
]
=
0
;
segment
.
vars
[
"pts_start"
]
=
buffer
;
}
fclose
(
pts
);
/* store 3 seconds of the context before this segment and after this segment (if available) */
const
size_t
ctx_3sec
=
3
*
out
.
sample_rate
();
const
size_t
prev_size
=
min
<
size_t
>
(
start_pos
,
ctx_3sec
);
const
size_t
next_size
=
min
<
size_t
>
(
audio_master_data
.
n_frames
()
-
(
segment
.
size
+
start_pos
),
ctx_3sec
);
segment
.
vars
[
"start_pos"
]
=
string_printf
(
"%zd"
,
start_pos
);
segment
.
vars
[
"size"
]
=
string_printf
(
"%zd"
,
segment
.
size
);
segment
.
vars
[
"prev_size"
]
=
string_printf
(
"%zd"
,
prev_size
);
segment
.
vars
[
"next_size"
]
=
string_printf
(
"%zd"
,
next_size
);
/* write audio segment with context */
const
size_t
start_point
=
start_pos
-
prev_size
;
const
size_t
end_point
=
start_point
+
prev_size
+
segment
.
size
+
next_size
;
vector
<
float
>
out_signal
(
audio_master_data
.
samples
().
begin
()
+
start_point
*
audio_master_data
.
n_channels
(),
audio_master_data
.
samples
().
begin
()
+
end_point
*
audio_master_data
.
n_channels
());
vector
<
unsigned
char
>
full_flac_mem
;
SFOutputStream
out_stream
;
err
=
out_stream
.
open
(
&
full_flac_mem
,
audio_master_data
.
n_channels
(),
audio_master_data
.
sample_rate
(),
audio_master_data
.
bit_depth
(),
SFOutputStream
::
OutFormat
::
FLAC
);
if
(
err
)
{
error
(
"audiowmark: hls: open context flac failed: %s
\n
"
,
err
.
message
());
return
1
;
}
err
=
out_stream
.
write_frames
(
out_signal
);
if
(
err
)
{
error
(
"audiowmark: hls: write context flac failed: %s
\n
"
,
err
.
message
());
return
1
;
}
err
=
out_stream
.
close
();
if
(
err
)
{
error
(
"audiowmark: hls: close context flac failed: %s
\n
"
,
err
.
message
());
return
1
;
}
/* store everything we need in a mpegts file */
TSWriter
writer
;
writer
.
append_data
(
"full.flac"
,
full_flac_mem
);
writer
.
append_vars
(
"vars"
,
segment
.
vars
);
writer
.
process
(
in_dir
+
"/"
+
segment
.
name
,
out_dir
+
"/"
+
segment
.
name
);
/* start position for the next segment */
start_pos
+=
segment
.
size
;
}
return
0
;
}
src/hls.hh
View file @
55bab8bb
...
...
@@ -15,11 +15,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef AUDIOWMARK_
MPEGT
S_HH
#define AUDIOWMARK_
MPEGT
S_HH
#ifndef AUDIOWMARK_
HL
S_HH
#define AUDIOWMARK_
HL
S_HH
#include <string>
int
hls_add
(
const
std
::
string
&
infile
,
const
std
::
string
&
outfile
,
const
std
::
string
&
bits
);
int
hls_prepare
(
const
std
::
string
&
in_dir
,
const
std
::
string
&
out_dir
,
const
std
::
string
&
filename
,
const
std
::
string
&
audio_master
);
Error
ff_decode
(
const
std
::
string
&
filename
,
WavData
&
out_wav_data
);
#endif
/* AUDIOWMARK_MPEGTS_HH */
src/testhls.cc
View file @
55bab8bb
...
...
@@ -24,9 +24,9 @@
#include "mpegts.hh"
#include "wavdata.hh"
#include "wmcommon.hh"
#include "hls.hh"
#include "sfinputstream.hh"
#include "hlsoutputstream.hh"
#include "sfoutputstream.hh"
using
std
::
string
;
using
std
::
regex
;
...
...
@@ -34,193 +34,6 @@ using std::vector;
using
std
::
map
;
using
std
::
min
;
Error
xsystem
(
const
string
&
cmd
)
{
info
(
"+++ %s
\n
"
,
cmd
.
c_str
());
int
rc
=
system
(
cmd
.
c_str
());
int
exit_status
=
WEXITSTATUS
(
rc
);
if
(
exit_status
!=
0
)
{
error
(
"audiowmark: failed to execute command:
\n
%s
\n
"
,
cmd
.
c_str
());
return
Error
(
string_printf
(
"system failed / exit status %d"
,
exit_status
));
}
return
Error
::
Code
::
NONE
;
}
Error
ff_decode
(
const
string
&
filename
,
WavData
&
out_wav_data
)
{
FILE
*
tmp_file
=
tmpfile
();
ScopedFile
tmp_file_s
(
tmp_file
);
string
tmp_file_name
=
string_printf
(
"/dev/fd/%d"
,
fileno
(
tmp_file
));
FILE
*
input_tmp_file
=
tmpfile
();
ScopedFile
input_tmp_file_s
(
input_tmp_file
);
string
input_tmp_file_name
=
string_printf
(
"/dev/fd/%d"
,
fileno
(
input_tmp_file
));
// write current ts
FILE
*
main
=
fopen
(
filename
.
c_str
(),
"r"
);
ScopedFile
main_s
(
main
);
int
c
;
while
((
c
=
fgetc
(
main
))
>=
0
)
fputc
(
c
,
input_tmp_file
);
fflush
(
input_tmp_file
);
string
cmd
=
string_printf
(
"ffmpeg -v error -y -f mpegts -i %s -f wav %s"
,
input_tmp_file_name
.
c_str
(),
tmp_file_name
.
c_str
());
Error
err
=
xsystem
(
cmd
.
c_str
());
if
(
err
)
return
err
;
err
=
out_wav_data
.
load
(
tmp_file_name
);
return
err
;
}
int
hls_embed_context
(
const
string
&
in_dir
,
const
string
&
out_dir
,
const
string
&
filename
,
const
string
&
audio_master
)
{
string
in_name
=
in_dir
+
"/"
+
filename
;
FILE
*
in_file
=
fopen
(
in_name
.
c_str
(),
"r"
);
ScopedFile
in_file_s
(
in_file
);
if
(
!
in_file
)
{
error
(
"audiowmark: error opening input playlist %s
\n
"
,
in_name
.
c_str
());
return
1
;
}
string
out_name
=
out_dir
+
"/"
+
filename
;
FILE
*
out_file
=
fopen
(
out_name
.
c_str
(),
"w"
);
ScopedFile
out_file_s
(
out_file
);
if
(
!
out_file
)
{
error
(
"audiowmark: error opening output playlist %s
\n
"
,
out_name
.
c_str
());
return
1
;
}
WavData
audio_master_data
;
Error
err
=
audio_master_data
.
load
(
audio_master
);
if
(
err
)
{
error
(
"audiowmark: failed to load audio master: %s
\n
"
,
audio_master
.
c_str
());
return
1
;
}
struct
Segment
{
string
name
;
size_t
size
;
map
<
string
,
string
>
vars
;
};
vector
<
Segment
>
segments
;
char
buffer
[
1024
];
int
line
=
1
;
const
regex
blank_re
(
R"(\s*(#.*)?)"
);
while
(
fgets
(
buffer
,
1024
,
in_file
))
{
/* kill newline chars at end */
int
last
=
strlen
(
buffer
)
-
1
;
while
(
last
>
0
&&
(
buffer
[
last
]
==
'\n'
||
buffer
[
last
]
==
'\r'
))
buffer
[
last
--
]
=
0
;
string
s
=
buffer
;
std
::
smatch
match
;
if
(
regex_match
(
s
,
blank_re
))
{
/* blank line or comment */
fprintf
(
out_file
,
"%s
\n
"
,
s
.
c_str
());
}
else
{
fprintf
(
out_file
,
"%s
\n
"
,
s
.
c_str
());
Segment
segment
;
segment
.
name
=
s
;
segments
.
push_back
(
segment
);
}
line
++
;
}
size_t
start_pos
=
0
;
for
(
auto
&
segment
:
segments
)
{
WavData
out
;
Error
err
=
ff_decode
(
in_dir
+
"/"
+
segment
.
name
,
out
);
if
(
err
)
{
error
(
"audiowmark: hls: ff_decode failed: %s
\n
"
,
err
.
message
());
return
1
;
}
printf
(
"%d %zd
\n
"
,
out
.
sample_rate
(),
out
.
n_values
()
/
out
.
n_channels
());
segment
.
size
=
out
.
n_values
()
/
out
.
n_channels
();
/* obtain pts for first frame */
string
cmd
=
string_printf
(
"ffprobe -v 0 -show_entries packet=pts_time %s/%s -of compact=p=0:nk=1 | grep '^[0-9]'"
,
in_dir
.
c_str
(),
segment
.
name
.
c_str
());
FILE
*
pts
=
popen
(
cmd
.
c_str
(),
"r"
);
char
buffer
[
1024
];
if
(
fgets
(
buffer
,
1024
,
pts
))
{
if
(
strlen
(
buffer
)
&&
buffer
[
strlen
(
buffer
)
-
1
]
==
'\n'
)
buffer
[
strlen
(
buffer
)
-
1
]
=
0
;
segment
.
vars
[
"pts_start"
]
=
buffer
;
}
fclose
(
pts
);
/* store 3 seconds of the context before this segment and after this segment (if available) */
const
size_t
ctx_3sec
=
3
*
out
.
sample_rate
();
const
size_t
prev_size
=
min
<
size_t
>
(
start_pos
,
ctx_3sec
);
const
size_t
next_size
=
min
<
size_t
>
(
audio_master_data
.
n_frames
()
-
(
segment
.
size
+
start_pos
),
ctx_3sec
);
segment
.
vars
[
"start_pos"
]
=
string_printf
(
"%zd"
,
start_pos
);
segment
.
vars
[
"size"
]
=
string_printf
(
"%zd"
,
segment
.
size
);
segment
.
vars
[
"prev_size"
]
=
string_printf
(
"%zd"
,
prev_size
);
segment
.
vars
[
"next_size"
]
=
string_printf
(
"%zd"
,
next_size
);
/* write audio segment with context */
const
size_t
start_point
=
start_pos
-
prev_size
;
const
size_t
end_point
=
start_point
+
prev_size
+
segment
.
size
+
next_size
;
vector
<
float
>
out_signal
(
audio_master_data
.
samples
().
begin
()
+
start_point
*
audio_master_data
.
n_channels
(),
audio_master_data
.
samples
().
begin
()
+
end_point
*
audio_master_data
.
n_channels
());
vector
<
unsigned
char
>
full_flac_mem
;
SFOutputStream
out_stream
;
err
=
out_stream
.
open
(
&
full_flac_mem
,
audio_master_data
.
n_channels
(),
audio_master_data
.
sample_rate
(),
audio_master_data
.
bit_depth
(),
SFOutputStream
::
OutFormat
::
FLAC
);
if
(
err
)
{
error
(
"audiowmark: hls: open context flac failed: %s
\n
"
,
err
.
message
());
return
1
;
}
err
=
out_stream
.
write_frames
(
out_signal
);
if
(
err
)
{
error
(
"audiowmark: hls: write context flac failed: %s
\n
"
,
err
.
message
());
return
1
;
}
err
=
out_stream
.
close
();
if
(
err
)
{
error
(
"audiowmark: hls: close context flac failed: %s
\n
"
,
err
.
message
());
return
1
;
}
/* store everything we need in a mpegts file */
TSWriter
writer
;
writer
.
append_data
(
"full.flac"
,
full_flac_mem
);
writer
.
append_vars
(
"vars"
,
segment
.
vars
);
writer
.
process
(
in_dir
+
"/"
+
segment
.
name
,
out_dir
+
"/"
+
segment
.
name
);
/* start position for the next segment */
start_pos
+=
segment
.
size
;
}
return
0
;
}
class
WDInputStream
:
public
AudioInputStream
{
WavData
*
wav_data
;
...
...
@@ -378,12 +191,7 @@ seek_perf (int sample_rate, double seconds)
int
main
(
int
argc
,
char
**
argv
)
{
if
(
argc
==
6
&&
strcmp
(
argv
[
1
],
"hls-embed-context"
)
==
0
)
{
info
(
"hls-embed-context: in_dir=%s out_dir=%s m3u8=%s audio_master=%s
\n
"
,
argv
[
2
],
argv
[
3
],
argv
[
4
],
argv
[
5
]);
return
hls_embed_context
(
argv
[
2
],
argv
[
3
],
argv
[
4
],
argv
[
5
]);
}
else
if
(
argc
==
6
&&
strcmp
(
argv
[
1
],
"test-seek"
)
==
0
)
if
(
argc
==
6
&&
strcmp
(
argv
[
1
],
"test-seek"
)
==
0
)
{
return
test_seek
(
argv
[
2
],
argv
[
3
],
atoi
(
argv
[
4
]),
argv
[
5
]);
}
...
...
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