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
ef363ebd
Commit
ef363ebd
authored
Apr 12, 2014
by
Anton Khirnov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mp3enc: write full LAME frame
Most importantly, it contains the encoder delay and replaygain info.
parent
88b32673
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
129 additions
and
34 deletions
+129
-34
muxers.texi
doc/muxers.texi
+2
-1
mp3enc.c
libavformat/mp3enc.c
+127
-33
No files found.
doc/muxers.texi
View file @
ef363ebd
...
...
@@ -370,7 +370,8 @@ to provide the pictures as soon as possible to avoid excessive buffering.
A Xing/LAME frame right after the ID3v2 header (if present). It is enabled by
default, but will be written only if the output is seekable. The
@code{write_xing} private option can be used to disable it. The frame contains
various information that may be useful to the decoder, like the audio duration.
various information that may be useful to the decoder, like the audio duration
or encoder delay.
@item
A legacy ID3v1 tag at the end of the file (disabled by default). It may be
...
...
libavformat/mp3enc.c
View file @
ef363ebd
...
...
@@ -32,6 +32,9 @@
#include "libavutil/opt.h"
#include "libavutil/dict.h"
#include "libavutil/avassert.h"
#include "libavutil/crc.h"
#include "libavutil/mathematics.h"
#include "libavutil/replaygain.h"
static
int
id3v1_set_string
(
AVFormatContext
*
s
,
const
char
*
key
,
uint8_t
*
buf
,
int
buf_size
)
...
...
@@ -76,8 +79,8 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf)
#define XING_NUM_BAGS 400
#define XING_TOC_SIZE 100
//
maximum size of the xing frame: offset/Xing/flags/frames/size/TOC
#define XING_
MAX_SIZE (32 + 4 + 4 + 4 + 4 + XING_TOC_SIZE)
//
size of the XING/LAME data, starting from the Xing tag
#define XING_
SIZE 156
typedef
struct
MP3Context
{
const
AVClass
*
class
;
...
...
@@ -87,7 +90,18 @@ typedef struct MP3Context {
int
write_xing
;
/* xing header */
int64_t
xing_offset
;
// a buffer containing the whole XING/LAME frame
uint8_t
*
xing_frame
;
int
xing_frame_size
;
AVCRC
audio_crc
;
// CRC of the audio data
uint32_t
audio_size
;
// total size of the audio data
// offset of the XING/LAME frame in the file
int64_t
xing_frame_offset
;
// offset of the XING/INFO tag in the frame
int
xing_offset
;
int32_t
frames
;
int32_t
size
;
uint32_t
want
;
...
...
@@ -115,13 +129,15 @@ static void mp3_write_xing(AVFormatContext *s)
{
MP3Context
*
mp3
=
s
->
priv_data
;
AVCodecContext
*
codec
=
s
->
streams
[
mp3
->
audio_stream_idx
]
->
codec
;
AVDictionaryEntry
*
enc
=
av_dict_get
(
s
->
streams
[
mp3
->
audio_stream_idx
]
->
metadata
,
"encoder"
,
NULL
,
0
);
AVIOContext
*
dyn_ctx
;
int32_t
header
;
MPADecodeHeader
mpah
;
int
srate_idx
,
i
,
channels
;
int
bitrate_idx
;
int
best_bitrate_idx
;
int
best_bitrate_error
=
INT_MAX
;
int
xing_offs
et
;
int
r
et
;
int
ver
=
0
;
int
lsf
,
bytes_needed
;
...
...
@@ -161,14 +177,8 @@ static void mp3_write_xing(AVFormatContext *s)
lsf
=
!
((
header
&
(
1
<<
20
)
&&
header
&
(
1
<<
19
)));
xing_offset
=
xing_offtbl
[
ver
!=
3
][
channels
==
1
];
bytes_needed
=
4
// header
+
xing_offset
+
4
// xing tag
+
4
// frames/size/toc flags
+
4
// frames
+
4
// size
+
XING_TOC_SIZE
;
// toc
mp3
->
xing_offset
=
xing_offtbl
[
ver
!=
3
][
channels
==
1
]
+
4
;
bytes_needed
=
mp3
->
xing_offset
+
XING_SIZE
;
for
(
bitrate_idx
=
1
;
bitrate_idx
<
15
;
bitrate_idx
++
)
{
int
bit_rate
=
1000
*
avpriv_mpa_bitrate_tab
[
lsf
][
3
-
1
][
bitrate_idx
];
...
...
@@ -192,28 +202,72 @@ static void mp3_write_xing(AVFormatContext *s)
header
&=
~
mask
;
}
avio_wb32
(
s
->
pb
,
header
);
ret
=
avio_open_dyn_buf
(
&
dyn_ctx
);
if
(
ret
<
0
)
return
;
avio_wb32
(
dyn_ctx
,
header
);
avpriv_mpegaudio_decode_header
(
&
mpah
,
header
);
av_assert0
(
mpah
.
frame_size
>=
XING_MAX_SIZE
);
av_assert0
(
mpah
.
frame_size
>=
bytes_needed
);
ffio_fill
(
s
->
pb
,
0
,
xing_offset
);
mp3
->
xing_offset
=
avio_tell
(
s
->
pb
);
ffio_wfourcc
(
s
->
pb
,
"Xing"
);
avio_wb32
(
s
->
pb
,
0x01
|
0x02
|
0x04
);
// frames / size / TOC
ffio_fill
(
dyn_ctx
,
0
,
mp3
->
xing_offset
-
4
);
ffio_wfourcc
(
dyn_ctx
,
"Xing"
);
avio_wb32
(
dyn_ctx
,
0x01
|
0x02
|
0x04
|
0x08
);
// frames / size / TOC / vbr scale
mp3
->
size
=
mpah
.
frame_size
;
mp3
->
want
=
1
;
avio_wb32
(
s
->
pb
,
0
);
// frames
avio_wb32
(
s
->
pb
,
0
);
// size
avio_wb32
(
dyn_ctx
,
0
);
// frames
avio_wb32
(
dyn_ctx
,
0
);
// size
// TOC
for
(
i
=
0
;
i
<
XING_TOC_SIZE
;
i
++
)
avio_w8
(
s
->
pb
,
255
*
i
/
XING_TOC_SIZE
);
avio_w8
(
dyn_ctx
,
255
*
i
/
XING_TOC_SIZE
);
// vbr quality
// we write it, because some (broken) tools always expect it to be present
avio_wb32
(
dyn_ctx
,
0
);
// encoder short version string
if
(
enc
)
{
uint8_t
encoder_str
[
9
]
=
{
0
};
memcpy
(
encoder_str
,
enc
->
value
,
FFMIN
(
strlen
(
enc
->
value
),
sizeof
(
encoder_str
)));
avio_write
(
dyn_ctx
,
encoder_str
,
sizeof
(
encoder_str
));
}
else
ffio_fill
(
dyn_ctx
,
0
,
9
);
avio_w8
(
dyn_ctx
,
0
);
// tag revision 0 / unknown vbr method
avio_w8
(
dyn_ctx
,
0
);
// unknown lowpass filter value
ffio_fill
(
dyn_ctx
,
0
,
8
);
// empty replaygain fields
avio_w8
(
dyn_ctx
,
0
);
// unknown encoding flags
avio_w8
(
dyn_ctx
,
0
);
// unknown abr/minimal bitrate
// encoder delay
if
(
codec
->
initial_padding
>=
1
<<
12
)
{
av_log
(
s
,
AV_LOG_WARNING
,
"Too many samples of initial padding.
\n
"
);
avio_wb24
(
dyn_ctx
,
0
);
}
else
{
avio_wb24
(
dyn_ctx
,
codec
->
initial_padding
<<
12
);
}
avio_w8
(
dyn_ctx
,
0
);
// misc
avio_w8
(
dyn_ctx
,
0
);
// mp3gain
avio_wb16
(
dyn_ctx
,
0
);
// preset
// audio length and CRCs (will be updated later)
avio_wb32
(
dyn_ctx
,
0
);
// music length
avio_wb16
(
dyn_ctx
,
0
);
// music crc
avio_wb16
(
dyn_ctx
,
0
);
// tag crc
ffio_fill
(
dyn_ctx
,
0
,
mpah
.
frame_size
-
bytes_needed
);
ffio_fill
(
s
->
pb
,
0
,
mpah
.
frame_size
-
bytes_needed
);
mp3
->
xing_frame_size
=
avio_close_dyn_buf
(
dyn_ctx
,
&
mp3
->
xing_frame
);
mp3
->
xing_frame_offset
=
avio_tell
(
s
->
pb
);
avio_write
(
s
->
pb
,
mp3
->
xing_frame
,
mp3
->
xing_frame_size
);
mp3
->
audio_size
=
mp3
->
xing_frame_size
;
}
/*
...
...
@@ -264,6 +318,12 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt)
}
mp3_xing_add_frame
(
mp3
,
pkt
);
if
(
mp3
->
xing_offset
)
{
mp3
->
audio_size
+=
pkt
->
size
;
mp3
->
audio_crc
=
av_crc
(
av_crc_get_table
(
AV_CRC_16_ANSI_LE
),
mp3
->
audio_crc
,
pkt
->
data
,
pkt
->
size
);
}
}
return
ff_raw_write_packet
(
s
,
pkt
);
...
...
@@ -292,26 +352,58 @@ static int mp3_queue_flush(AVFormatContext *s)
static
void
mp3_update_xing
(
AVFormatContext
*
s
)
{
MP3Context
*
mp3
=
s
->
priv_data
;
int
i
;
AVReplayGain
*
rg
;
uint16_t
tag_crc
;
uint8_t
*
toc
;
int
i
,
rg_size
;
/* replace "Xing" identification string with "Info" for CBR files. */
if
(
!
mp3
->
has_variable_bitrate
)
{
avio_seek
(
s
->
pb
,
mp3
->
xing_offset
,
SEEK_SET
);
ffio_wfourcc
(
s
->
pb
,
"Info"
);
}
avio_seek
(
s
->
pb
,
mp3
->
xing_offset
+
8
,
SEEK_SET
);
avio_wb32
(
s
->
pb
,
mp3
->
frames
);
avio_wb32
(
s
->
pb
,
mp3
->
size
);
if
(
!
mp3
->
has_variable_bitrate
)
AV_WL32
(
mp3
->
xing_frame
+
mp3
->
xing_offset
,
MKTAG
(
'I'
,
'n'
,
'f'
,
'o'
));
avio_w8
(
s
->
pb
,
0
);
// first toc entry has to be zero.
AV_WB32
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
8
,
mp3
->
frames
);
AV_WB32
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
12
,
mp3
->
size
);
toc
=
mp3
->
xing_frame
+
mp3
->
xing_offset
+
16
;
toc
[
0
]
=
0
;
// first toc entry has to be zero.
for
(
i
=
1
;
i
<
XING_TOC_SIZE
;
++
i
)
{
int
j
=
i
*
mp3
->
pos
/
XING_TOC_SIZE
;
int
seek_point
=
256LL
*
mp3
->
bag
[
j
]
/
mp3
->
size
;
avio_w8
(
s
->
pb
,
FFMIN
(
seek_point
,
255
));
toc
[
i
]
=
FFMIN
(
seek_point
,
255
);
}
/* write replaygain */
rg
=
(
AVReplayGain
*
)
av_stream_get_side_data
(
s
->
streams
[
0
],
AV_PKT_DATA_REPLAYGAIN
,
&
rg_size
);
if
(
rg
&&
rg_size
>=
sizeof
(
*
rg
))
{
uint16_t
val
;
AV_WB32
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
131
,
av_rescale
(
rg
->
track_peak
,
1
<<
23
,
100000
));
if
(
rg
->
track_gain
!=
INT32_MIN
)
{
val
=
FFABS
(
rg
->
track_gain
/
10000
)
&
((
1
<<
9
)
-
1
);
val
|=
(
rg
->
track_gain
<
0
)
<<
9
;
val
|=
1
<<
13
;
AV_WB16
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
135
,
val
);
}
if
(
rg
->
album_gain
!=
INT32_MIN
)
{
val
=
FFABS
(
rg
->
album_gain
/
10000
)
&
((
1
<<
9
)
-
1
);
val
|=
(
rg
->
album_gain
<
0
)
<<
9
;
val
|=
1
<<
14
;
AV_WB16
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
137
,
val
);
}
}
AV_WB32
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
XING_SIZE
-
8
,
mp3
->
audio_size
);
AV_WB16
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
XING_SIZE
-
4
,
mp3
->
audio_crc
);
tag_crc
=
av_crc
(
av_crc_get_table
(
AV_CRC_16_ANSI_LE
),
0
,
mp3
->
xing_frame
,
190
);
AV_WB16
(
mp3
->
xing_frame
+
mp3
->
xing_offset
+
XING_SIZE
-
2
,
tag_crc
);
avio_seek
(
s
->
pb
,
mp3
->
xing_frame_offset
,
SEEK_SET
);
avio_write
(
s
->
pb
,
mp3
->
xing_frame
,
mp3
->
xing_frame_size
);
avio_seek
(
s
->
pb
,
0
,
SEEK_END
);
}
...
...
@@ -334,6 +426,8 @@ static int mp3_write_trailer(struct AVFormatContext *s)
if
(
mp3
->
xing_offset
)
mp3_update_xing
(
s
);
av_freep
(
&
mp3
->
xing_frame
);
return
0
;
}
...
...
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