aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Rudolf Polzer <divverent@xonotic.org>2012-09-14 17:51:26 +0200
committerGravatar wm4 <wm4@nowhere>2012-09-18 21:08:20 +0200
commitf5b8b6ac126d8cef3860db16d3db8e72507a2258 (patch)
treec86a6160cee076d3a632e4d3247e566e8c064390
parent5617bf483e563aae22100c0ca1d8182f71d4f82d (diff)
encode: video encoding now supported using mencoder-like options
-rw-r--r--DOCS/encoding.rst141
-rw-r--r--DOCS/man/en/changes.rst1
-rw-r--r--DOCS/man/en/encode.rst134
-rw-r--r--DOCS/man/en/mplayer-old.1196
-rw-r--r--DOCS/man/en/mplayer.rst2
-rw-r--r--Makefile1
-rw-r--r--cfg-mplayer.h18
-rwxr-xr-xconfigure14
-rw-r--r--defaultopts.c2
-rw-r--r--encode.h21
-rw-r--r--encode_lavc.c1062
-rw-r--r--encode_lavc.h93
-rw-r--r--etc/encoding-example-profiles.conf189
-rw-r--r--etc/input.conf2
-rw-r--r--libao2/ao_lavc.c588
-rw-r--r--libao2/audio_out.c4
-rw-r--r--libao2/audio_out.h1
-rw-r--r--libvo/csputils.c47
-rw-r--r--libvo/csputils.h4
-rw-r--r--libvo/video_out.c8
-rw-r--r--libvo/video_out.h4
-rw-r--r--libvo/vo_lavc.c590
-rw-r--r--mp_core.h2
-rw-r--r--mp_msg.h2
-rw-r--r--mplayer.c97
-rw-r--r--options.h18
26 files changed, 3208 insertions, 33 deletions
diff --git a/DOCS/encoding.rst b/DOCS/encoding.rst
new file mode 100644
index 0000000000..67ad19bae6
--- /dev/null
+++ b/DOCS/encoding.rst
@@ -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
diff --git a/DOCS/man/en/changes.rst b/DOCS/man/en/changes.rst
index e4f331e807..c4fc3a0812 100644
--- a/DOCS/man/en/changes.rst
+++ b/DOCS/man/en/changes.rst
@@ -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
diff --git a/DOCS/man/en/encode.rst b/DOCS/man/en/encode.rst
new file mode 100644
index 0000000000..7ebd8e6c01
--- /dev/null
+++ b/DOCS/man/en/encode.rst
@@ -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.
diff --git a/DOCS/man/en/mplayer-old.1 b/DOCS/man/en/mplayer-old.1
index 477244cc58..92592c7f27 100644
--- a/DOCS/man/en/mplayer-old.1
+++ b/DOCS/man/en/mplayer-old.1
@@ -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
diff --git a/DOCS/man/en/mplayer.rst b/DOCS/man/en/mplayer.rst
index 8485ee7064..89b03737c3 100644
--- a/DOCS/man/en/mplayer.rst
+++ b/DOCS/man/en/mplayer.rst
@@ -407,6 +407,8 @@ OPTIONS
.. include:: vf.rst
+.. include:: encode.rst
+
Taking screenshots
==================
diff --git a/Makefile b/Makefile
index 4ec75eda73..c93fa6b31c 100644
--- a/Makefile
+++ b/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
diff --git a/cfg-mplayer.h b/cfg-mplayer.h
index 529df35caf..8278945c3d 100644
--- a/cfg-mplayer.h
+++ b/cfg-mplayer.h
@@ -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}
};
diff --git a/configure b/configure
index b77fd13076..8526a5879b 100755
--- a/configure
+++ b/configure
@@ -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
diff --git a/defaultopts.c b/defaultopts.c
index 672dbd5f5c..98abc00831 100644
--- a/defaultopts.c
+++ b/defaultopts.c
@@ -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,
diff --git a/encode.h b/encode.h
new file mode 100644
index 0000000000..f0f65f14c5
--- /dev/null
+++ b/encode.h
@@ -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
diff --git a/encode_lavc.c b/encode_lavc.c
new file mode 100644
index 0000000000..f8e04f99bc
--- /dev/null
+++ b/encode_lavc.c
@@ -0,0 +1,1062 @@
+/*
+ * Raw video muxing 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 "encode_lavc.h"
+#include "mp_msg.h"
+#include "libmpcodecs/vfcap.h"
+#include "options.h"
+#include "osdep/timer.h"
+#include "libvo/video_out.h"
+#include "talloc.h"
+#include "stream/stream.h"
+
+static int set_to_avdictionary(void *ctx, AVDictionary **dictp, void *octx,
+ const char *str, const char *key_val_sep,
+ const char *pairs_sep)
+{
+ int good = 0;
+ int errorcode = 0;
+ const AVOption *o;
+
+ while (*str) {
+ char *key_ = av_get_token(&str, key_val_sep);
+ char *val_;
+ char *key, *val;
+ char valuebuf[1024];
+
+ if (*key_ && strspn(str, key_val_sep)) {
+ str++;
+ val_ = av_get_token(&str, pairs_sep);
+ } else {
+ av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value "
+ "separator found after key '%s'\n", key_);
+ av_free(key_);
+ if (!errorcode)
+ errorcode = AVERROR(EINVAL);
+ if (*str)
+ ++str;
+ continue;
+ }
+
+ key = key_;
+ val = val_;
+
+ if(!strcmp(key, "qscale") && val[0] != '+' && val[0] != '-' && !av_opt_find(octx, key, NULL, 0, AV_OPT_SEARCH_CHILDREN))
+ {
+ // hack: support "qscale" key as virtual "global_quality" key that multiplies by QP2LAMBDA
+ key = "global_quality";
+ snprintf(valuebuf, sizeof(valuebuf), "(%s)*QP2LAMBDA", val);
+ valuebuf[sizeof(valuebuf)-1] = 0;
+ val = valuebuf;
+ }
+
+ av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n",
+ val, key);
+
+ if((o = av_opt_find(octx, key, NULL, 0, AV_OPT_SEARCH_CHILDREN))) {
+ if (av_dict_set(dictp, key, *val ? val : NULL, (o->type == FF_OPT_TYPE_FLAGS && (val[0] == '+' || val[0] == '-')) ? AV_DICT_APPEND : 0) >= 0)
+ ++good;
+ else
+ errorcode = AVERROR(EINVAL);
+ } else {
+ errorcode = AVERROR(ENOENT);
+ }
+
+ av_free(key_);
+ av_free(val_);
+
+ if (*str)
+ ++str;
+ }
+ return errorcode ? errorcode : good;
+}
+
+static bool value_has_flag(const char *value, const char *flag)
+{
+ bool state = true;
+ bool ret = false;
+ while(*value)
+ {
+ size_t l = strcspn(value, "+-");
+ if(l == 0)
+ {
+ state = (*value == '+');
+ ++value;
+ }
+ else
+ {
+ if(l == strlen(flag))
+ if(!memcmp(value, flag, l))
+ ret = state;
+ value += l;
+ }
+ }
+ return ret;
+}
+
+#define CHECK_FAIL(ctx, val) \
+ if(ctx && (ctx->failed || ctx->finished)) { \
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "Called a function on a %s encoding context. Bailing out.\n", ctx->failed ? "failed" : "finished"); \
+ return val; \
+ }
+
+int encode_lavc_available(struct encode_lavc_context *ctx)
+{
+ CHECK_FAIL(ctx, 0);
+ return ctx && ctx->avc;
+}
+
+int encode_lavc_oformat_flags(struct encode_lavc_context *ctx)
+{
+ CHECK_FAIL(ctx, 0);
+ return ctx->avc ? ctx->avc->oformat->flags : 0;
+}
+
+struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options)
+{
+ struct encode_lavc_context *ctx;
+
+ ctx = talloc_zero(NULL, struct encode_lavc_context);
+ encode_lavc_discontinuity(ctx);
+ ctx->options = options;
+
+ ctx->avc = avformat_alloc_context();
+
+ if (ctx->options->format) {
+ char *tok;
+ const char *in = ctx->options->format;
+ while (*in) {
+ tok = av_get_token(&in, ",");
+ ctx->vc = avcodec_find_encoder_by_name(tok);
+ ctx->avc->oformat = av_guess_format(tok, ctx->options->file, NULL);
+ av_free(tok);
+ if (ctx->avc->oformat)
+ ctx->vc = NULL;
+ if (ctx->vc)
+ break;
+ if (*in)
+ ++in;
+ }
+ } else {
+ ctx->avc->oformat = av_guess_format(NULL, ctx->options->file, NULL);
+ }
+
+ if (!ctx->avc->oformat) {
+ encode_lavc_fail(ctx, "encode-lavc: format not found\n");
+ return NULL;
+ }
+
+ av_strlcpy(ctx->avc->filename, ctx->options->file,
+ sizeof(ctx->avc->filename));
+
+ ctx->foptions = NULL;
+ if (ctx->options->fopts) {
+ char **p;
+ for (p = ctx->options->fopts; *p; ++p) {
+ if (set_to_avdictionary(ctx->avc, &ctx->foptions, ctx->avc, *p, "=", "")
+ <= 0)
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "encode-lavc: could not set option %s\n", *p);
+ }
+ }
+
+ if (ctx->options->vcodec) {
+ char *tok;
+ const char *in = ctx->options->vcodec;
+ while (*in) {
+ tok = av_get_token(&in, ",");
+ ctx->vc = avcodec_find_encoder_by_name(tok);
+ av_free(tok);
+ if (ctx->vc && ctx->vc->type != AVMEDIA_TYPE_VIDEO)
+ ctx->vc = NULL;
+ if (ctx->vc)
+ break;
+ if (*in)
+ ++in;
+ }
+ } else
+ ctx->vc = avcodec_find_encoder(av_guess_codec(ctx->avc->oformat, NULL,
+ ctx->avc->filename, NULL, AVMEDIA_TYPE_VIDEO));
+
+ if (ctx->options->acodec) {
+ char *tok;
+ const char *in = ctx->options->acodec;
+ while (*in) {
+ tok = av_get_token(&in, ",");
+ ctx->ac = avcodec_find_encoder_by_name(tok);
+ av_free(tok);
+ if (ctx->ac && ctx->ac->type != AVMEDIA_TYPE_AUDIO)
+ ctx->ac = NULL;
+ if (ctx->ac)
+ break;
+ if (*in)
+ ++in;
+ }
+ } else
+ ctx->ac = avcodec_find_encoder(av_guess_codec(ctx->avc->oformat, NULL,
+ ctx->avc->filename, NULL, AVMEDIA_TYPE_AUDIO));
+
+ if (!ctx->vc && !ctx->ac) {
+ encode_lavc_fail(ctx, "encode-lavc: neither audio nor video codec was found\n");
+ return NULL;
+ }
+
+ /* taken from ffmpeg unchanged
+ * TODO turn this into an option if anyone needs this */
+
+ ctx->avc->max_delay = 0.7 * AV_TIME_BASE;
+
+ ctx->abytes = 0;
+ ctx->vbytes = 0;
+ ctx->frames = 0;
+
+ return ctx;
+}
+
+int encode_lavc_start(struct encode_lavc_context *ctx)
+{
+ AVDictionaryEntry *de;
+ unsigned i;
+
+ if (ctx->header_written < 0)
+ return 0;
+ if (ctx->header_written > 0)
+ return 1;
+
+ CHECK_FAIL(ctx, 0);
+
+ if (ctx->expect_video) {
+ for (i = 0; i < ctx->avc->nb_streams; ++i)
+ if (ctx->avc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ break;
+ if (i >= ctx->avc->nb_streams)
+ return 0;
+ }
+ if (ctx->expect_audio) {
+ for (i = 0; i < ctx->avc->nb_streams; ++i)
+ if (ctx->avc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ break;
+ if (i >= ctx->avc->nb_streams)
+ return 0;
+ }
+
+ ctx->header_written = -1;
+
+ if (!(ctx->avc->oformat->flags & AVFMT_NOFILE)) {
+ if (avio_open(&ctx->avc->pb, ctx->avc->filename, AVIO_FLAG_WRITE) < 0) {
+ encode_lavc_fail(ctx, "encode-lavc: could not open '%s'\n", ctx->avc->filename);
+ return 0;
+ }
+ }
+
+ ctx->t0 = GetTimerMS();
+
+ if (avformat_write_header(ctx->avc, &ctx->foptions) < 0) {
+ encode_lavc_fail(ctx, "encode-lavc: could not write header\n");
+ return 0;
+ }
+
+ for (de = NULL; (de = av_dict_get(ctx->foptions, "", de,
+ AV_DICT_IGNORE_SUFFIX));)
+ av_log(ctx->avc, AV_LOG_ERROR, "Key '%s' not found.\n", de->key);
+ av_dict_free(&ctx->foptions);
+
+ ctx->header_written = 1;
+ return 1;
+}
+
+void encode_lavc_free(struct encode_lavc_context *ctx)
+{
+ if (!ctx)
+ return;
+
+ if (!ctx->finished)
+ encode_lavc_fail(ctx, "called encode_lavc_free without encode_lavc_finish\n");
+
+ talloc_free(ctx);
+}
+
+void encode_lavc_finish(struct encode_lavc_context *ctx)
+{
+ unsigned i;
+
+ if (!ctx)
+ return;
+
+ if (ctx->finished)
+ return;
+
+ if (ctx->avc) {
+ if (ctx->header_written > 0)
+ av_write_trailer(ctx->avc); // this is allowed to fail
+
+ for (i = 0; i < ctx->avc->nb_streams; i++) {
+ switch (ctx->avc->streams[i]->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (ctx->twopass_bytebuffer_v) {
+ char *stats = ctx->avc->streams[i]->codec->stats_out;
+ if (stats)
+ stream_write_buffer(ctx->twopass_bytebuffer_v,
+ stats, strlen(stats));
+ }
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (ctx->twopass_bytebuffer_a) {
+ char *stats = ctx->avc->streams[i]->codec->stats_out;
+ if (stats)
+ stream_write_buffer(ctx->twopass_bytebuffer_a,
+ stats, strlen(stats));
+ }
+ break;
+ default:
+ break;
+ }
+ avcodec_close(ctx->avc->streams[i]->codec);
+ talloc_free(ctx->avc->streams[i]->codec->stats_in);
+ av_free(ctx->avc->streams[i]->codec);
+ av_free(ctx->avc->streams[i]->info);
+ av_free(ctx->avc->streams[i]);
+ }
+
+ if (ctx->twopass_bytebuffer_v) {
+ free_stream(ctx->twopass_bytebuffer_v);
+ ctx->twopass_bytebuffer_v = NULL;
+ }
+
+ if (ctx->twopass_bytebuffer_a) {
+ free_stream(ctx->twopass_bytebuffer_a);
+ ctx->twopass_bytebuffer_a = NULL;
+ }
+
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: encoded %lld bytes\n",
+ ctx->vbytes);
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "ao-lavc: encoded %lld bytes\n",
+ ctx->abytes);
+ if (ctx->avc->pb) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "encode-lavc: muxing overhead %lld bytes\n",
+ (long long) (avio_size(ctx->avc->pb) - ctx->vbytes
+ - ctx->abytes));
+ avio_close(ctx->avc->pb);
+ }
+
+ av_free(ctx->avc);
+ }
+
+ ctx->finished = true;
+}
+
+static void encode_2pass_prepare(struct encode_lavc_context *ctx, AVDictionary **dictp, void *octx,
+ AVStream *stream, struct stream **bytebuf,
+ const char *prefix)
+{
+ if (!*bytebuf) {
+ char buf[sizeof(ctx->avc->filename) + 12];
+ AVDictionaryEntry *de = av_dict_get(ctx->voptions, "flags", NULL, 0);
+
+ snprintf(buf, sizeof(buf), "%s-%s-pass1.log", ctx->avc->filename,
+ prefix);
+ buf[sizeof(buf) - 1] = 0;
+
+ if (value_has_flag(de ? de->value : "", "pass2")) {
+ if (!(*bytebuf = open_stream(buf, NULL, NULL))) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "%s: could not open '%s', "
+ "disabling 2-pass encoding at pass 2\n", prefix, buf);
+ stream->codec->flags &= ~CODEC_FLAG_PASS2;
+ set_to_avdictionary(stream->codec, dictp, octx, "flags=-pass2", "=", "");
+ } else {
+ struct bstr content = stream_read_complete(*bytebuf, NULL,
+ 1000000000, 1);
+ if (content.start == NULL) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "%s: could not read '%s', "
+ "disabling 2-pass encoding at pass 1\n",
+ prefix, ctx->avc->filename);
+ } else {
+ content.start[content.len] = 0;
+ stream->codec->stats_in = content.start;
+ }
+ free_stream(*bytebuf);
+ *bytebuf = NULL;
+ }
+ }
+
+ if (value_has_flag(de ? de->value : "", "pass1")) {
+ if (!(*bytebuf = open_output_stream(buf, NULL))) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "%s: could not open '%s', disabling "
+ "2-pass encoding at pass 1\n",
+ prefix, ctx->avc->filename);
+ set_to_avdictionary(stream->codec, dictp, octx, "flags=-pass1", "=", "");
+ }
+ }
+ }
+}
+
+AVStream *encode_lavc_alloc_stream(struct encode_lavc_context *ctx,
+ enum AVMediaType mt)
+{
+ AVDictionaryEntry *de;
+ AVStream *stream = NULL;
+ char **p;
+ int i;
+ AVCodecContext *dummy;
+
+ CHECK_FAIL(ctx, NULL);
+
+ if (ctx->header_written)
+ return NULL;
+
+ for (i = 0; i < ctx->avc->nb_streams; ++i)
+ if (ctx->avc->streams[i]->codec->codec_type == mt)
+ // already have a stream of that type, this cannot really happen
+ return NULL;
+
+ if (ctx->timebase.den == 0) {
+ AVRational r;
+
+ if (ctx->options->fps > 0)
+ r = av_d2q(ctx->options->fps, ctx->options->fps * 1001 + 2);
+ else if (ctx->options->autofps && vo_fps > 0) {
+ r = av_d2q(vo_fps, vo_fps * 1001 + 2);
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: option -ofps not specified "
+ "but -oautofps is active, using guess of %u/%u\n",
+ (unsigned)r.num, (unsigned)r.den);
+ } else {
+ // we want to handle:
+ // 1/25
+ // 1001/24000
+ // 1001/30000
+ // for this we would need 120000fps...
+ // however, mpeg-4 only allows 16bit values
+ // so let's take 1001/30000 out
+ r.num = 24000;
+ r.den = 1;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: option -ofps not specified "
+ "and fps could not be inferred, using guess of %u/%u\n",
+ (unsigned)r.num, (unsigned)r.den);
+ }
+
+ if (ctx->vc && ctx->vc->supported_framerates)
+ r = ctx->vc->supported_framerates[av_find_nearest_q_idx(r,
+ ctx->vc->supported_framerates)];
+
+ ctx->timebase.num = r.den;
+ ctx->timebase.den = r.num;
+ }
+
+ switch (mt) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (!ctx->vc) {
+ encode_lavc_fail(ctx, "vo-lavc: encoder not found\n");
+ return NULL;
+ }
+ stream = avformat_new_stream(ctx->avc, ctx->vc);
+
+ // stream->time_base = ctx->timebase;
+ // 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
+
+ stream->codec->codec_id = ctx->vc->id;
+ stream->codec->time_base = ctx->timebase;
+ stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+
+ dummy = avcodec_alloc_context3(ctx->vc);
+ dummy->codec = ctx->vc; // FIXME remove this once we can, caused by a bug in libav, elenril is aware of this
+ // FIXME:
+ // currently, to eradicate this dummy:
+ // add here: stream->codec->codec = ctx->vc; // SAME PROBLEM AS ABOVE
+ // replace dummy by stream->codec
+ // at the end of this block: stream->codec->codec = NULL; // OR SEGV LATER
+
+ ctx->voptions = NULL;
+
+ // libx264: default to preset=medium
+ if (!strcmp(ctx->vc->name, "libx264"))
+ set_to_avdictionary(stream->codec, &ctx->voptions, dummy, "preset=medium", "=", "");
+
+ if (ctx->options->vopts)
+ for (p = ctx->options->vopts; *p; ++p)
+ if (set_to_avdictionary(stream->codec, &ctx->voptions, dummy,
+ *p, "=", "") <= 0)
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "vo-lavc: could not set option %s\n", *p);
+
+ de = av_dict_get(ctx->voptions, "global_quality", NULL, 0);
+ if(de)
+ set_to_avdictionary(stream->codec, &ctx->voptions, dummy, "flags=+qscale", "=", "");
+
+ if (ctx->avc->oformat->flags & AVFMT_GLOBALHEADER)
+ set_to_avdictionary(stream->codec, &ctx->voptions, dummy, "flags=+global_header", "=", "");
+
+ encode_2pass_prepare(ctx, &ctx->voptions, dummy, stream, &ctx->twopass_bytebuffer_v,
+ "vo-lavc");
+
+ av_free(dummy);
+ break;
+
+ case AVMEDIA_TYPE_AUDIO:
+ if (!ctx->ac) {
+ encode_lavc_fail(ctx, "ao-lavc: encoder not found\n");
+ return NULL;
+ }
+ stream = avformat_new_stream(ctx->avc, ctx->ac);
+
+ stream->codec->codec_id = ctx->ac->id;
+ stream->codec->time_base = ctx->timebase;
+ stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ dummy = avcodec_alloc_context3(ctx->ac);
+ dummy->codec = ctx->ac; // FIXME remove this once we can, caused by a bug in libav, elenril is aware of this
+ // FIXME:
+ // currently, to eradicate this dummy:
+ // add here: stream->codec->codec = ctx->ac; // SAME PROBLEM AS ABOVE
+ // replace dummy by stream->codec
+ // at the end of this block: stream->codec->codec = NULL; // OR SEGV LATER
+
+ ctx->aoptions = NULL;
+
+ if (ctx->options->aopts)
+ for (p = ctx->options->aopts; *p; ++p)
+ if (set_to_avdictionary(stream->codec, &ctx->aoptions, dummy,
+ *p, "=", "") <= 0)
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "ao-lavc: could not set option %s\n", *p);
+
+ de = av_dict_get(ctx->aoptions, "global_quality", NULL, 0);
+ if(de)
+ set_to_avdictionary(stream->codec, &ctx->aoptions, dummy, "flags=+qscale", "=", "");
+
+ if (ctx->avc->oformat->flags & AVFMT_GLOBALHEADER)
+ set_to_avdictionary(stream->codec, &ctx->aoptions, dummy, "flags=+global_header", "=", "");
+
+ encode_2pass_prepare(ctx, &ctx->aoptions, dummy, stream, &ctx->twopass_bytebuffer_a,
+ "ao-lavc");
+
+ av_free(dummy);
+ break;
+
+ default:
+ encode_lavc_fail(ctx, "encode-lavc: requested invalid stream type\n");
+ return NULL;
+ }
+
+ return stream;
+}
+
+AVCodec *encode_lavc_get_codec(struct encode_lavc_context *ctx,
+ AVStream *stream)
+{
+ CHECK_FAIL(ctx, NULL);
+
+ switch (stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ return ctx->vc;
+ case AVMEDIA_TYPE_AUDIO:
+ return ctx->ac;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+int encode_lavc_open_codec(struct encode_lavc_context *ctx, AVStream *stream)
+{
+ AVDictionaryEntry *de;
+ int ret;
+
+ CHECK_FAIL(ctx, -1);
+
+ switch (stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (ctx->vc->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ stream->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ av_log(ctx->avc, AV_LOG_ERROR, _(
+ "\n\n"
+ " ********************************************\n"
+ " **** Experimental VIDEO codec selected! ****\n"
+ " ********************************************\n\n"
+ "This means the output file may be broken or bad.\n"
+ "Possible reasons, problems, workarounds:\n"
+ "- Codec implementation in ffmpeg/libav is not finished yet.\n"
+ " Try updating ffmpeg or libav.\n"
+ "- Bad picture quality, blocks, blurriness.\n"
+ " Experiment with codec settings (-ovcopts) to maybe still get the\n"
+ " desired quality output at the expense of bitrate.\n"
+ "- Slow compression.\n"
+ " Bear with it.\n"
+ "- Crashes.\n"
+ " Happens. Try varying options to work around.\n"
+ "If none of this helps you, try another codec in place of %s.\n\n"),
+ ctx->vc->name);
+ }
+
+ ret = avcodec_open2(stream->codec, ctx->vc, &ctx->voptions);
+
+ // complain about all remaining options, then free the dict
+ for (de = NULL; (de = av_dict_get(ctx->voptions, "", de,
+ AV_DICT_IGNORE_SUFFIX));)
+ av_log(ctx->avc, AV_LOG_ERROR, "Key '%s' not found.\n", de->key);
+ av_dict_free(&ctx->voptions);
+
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (ctx->ac->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ stream->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ av_log(ctx->avc, AV_LOG_ERROR, _(
+ "\n\n"
+ " ********************************************\n"
+ " **** Experimental AUDIO codec selected! ****\n"
+ " ********************************************\n\n"
+ "This means the output file may be broken or bad.\n"
+ "Possible reasons, problems, workarounds:\n"
+ "- Codec implementation in ffmpeg/libav is not finished yet.\n"
+ " Try updating ffmpeg or libav.\n"
+ "- Bad sound quality, noise, clicking, whistles, choppiness.\n"
+ " Experiment with codec settings (-oacopts) to maybe still get the\n"
+ " desired quality output at the expense of bitrate.\n"
+ "- Slow compression.\n"
+ " Bear with it.\n"
+ "- Crashes.\n"
+ " Happens. Try varying options to work around.\n"
+ "If none of this helps you, try another codec in place of %s.\n\n"),
+ ctx->ac->name);
+ }
+ ret = avcodec_open2(stream->codec, ctx->ac, &ctx->aoptions);
+
+ // complain about all remaining options, then free the dict
+ for (de = NULL; (de = av_dict_get(ctx->aoptions, "", de,
+ AV_DICT_IGNORE_SUFFIX));)
+ av_log(ctx->avc, AV_LOG_ERROR, "Key '%s' not found.\n", de->key);
+ av_dict_free(&ctx->aoptions);
+
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if(ret < 0) {
+ encode_lavc_fail(ctx, "unable to open encoder (see above for the cause)");
+ }
+
+ return ret;
+}
+
+void encode_lavc_write_stats(struct encode_lavc_context *ctx, AVStream *stream)
+{
+ CHECK_FAIL(ctx, );
+
+ switch (stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (ctx->twopass_bytebuffer_v)
+ if (stream->codec->stats_out)
+ stream_write_buffer(ctx->twopass_bytebuffer_v,
+ stream->codec->stats_out,
+ strlen(stream->codec->stats_out));
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (ctx->twopass_bytebuffer_a)
+ if (stream->codec->stats_out)
+ stream_write_buffer(ctx->twopass_bytebuffer_a,
+ stream->codec->stats_out,
+ strlen(stream->codec->stats_out));
+ break;
+ default:
+ break;
+ }
+}
+
+int encode_lavc_write_frame(struct encode_lavc_context *ctx, AVPacket *packet)
+{
+ int r;
+
+ CHECK_FAIL(ctx, -1);
+
+ if (ctx->header_written <= 0)
+ return -1;
+
+ mp_msg(MSGT_ENCODE, MSGL_DBG2,
+ "encode-lavc: write frame: stream %d ptsi %d (%f) size %d\n",
+ (int)packet->stream_index,
+ (int)packet->pts,
+ packet->pts * (double)ctx->avc->streams[packet->stream_index]->
+ time_base.num / (double)ctx->avc->streams[packet->
+ stream_index]->time_base.den,
+ (int)packet->size);
+
+ switch (ctx->avc->streams[packet->stream_index]->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ ctx->vbytes += packet->size;
+ ++ctx->frames;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ ctx->abytes += packet->size;
+ break;
+ default:
+ break;
+ }
+
+ r = av_interleaved_write_frame(ctx->avc, packet);
+
+ return r;
+}
+
+int encode_lavc_supports_pixfmt(struct encode_lavc_context *ctx,
+ enum PixelFormat pix_fmt)
+{
+ CHECK_FAIL(ctx, 0);
+
+ if (!ctx->vc)
+ return 0;
+ if (pix_fmt == PIX_FMT_NONE)
+ return 0;
+
+ if (!ctx->vc->pix_fmts) {
+ return VFCAP_CSP_SUPPORTED;
+ } else {
+ const enum PixelFormat *p;
+ for (p = ctx->vc->pix_fmts; *p >= 0; ++p) {
+ if (pix_fmt == *p)
+ return VFCAP_CSP_SUPPORTED;
+ }
+ }
+ return 0;
+}
+
+void encode_lavc_discontinuity(struct encode_lavc_context *ctx)
+{
+ if (!ctx)
+ return;
+
+ CHECK_FAIL(ctx, );
+
+ ctx->audio_pts_offset = MP_NOPTS_VALUE;
+ ctx->last_video_in_pts = MP_NOPTS_VALUE;
+ ctx->discontinuity_pts_offset = MP_NOPTS_VALUE;
+}
+
+static void encode_lavc_printoptions(void *obj, const char *indent,
+ const char *subindent, const char *unit,
+ int filter_and, int filter_eq)
+{
+ const AVOption *opt = NULL;
+ char optbuf[32];
+ while ((opt = av_opt_next(obj, opt))) {
+ // if flags are 0, it simply hasn't been filled in yet and may be
+ // potentially useful
+ if (opt->flags)
+ if ((opt->flags & filter_and) != filter_eq)
+ continue;
+ /* Don't print CONST's on level one.
+ * Don't print anything but CONST's on level two.
+ * Only print items from the requested unit.
+ */
+ if (!unit && opt->type == FF_OPT_TYPE_CONST)
+ continue;
+ else if (unit && opt->type != FF_OPT_TYPE_CONST)
+ continue;
+ else if (unit && opt->type == FF_OPT_TYPE_CONST
+ && strcmp(unit, opt->unit))
+ continue;
+ else if (unit && opt->type == FF_OPT_TYPE_CONST)
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%s", subindent);
+ else
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%s", indent);
+
+ switch (opt->type) {
+ case FF_OPT_TYPE_FLAGS:
+ snprintf(optbuf, sizeof(optbuf), "%s=<flags>", opt->name);
+ break;
+ case FF_OPT_TYPE_INT:
+ snprintf(optbuf, sizeof(optbuf), "%s=<int>", opt->name);
+ break;
+ case FF_OPT_TYPE_INT64:
+ snprintf(optbuf, sizeof(optbuf), "%s=<int64>", opt->name);
+ break;
+ case FF_OPT_TYPE_DOUBLE:
+ snprintf(optbuf, sizeof(optbuf), "%s=<double>", opt->name);
+ break;
+ case FF_OPT_TYPE_FLOAT:
+ snprintf(optbuf, sizeof(optbuf), "%s=<float>", opt->name);
+ break;
+ case FF_OPT_TYPE_STRING:
+ snprintf(optbuf, sizeof(optbuf), "%s=<string>", opt->name);
+ break;
+ case FF_OPT_TYPE_RATIONAL:
+ snprintf(optbuf, sizeof(optbuf), "%s=<rational>", opt->name);
+ break;
+ case FF_OPT_TYPE_BINARY:
+ snprintf(optbuf, sizeof(optbuf), "%s=<binary>", opt->name);
+ break;
+ case FF_OPT_TYPE_CONST:
+ snprintf(optbuf, sizeof(optbuf), " [+-]%s", opt->name);
+ break;
+ default:
+ snprintf(optbuf, sizeof(optbuf), "%s", opt->name);
+ break;
+ }
+ optbuf[sizeof(optbuf) - 1] = 0;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%-32s ", optbuf);
+ if (opt->help)
+ mp_msg(MSGT_ENCODE, MSGL_INFO, " %s", opt->help);
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "\n");
+ if (opt->unit && opt->type != FF_OPT_TYPE_CONST)
+ encode_lavc_printoptions(obj, indent, subindent, opt->unit,
+ filter_and, filter_eq);
+ }
+}
+
+bool encode_lavc_showhelp(struct MPOpts *opts)
+{
+ bool help_output = false;
+ if(av_codec_next(NULL) == NULL)
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "NO CODECS\n");
+#define CHECKS(str) ((str) && strcmp((str), "help") == 0 ? (help_output |= 1) : 0)
+#define CHECKV(strv) ((strv) && (strv)[0] && strcmp((strv)[0], "help") == 0 ? (help_output |= 1) : 0)
+ if (CHECKS(opts->encode_output.format)) {
+ AVOutputFormat *c = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Available output formats:\n");
+ while ((c = av_oformat_next(c)))
+ mp_msg(MSGT_ENCODE, MSGL_INFO, " -of %-13s %s\n", c->name,
+ c->long_name ? c->long_name : "");
+ av_free(c);
+ }
+ if (CHECKV(opts->encode_output.fopts)) {
+ AVFormatContext *c = avformat_alloc_context();
+ AVOutputFormat *format = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Available output format ctx->options:\n");
+ encode_lavc_printoptions(c, " -ofopts ", " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM);
+ av_free(c);
+ while ((format = av_oformat_next(format))) {
+ if (format->priv_class) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Additionally, for -of %s:\n",
+ format->name);
+ encode_lavc_printoptions(&format->priv_class, " -ofopts ",
+ " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM);
+ }
+ }
+ }
+ if (CHECKV(opts->encode_output.vopts)) {
+ AVCodecContext *c = avcodec_alloc_context3(NULL);
+ AVCodec *codec = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "Available output video codec ctx->options:\n");
+ encode_lavc_printoptions(c, " -ovcopts ", " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM);
+ av_free(c);
+ while ((codec = av_codec_next(codec))) {
+ if (!av_codec_is_encoder(codec))
+ continue;
+ if (codec->type != AVMEDIA_TYPE_VIDEO)
+ continue;
+ if (opts->encode_output.vcodec && opts->encode_output.vcodec[0] && strcmp(opts->encode_output.vcodec, codec->name) != 0)
+ continue;
+ if (codec->priv_class) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Additionally, for -ovc %s:\n",
+ codec->name);
+ encode_lavc_printoptions(&codec->priv_class, " -ovcopts ",
+ " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM);
+ }
+ }
+ }
+ if (CHECKV(opts->encode_output.aopts)) {
+ AVCodecContext *c = avcodec_alloc_context3(NULL);
+ AVCodec *codec = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "Available output audio codec ctx->options:\n");
+ encode_lavc_printoptions(c, " -oacopts ", " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM);
+ av_free(c);
+ while ((codec = av_codec_next(codec))) {
+ if (!av_codec_is_encoder(codec))
+ continue;
+ if (codec->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (opts->encode_output.acodec && opts->encode_output.acodec[0] && strcmp(opts->encode_output.acodec, codec->name) != 0)
+ continue;
+ if (codec->priv_class) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Additionally, for -oac %s:\n",
+ codec->name);
+ encode_lavc_printoptions(&codec->priv_class, " -oacopts ",
+ " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM);
+ }
+ }
+ }
+ if (CHECKS(opts->encode_output.vcodec)) {
+ AVCodec *c = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Available output video codecs:\n");
+ while ((c = av_codec_next(c))) {
+ if (!av_codec_is_encoder(c))
+ continue;
+ if (c->type != AVMEDIA_TYPE_VIDEO)
+ continue;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, " -ovc %-12s %s\n", c->name,
+ c->long_name ? c->long_name : "");
+ }
+ av_free(c);
+ }
+ if (CHECKS(opts->encode_output.acodec)) {
+ AVCodec *c = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Available output audio codecs:\n");
+ while ((c = av_codec_next(c))) {
+ if (!av_codec_is_encoder(c))
+ continue;
+ if (c->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, " -oac %-12s %s\n", c->name,
+ c->long_name ? c->long_name : "");
+ }
+ av_free(c);
+ }
+ return help_output;
+}
+
+double encode_lavc_getoffset(struct encode_lavc_context *ctx, AVStream *stream)
+{
+ CHECK_FAIL(ctx, 0);
+
+ switch (stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ return ctx->options->voffset;
+ case AVMEDIA_TYPE_AUDIO:
+ return ctx->options->aoffset;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int encode_lavc_getstatus(struct encode_lavc_context *ctx,
+ char *buf, int bufsize,
+ float relative_position, float playback_time)
+{
+ float minutes, megabytes, fps, x;
+ float f = FFMAX(0.0001, relative_position);
+ if (!ctx)
+ return -1;
+
+ CHECK_FAIL(ctx, -1);
+
+ minutes = (GetTimerMS() - ctx->t0) / 60000.0 * (1-f) / f;
+ megabytes = ctx->avc->pb ? (avio_size(ctx->avc->pb) / 1048576.0 / f) : 0;
+ fps = ctx->frames / ((GetTimerMS() - ctx->t0) / 1000.0);
+ x = playback_time / ((GetTimerMS() - ctx->t0) / 1000.0);
+ if (ctx->frames)
+ snprintf(buf, bufsize, "{%.1f%% %.1fmin %.1ffps %.1fMB}",
+ relative_position * 100.0, minutes, fps, megabytes);
+ else
+ snprintf(buf, bufsize, "{%.1f%% %.1fmin %.2fx %.1fMB}",
+ relative_position * 100.0, minutes, x, megabytes);
+ buf[bufsize - 1] = 0;
+ return 0;
+}
+
+void encode_lavc_expect_stream(struct encode_lavc_context *ctx, enum AVMediaType mt)
+{
+ CHECK_FAIL(ctx, );
+
+ switch(mt) {
+ case AVMEDIA_TYPE_VIDEO:
+ ctx->expect_video = true;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ ctx->expect_audio = true;
+ break;
+ }
+}
+
+bool encode_lavc_didfail(struct encode_lavc_context *ctx)
+{
+ return ctx && ctx->failed;
+}
+
+void encode_lavc_fail(struct encode_lavc_context *ctx, const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ mp_msg_va(MSGT_ENCODE, MSGL_ERR, format, va);
+ if(ctx->failed)
+ return;
+ ctx->failed = true;
+ encode_lavc_finish(ctx);
+}
+
+bool encode_lavc_set_csp(struct encode_lavc_context *ctx,
+ AVStream *stream, enum mp_csp csp)
+{
+ CHECK_FAIL(ctx, NULL);
+
+ if (ctx->header_written) {
+ if (stream->codec->colorspace != mp_csp_to_avcol_spc(csp))
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "encode-lavc: can not change color space during encoding\n");
+ return false;
+ }
+
+ stream->codec->colorspace = mp_csp_to_avcol_spc(csp);
+ return true;
+}
+
+bool encode_lavc_set_csp_levels(struct encode_lavc_context *ctx,
+ AVStream *stream, enum mp_csp_levels lev)
+{
+ CHECK_FAIL(ctx, NULL);
+
+ if (ctx->header_written) {
+ if (stream->codec->color_range != mp_csp_levels_to_avcol_range(lev))
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "encode-lavc: can not change color space during encoding\n");
+ return false;
+ }
+
+ stream->codec->color_range = mp_csp_levels_to_avcol_range(lev);
+ return true;
+}
+
+enum mp_csp encode_lavc_get_csp(struct encode_lavc_context *ctx,
+ AVStream *stream)
+{
+ CHECK_FAIL(ctx, 0);
+
+ return avcol_spc_to_mp_csp(stream->codec->colorspace);
+}
+
+enum mp_csp_levels encode_lavc_get_csp_levels(struct encode_lavc_context *ctx,
+ AVStream *stream)
+{
+ CHECK_FAIL(ctx, 0);
+
+ return avcol_range_to_mp_csp_levels(stream->codec->color_range);
+}
+
+// vim: ts=4 sw=4 et
diff --git a/encode_lavc.h b/encode_lavc.h
new file mode 100644
index 0000000000..7c1ca07545
--- /dev/null
+++ b/encode_lavc.h
@@ -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
diff --git a/etc/encoding-example-profiles.conf b/etc/encoding-example-profiles.conf
new file mode 100644
index 0000000000..5dc2908599
--- /dev/null
+++ b/etc/encoding-example-profiles.conf
@@ -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
diff --git a/etc/input.conf b/etc/input.conf
index aaadbfc8e3..2dd9fee758 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -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
diff --git a/libao2/ao_lavc.c b/libao2/ao_lavc.c
new file mode 100644
index 0000000000..b22cd325dc
--- /dev/null
+++ b/libao2/ao_lavc.c
@@ -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,
+};
diff --git a/libao2/audio_out.c b/libao2/audio_out.c
index 1ebb0cc4c3..47c18655f3 100644
--- a/libao2/audio_out.c
+++ b/libao2/audio_out.c
@@ -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[] = {
@@ -72,6 +73,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
#ifdef CONFIG_OPENAL
&audio_out_openal,
#endif
+#ifdef CONFIG_ENCODING
+ &audio_out_lavc,
+#endif
&audio_out_null,
// should not be auto-selected:
&audio_out_pcm,
diff --git a/libao2/audio_out.h b/libao2/audio_out.h
index 316341752b..129c815bc3 100644
--- a/libao2/audio_out.h
+++ b/libao2/audio_out.h
@@ -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;
};
diff --git a/libvo/csputils.c b/libvo/csputils.c
index d6aed97864..ed74b9ae74 100644
--- a/libvo/csputils.c
+++ b/libvo/csputils.c
@@ -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;
}
}
diff --git a/libvo/csputils.h b/libvo/csputils.h
index da826e84da..4ec0f14ba2 100644
--- a/libvo/csputils.h
+++ b/libvo/csputils.h
@@ -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);
diff --git a/libvo/video_out.c b/libvo/video_out.c
index 64b2a605c5..2be311f87c 100644
--- a/libvo/video_out.c
+++ b/libvo/video_out.c
@@ -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,
diff --git a/libvo/video_out.h b/libvo/video_out.h
index dd2a1f79bd..2cd314f281 100644
--- a/libvo/video_out.h
+++ b/libvo/video_out.h
@@ -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);
diff --git a/libvo/vo_lavc.c b/libvo/vo_lavc.c
new file mode 100644
index 0000000000..4a1af15eb0
--- /dev/null
+++ b/libvo/vo_lavc.c
@@ -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
diff --git a/mp_core.h b/mp_core.h
index 9a9c6589e9..c786663182 100644
--- a/mp_core.h
+++ b/mp_core.h
@@ -252,6 +252,8 @@ typedef struct MPContext {
struct screenshot_ctx *screenshot_ctx;
char *track_layout_hash;
+
+ struct encode_lavc_context *encode_lavc_ctx;
} MPContext;
diff --git a/mp_msg.h b/mp_msg.h
index 5d7a0ce4f0..5f043243fa 100644
--- a/mp_msg.h
+++ b/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
diff --git a/mplayer.c b/mplayer.c
index 11cb4235ca..784de4b2ee 100644
--- a/mplayer.c
+++ b/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);
}
- // VO stats
- if (sh_video && drop_frame_cnt)
- saddf(line, width, " D: %d", drop_frame_cnt);
+#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
diff --git a/options.h b/options.h
index e704c87cd7..6438f0e97b 100644
--- a/options.h
+++ b/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