Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
ffmpeg
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Samuel Mira
ffmpeg
Commits
ef363ebd
Commit
ef363ebd
authored
10 years ago
by
Anton Khirnov
Browse files
Options
Downloads
Patches
Plain Diff
mp3enc: write full LAME frame
Most importantly, it contains the encoder delay and replaygain info.
parent
88b32673
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
doc/muxers.texi
+2
-1
2 additions, 1 deletion
doc/muxers.texi
libavformat/mp3enc.c
+127
-33
127 additions, 33 deletions
libavformat/mp3enc.c
with
129 additions
and
34 deletions
doc/muxers.texi
+
2
−
1
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
...
...
This diff is collapsed.
Click to expand it.
libavformat/mp3enc.c
+
127
−
33
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
(
s
->
pb
,
0
,
mpah
.
frame_size
-
bytes_needed
);
ffio_fill
(
dyn_ctx
,
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
;
}
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment