mirror of
https://github.com/mpv-player/mpv
synced 2025-01-02 21:12:23 +00:00
encode: video encoding now supported using mencoder-like options
This commit is contained in:
parent
5617bf483e
commit
f5b8b6ac12
141
DOCS/encoding.rst
Normal file
141
DOCS/encoding.rst
Normal file
@ -0,0 +1,141 @@
|
||||
General usage
|
||||
=============
|
||||
|
||||
::
|
||||
|
||||
mplayer infile -o outfile [-of outfileformat] [-ofopts formatoptions] \
|
||||
[-ofps outfps | -oautofps] [-oharddup] [-ocopyts | -orawts] [-oneverdrop] \
|
||||
[(any other mplayer options)] \
|
||||
-ovc outvideocodec [-ovcopts outvideocodecoptions] \
|
||||
-oac outaudiocodec [-oacopts outaudiocodecoptions]
|
||||
|
||||
Help for these options is provided if giving help as parameter, as in::
|
||||
|
||||
mplayer -ovc help
|
||||
|
||||
The suboptions of these generally are identical to ffmpeg's (as option parsing
|
||||
is simply delegated to ffmpeg). The option -ocopyts enables copying timestamps
|
||||
from the source as-is, instead of fixing them to match audio playback time
|
||||
(note: this doesn't work with all output container formats); -orawts even turns
|
||||
off discontinuity fixing.
|
||||
|
||||
Note that if neither -ofps nor -oautofps is specified, VFR encoding is assumed
|
||||
and the time base is 24000fps. -oautofps sets -ofps to a guessed fps number
|
||||
from the input video. Note that not all codecs and not all formats support VFR
|
||||
encoding, and some which do have bugs when a target bitrate is specified - use
|
||||
-ofps or -oautofps to force CFR encoding in these cases.
|
||||
|
||||
Of course, the options can be stored in a profile, like this .mplayer/config
|
||||
section::
|
||||
|
||||
[myencprofile]
|
||||
vf-add = scale=480:-2
|
||||
ovc = libx264
|
||||
ovcopts-add = preset=medium,tune=fastdecode
|
||||
ovcopts-add = crf=23
|
||||
ovcopts-add = maxrate=1500k,bufsize=1000k,rc_init_occupancy=900k,refs=2
|
||||
ovcopts-add = profile=baseline
|
||||
oac = aac
|
||||
oacopts-add = b=96k
|
||||
|
||||
One can then encode using this profile using the command::
|
||||
|
||||
mplayer infile -o outfile.mp4 -profile myencprofile
|
||||
|
||||
Some example profiles are provided in a file
|
||||
etc/encoding-example-profiles.conf; as for this, see below.
|
||||
|
||||
|
||||
Encoding examples
|
||||
=================
|
||||
|
||||
These are some examples of encoding targets this code has been used and tested
|
||||
for.
|
||||
|
||||
Typical MPEG-4 Part 2 ("ASP", "DivX") encoding, AVI container::
|
||||
|
||||
mplayer infile -o outfile.avi \
|
||||
-ofps 25 \
|
||||
-ovc mpeg4 -ovcopts qscale=4 \
|
||||
-oac libmp3lame -oacopts ab=128k
|
||||
|
||||
Note: AVI does not support variable frame rate, so -ofps must be used. The
|
||||
frame rate should ideally match the input (25 for PAL, 24000/1001 or 30000/1001
|
||||
for NTSC)
|
||||
|
||||
Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, Matroska (MKV) container::
|
||||
|
||||
mplayer infile -o outfile.mkv \
|
||||
-ovc libx264 -ovcopts preset=medium,crf=23,profile=baseline \
|
||||
-oac vorbis -oacopts qscale=3
|
||||
|
||||
Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, MPEG-4 (MP4) container::
|
||||
|
||||
mplayer infile -o outfile.mp4 \
|
||||
-ovc libx264 -ovcopts preset=medium,crf=23,profile=baseline \
|
||||
-oac aac -oacopts ab=128k
|
||||
|
||||
Typical VP8 encoding, WebM (restricted Matroska) container::
|
||||
|
||||
mplayer infile -o outfile.mkv \
|
||||
-of webm \
|
||||
-ovc libvpx -ovcopts qmin=6,b=1000000k \
|
||||
-oac libvorbis -oacopts qscale=3
|
||||
|
||||
|
||||
Device targets
|
||||
==============
|
||||
|
||||
As the options for various devices can get complex, profiles can be used.
|
||||
|
||||
An example profile file for encoding is provided in
|
||||
etc/encoding-example-profiles.conf in the source tree. You can include it into
|
||||
your configuration by doing, from the mplayer2-build directory::
|
||||
|
||||
mkdir -p ~/.mplayer
|
||||
echo "include = $PWD/mplayer/etc/encoding-example-profiles.conf" >> ~/.mplayer/config
|
||||
|
||||
Refer to the top of that file for more comments - in a nutshell, the following
|
||||
options are added by it::
|
||||
|
||||
-profile enc-to-dvdpal DVD-Video PAL, use dvdauthor -v pal+4:3 -a ac3+en
|
||||
-profile enc-to-dvdntsc DVD-Video NTSC, use dvdauthor -v ntsc+4:3 -a ac3+en
|
||||
-profile enc-to-bb-9000 MP4 for Blackberry Bold 9000
|
||||
-profile enc-to-nok-6300 3GP for Nokia 6300
|
||||
-profile enc-to-psp MP4 for PlayStation Portable
|
||||
-profile enc-to-iphone MP4 for iPhone
|
||||
-profile enc-to-iphone4 MP4 for iPhone 4 (double res)
|
||||
|
||||
You can encode using these with a command line like::
|
||||
|
||||
mplayer infile -o outfile.mp4 -profile enc-to-bb-9000
|
||||
|
||||
Of course, you are free to override options set by these profiles by specifying
|
||||
them after the -profile option.
|
||||
|
||||
|
||||
What works
|
||||
==========
|
||||
|
||||
* Encoding at variable frame rate (default)
|
||||
* Encoding at constant frame rate using -ofps framerate -oharddup
|
||||
* 2-pass encoding (specify flags=+pass1 in the first pass's -ovcopts, specify
|
||||
flags=+pass2 in the second pass)
|
||||
* Hardcoding subtitles using vobsub, ass or srt subtitle rendering (just
|
||||
configure mplayer for the subtitles as usual)
|
||||
* Hardcoding any other mplayer OSD (e.g. time codes, using -osdlevel 3 and -vf
|
||||
expand=::::1)
|
||||
* Encoding directly from a DVD, network stream, webcam, or any other source
|
||||
mplayer supports
|
||||
* Using x264 presets/tunings/profiles (by using profile=, tune=, preset= in the
|
||||
-ovcopts)
|
||||
* Deinterlacing/Inverse Telecine with any of mplayer's filters for that
|
||||
* Audio file converting: mplayer -o outfile.mp3 infile.flac -novideo -oac
|
||||
libmp3lame -oacopts ab=320k
|
||||
* inverse telecine filters (confirmed working: detc, pullup, filmdint)
|
||||
|
||||
What does not work yet
|
||||
|
||||
* 3-pass encoding (ensuring constant total size and bitrate constraints while
|
||||
having VBR audio; mencoder calls this "frameno")
|
||||
* Direct stream copy
|
@ -63,6 +63,7 @@ General changes for mplayer2 to mplayer3
|
||||
* Do not lose settings when playing a new file in the same player instance
|
||||
* New location for config files, new name for the binary. (Planned change.)
|
||||
* Slave mode compatibility broken (see below)
|
||||
* Encoding functionality (replacement for mencoder)
|
||||
* General code cleanups
|
||||
* Many more changes
|
||||
|
||||
|
134
DOCS/man/en/encode.rst
Normal file
134
DOCS/man/en/encode.rst
Normal file
@ -0,0 +1,134 @@
|
||||
.. _encode:
|
||||
|
||||
ENCODING
|
||||
========
|
||||
|
||||
You can encode files from one format/codec to another using this facility.
|
||||
|
||||
-o <filename>
|
||||
Enables encoding mode and specifies the output file name.
|
||||
|
||||
--of=<format>
|
||||
Specifies the output format (overrides autodetection by the extension of
|
||||
the file specified by -o).
|
||||
See --of=help for a full list of supported formats.
|
||||
|
||||
--ofopts=<options>
|
||||
Specifies the output format options for libavformat.
|
||||
See --ofopts=help for a full list of supported options.
|
||||
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
|
||||
--ofopts-add=<options1[,options2,...]>
|
||||
Appends the options given as arguments to the options list.
|
||||
|
||||
--ofopts-pre=<options1[,options2,...]>
|
||||
Prepends the options given as arguments to the options list.
|
||||
|
||||
--ofopts-del=<index1[,index2,...]>
|
||||
Deletes the options at the given indexes. Index numbers start at 0,
|
||||
negative numbers address the end of the list (-1 is the last).
|
||||
|
||||
--ofopts-clr
|
||||
Completely empties the options list.
|
||||
|
||||
--ofps=<float value>
|
||||
Specifies the output format time base (default: 24000). Low values like 25
|
||||
limit video fps by dropping frames.
|
||||
|
||||
--oautofps
|
||||
Sets the output format time base to the guessed frame rate of the input
|
||||
video (simulates mencoder behaviour, useful for AVI; may cause frame
|
||||
drops). Note that not all codecs and not all formats support VFR
|
||||
encoding, and some which do have bugs when a target bitrate is
|
||||
specified - use --ofps or --oautofps to force CFR encoding in these
|
||||
cases.
|
||||
|
||||
--oharddup
|
||||
If set, the frame rate given by --ofps is attained not by skipping time
|
||||
codes, but by duplicating frames (constant frame rate mode).
|
||||
|
||||
--oneverdrop
|
||||
If set, frames are never dropped. Instead, time codes of video are
|
||||
readjusted to always increase. This may cause AV desync, though; to
|
||||
work around this, use a high-fps time base using --ofps and absolutely
|
||||
avoid --oautofps.
|
||||
|
||||
--oac=<codec>
|
||||
Specifies the output audio codec.
|
||||
See --oac=help for a full list of supported codecs.
|
||||
|
||||
--oaoffset=<value>
|
||||
Shifts audio data by the given time (in seconds) by adding/removing
|
||||
samples at the start.
|
||||
|
||||
--oacopts=<options>
|
||||
Specifies the output audio codec options for libavcodec.
|
||||
See --oacopts=help for a full list of supported options.
|
||||
|
||||
EXAMPLE: "--oac=libmp3lame --oacopts=b=128000" selects 128kbps MP3
|
||||
encoding.
|
||||
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
|
||||
--oacopts-add=<options1[,options2,...]>
|
||||
Appends the options given as arguments to the options list.
|
||||
|
||||
--oacopts-pre=<options1[,options2,...]>
|
||||
Prepends the options given as arguments to the options list.
|
||||
|
||||
--oacopts-del=<index1[,index2,...]>
|
||||
Deletes the options at the given indexes. Index numbers start at 0,
|
||||
negative numbers address the end of the list (-1 is the last).
|
||||
|
||||
--oacopts-clr
|
||||
Completely empties the options list.
|
||||
|
||||
--ovc=<codec>
|
||||
Specifies the output video codec.
|
||||
See --ovc=help for a full list of supported codecs.
|
||||
|
||||
--ovoffset=<value>
|
||||
Shifts video data by the given time (in seconds) by shifting the pts
|
||||
values.
|
||||
|
||||
--ocopyts
|
||||
Copies input pts to the output video (not supported by some output
|
||||
container formats, e.g. avi). Discontinuities are still fixed.
|
||||
By default, audio pts are set to playback time and video pts are
|
||||
synchronized to match audio pts, as some output formats do not support
|
||||
anything else.
|
||||
|
||||
--orawts
|
||||
Copies input pts to the output video (not supported by some output
|
||||
container formats, e.g. avi). In this modem discontinuities are not fixed
|
||||
and all pts are passed through as-is. Never seek backwards or use multiple
|
||||
input files in this mode!
|
||||
|
||||
--ovcopts <options>
|
||||
Specifies the output video codec options for libavcodec.
|
||||
See --ovcopts=help for a full list of supported options.
|
||||
|
||||
EXAMPLE: "--ovc=mpeg4 --oacopts=qscale=5" selects constant quantizer scale
|
||||
5 for MPEG-4 encoding.
|
||||
|
||||
EXAMPLE: "--ovc=libx264 --ovcopts=crf=23" selects VBR quality factor 23 for
|
||||
H.264 encoding.
|
||||
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
|
||||
--ovcopts-add=<options1[,options2,...]>
|
||||
Appends the options given as arguments to the options list.
|
||||
|
||||
--ovcopts-pre=<options1[,options2,...]>
|
||||
Prepends the options given as arguments to the options list.
|
||||
|
||||
--ovcopts-del=<index1[,index2,...]>
|
||||
Deletes the options at the given indexes. Index numbers start at 0,
|
||||
negative numbers address the end of the list (-1 is the last).
|
||||
|
||||
--ovcopts-clr
|
||||
Completely empties the options list.
|
@ -6707,7 +6707,7 @@ This will give much better results for material that has undergone
|
||||
heavy editing after telecine was applied, but as a result it is not as
|
||||
forgiving of noisy input, for example TV capture.
|
||||
The optional parameter (ivtc=1) corresponds to the dr=1 option for the
|
||||
detc filter, and should not be used with MPlayer.
|
||||
detc filter, and should not be used for playback.
|
||||
Further development on ivtc has stopped, as the pullup and filmdint
|
||||
filters appear to be much more accurate.
|
||||
.
|
||||
@ -6765,9 +6765,6 @@ access to the field-flags set by the MPEG-2 decoder.
|
||||
Depending on the source MPEG, you may be fine ignoring this advice, as
|
||||
long as you do not see lots of "Bottom-first field" warnings.
|
||||
With no options it does normal inverse telecine.
|
||||
When this filter is used with MPlayer, it will result in an uneven
|
||||
framerate during playback, but it is still generally better than using
|
||||
pp=lb or no deinterlacing at all.
|
||||
Multiple options can be specified separated by /.
|
||||
.RSs
|
||||
.IPs crop=<w>:<h>:<x>:<y>
|
||||
@ -7510,6 +7507,197 @@ Using this filter together with any sort of seeking (including -ss and EDLs)
|
||||
may make demons fly out of your nose.
|
||||
.RE
|
||||
.
|
||||
.\" --------------------------------------------------------------------------
|
||||
.\" encoding
|
||||
.\" --------------------------------------------------------------------------
|
||||
.
|
||||
.SH ENCODING OPTIONS
|
||||
.
|
||||
.TP
|
||||
.B \-o <filename>
|
||||
Enable encoding mode and specify the output file name.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-of <format>
|
||||
Specify the output format (overrides autodetection by the extension of the file specified by \-o).
|
||||
See \-of help for a full list of supported formats.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-ofopts <options>
|
||||
Specify the output format options for libavformat.
|
||||
See \-ofopts help for a full list of supported options.
|
||||
.RE
|
||||
.PP
|
||||
.I NOTE:
|
||||
To get a full list of available format options, see \-ofopts help.
|
||||
.sp 1
|
||||
Options are managed in lists.
|
||||
There are a few commands to manage the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-ofopts\-add <option1[,option2,...]>
|
||||
Appends the options given as arguments to the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-ofopts\-pre <option1[,option2,...]>
|
||||
Prepends the options given as arguments to the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-ofopts\-del <index1[,index2,...]>
|
||||
Deletes the options at the given indexes.
|
||||
Index numbers start at 0, negative numbers address the end of the
|
||||
list (\-1 is the last).
|
||||
.
|
||||
.TP
|
||||
.B \-ofopts\-clr
|
||||
Completely empties the options list.
|
||||
.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-ofps <float value>
|
||||
Specifies the output format time base (default: 24000). Low values like 25 limit video fps by dropping frames.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-oautofps
|
||||
Sets the output format time base to the guessed frame rate of the input video (simulates mencoder behaviour, useful for AVI; may cause frame drops). Note that not all codecs and not all formats support VFR encoding, and some which do have bugs when a target bitrate is specified - use
|
||||
.B \-ofps
|
||||
or
|
||||
.B \-oautofps
|
||||
to force CFR encoding in these cases.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-oharddup
|
||||
If set, the frame rate given by
|
||||
.B \-ofps
|
||||
is attained not by skipping time codes, but by duplicating frames (constant frame rate mode).
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-oneverdrop
|
||||
If set, frames are never dropped. Instead, time codes of video are readjusted
|
||||
to always increase. This may cause AV desync, though; to work around this,
|
||||
use a high-fps time base using
|
||||
.B \-ofps
|
||||
and absolutely avoid
|
||||
.B \-oautofps
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-oac <codec>
|
||||
Specify the output audio codec.
|
||||
See \-oac help for a full list of supported codecs.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-oaoffset <value>
|
||||
Shifts audio data by the given time (in seconds) by adding/removing samples at the start.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-oacopts <options>
|
||||
Specify the output audio codec options for libavcodec.
|
||||
See \-oacopts help for a full list of supported options.
|
||||
.RE
|
||||
.PP
|
||||
.I EXAMPLE:
|
||||
.B \-oac libmp3lame \-oacopts b=128000
|
||||
selects 128kbps MP3 encoding.
|
||||
.PP
|
||||
.I NOTE:
|
||||
To get a full list of available audio codec options, see \-oacopts help.
|
||||
.sp 1
|
||||
Options are managed in lists.
|
||||
There are a few commands to manage the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-oacopts\-add <option1[,option2,...]>
|
||||
Appends the options given as arguments to the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-oacopts\-pre <option1[,option2,...]>
|
||||
Prepends the options given as arguments to the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-oacopts\-del <index1[,index2,...]>
|
||||
Deletes the options at the given indexes.
|
||||
Index numbers start at 0, negative numbers address the end of the
|
||||
list (\-1 is the last).
|
||||
.
|
||||
.TP
|
||||
.B \-oacopts\-clr
|
||||
Completely empties the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-ovc <codec>
|
||||
Specify the output video codec.
|
||||
See \-ovc help for a full list of supported codecs.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-ovoffset <value>
|
||||
Shifts video data by the given time (in seconds) by shifting the pts values.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-ocopyts
|
||||
Copies input pts to the output video (not supported by some output container formats, e.g. avi).
|
||||
By default, audio pts are set to playback time and video pts are synchronized to match audio pts, as some output formats do not support anything else.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-ovcopts <options>
|
||||
Specify the output video codec options for libavcodec.
|
||||
See \-ovcopts help for a full list of supported options.
|
||||
.RE
|
||||
.PP
|
||||
.I EXAMPLE:
|
||||
.B \-ovc mpeg4 \-ovcopts qscale=5
|
||||
selects constant quantizer scale 5 for MPEG-4 encoding.
|
||||
.PP
|
||||
.B \-ovc libx264 \-ovcopts crf=23
|
||||
selects VBR quality factor 23 for H.264 encoding.
|
||||
.I NOTE:
|
||||
To get a full list of available video codec options, see \-ovcopts help.
|
||||
.PP
|
||||
.sp 1
|
||||
Options are managed in lists.
|
||||
There are a few commands to manage the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-ovcopts\-add <option1[,option2,...]>
|
||||
Appends the options given as arguments to the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-ovcopts\-pre <option1[,option2,...]>
|
||||
Prepends the options given as arguments to the options list.
|
||||
.
|
||||
.TP
|
||||
.B \-ovcopts\-del <index1[,index2,...]>
|
||||
Deletes the options at the given indexes.
|
||||
Index numbers start at 0, negative numbers address the end of the
|
||||
list (\-1 is the last).
|
||||
.
|
||||
.TP
|
||||
.B \-ovcopts\-clr
|
||||
Completely empties the options list.
|
||||
.
|
||||
.PP
|
||||
.I NOTE for \-ovc libx264:
|
||||
The x264 codec provides a set of presets/tunings/profiles which can be included with the ovcopts.
|
||||
.PP
|
||||
Available presets: preset=ultrafast, preset=superfast, preset=veryfast, preset=faster, preset=fast, preset=medium, preset=slow, preset=slower, preset=veryslow, preset=placebo (default: preset=medium)
|
||||
.PP
|
||||
Available tunings: tune=film, tune=animation, tune=grain, tune=stillimage, tune=psnr, tune=ssim, tune=fastdecode, tune=zerolatency (default: none)
|
||||
.PP
|
||||
Available profiles: profile=baseline, profile=main, profile=high (default: profile=high)
|
||||
.PP
|
||||
.I EXAMPLE:
|
||||
.B mplayer \-o <outfile> <infile> \-ovc libx264 \-ovcopts preset=veryslow,crf=23,tune=animation,profile=main \-oac aac \-oacopts b=128000
|
||||
.
|
||||
.\" --------------------------------------------------------------------------
|
||||
.\" environment variables
|
||||
|
@ -407,6 +407,8 @@ OPTIONS
|
||||
|
||||
.. include:: vf.rst
|
||||
|
||||
.. include:: encode.rst
|
||||
|
||||
Taking screenshots
|
||||
==================
|
||||
|
||||
|
1
Makefile
1
Makefile
@ -284,6 +284,7 @@ SRCS_MPLAYER-$(COREVIDEO) += libvo/vo_corevideo.m
|
||||
SRCS_MPLAYER-$(DIRECT3D) += libvo/vo_direct3d.c libvo/w32_common.c
|
||||
SRCS_MPLAYER-$(GL) += libvo/gl_common.c libvo/vo_gl.c libvo/vo_gl3.c \
|
||||
pnm_loader.c
|
||||
SRCS_MPLAYER-$(ENCODING) += libvo/vo_lavc.c libao2/ao_lavc.c encode_lavc.c
|
||||
SRCS_MPLAYER-$(GL_WIN32) += libvo/w32_common.c
|
||||
SRCS_MPLAYER-$(GL_X11) += libvo/x11_common.c
|
||||
|
||||
|
@ -245,6 +245,7 @@ const m_option_t msgl_config[]={
|
||||
{ "lirc", &mp_msg_levels[MSGT_LIRC], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
|
||||
{ "stream", &mp_msg_levels[MSGT_STREAM], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
|
||||
{ "cache", &mp_msg_levels[MSGT_CACHE], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
|
||||
{ "encode", &mp_msg_levels[MSGT_ENCODE], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
|
||||
{ "xacodec", &mp_msg_levels[MSGT_XACODEC], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
|
||||
{ "tv", &mp_msg_levels[MSGT_TV], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
|
||||
{ "radio", &mp_msg_levels[MSGT_RADIO], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
|
||||
@ -289,6 +290,7 @@ const m_option_t msgl_config[]={
|
||||
" lirc - lirc_mp.c and input lirc driver\n"
|
||||
" stream - stream.c\n"
|
||||
" cache - cache2.c\n"
|
||||
" encode - encode_lavc.c and associated vo/ao drivers\n"
|
||||
" xacodec - XAnim codecs\n"
|
||||
" tv - TV input subsystem\n"
|
||||
" osdep - OS-dependent parts\n"
|
||||
@ -739,6 +741,22 @@ const m_option_t mplayer_opts[]={
|
||||
{"help", (void *) help_text, CONF_TYPE_PRINT, CONF_NOCFG|CONF_GLOBAL, 0, 0, NULL},
|
||||
{"h", (void *) help_text, CONF_TYPE_PRINT, CONF_NOCFG|CONF_GLOBAL, 0, 0, NULL},
|
||||
|
||||
OPT_STRING("o", encode_output.file, CONF_GLOBAL),
|
||||
OPT_STRING("of", encode_output.format, CONF_GLOBAL),
|
||||
OPT_STRINGLIST("ofopts*", encode_output.fopts, CONF_GLOBAL),
|
||||
OPT_FLOATRANGE("ofps", encode_output.fps, CONF_GLOBAL, 0.0, 1000000.0),
|
||||
OPT_STRING("ovc", encode_output.vcodec, CONF_GLOBAL),
|
||||
OPT_STRINGLIST("ovcopts*", encode_output.vopts, CONF_GLOBAL),
|
||||
OPT_STRING("oac", encode_output.acodec, CONF_GLOBAL),
|
||||
OPT_STRINGLIST("oacopts*", encode_output.aopts, CONF_GLOBAL),
|
||||
OPT_MAKE_FLAGS("oharddup", encode_output.harddup, CONF_GLOBAL),
|
||||
OPT_FLOATRANGE("ovoffset", encode_output.voffset, CONF_GLOBAL, -1000000.0, 1000000.0),
|
||||
OPT_FLOATRANGE("oaoffset", encode_output.aoffset, CONF_GLOBAL, -1000000.0, 1000000.0),
|
||||
OPT_MAKE_FLAGS("ocopyts", encode_output.copyts, CONF_GLOBAL),
|
||||
OPT_MAKE_FLAGS("orawts", encode_output.rawts, CONF_GLOBAL),
|
||||
OPT_MAKE_FLAGS("oautofps", encode_output.autofps, CONF_GLOBAL),
|
||||
OPT_MAKE_FLAGS("oneverdrop", encode_output.neverdrop, CONF_GLOBAL),
|
||||
|
||||
{NULL, NULL, 0, 0, 0, 0, NULL}
|
||||
};
|
||||
|
||||
|
14
configure
vendored
14
configure
vendored
@ -298,6 +298,7 @@ Installation directories:
|
||||
|
||||
Optional features:
|
||||
--disable-mplayer disable MPlayer compilation [enable]
|
||||
--disable-encoding disable encoding functionality [enable]
|
||||
--enable-termcap use termcap database for key codes [autodetect]
|
||||
--enable-termios use termios database for key codes [autodetect]
|
||||
--disable-iconv disable iconv for encoding conversion [autodetect]
|
||||
@ -436,6 +437,7 @@ _prefix="/usr/local"
|
||||
ffmpeg=auto
|
||||
ffmpeg_internals=no
|
||||
_mplayer=yes
|
||||
_encoding=yes
|
||||
_x11=auto
|
||||
_xshape=auto
|
||||
_xss=auto
|
||||
@ -620,6 +622,8 @@ for ac_option do
|
||||
--disable-cross-compile) _cross_compile=no ;;
|
||||
--enable-mplayer) _mplayer=yes ;;
|
||||
--disable-mplayer) _mplayer=no ;;
|
||||
--enable-encoding) _encoding=yes ;;
|
||||
--disable-encoding) _encoding=no ;;
|
||||
--enable-x11) _x11=yes ;;
|
||||
--disable-x11) _x11=no ;;
|
||||
--enable-xshape) _xshape=yes ;;
|
||||
@ -3180,6 +3184,14 @@ else
|
||||
fi
|
||||
echores "$_sortsub"
|
||||
|
||||
echocheck "encoding"
|
||||
if test "$_encoding" = yes ; then
|
||||
def_encoding="#define CONFIG_ENCODING 1"
|
||||
else
|
||||
def_encoding="#undef CONFIG_ENCODING"
|
||||
fi
|
||||
echores "$_encoding"
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
||||
@ -3382,6 +3394,7 @@ X11 = $_x11
|
||||
XV = $_xv
|
||||
|
||||
# FFmpeg
|
||||
ENCODING = $_encoding
|
||||
FFMPEG_INTERNALS = $ffmpeg_internals
|
||||
FFMPEG_SOURCE_PATH = $_ffmpeg_source
|
||||
|
||||
@ -3599,6 +3612,7 @@ $def_xv
|
||||
|
||||
|
||||
/* FFmpeg */
|
||||
$def_encoding
|
||||
$def_ffmpeg_internals
|
||||
|
||||
$def_fast_64bit
|
||||
|
@ -22,7 +22,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
|
||||
.vo_gamma_contrast = 1000,
|
||||
.vo_gamma_saturation = 1000,
|
||||
.vo_gamma_hue = 1000,
|
||||
.osd_level = 1,
|
||||
.osd_level = -1,
|
||||
.osd_duration = 1000,
|
||||
.loop_times = -1,
|
||||
.ordered_chapters = 1,
|
||||
|
21
encode.h
Normal file
21
encode.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef MPLAYER_ENCODE_H
|
||||
#define MPLAYER_ENCODE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libavutil/avutil.h>
|
||||
|
||||
struct MPOpts;
|
||||
struct encode_lavc_context;
|
||||
struct encode_output_conf;
|
||||
|
||||
// interface for mplayer.c
|
||||
struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options);
|
||||
void encode_lavc_finish(struct encode_lavc_context *ctx);
|
||||
void encode_lavc_free(struct encode_lavc_context *ctx);
|
||||
void encode_lavc_discontinuity(struct encode_lavc_context *ctx);
|
||||
bool encode_lavc_showhelp(struct MPOpts *opts);
|
||||
int encode_lavc_getstatus(struct encode_lavc_context *ctx, char *buf, int bufsize, float relative_position, float playback_time);
|
||||
void encode_lavc_expect_stream(struct encode_lavc_context *ctx, enum AVMediaType mt);
|
||||
bool encode_lavc_didfail(struct encode_lavc_context *ctx); // check if encoding failed
|
||||
|
||||
#endif
|
1062
encode_lavc.c
Normal file
1062
encode_lavc.c
Normal file
File diff suppressed because it is too large
Load Diff
93
encode_lavc.h
Normal file
93
encode_lavc.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
* Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_ENCODE_LAVC_H
|
||||
#define MPLAYER_ENCODE_LAVC_H
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/avstring.h>
|
||||
#include <libavutil/pixfmt.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/mathematics.h>
|
||||
|
||||
#include "encode.h"
|
||||
#include "libvo/csputils.h"
|
||||
|
||||
struct encode_lavc_context {
|
||||
struct encode_output_conf *options;
|
||||
|
||||
// these are processed from the options
|
||||
AVFormatContext *avc;
|
||||
AVRational timebase;
|
||||
AVCodec *vc;
|
||||
AVCodec *ac;
|
||||
AVDictionary *foptions;
|
||||
AVDictionary *aoptions;
|
||||
AVDictionary *voptions;
|
||||
|
||||
// values created during encoding
|
||||
int header_written; // -1 means currently writing
|
||||
|
||||
// sync to audio mode
|
||||
double audio_pts_offset;
|
||||
double last_video_in_pts;
|
||||
|
||||
// anti discontinuity mode
|
||||
double next_in_pts;
|
||||
double discontinuity_pts_offset;
|
||||
|
||||
long long abytes;
|
||||
long long vbytes;
|
||||
struct stream *twopass_bytebuffer_a;
|
||||
struct stream *twopass_bytebuffer_v;
|
||||
unsigned int t0;
|
||||
unsigned int frames;
|
||||
bool expect_video;
|
||||
bool expect_audio;
|
||||
|
||||
// has encoding failed?
|
||||
bool failed;
|
||||
bool finished;
|
||||
};
|
||||
|
||||
// interface for vo/ao drivers
|
||||
AVStream *encode_lavc_alloc_stream(struct encode_lavc_context *ctx, enum AVMediaType mt);
|
||||
void encode_lavc_write_stats(struct encode_lavc_context *ctx, AVStream *stream);
|
||||
int encode_lavc_write_frame(struct encode_lavc_context *ctx, AVPacket *packet);
|
||||
int encode_lavc_supports_pixfmt(struct encode_lavc_context *ctx, enum PixelFormat format);
|
||||
AVCodec *encode_lavc_get_codec(struct encode_lavc_context *ctx, AVStream *stream);
|
||||
int encode_lavc_open_codec(struct encode_lavc_context *ctx, AVStream *stream);
|
||||
int encode_lavc_available(struct encode_lavc_context *ctx);
|
||||
int encode_lavc_timesyncfailed(struct encode_lavc_context *ctx);
|
||||
int encode_lavc_start(struct encode_lavc_context *ctx); // returns 1 on success
|
||||
int encode_lavc_oformat_flags(struct encode_lavc_context *ctx);
|
||||
double encode_lavc_getoffset(struct encode_lavc_context *ctx, AVStream *stream);
|
||||
void encode_lavc_fail(struct encode_lavc_context *ctx, const char *format, ...); // report failure of encoding
|
||||
|
||||
bool encode_lavc_set_csp(struct encode_lavc_context *ctx,
|
||||
AVStream *stream, enum mp_csp csp);
|
||||
bool encode_lavc_set_csp_levels(struct encode_lavc_context *ctx,
|
||||
AVStream *stream, enum mp_csp_levels lev);
|
||||
enum mp_csp encode_lavc_get_csp(struct encode_lavc_context *ctx,
|
||||
AVStream *stream);
|
||||
enum mp_csp_levels encode_lavc_get_csp_levels(struct encode_lavc_context *ctx,
|
||||
AVStream *stream);
|
||||
|
||||
#endif
|
189
etc/encoding-example-profiles.conf
Normal file
189
etc/encoding-example-profiles.conf
Normal file
@ -0,0 +1,189 @@
|
||||
#
|
||||
# MPlayer configuration file
|
||||
#
|
||||
|
||||
#########################
|
||||
# encoding profile file #
|
||||
#########################
|
||||
#
|
||||
# Usage of this file: copy/symlink it to a fixed location, and add
|
||||
# include = /path/to/this/encoding-example-profiles.conf
|
||||
# to your ~/.mplayer/config
|
||||
#
|
||||
# Then, list all profiles by
|
||||
# mplayer -profile help | grep enc-
|
||||
#
|
||||
# The following kinds of encoding profiles exist:
|
||||
# enc-a-*: initialize an audio codec including good defaults
|
||||
# enc-v-*: initialize a video codec including good defaults
|
||||
# enc-f-*: initialize a file format including good defaults, including
|
||||
# selecting and initializing a good audio and video codec
|
||||
# enc-to-*: load known good settings for a target device; this typically
|
||||
# includes selecting an enc-f-* profile, then adjusting some
|
||||
# settings like frame rate, resolution and codec parameters
|
||||
#
|
||||
# AFTER including a profile of these, you can of course still change
|
||||
# options, or even switch to another codec.
|
||||
#
|
||||
# You can view the exact options a profile sets by
|
||||
# mplayer -show-profile enc-to-bb-9000
|
||||
#
|
||||
# Examples:
|
||||
# mplayer -profile enc-to-dvdpal -o outfile.mpg infile.mkv
|
||||
# mplayer -profile enc-f-avi -ofps 30 -o outfile.avi infile.mkv
|
||||
# mplayer -profile enc-v-mpeg4 -ovcopts-add global_quality=7 -profile enc-a-mp3 -oacopts-add b=320k -o outfile.avi infile.mkv
|
||||
|
||||
################
|
||||
# audio codecs #
|
||||
################
|
||||
[enc-a-aac]
|
||||
profile-desc = "AAC (libfaac or FFmpeg)"
|
||||
oac = libfaac,aac
|
||||
oacopts = b=128k
|
||||
|
||||
[enc-a-ac3]
|
||||
profile-desc = "AC3 (FFmpeg)"
|
||||
oac = ac3
|
||||
oacopts = b=448k
|
||||
|
||||
[enc-a-mp3]
|
||||
profile-desc = "MP3 (LAME)"
|
||||
oac = libmp3lame
|
||||
oacopts = b=128k
|
||||
|
||||
[enc-a-vorbis]
|
||||
profile-desc = "Vorbis (libvorbis)"
|
||||
oac = libvorbis
|
||||
oacopts = global_quality=3
|
||||
|
||||
################
|
||||
# video codecs #
|
||||
################
|
||||
[enc-v-h263]
|
||||
profile-desc = "H.263 (FFmpeg)"
|
||||
ovc = h263
|
||||
ovcopts = global_quality=4
|
||||
|
||||
[enc-v-h264]
|
||||
profile-desc = "H.264 (x264)"
|
||||
ovc = libx264
|
||||
ovcopts = preset=medium,crf=23,threads=0
|
||||
|
||||
[enc-v-mpeg2]
|
||||
profile-desc = "MPEG-2 Video (FFmpeg)"
|
||||
ovc = mpeg2video
|
||||
ovcopts-clr = yes
|
||||
|
||||
[enc-v-mpeg4]
|
||||
profile-desc = "MPEG-4 Part 2 (FFmpeg)"
|
||||
ovc = mpeg4
|
||||
ovcopts = global_quality=4
|
||||
|
||||
[enc-v-vp8]
|
||||
profile-desc = "VP8 (libvpx)"
|
||||
oac = libvpx
|
||||
oacopts = qmin=4,b=10000000k # ought to be enough for anyone; for CBR use, set b=; for VBR use, set qmin= to quality
|
||||
|
||||
###########
|
||||
# formats #
|
||||
###########
|
||||
[enc-f-3gp]
|
||||
profile-desc = "H.263 + AAC (for 3GP)"
|
||||
of = 3gp
|
||||
ocopyts = yes
|
||||
profile = enc-v-h263
|
||||
profile = enc-a-aac
|
||||
ofopts-clr = yes
|
||||
|
||||
[enc-f-avi]
|
||||
profile-desc = "MPEG-4 + MP3 (for AVI)"
|
||||
of = avi
|
||||
ocopyts = no
|
||||
oautofps = yes
|
||||
profile = enc-v-mpeg4
|
||||
profile = enc-a-mp3
|
||||
ofopts-clr = yes
|
||||
|
||||
[enc-f-mp4]
|
||||
profile-desc = "H.264 + AAC (for MP4)"
|
||||
of = mp4
|
||||
ocopyts = yes
|
||||
profile = enc-v-h264
|
||||
profile = enc-a-aac
|
||||
ofopts-clr = yes
|
||||
|
||||
[enc-f-webm]
|
||||
profile-desc = "VP8 + Vorbis (for WebM)"
|
||||
of = webm
|
||||
ocopyts = yes
|
||||
profile = enc-v-vp8
|
||||
profile = enc-a-vorbis
|
||||
ofopts-clr = yes
|
||||
|
||||
##################
|
||||
# target devices #
|
||||
##################
|
||||
[enc-to-dvdpal]
|
||||
profile-desc = "DVD-Video PAL, use dvdauthor -v pal+4:3 -a ac3+en"
|
||||
profile = enc-v-mpeg2
|
||||
profile = enc-a-ac3
|
||||
of = dvd
|
||||
ofopts-add = packetsize=2048,muxrate=10080000
|
||||
ofps = 25
|
||||
oharddup = yes
|
||||
vf = expand=aspect=4/3,scale=720:576
|
||||
srate = 48000
|
||||
ovcopts-add = g=15,b=6000000,maxrate=9000000,minrate=0,bufsize=1835008
|
||||
|
||||
[enc-to-dvdntsc]
|
||||
profile-desc = "DVD-Video NTSC, use dvdauthor -v ntsc+4:3 -a ac3+en"
|
||||
profile = enc-v-mpeg2
|
||||
profile = enc-a-ac3
|
||||
of = dvd
|
||||
ofopts-add = packetsize=2048,muxrate=10080000
|
||||
ofps = 24000/1001
|
||||
oharddup = yes
|
||||
vf-add = expand=aspect=4/3,scale=720:480
|
||||
srate = 48000
|
||||
ovcopts-add = g=18,b=6000000,maxrate=9000000,minrate=0,bufsize=1835008
|
||||
|
||||
[enc-to-bb-9000]
|
||||
profile-desc = "MP4 for Blackberry Bold 9000"
|
||||
profile = enc-f-mp4
|
||||
vf-add = scale=480:-2
|
||||
ovcopts-add = maxrate=1500k,bufsize=1000k,rc_init_occupancy=900k,refs=1,profile=baseline
|
||||
oacopts-add = b=96k
|
||||
|
||||
[enc-to-nok-6300]
|
||||
profile-desc = "3GP for Nokia 6300"
|
||||
profile = enc-f-3gp
|
||||
ofps = 25
|
||||
vf-add = scale=176:144
|
||||
srate = 16000
|
||||
channels = 1
|
||||
oacopts-add = b=32k
|
||||
|
||||
[enc-to-psp]
|
||||
profile-desc = "MP4 for PlayStation Portable"
|
||||
profile = enc-f-mp4
|
||||
ofps = 30000/1001
|
||||
vf-add = scale=480:272,dsize=480:270
|
||||
srate = 48000
|
||||
channels = 2
|
||||
ovcopts-add = b=512k,profile=baseline
|
||||
|
||||
[enc-to-iphone]
|
||||
profile-desc = "MP4 for iPhone"
|
||||
profile = enc-f-mp4
|
||||
oautofps = yes # iphone supports 30fps max
|
||||
vf-add = scale=-2:320 # half native screen res is probably best here, full res is no fun on tiny display anyway
|
||||
ovcopts-add = maxrate=2500k,bufsize=1000k,rc_init_occupancy=900k,level=30,profile=baseline
|
||||
oacopts-add = b=128k
|
||||
|
||||
[enc-to-iphone-4]
|
||||
profile-desc = "MP4 for iPhone 4 (960x640)"
|
||||
profile = enc-f-mp4
|
||||
oautofps = yes # iphone supports 30fps max
|
||||
vf-add = scale=-2:640 # native screen res
|
||||
ovcopts-add = maxrate=2500k,bufsize=1000k,rc_init_occupancy=900k,level=30,profile=baseline
|
||||
oacopts-add = b=128k
|
@ -53,6 +53,7 @@ PGDWN seek -600
|
||||
} speed_mult 2.0
|
||||
BS speed_set 1.0 # reset speed to normal
|
||||
q quit
|
||||
q {encode} quit
|
||||
ESC quit
|
||||
p pause # toggle pause/playback mode
|
||||
. frame_step # advance one frame and pause
|
||||
@ -122,6 +123,7 @@ VOLUME_UP volume 1
|
||||
VOLUME_DOWN volume -1
|
||||
MUTE mute
|
||||
CLOSE_WIN quit
|
||||
CLOSE_WIN {encode} quit
|
||||
! seek_chapter -1 # skip to previous chapter
|
||||
@ seek_chapter 1 # next
|
||||
E step_property_osd edition # next edition
|
||||
|
588
libao2/ao_lavc.c
Normal file
588
libao2/ao_lavc.c
Normal file
@ -0,0 +1,588 @@
|
||||
/*
|
||||
* audio encoding using libavformat
|
||||
* Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
|
||||
* NOTE: this file is partially based on ao_pcm.c by Atmosfear
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/audioconvert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "options.h"
|
||||
#include "mpcommon.h"
|
||||
#include "fmt-conversion.h"
|
||||
#include "libaf/format.h"
|
||||
#include "libaf/reorder_ch.h"
|
||||
#include "talloc.h"
|
||||
#include "audio_out.h"
|
||||
#include "mp_msg.h"
|
||||
|
||||
#include "encode_lavc.h"
|
||||
|
||||
static const char *sample_padding_signed = "\x00\x00\x00\x00";
|
||||
static const char *sample_padding_u8 = "\x80";
|
||||
static const char *sample_padding_float = "\x00\x00\x00\x00";
|
||||
|
||||
struct priv {
|
||||
uint8_t *buffer;
|
||||
size_t buffer_size;
|
||||
AVStream *stream;
|
||||
int pcmhack;
|
||||
int aframesize;
|
||||
int aframecount;
|
||||
int offset;
|
||||
int offset_left;
|
||||
int64_t savepts;
|
||||
int framecount;
|
||||
int64_t lastpts;
|
||||
int sample_size;
|
||||
const void *sample_padding;
|
||||
|
||||
AVRational worst_time_base;
|
||||
int worst_time_base_is_stream;
|
||||
};
|
||||
|
||||
// open & setup audio device
|
||||
static int init(struct ao *ao, char *params)
|
||||
{
|
||||
struct priv *ac = talloc_zero(ao, struct priv);
|
||||
const enum AVSampleFormat *sampleformat;
|
||||
AVCodec *codec;
|
||||
|
||||
if (!encode_lavc_available(ao->encode_lavc_ctx)) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR,
|
||||
"ao-lavc: the option -o (output file) must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ac->stream) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: rejecting reinitialization\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ac->stream = encode_lavc_alloc_stream(ao->encode_lavc_ctx,
|
||||
AVMEDIA_TYPE_AUDIO);
|
||||
|
||||
if (!ac->stream) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: could not get a new audio stream\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
codec = encode_lavc_get_codec(ao->encode_lavc_ctx, ac->stream);
|
||||
|
||||
// ac->stream->time_base.num = 1;
|
||||
// ac->stream->time_base.den = ao->samplerate;
|
||||
// doing this breaks mpeg2ts in ffmpeg
|
||||
// which doesn't properly force the time base to be 90000
|
||||
// furthermore, ffmpeg.c doesn't do this either and works
|
||||
|
||||
ac->stream->codec->time_base.num = 1;
|
||||
ac->stream->codec->time_base.den = ao->samplerate;
|
||||
|
||||
ac->stream->codec->sample_rate = ao->samplerate;
|
||||
ac->stream->codec->channels = ao->channels;
|
||||
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
|
||||
|
||||
{
|
||||
// first check if the selected format is somewhere in the list of
|
||||
// supported formats by the codec
|
||||
for (sampleformat = codec->sample_fmts;
|
||||
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
|
||||
++sampleformat) {
|
||||
switch (*sampleformat) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
if (ao->format == AF_FORMAT_U8)
|
||||
goto out_search;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
if (ao->format == AF_FORMAT_S16_BE)
|
||||
goto out_search;
|
||||
if (ao->format == AF_FORMAT_S16_LE)
|
||||
goto out_search;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
if (ao->format == AF_FORMAT_S32_BE)
|
||||
goto out_search;
|
||||
if (ao->format == AF_FORMAT_S32_LE)
|
||||
goto out_search;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
if (ao->format == AF_FORMAT_FLOAT_BE)
|
||||
goto out_search;
|
||||
if (ao->format == AF_FORMAT_FLOAT_LE)
|
||||
goto out_search;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
out_search:
|
||||
;
|
||||
}
|
||||
|
||||
if (!sampleformat || *sampleformat == AV_SAMPLE_FMT_NONE) {
|
||||
// if the selected format is not supported, we have to pick the first
|
||||
// one we CAN support
|
||||
// note: not needing to select endianness here, as the switch() below
|
||||
// does that anyway for us
|
||||
for (sampleformat = codec->sample_fmts;
|
||||
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
|
||||
++sampleformat) {
|
||||
switch (*sampleformat) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
ao->format = AF_FORMAT_U8;
|
||||
goto out_takefirst;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
ao->format = AF_FORMAT_S16_NE;
|
||||
goto out_takefirst;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
ao->format = AF_FORMAT_S32_NE;
|
||||
goto out_takefirst;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
ao->format = AF_FORMAT_FLOAT_NE;
|
||||
goto out_takefirst;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
out_takefirst:
|
||||
;
|
||||
}
|
||||
|
||||
switch (ao->format) {
|
||||
// now that we have chosen a format, set up the fields for it, boldly
|
||||
// switching endianness if needed (mplayer code will convert for us
|
||||
// anyway, but ffmpeg always expects native endianness)
|
||||
case AF_FORMAT_U8:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_U8;
|
||||
ac->sample_size = 1;
|
||||
ac->sample_padding = sample_padding_u8;
|
||||
ao->format = AF_FORMAT_U8;
|
||||
break;
|
||||
default:
|
||||
case AF_FORMAT_S16_BE:
|
||||
case AF_FORMAT_S16_LE:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
ac->sample_size = 2;
|
||||
ac->sample_padding = sample_padding_signed;
|
||||
ao->format = AF_FORMAT_S16_NE;
|
||||
break;
|
||||
case AF_FORMAT_S32_BE:
|
||||
case AF_FORMAT_S32_LE:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S32;
|
||||
ac->sample_size = 4;
|
||||
ac->sample_padding = sample_padding_signed;
|
||||
ao->format = AF_FORMAT_S32_NE;
|
||||
break;
|
||||
case AF_FORMAT_FLOAT_BE:
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_FLT;
|
||||
ac->sample_size = 4;
|
||||
ac->sample_padding = sample_padding_float;
|
||||
ao->format = AF_FORMAT_FLOAT_NE;
|
||||
break;
|
||||
}
|
||||
|
||||
ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
|
||||
|
||||
switch (ao->channels) {
|
||||
case 1:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_MONO;
|
||||
break;
|
||||
case 2:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
break;
|
||||
/* someone please check if these are what mplayer normally assumes
|
||||
case 3:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_SURROUND;
|
||||
break;
|
||||
case 4:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_2_2;
|
||||
break;
|
||||
*/
|
||||
case 5:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT0;
|
||||
break;
|
||||
case 6:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT1;
|
||||
break;
|
||||
case 8:
|
||||
ac->stream->codec->channel_layout = AV_CH_LAYOUT_7POINT1;
|
||||
break;
|
||||
default:
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR,
|
||||
"ao-lavc: unknown channel layout; hoping for the best\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
|
||||
return -1;
|
||||
|
||||
ac->pcmhack = 0;
|
||||
if (ac->stream->codec->frame_size <= 1)
|
||||
ac->pcmhack = av_get_bits_per_sample(ac->stream->codec->codec_id) / 8;
|
||||
|
||||
if (ac->pcmhack) {
|
||||
ac->aframesize = 16384; // "enough"
|
||||
ac->buffer_size = ac->aframesize * ac->pcmhack * ao->channels * 2 + 200;
|
||||
} else {
|
||||
ac->aframesize = ac->stream->codec->frame_size;
|
||||
ac->buffer_size = ac->aframesize * ac->sample_size * ao->channels * 2 +
|
||||
200;
|
||||
}
|
||||
if (ac->buffer_size < FF_MIN_BUFFER_SIZE)
|
||||
ac->buffer_size = FF_MIN_BUFFER_SIZE;
|
||||
ac->buffer = talloc_size(ac, ac->buffer_size);
|
||||
|
||||
// enough frames for at least 0.25 seconds
|
||||
ac->framecount = ceil(ao->samplerate * 0.25 / ac->aframesize);
|
||||
// but at least one!
|
||||
ac->framecount = FFMAX(ac->framecount, 1);
|
||||
|
||||
ac->savepts = MP_NOPTS_VALUE;
|
||||
ac->lastpts = MP_NOPTS_VALUE;
|
||||
ac->offset = ac->stream->codec->sample_rate *
|
||||
encode_lavc_getoffset(ao->encode_lavc_ctx, ac->stream);
|
||||
ac->offset_left = ac->offset;
|
||||
|
||||
//fill_ao_data:
|
||||
ao->outburst = ac->aframesize * ac->sample_size * ao->channels *
|
||||
ac->framecount;
|
||||
ao->buffersize = ao->outburst * 2;
|
||||
ao->bps = ao->channels * ao->samplerate * ac->sample_size;
|
||||
ao->untimed = true;
|
||||
ao->priv = ac;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fill_with_padding(void *buf, int cnt, int sz, const void *padding)
|
||||
{
|
||||
int i;
|
||||
if (sz == 1) {
|
||||
memset(buf, cnt, *(char *)padding);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < cnt; ++i)
|
||||
memcpy((char *) buf + i * sz, padding, sz);
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static int encode(struct ao *ao, int ptsvalid, double apts, void *data);
|
||||
static void uninit(struct ao *ao, bool cut_audio)
|
||||
{
|
||||
struct priv *ac = ao->priv;
|
||||
if (ac->buffer) {
|
||||
double pts = ao->pts + ac->offset / (double) ao->samplerate;
|
||||
if (ao->buffer.len > 0) {
|
||||
void *paddingbuf = talloc_size(ao,
|
||||
ac->aframesize * ao->channels * ac->sample_size);
|
||||
memcpy(paddingbuf, ao->buffer.start, ao->buffer.len);
|
||||
fill_with_padding((char *) paddingbuf + ao->buffer.len,
|
||||
(ac->aframesize * ao->channels * ac->sample_size
|
||||
- ao->buffer.len) / ac->sample_size,
|
||||
ac->sample_size, ac->sample_padding);
|
||||
encode(ao, ao->pts != MP_NOPTS_VALUE, pts, paddingbuf);
|
||||
pts += ac->aframesize / (double) ao->samplerate;
|
||||
talloc_free(paddingbuf);
|
||||
ao->buffer.len = 0;
|
||||
}
|
||||
while (encode(ao, true, pts, NULL) > 0) ;
|
||||
}
|
||||
|
||||
ao->priv = NULL;
|
||||
}
|
||||
|
||||
// return: how many bytes can be played without blocking
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
return ao->outburst;
|
||||
}
|
||||
|
||||
// must get exactly ac->aframesize amount of data
|
||||
static int encode(struct ao *ao, int ptsvalid, double apts, void *data)
|
||||
{
|
||||
AVFrame *frame;
|
||||
AVPacket packet;
|
||||
struct priv *ac = ao->priv;
|
||||
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
|
||||
double realapts = ac->aframecount * (double) ac->aframesize /
|
||||
ao->samplerate;
|
||||
int status, gotpacket;
|
||||
|
||||
ac->aframecount++;
|
||||
if (data && (ao->channels == 5 || ao->channels == 6 || ao->channels == 8)) {
|
||||
reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
|
||||
AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
|
||||
ao->channels,
|
||||
ac->aframesize * ao->channels, ac->sample_size);
|
||||
}
|
||||
|
||||
if (data && ptsvalid)
|
||||
ectx->audio_pts_offset = realapts - apts;
|
||||
|
||||
av_init_packet(&packet);
|
||||
packet.data = ac->buffer;
|
||||
packet.size = ac->buffer_size;
|
||||
if(data)
|
||||
{
|
||||
frame = avcodec_alloc_frame();
|
||||
frame->nb_samples = ac->aframesize;
|
||||
if(avcodec_fill_audio_frame(frame, ao->channels, ac->stream->codec->sample_fmt, data, ac->aframesize * ao->channels * ac->sample_size, 1))
|
||||
{
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error filling\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ao->encode_lavc_ctx->options->rawts) {
|
||||
// raw audio pts
|
||||
frame->pts = floor(apts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
|
||||
} else if (ectx->options->copyts) {
|
||||
// real audio pts
|
||||
frame->pts = floor((apts + ectx->discontinuity_pts_offset) * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
|
||||
} else {
|
||||
// audio playback time
|
||||
frame->pts = floor(realapts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
|
||||
}
|
||||
|
||||
int64_t frame_pts = av_rescale_q(frame->pts, ac->stream->codec->time_base, ac->worst_time_base);
|
||||
if (ac->lastpts != MP_NOPTS_VALUE && frame_pts <= ac->lastpts) {
|
||||
// this indicates broken video
|
||||
// (video pts failing to increase fast enough to match audio)
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: audio frame pts went backwards "
|
||||
"(%d <- %d), autofixed\n", (int)frame->pts,
|
||||
(int)ac->lastpts);
|
||||
frame_pts = ac->lastpts + 1;
|
||||
frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, ac->stream->codec->time_base);
|
||||
}
|
||||
ac->lastpts = frame_pts;
|
||||
|
||||
frame->quality = ac->stream->codec->global_quality;
|
||||
status = avcodec_encode_audio2(ac->stream->codec, &packet, frame, &gotpacket);
|
||||
|
||||
if (!status) {
|
||||
if (ac->savepts == MP_NOPTS_VALUE)
|
||||
ac->savepts = frame->pts;
|
||||
}
|
||||
|
||||
av_free(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = avcodec_encode_audio2(ac->stream->codec, &packet, NULL, &gotpacket);
|
||||
}
|
||||
|
||||
if(status)
|
||||
{
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error encoding\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!gotpacket)
|
||||
return 0;
|
||||
|
||||
mp_msg(MSGT_ENCODE, MSGL_DBG2,
|
||||
"ao-lavc: got pts %f (playback time: %f); out size: %d\n",
|
||||
apts, realapts, packet.size);
|
||||
|
||||
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->stream);
|
||||
|
||||
// Do we need this at all? Better be safe than sorry...
|
||||
if (packet.pts == AV_NOPTS_VALUE) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: encoder lost pts, why?\n");
|
||||
if (ac->savepts != MP_NOPTS_VALUE)
|
||||
packet.pts = ac->savepts;
|
||||
}
|
||||
|
||||
if (packet.pts != AV_NOPTS_VALUE)
|
||||
packet.pts = av_rescale_q(packet.pts, ac->stream->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
|
||||
if (packet.dts != AV_NOPTS_VALUE)
|
||||
packet.dts = av_rescale_q(packet.dts, ac->stream->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
|
||||
if(packet.duration > 0)
|
||||
packet.duration = av_rescale_q(packet.duration, ac->stream->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
|
||||
ac->savepts = MP_NOPTS_VALUE;
|
||||
|
||||
if (encode_lavc_write_frame(ao->encode_lavc_ctx, &packet) < 0) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error writing at %f %f/%f\n",
|
||||
realapts, (double) ac->stream->time_base.num,
|
||||
(double) ac->stream->time_base.den);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return packet.size;
|
||||
}
|
||||
|
||||
// plays 'len' bytes of 'data'
|
||||
// it should round it down to outburst*n
|
||||
// return: number of bytes played
|
||||
static int play(struct ao *ao, void *data, int len, int flags)
|
||||
{
|
||||
struct priv *ac = ao->priv;
|
||||
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
|
||||
int bufpos = 0;
|
||||
int64_t ptsoffset;
|
||||
void *paddingbuf = NULL;
|
||||
double nextpts;
|
||||
|
||||
len /= ac->sample_size * ao->channels;
|
||||
|
||||
if (!encode_lavc_start(ectx)) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: NOTE: deferred initial audio frame (probably because video is not there yet)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ac->worst_time_base.den == 0) {
|
||||
//if (ac->stream->codec->time_base.num / ac->stream->codec->time_base.den >= ac->stream->time_base.num / ac->stream->time_base.den)
|
||||
if (ac->stream->codec->time_base.num * (double) ac->stream->time_base.den >=
|
||||
ac->stream->time_base.num * (double) ac->stream->codec->time_base.den) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_V, "ao-lavc: NOTE: using codec time base "
|
||||
"(%d/%d) for pts adjustment; the stream base (%d/%d) is "
|
||||
"not worse.\n", (int)ac->stream->codec->time_base.num,
|
||||
(int)ac->stream->codec->time_base.den, (int)ac->stream->time_base.num,
|
||||
(int)ac->stream->time_base.den);
|
||||
ac->worst_time_base = ac->stream->codec->time_base;
|
||||
ac->worst_time_base_is_stream = 0;
|
||||
} else {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: NOTE: not using codec time "
|
||||
"base (%d/%d) for pts adjustment; the stream base (%d/%d) "
|
||||
"is worse.\n", (int)ac->stream->codec->time_base.num,
|
||||
(int)ac->stream->codec->time_base.den, (int)ac->stream->time_base.num,
|
||||
(int)ac->stream->time_base.den);
|
||||
ac->worst_time_base = ac->stream->time_base;
|
||||
ac->worst_time_base_is_stream = 1;
|
||||
}
|
||||
|
||||
// NOTE: we use the following "axiom" of av_rescale_q:
|
||||
// if time base A is worse than time base B, then
|
||||
// av_rescale_q(av_rescale_q(x, A, B), B, A) == x
|
||||
// this can be proven as long as av_rescale_q rounds to nearest, which
|
||||
// it currently does
|
||||
|
||||
// av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B"
|
||||
// and:
|
||||
// av_rescale_q(av_rescale_q(x, A, B), B, A) * A
|
||||
// == "round av_rescale_q(x, A, B)*B to nearest multiple of A"
|
||||
// == "round 'round x*A to nearest multiple of B' to nearest multiple of A"
|
||||
//
|
||||
// assume this fails. Then there is a value of x*A, for which the
|
||||
// nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[.
|
||||
// Absurd, as this range MUST contain at least one multiple of B.
|
||||
}
|
||||
|
||||
ptsoffset = ac->offset;
|
||||
// this basically just edits ao->apts for syncing purposes
|
||||
|
||||
if (ectx->options->copyts || ectx->options->rawts) {
|
||||
// we do not send time sync data to the video side,
|
||||
// but we always need the exact pts, even if zero
|
||||
} else {
|
||||
// here we must "simulate" the pts editing
|
||||
// 1. if we have to skip stuff, we skip it
|
||||
// 2. if we have to add samples, we add them
|
||||
// 3. we must still adjust ptsoffset appropriately for AV sync!
|
||||
// invariant:
|
||||
// if no partial skipping is done, the first frame gets ao->apts passed as pts!
|
||||
|
||||
if (ac->offset_left < 0) {
|
||||
if (ac->offset_left <= -len) {
|
||||
// skip whole frame
|
||||
ac->offset_left += len;
|
||||
return len * ac->sample_size * ao->channels;
|
||||
} else {
|
||||
// skip part of this frame, buffer/encode the rest
|
||||
bufpos -= ac->offset_left;
|
||||
ptsoffset += ac->offset_left;
|
||||
ac->offset_left = 0;
|
||||
}
|
||||
} else if (ac->offset_left > 0) {
|
||||
// make a temporary buffer, filled with zeroes at the start
|
||||
// (don't worry, only happens once)
|
||||
|
||||
paddingbuf = talloc_size(ac, ac->sample_size * ao->channels *
|
||||
(ac->offset_left + len));
|
||||
fill_with_padding(paddingbuf, ac->offset_left, ac->sample_size,
|
||||
ac->sample_padding);
|
||||
data = (char *) paddingbuf + ac->sample_size * ao->channels *
|
||||
ac->offset_left;
|
||||
bufpos -= ac->offset_left; // yes, negative!
|
||||
ptsoffset += ac->offset_left;
|
||||
ac->offset_left = 0;
|
||||
|
||||
// now adjust the bufpos so the final value of bufpos is positive!
|
||||
/*
|
||||
int cnt = (len - bufpos) / ac->aframesize;
|
||||
int finalbufpos = bufpos + cnt * ac->aframesize;
|
||||
*/
|
||||
int finalbufpos = len - (len - bufpos) % ac->aframesize;
|
||||
if (finalbufpos < 0) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: cannot attain the "
|
||||
"exact requested audio sync; shifting by %d frames\n",
|
||||
-finalbufpos);
|
||||
bufpos -= finalbufpos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fix the discontinuity pts offset
|
||||
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
|
||||
nextpts = ao->pts + ptsoffset / (double) ao->samplerate;
|
||||
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
|
||||
}
|
||||
|
||||
while (len - bufpos >= ac->aframesize) {
|
||||
encode(ao, ao->pts != MP_NOPTS_VALUE,
|
||||
ao->pts + (bufpos + ptsoffset) / (double) ao->samplerate +
|
||||
encode_lavc_getoffset(ectx, ac->stream),
|
||||
(char *) data + ac->sample_size * bufpos * ao->channels);
|
||||
bufpos += ac->aframesize;
|
||||
}
|
||||
|
||||
talloc_free(paddingbuf);
|
||||
|
||||
// set next allowed output pts value
|
||||
nextpts = ao->pts + ectx->discontinuity_pts_offset + (bufpos + ptsoffset) / (double) ao->samplerate;
|
||||
if (nextpts > ectx->next_in_pts)
|
||||
ectx->next_in_pts = nextpts;
|
||||
|
||||
return bufpos * ac->sample_size * ao->channels;
|
||||
}
|
||||
|
||||
const struct ao_driver audio_out_lavc = {
|
||||
.is_new = true,
|
||||
.info = &(const struct ao_info) {
|
||||
"audio encoding using libavcodec",
|
||||
"lavc",
|
||||
"Rudolf Polzer <divVerent@xonotic.org>",
|
||||
""
|
||||
},
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.get_space = get_space,
|
||||
.play = play,
|
||||
};
|
@ -43,6 +43,7 @@ extern const struct ao_driver audio_out_alsa;
|
||||
extern const struct ao_driver audio_out_dsound;
|
||||
extern const struct ao_driver audio_out_pcm;
|
||||
extern const struct ao_driver audio_out_pss;
|
||||
extern const struct ao_driver audio_out_lavc;
|
||||
extern const struct ao_driver audio_out_portaudio;
|
||||
|
||||
static const struct ao_driver * const audio_out_drivers[] = {
|
||||
@ -71,6 +72,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
|
||||
#endif
|
||||
#ifdef CONFIG_OPENAL
|
||||
&audio_out_openal,
|
||||
#endif
|
||||
#ifdef CONFIG_ENCODING
|
||||
&audio_out_lavc,
|
||||
#endif
|
||||
&audio_out_null,
|
||||
// should not be auto-selected:
|
||||
|
@ -106,6 +106,7 @@ struct ao {
|
||||
bool no_persistent_volume;
|
||||
const struct ao_driver *driver;
|
||||
void *priv;
|
||||
struct encode_lavc_context *encode_lavc_ctx;
|
||||
struct MPOpts *opts;
|
||||
struct input_ctx *input_ctx;
|
||||
};
|
||||
|
@ -50,32 +50,39 @@ char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
|
||||
enum mp_csp avcol_spc_to_mp_csp(enum AVColorSpace colorspace)
|
||||
{
|
||||
switch (colorspace) {
|
||||
case AVCOL_SPC_BT709:
|
||||
return MP_CSP_BT_709;
|
||||
break;
|
||||
case AVCOL_SPC_BT470BG:
|
||||
case AVCOL_SPC_SMPTE170M:
|
||||
return MP_CSP_BT_601;
|
||||
break;
|
||||
case AVCOL_SPC_SMPTE240M:
|
||||
return MP_CSP_SMPTE_240M;
|
||||
break;
|
||||
default:
|
||||
return MP_CSP_AUTO;
|
||||
case AVCOL_SPC_BT709: return MP_CSP_BT_709;
|
||||
case AVCOL_SPC_BT470BG: return MP_CSP_BT_601;
|
||||
case AVCOL_SPC_SMPTE170M: return MP_CSP_BT_601;
|
||||
case AVCOL_SPC_SMPTE240M: return MP_CSP_SMPTE_240M;
|
||||
default: return MP_CSP_AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
enum mp_csp_levels avcol_range_to_mp_csp_levels(enum AVColorRange range)
|
||||
{
|
||||
switch (range) {
|
||||
case AVCOL_RANGE_MPEG:
|
||||
return MP_CSP_LEVELS_TV;
|
||||
break;
|
||||
case AVCOL_RANGE_JPEG:
|
||||
return MP_CSP_LEVELS_PC;
|
||||
break;
|
||||
default:
|
||||
return MP_CSP_LEVELS_AUTO;
|
||||
case AVCOL_RANGE_MPEG: return MP_CSP_LEVELS_TV;
|
||||
case AVCOL_RANGE_JPEG: return MP_CSP_LEVELS_PC;
|
||||
default: return MP_CSP_LEVELS_AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
enum AVColorSpace mp_csp_to_avcol_spc(enum mp_csp colorspace)
|
||||
{
|
||||
switch (colorspace) {
|
||||
case MP_CSP_BT_709: return AVCOL_SPC_BT709;
|
||||
case MP_CSP_BT_601: return AVCOL_SPC_BT470BG;
|
||||
case MP_CSP_SMPTE_240M: return AVCOL_SPC_SMPTE240M;
|
||||
default: return AVCOL_SPC_RGB;
|
||||
}
|
||||
}
|
||||
|
||||
enum AVColorRange mp_csp_levels_to_avcol_range(enum mp_csp_levels range)
|
||||
{
|
||||
switch (range) {
|
||||
case MP_CSP_LEVELS_TV: return AVCOL_RANGE_MPEG;
|
||||
case MP_CSP_LEVELS_PC: return AVCOL_RANGE_JPEG;
|
||||
default: return AVCOL_RANGE_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,6 +116,10 @@ enum mp_csp avcol_spc_to_mp_csp(enum AVColorSpace colorspace);
|
||||
|
||||
enum mp_csp_levels avcol_range_to_mp_csp_levels(enum AVColorRange range);
|
||||
|
||||
enum AVColorSpace mp_csp_to_avcol_spc(enum mp_csp colorspace);
|
||||
|
||||
enum AVColorRange mp_csp_levels_to_avcol_range(enum mp_csp_levels range);
|
||||
|
||||
enum mp_csp mp_csp_guess_colorspace(int width, int height);
|
||||
|
||||
void mp_gen_gamma_map(unsigned char *map, int size, float gamma);
|
||||
|
@ -78,6 +78,7 @@ extern struct vo_driver video_out_gl;
|
||||
extern struct vo_driver video_out_gl3;
|
||||
extern struct vo_driver video_out_null;
|
||||
extern struct vo_driver video_out_image;
|
||||
extern struct vo_driver video_out_lavc;
|
||||
extern struct vo_driver video_out_caca;
|
||||
extern struct vo_driver video_out_direct3d;
|
||||
extern struct vo_driver video_out_direct3d_shaders;
|
||||
@ -116,6 +117,9 @@ const struct vo_driver *video_out_drivers[] =
|
||||
&video_out_null,
|
||||
// should not be auto-selected
|
||||
&video_out_image,
|
||||
#ifdef CONFIG_ENCODING
|
||||
&video_out_lavc,
|
||||
#endif
|
||||
#ifdef CONFIG_X11
|
||||
#ifdef CONFIG_GL
|
||||
&video_out_gl_nosw,
|
||||
@ -283,7 +287,8 @@ void list_video_out(void)
|
||||
|
||||
struct vo *init_best_video_out(struct MPOpts *opts,
|
||||
struct mp_fifo *key_fifo,
|
||||
struct input_ctx *input_ctx)
|
||||
struct input_ctx *input_ctx,
|
||||
struct encode_lavc_context *encode_lavc_ctx)
|
||||
{
|
||||
char **vo_list = opts->video_driver_list;
|
||||
int i;
|
||||
@ -291,6 +296,7 @@ struct vo *init_best_video_out(struct MPOpts *opts,
|
||||
struct vo initial_values = {
|
||||
.opts = opts,
|
||||
.key_fifo = key_fifo,
|
||||
.encode_lavc_ctx = encode_lavc_ctx,
|
||||
.input_ctx = input_ctx,
|
||||
.event_fd = -1,
|
||||
.registered_fd = -1,
|
||||
|
@ -249,6 +249,7 @@ struct vo {
|
||||
struct vo_x11_state *x11;
|
||||
struct vo_w32_state *w32;
|
||||
struct mp_fifo *key_fifo;
|
||||
struct encode_lavc_context *encode_lavc_ctx;
|
||||
struct input_ctx *input_ctx;
|
||||
int event_fd; // check_events() should be called when this has input
|
||||
int registered_fd; // set to event_fd when registered in input system
|
||||
@ -278,7 +279,8 @@ struct vo {
|
||||
|
||||
struct vo *init_best_video_out(struct MPOpts *opts,
|
||||
struct mp_fifo *key_fifo,
|
||||
struct input_ctx *input_ctx);
|
||||
struct input_ctx *input_ctx,
|
||||
struct encode_lavc_context *encode_lavc_ctx);
|
||||
int vo_config(struct vo *vo, uint32_t width, uint32_t height,
|
||||
uint32_t d_width, uint32_t d_height, uint32_t flags,
|
||||
uint32_t format);
|
||||
|
590
libvo/vo_lavc.c
Normal file
590
libvo/vo_lavc.c
Normal file
@ -0,0 +1,590 @@
|
||||
/*
|
||||
* video encoding using libavformat
|
||||
* Copyright (C) 2010 Nicolas George <george@nsup.org>
|
||||
* Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "mpcommon.h"
|
||||
#include "options.h"
|
||||
#include "fmt-conversion.h"
|
||||
#include "libmpcodecs/mp_image.h"
|
||||
#include "libmpcodecs/vfcap.h"
|
||||
#include "subopt-helper.h"
|
||||
#include "talloc.h"
|
||||
#include "video_out.h"
|
||||
|
||||
#include "encode_lavc.h"
|
||||
|
||||
#include "sub/sub.h"
|
||||
#include "libvo/osd.h"
|
||||
|
||||
struct priv {
|
||||
uint8_t *buffer;
|
||||
size_t buffer_size;
|
||||
AVStream *stream;
|
||||
int have_first_packet;
|
||||
|
||||
int harddup;
|
||||
|
||||
double lastpts;
|
||||
int64_t lastipts;
|
||||
int64_t lastframeipts;
|
||||
mp_image_t *lastimg;
|
||||
int lastdisplaycount;
|
||||
|
||||
AVRational worst_time_base;
|
||||
int worst_time_base_is_stream;
|
||||
|
||||
struct osd_state *osd;
|
||||
|
||||
struct mp_csp_details colorspace;
|
||||
};
|
||||
|
||||
static int preinit(struct vo *vo, const char *arg)
|
||||
{
|
||||
struct priv *vc;
|
||||
if (!encode_lavc_available(vo->encode_lavc_ctx)) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR,
|
||||
"vo-lavc: the option -o (output file) must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
vo->priv = talloc_zero(vo, struct priv);
|
||||
vc = vo->priv;
|
||||
vc->harddup = vo->encode_lavc_ctx->options->harddup;
|
||||
vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void draw_image(struct vo *vo, mp_image_t *mpi, double pts);
|
||||
static void uninit(struct vo *vo)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
if (!vc)
|
||||
return;
|
||||
|
||||
if (vc->lastipts >= 0 && vc->stream)
|
||||
draw_image(vo, NULL, MP_NOPTS_VALUE);
|
||||
|
||||
if (vc->lastimg) {
|
||||
// palette hack
|
||||
if (vc->lastimg->imgfmt == IMGFMT_RGB8
|
||||
|| vc->lastimg->imgfmt == IMGFMT_BGR8)
|
||||
vc->lastimg->planes[1] = NULL;
|
||||
free_mp_image(vc->lastimg);
|
||||
vc->lastimg = NULL;
|
||||
}
|
||||
|
||||
vo->priv = NULL;
|
||||
}
|
||||
|
||||
static int config(struct vo *vo, uint32_t width, uint32_t height,
|
||||
uint32_t d_width, uint32_t d_height, uint32_t flags,
|
||||
uint32_t format)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
|
||||
AVRational display_aspect_ratio, image_aspect_ratio;
|
||||
AVRational aspect;
|
||||
|
||||
if (!vc)
|
||||
return -1;
|
||||
|
||||
display_aspect_ratio.num = d_width;
|
||||
display_aspect_ratio.den = d_height;
|
||||
image_aspect_ratio.num = width;
|
||||
image_aspect_ratio.den = height;
|
||||
aspect = av_div_q(display_aspect_ratio, image_aspect_ratio);
|
||||
|
||||
if (vc->stream) {
|
||||
/* NOTE:
|
||||
* in debug builds we get a "comparison between signed and unsigned"
|
||||
* warning here. We choose to ignore that; just because ffmpeg currently
|
||||
* uses a plain 'int' for these struct fields, it doesn't mean it always
|
||||
* will */
|
||||
if (width == vc->stream->codec->width &&
|
||||
height == vc->stream->codec->height) {
|
||||
if (aspect.num != vc->stream->codec->sample_aspect_ratio.num ||
|
||||
aspect.den != vc->stream->codec->sample_aspect_ratio.den) {
|
||||
/* aspect-only changes are not critical */
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: unsupported pixel aspect "
|
||||
"ratio change from %d:%d to %d:%d\n",
|
||||
vc->stream->codec->sample_aspect_ratio.num,
|
||||
vc->stream->codec->sample_aspect_ratio.den,
|
||||
aspect.num, aspect.den);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME Is it possible with raw video? */
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR,
|
||||
"vo-lavc: resolution changes not supported.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
vc->lastipts = MP_NOPTS_VALUE;
|
||||
vc->lastframeipts = MP_NOPTS_VALUE;
|
||||
|
||||
if (pix_fmt == PIX_FMT_NONE)
|
||||
goto error; /* imgfmt2pixfmt already prints something */
|
||||
|
||||
vc->stream = encode_lavc_alloc_stream(vo->encode_lavc_ctx,
|
||||
AVMEDIA_TYPE_VIDEO);
|
||||
vc->stream->sample_aspect_ratio = vc->stream->codec->sample_aspect_ratio =
|
||||
aspect;
|
||||
vc->stream->codec->width = width;
|
||||
vc->stream->codec->height = height;
|
||||
vc->stream->codec->pix_fmt = pix_fmt;
|
||||
|
||||
encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
|
||||
encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
|
||||
vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
|
||||
vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
|
||||
|
||||
if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->stream) < 0)
|
||||
goto error;
|
||||
|
||||
vc->buffer_size = 6 * width * height + 200;
|
||||
if (vc->buffer_size < FF_MIN_BUFFER_SIZE)
|
||||
vc->buffer_size = FF_MIN_BUFFER_SIZE;
|
||||
if (vc->buffer_size < sizeof(AVPicture))
|
||||
vc->buffer_size = sizeof(AVPicture);
|
||||
|
||||
vc->buffer = talloc_size(vc, vc->buffer_size);
|
||||
|
||||
vc->lastimg = alloc_mpi(width, height, format);
|
||||
|
||||
// palette hack
|
||||
if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
|
||||
vc->lastimg->imgfmt == IMGFMT_BGR8)
|
||||
vc->lastimg->planes[1] = talloc_zero_size(vc, 1024);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uninit(vo);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int query_format(struct vo *vo, uint32_t format)
|
||||
{
|
||||
enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
|
||||
|
||||
if (!vo->encode_lavc_ctx)
|
||||
return 0;
|
||||
|
||||
return encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt) ?
|
||||
VFCAP_CSP_SUPPORTED : 0;
|
||||
}
|
||||
|
||||
static void write_packet(struct vo *vo, int size, AVPacket *packet)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
|
||||
if (size < 0) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error encoding\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
packet->stream_index = vc->stream->index;
|
||||
if (packet->pts != AV_NOPTS_VALUE) {
|
||||
packet->pts = av_rescale_q(packet->pts,
|
||||
vc->stream->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
} else {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: codec did not provide pts\n");
|
||||
packet->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
|
||||
vc->stream->time_base);
|
||||
}
|
||||
if (packet->dts != AV_NOPTS_VALUE) {
|
||||
packet->dts = av_rescale_q(packet->dts,
|
||||
vc->stream->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
}
|
||||
if (packet->duration > 0) {
|
||||
packet->duration = av_rescale_q(packet->duration,
|
||||
vc->stream->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
} else {
|
||||
// HACK: libavformat calculates dts wrong if the initial packet
|
||||
// duration is not set, but ONLY if the time base is "high" and if we
|
||||
// have b-frames!
|
||||
if (!packet->duration)
|
||||
if (!vc->have_first_packet)
|
||||
if (vc->stream->codec->has_b_frames
|
||||
|| vc->stream->codec->max_b_frames)
|
||||
if (vc->stream->time_base.num * 1000LL <=
|
||||
vc->stream->time_base.den)
|
||||
packet->duration = FFMAX(1, av_rescale_q(1,
|
||||
vc->stream->codec->time_base, vc->stream->time_base));
|
||||
}
|
||||
|
||||
if (encode_lavc_write_frame(vo->encode_lavc_ctx, packet) < 0) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error writing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
vc->have_first_packet = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
if (encode_lavc_oformat_flags(vo->encode_lavc_ctx) & AVFMT_RAWPICTURE) {
|
||||
if (!frame)
|
||||
return 0;
|
||||
memcpy(vc->buffer, frame, sizeof(AVPicture));
|
||||
mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f\n",
|
||||
frame->pts * (double) vc->stream->codec->time_base.num /
|
||||
(double) vc->stream->codec->time_base.den);
|
||||
packet->size = sizeof(AVPicture);
|
||||
return packet->size;
|
||||
} else {
|
||||
int got_packet = 0;
|
||||
int status = avcodec_encode_video2(vc->stream->codec, packet,
|
||||
frame, &got_packet);
|
||||
int size = (status < 0) ? status : got_packet ? packet->size : 0;
|
||||
|
||||
if (frame)
|
||||
mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f; out size: %d\n",
|
||||
frame->pts * (double) vc->stream->codec->time_base.num /
|
||||
(double) vc->stream->codec->time_base.den, size);
|
||||
|
||||
encode_lavc_write_stats(vo->encode_lavc_ctx, vc->stream);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_osd_to_lastimg_draw_func(void *ctx, int x0,int y0, int w,int h,unsigned char* src, unsigned char *srca, int stride){
|
||||
struct priv *vc = ctx;
|
||||
unsigned char* dst;
|
||||
if(w<=0 || h<=0) return; // nothing to do...
|
||||
// printf("OSD redraw: %d;%d %dx%d \n",x0,y0,w,h);
|
||||
dst=vc->lastimg->planes[0]+
|
||||
vc->lastimg->stride[0]*y0+
|
||||
(vc->lastimg->bpp>>3)*x0;
|
||||
switch(vc->lastimg->imgfmt){
|
||||
case IMGFMT_BGR12:
|
||||
case IMGFMT_RGB12:
|
||||
vo_draw_alpha_rgb12(w, h, src, srca, stride, dst, vc->lastimg->stride[0]);
|
||||
break;
|
||||
case IMGFMT_BGR15:
|
||||
case IMGFMT_RGB15:
|
||||
vo_draw_alpha_rgb15(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||||
break;
|
||||
case IMGFMT_BGR16:
|
||||
case IMGFMT_RGB16:
|
||||
vo_draw_alpha_rgb16(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||||
break;
|
||||
case IMGFMT_BGR24:
|
||||
case IMGFMT_RGB24:
|
||||
vo_draw_alpha_rgb24(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||||
break;
|
||||
case IMGFMT_BGR32:
|
||||
case IMGFMT_RGB32:
|
||||
vo_draw_alpha_rgb32(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||||
break;
|
||||
case IMGFMT_YV12:
|
||||
case IMGFMT_I420:
|
||||
case IMGFMT_IYUV:
|
||||
case IMGFMT_YVU9:
|
||||
case IMGFMT_IF09:
|
||||
case IMGFMT_Y800:
|
||||
case IMGFMT_Y8:
|
||||
vo_draw_alpha_yv12(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||||
break;
|
||||
case IMGFMT_YUY2:
|
||||
vo_draw_alpha_yuy2(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||||
break;
|
||||
case IMGFMT_UYVY:
|
||||
vo_draw_alpha_yuy2(w,h,src,srca,stride,dst+1,vc->lastimg->stride[0]);
|
||||
break;
|
||||
default:
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: tried to draw OSD on an usnupported pixel format\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void add_osd_to_lastimg(struct vo *vo)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
if(vc->osd) {
|
||||
osd_draw_text(vc->osd, vc->lastimg->w, vc->lastimg->h, add_osd_to_lastimg_draw_func, vc);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
struct encode_lavc_context *ectx = vo->encode_lavc_ctx;
|
||||
int i, size;
|
||||
AVFrame *frame;
|
||||
AVCodecContext *avc;
|
||||
int64_t frameipts;
|
||||
double nextpts;
|
||||
|
||||
if (!vc)
|
||||
return;
|
||||
if (!encode_lavc_start(ectx)) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: skipped initial video frame (probably because audio is not there yet)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
avc = vc->stream->codec;
|
||||
|
||||
if (vc->worst_time_base.den == 0) {
|
||||
//if (avc->time_base.num / avc->time_base.den >= vc->stream->time_base.num / vc->stream->time_base.den)
|
||||
if (avc->time_base.num * (double) vc->stream->time_base.den >=
|
||||
vc->stream->time_base.num * (double) avc->time_base.den) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_V, "vo-lavc: NOTE: using codec time base "
|
||||
"(%d/%d) for frame dropping; the stream base (%d/%d) is "
|
||||
"not worse.\n", (int)avc->time_base.num,
|
||||
(int)avc->time_base.den, (int)vc->stream->time_base.num,
|
||||
(int)vc->stream->time_base.den);
|
||||
vc->worst_time_base = avc->time_base;
|
||||
vc->worst_time_base_is_stream = 0;
|
||||
} else {
|
||||
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: not using codec time "
|
||||
"base (%d/%d) for frame dropping; the stream base (%d/%d) "
|
||||
"is worse.\n", (int)avc->time_base.num,
|
||||
(int)avc->time_base.den, (int)vc->stream->time_base.num,
|
||||
(int)vc->stream->time_base.den);
|
||||
vc->worst_time_base = vc->stream->time_base;
|
||||
vc->worst_time_base_is_stream = 1;
|
||||
}
|
||||
|
||||
// NOTE: we use the following "axiom" of av_rescale_q:
|
||||
// if time base A is worse than time base B, then
|
||||
// av_rescale_q(av_rescale_q(x, A, B), B, A) == x
|
||||
// this can be proven as long as av_rescale_q rounds to nearest, which
|
||||
// it currently does
|
||||
|
||||
// av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B"
|
||||
// and:
|
||||
// av_rescale_q(av_rescale_q(x, A, B), B, A) * A
|
||||
// == "round av_rescale_q(x, A, B)*B to nearest multiple of A"
|
||||
// == "round 'round x*A to nearest multiple of B' to nearest multiple of A"
|
||||
//
|
||||
// assume this fails. Then there is a value of x*A, for which the
|
||||
// nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[.
|
||||
// Absurd, as this range MUST contain at least one multiple of B.
|
||||
}
|
||||
|
||||
double timeunit = (double)vc->worst_time_base.num / vc->worst_time_base.den;
|
||||
|
||||
// fix the discontinuity pts offset
|
||||
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
|
||||
nextpts = pts;
|
||||
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
|
||||
}
|
||||
|
||||
// set next allowed output pts value
|
||||
nextpts = pts + ectx->discontinuity_pts_offset + timeunit;
|
||||
if (nextpts > ectx->next_in_pts)
|
||||
ectx->next_in_pts = nextpts;
|
||||
|
||||
// vc->lastipts is MP_NOPTS_VALUE, or the start time of vc->lastframe
|
||||
if (mpi) {
|
||||
if (pts == MP_NOPTS_VALUE) {
|
||||
// NOTE: this even applies to ectx->options->copyts!
|
||||
if (vc->lastipts == MP_NOPTS_VALUE)
|
||||
frameipts = 0;
|
||||
else
|
||||
frameipts = vc->lastipts + 1;
|
||||
|
||||
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: pts was missing, using %d - "
|
||||
"consider using -ofps or -vf fixpts\n", (int) frameipts);
|
||||
|
||||
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
|
||||
ectx->last_video_in_pts += timeunit;
|
||||
|
||||
// calculate backwards to set vc->lastpts matchingly
|
||||
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
|
||||
} else {
|
||||
double outpts;
|
||||
if (ectx->options->rawts)
|
||||
outpts = pts;
|
||||
else if (ectx->options->copyts)
|
||||
outpts = pts + ectx->discontinuity_pts_offset;
|
||||
else {
|
||||
double duration = 0;
|
||||
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
|
||||
duration = pts - ectx->last_video_in_pts;
|
||||
if (duration < 0)
|
||||
duration = timeunit; // XXX warn about discontinuity?
|
||||
outpts = vc->lastpts + duration;
|
||||
if (ectx->audio_pts_offset != MP_NOPTS_VALUE) {
|
||||
double adj = outpts - pts - ectx->audio_pts_offset;
|
||||
adj = FFMIN(adj, duration * 0.1);
|
||||
adj = FFMAX(adj, -duration * 0.1);
|
||||
outpts -= adj;
|
||||
}
|
||||
}
|
||||
vc->lastpts = outpts;
|
||||
ectx->last_video_in_pts = pts;
|
||||
frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->stream))
|
||||
/ timeunit + 0.5);
|
||||
}
|
||||
} else {
|
||||
if (vc->lastipts == MP_NOPTS_VALUE)
|
||||
frameipts = 0;
|
||||
else
|
||||
frameipts = vc->lastipts + 1;
|
||||
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
|
||||
}
|
||||
|
||||
// never-drop mode
|
||||
if (ectx->options->neverdrop && frameipts <= vc->lastipts) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: -oneverdrop increased pts by %d\n",
|
||||
(int) (vc->lastipts - frameipts + 1));
|
||||
frameipts = vc->lastipts + 1;
|
||||
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
|
||||
}
|
||||
|
||||
if (vc->lastipts != MP_NOPTS_VALUE) {
|
||||
frame = avcodec_alloc_frame();
|
||||
|
||||
// we have a valid image in lastimg
|
||||
while (vc->lastipts < frameipts) {
|
||||
int64_t thisduration = vc->harddup ? 1 : (frameipts - vc->lastipts);
|
||||
AVPacket packet;
|
||||
|
||||
avcodec_get_frame_defaults(frame);
|
||||
|
||||
// this is a nop, unless the worst time base is the STREAM time base
|
||||
frame->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
|
||||
avc->time_base);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
frame->data[i] = vc->lastimg->planes[i];
|
||||
frame->linesize[i] = vc->lastimg->stride[i];
|
||||
}
|
||||
frame->quality = avc->global_quality;
|
||||
|
||||
av_init_packet(&packet);
|
||||
packet.data = vc->buffer;
|
||||
packet.size = vc->buffer_size;
|
||||
size = encode_video(vo, frame, &packet);
|
||||
write_packet(vo, size, &packet);
|
||||
|
||||
vc->lastipts += thisduration;
|
||||
++vc->lastdisplaycount;
|
||||
}
|
||||
|
||||
av_free(frame);
|
||||
}
|
||||
|
||||
if (!mpi) {
|
||||
// finish encoding
|
||||
do {
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
packet.data = vc->buffer;
|
||||
packet.size = vc->buffer_size;
|
||||
size = encode_video(vo, NULL, &packet);
|
||||
write_packet(vo, size, &packet);
|
||||
} while (size > 0);
|
||||
} else {
|
||||
if (frameipts >= vc->lastframeipts) {
|
||||
if (vc->lastframeipts != MP_NOPTS_VALUE && vc->lastdisplaycount != 1)
|
||||
mp_msg(MSGT_ENCODE, MSGL_INFO,
|
||||
"vo-lavc: Frame at pts %d got displayed %d times\n",
|
||||
(int) vc->lastframeipts, vc->lastdisplaycount);
|
||||
copy_mpi(vc->lastimg, mpi);
|
||||
add_osd_to_lastimg(vo);
|
||||
|
||||
// palette hack
|
||||
if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
|
||||
vc->lastimg->imgfmt == IMGFMT_BGR8)
|
||||
memcpy(vc->lastimg->planes[1], mpi->planes[1], 1024);
|
||||
|
||||
vc->lastframeipts = vc->lastipts = frameipts;
|
||||
if (ectx->options->rawts && vc->lastipts < 0) {
|
||||
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: why does this happen? DEBUG THIS! vc->lastipts = %lld\n", (long long) vc->lastipts);
|
||||
vc->lastipts = -1;
|
||||
}
|
||||
vc->lastdisplaycount = 0;
|
||||
} else
|
||||
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: Frame at pts %d got dropped "
|
||||
"entirely because pts went backwards\n", (int) frameipts);
|
||||
}
|
||||
}
|
||||
|
||||
static int control(struct vo *vo, uint32_t request, void *data)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
switch (request) {
|
||||
case VOCTRL_QUERY_FORMAT:
|
||||
return query_format(vo, *((uint32_t *)data));
|
||||
case VOCTRL_DRAW_IMAGE:
|
||||
draw_image(vo, (mp_image_t *)data, vo->next_pts);
|
||||
return 0;
|
||||
case VOCTRL_SET_YUV_COLORSPACE:
|
||||
vc->colorspace = *(struct mp_csp_details *)data;
|
||||
if (vc->stream) {
|
||||
encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
|
||||
encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
|
||||
vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
|
||||
vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
|
||||
}
|
||||
return 1;
|
||||
case VOCTRL_GET_YUV_COLORSPACE:
|
||||
*(struct mp_csp_details *)data = vc->colorspace;
|
||||
return 1;
|
||||
}
|
||||
return VO_NOTIMPL;
|
||||
}
|
||||
|
||||
static void draw_osd(struct vo *vo, struct osd_state *osd)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
vc->osd = osd;
|
||||
if(vc->lastimg)
|
||||
osd_update(vc->osd, vc->lastimg->w, vc->lastimg->h);
|
||||
}
|
||||
|
||||
static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
|
||||
{
|
||||
}
|
||||
|
||||
static void check_events(struct vo *vo)
|
||||
{
|
||||
}
|
||||
|
||||
const struct vo_driver video_out_lavc = {
|
||||
.is_new = true,
|
||||
.buffer_frames = false,
|
||||
.info = &(const struct vo_info_s){
|
||||
"video encoding using libavcodec",
|
||||
"lavc",
|
||||
"Nicolas George <george@nsup.org>, Rudolf Polzer <divVerent@xonotic.org>",
|
||||
""
|
||||
},
|
||||
.preinit = preinit,
|
||||
.config = config,
|
||||
.control = control,
|
||||
.uninit = uninit,
|
||||
.check_events = check_events,
|
||||
.draw_osd = draw_osd,
|
||||
.flip_page_timed = flip_page_timed,
|
||||
};
|
||||
|
||||
// vim: sw=4 ts=4 et
|
@ -252,6 +252,8 @@ typedef struct MPContext {
|
||||
struct screenshot_ctx *screenshot_ctx;
|
||||
|
||||
char *track_layout_hash;
|
||||
|
||||
struct encode_lavc_context *encode_lavc_ctx;
|
||||
} MPContext;
|
||||
|
||||
|
||||
|
2
mp_msg.h
2
mp_msg.h
@ -79,6 +79,8 @@ extern int verbose;
|
||||
#define MSGT_STREAM 20 // stream.c
|
||||
#define MSGT_CACHE 21 // cache2.c
|
||||
|
||||
#define MSGT_ENCODE 22 // now encode_lavc.c
|
||||
|
||||
#define MSGT_XACODEC 23 // XAnim codecs
|
||||
|
||||
#define MSGT_TV 24 // TV input subsystem
|
||||
|
91
mplayer.c
91
mplayer.c
@ -98,6 +98,8 @@
|
||||
|
||||
#include "input/input.h"
|
||||
|
||||
#include "encode.h"
|
||||
|
||||
int slave_mode = 0;
|
||||
int enable_mouse_movements = 0;
|
||||
float start_volume = -1;
|
||||
@ -702,6 +704,14 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
|
||||
static void exit_player(struct MPContext *mpctx, enum exit_reason how, int rc)
|
||||
{
|
||||
uninit_player(mpctx, INITIALIZED_ALL);
|
||||
|
||||
#ifdef CONFIG_ENCODING
|
||||
encode_lavc_finish(mpctx->encode_lavc_ctx);
|
||||
encode_lavc_free(mpctx->encode_lavc_ctx);
|
||||
#endif
|
||||
|
||||
mpctx->encode_lavc_ctx = NULL;
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
timeEndPeriod(1);
|
||||
#endif
|
||||
@ -1243,9 +1253,27 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
||||
saddf(line, width, " ct:%7.3f", mpctx->total_avsync_change);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ENCODING
|
||||
float position = (get_current_time(mpctx) - opts->seek_to_sec) /
|
||||
(get_time_length(mpctx) - opts->seek_to_sec);
|
||||
if (end_at.type == END_AT_TIME)
|
||||
position = max(position, (get_current_time(mpctx) - opts->seek_to_sec)
|
||||
/ (end_at.pos - opts->seek_to_sec));
|
||||
if (play_n_frames_mf)
|
||||
position = max(position,
|
||||
1.0 - play_n_frames / (double) play_n_frames_mf);
|
||||
char lavcbuf[80];
|
||||
if (encode_lavc_getstatus(mpctx->encode_lavc_ctx, lavcbuf, sizeof(lavcbuf),
|
||||
position, get_current_time(mpctx) - opts->seek_to_sec) >= 0) {
|
||||
// encoding stats
|
||||
saddf(line, width, "%s ", lavcbuf);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// VO stats
|
||||
if (sh_video && drop_frame_cnt)
|
||||
saddf(line, width, " D: %d", drop_frame_cnt);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STREAM_CACHE
|
||||
// cache stats
|
||||
@ -1623,6 +1651,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
}
|
||||
if (!ao->initialized) {
|
||||
ao->buffersize = opts->ao_buffersize;
|
||||
ao->encode_lavc_ctx = mpctx->encode_lavc_ctx;
|
||||
ao_init(ao, opts->audio_driver_list);
|
||||
if (!ao->initialized) {
|
||||
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
|
||||
@ -2308,8 +2337,10 @@ int reinit_video_chain(struct MPContext *mpctx)
|
||||
double ar = -1.0;
|
||||
//================== Init VIDEO (codec & libvo) ==========================
|
||||
if (!opts->fixed_vo || !(mpctx->initialized_flags & INITIALIZED_VO)) {
|
||||
if (!(mpctx->video_out = init_best_video_out(opts, mpctx->key_fifo,
|
||||
mpctx->input))) {
|
||||
mpctx->video_out
|
||||
= init_best_video_out(opts, mpctx->key_fifo, mpctx->input,
|
||||
mpctx->encode_lavc_ctx);
|
||||
if (!mpctx->video_out) {
|
||||
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Error opening/initializing "
|
||||
"the selected video_out (-vo) device.\n");
|
||||
goto err_out;
|
||||
@ -2672,6 +2703,10 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
|
||||
mpctx->hrseek_framedrop = false;
|
||||
mpctx->total_avsync_change = 0;
|
||||
drop_frame_cnt = 0;
|
||||
|
||||
#ifdef CONFIG_ENCODING
|
||||
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool timeline_set_part(struct MPContext *mpctx, int i, bool force)
|
||||
@ -3073,6 +3108,13 @@ static void run_playloop(struct MPContext *mpctx)
|
||||
double sleeptime = WAKEUP_PERIOD;
|
||||
bool was_restart = mpctx->restart_playback;
|
||||
|
||||
#ifdef CONFIG_ENCODING
|
||||
if (encode_lavc_didfail(mpctx->encode_lavc_ctx)) {
|
||||
mpctx->stop_play = PT_QUIT;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add tracks that were added by the demuxer later (e.g. MPEG)
|
||||
if (!mpctx->timeline && mpctx->demuxer)
|
||||
add_demuxer_tracks(mpctx, mpctx->demuxer);
|
||||
@ -3756,6 +3798,10 @@ static void play_current_file(struct MPContext *mpctx)
|
||||
if (!mpctx->filename)
|
||||
goto terminate_playback;
|
||||
|
||||
#ifdef CONFIG_ENCODING
|
||||
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
|
||||
#endif
|
||||
|
||||
m_config_enter_file_local(mpctx->mconfig);
|
||||
|
||||
load_per_protocol_config(mpctx->mconfig, mpctx->filename);
|
||||
@ -3946,6 +3992,13 @@ goto_enable_cache:
|
||||
goto terminate_playback;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ENCODING
|
||||
if (mpctx->encode_lavc_ctx && mpctx->sh_video)
|
||||
encode_lavc_expect_stream(mpctx->encode_lavc_ctx, AVMEDIA_TYPE_VIDEO);
|
||||
if (mpctx->encode_lavc_ctx && mpctx->sh_audio)
|
||||
encode_lavc_expect_stream(mpctx->encode_lavc_ctx, AVMEDIA_TYPE_AUDIO);
|
||||
#endif
|
||||
|
||||
if (opts->playing_msg) {
|
||||
char *msg = property_expand_string(mpctx, opts->playing_msg);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", msg);
|
||||
@ -3982,6 +4035,8 @@ goto_enable_cache:
|
||||
//TODO: add desired (stream-based) sections here
|
||||
if (mpctx->master_demuxer->type == DEMUXER_TYPE_TV)
|
||||
mp_input_set_section(mpctx->input, "tv", 0);
|
||||
if (mpctx->encode_lavc_ctx)
|
||||
mp_input_set_section(mpctx->input, "encode", MP_INPUT_NO_DEFAULT_SECTION);
|
||||
|
||||
//==================== START PLAYING =======================
|
||||
|
||||
@ -4068,7 +4123,7 @@ terminate_playback: // don't jump here after ao/vo/getch initialization!
|
||||
int uninitialize_parts = INITIALIZED_ALL;
|
||||
if (opts->fixed_vo)
|
||||
uninitialize_parts -= INITIALIZED_VO;
|
||||
if (opts->gapless_audio && mpctx->stop_play == AT_END_OF_FILE)
|
||||
if (opts->gapless_audio && mpctx->stop_play == AT_END_OF_FILE || mpctx->encode_lavc_ctx)
|
||||
uninitialize_parts -= INITIALIZED_AO;
|
||||
uninit_player(mpctx, uninitialize_parts);
|
||||
|
||||
@ -4189,6 +4244,10 @@ static bool handle_help_options(struct MPContext *mpctx)
|
||||
property_print_help();
|
||||
opt_exit = 1;
|
||||
}
|
||||
#ifdef CONFIG_ENCODING
|
||||
if (encode_lavc_showhelp(&mpctx->opts))
|
||||
opt_exit = 1;
|
||||
#endif
|
||||
return opt_exit;
|
||||
}
|
||||
|
||||
@ -4325,6 +4384,32 @@ int main(int argc, char *argv[])
|
||||
set_priority();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ENCODING
|
||||
if (opts->encode_output.file) {
|
||||
mpctx->encode_lavc_ctx = encode_lavc_init(&opts->encode_output);
|
||||
if(!mpctx->encode_lavc_ctx) {
|
||||
mp_msg(MSGT_VO, MSGL_INFO, "Encoding initialization failed.");
|
||||
exit_player(mpctx, EXIT_ERROR, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->encode_output.file) {
|
||||
m_config_set_option0(mpctx->mconfig, "vo", "lavc");
|
||||
m_config_set_option0(mpctx->mconfig, "ao", "lavc");
|
||||
m_config_set_option0(mpctx->mconfig, "fixed-vo", "yes");
|
||||
m_config_set_option0(mpctx->mconfig, "gapless-audio", "yes");
|
||||
m_config_set_option0(mpctx->mconfig, "untimed", "yes");
|
||||
|
||||
// default osd level 0
|
||||
if (opts->osd_level < 0)
|
||||
m_config_set_option0(mpctx->mconfig, "osdlevel", "0");
|
||||
} else {
|
||||
// default osd level 1
|
||||
if (opts->osd_level < 0)
|
||||
m_config_set_option0(mpctx->mconfig, "osdlevel", "1");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ASS
|
||||
mpctx->ass_library = mp_ass_init(opts);
|
||||
#endif
|
||||
|
18
options.h
18
options.h
@ -159,6 +159,24 @@ typedef struct MPOpts {
|
||||
int use_ar; // apple remote
|
||||
int default_bindings;
|
||||
} input;
|
||||
|
||||
struct encode_output_conf {
|
||||
char *file;
|
||||
char *format;
|
||||
char **fopts;
|
||||
float fps;
|
||||
char *vcodec;
|
||||
char **vopts;
|
||||
char *acodec;
|
||||
char **aopts;
|
||||
int harddup;
|
||||
float voffset;
|
||||
float aoffset;
|
||||
int copyts;
|
||||
int rawts;
|
||||
int autofps;
|
||||
int neverdrop;
|
||||
} encode_output;
|
||||
} MPOpts;
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user