encode: video encoding now supported using mencoder-like options

This commit is contained in:
Rudolf Polzer 2012-09-14 17:51:26 +02:00 committed by wm4
parent 5617bf483e
commit f5b8b6ac12
26 changed files with 3208 additions and 33 deletions

141
DOCS/encoding.rst Normal file
View File

@ -0,0 +1,141 @@
General usage
=============
::
mplayer infile -o outfile [-of outfileformat] [-ofopts formatoptions] \
[-ofps outfps | -oautofps] [-oharddup] [-ocopyts | -orawts] [-oneverdrop] \
[(any other mplayer options)] \
-ovc outvideocodec [-ovcopts outvideocodecoptions] \
-oac outaudiocodec [-oacopts outaudiocodecoptions]
Help for these options is provided if giving help as parameter, as in::
mplayer -ovc help
The suboptions of these generally are identical to ffmpeg's (as option parsing
is simply delegated to ffmpeg). The option -ocopyts enables copying timestamps
from the source as-is, instead of fixing them to match audio playback time
(note: this doesn't work with all output container formats); -orawts even turns
off discontinuity fixing.
Note that if neither -ofps nor -oautofps is specified, VFR encoding is assumed
and the time base is 24000fps. -oautofps sets -ofps to a guessed fps number
from the input video. Note that not all codecs and not all formats support VFR
encoding, and some which do have bugs when a target bitrate is specified - use
-ofps or -oautofps to force CFR encoding in these cases.
Of course, the options can be stored in a profile, like this .mplayer/config
section::
[myencprofile]
vf-add = scale=480:-2
ovc = libx264
ovcopts-add = preset=medium,tune=fastdecode
ovcopts-add = crf=23
ovcopts-add = maxrate=1500k,bufsize=1000k,rc_init_occupancy=900k,refs=2
ovcopts-add = profile=baseline
oac = aac
oacopts-add = b=96k
One can then encode using this profile using the command::
mplayer infile -o outfile.mp4 -profile myencprofile
Some example profiles are provided in a file
etc/encoding-example-profiles.conf; as for this, see below.
Encoding examples
=================
These are some examples of encoding targets this code has been used and tested
for.
Typical MPEG-4 Part 2 ("ASP", "DivX") encoding, AVI container::
mplayer infile -o outfile.avi \
-ofps 25 \
-ovc mpeg4 -ovcopts qscale=4 \
-oac libmp3lame -oacopts ab=128k
Note: AVI does not support variable frame rate, so -ofps must be used. The
frame rate should ideally match the input (25 for PAL, 24000/1001 or 30000/1001
for NTSC)
Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, Matroska (MKV) container::
mplayer infile -o outfile.mkv \
-ovc libx264 -ovcopts preset=medium,crf=23,profile=baseline \
-oac vorbis -oacopts qscale=3
Typical MPEG-4 Part 10 ("AVC", "H.264") encoding, MPEG-4 (MP4) container::
mplayer infile -o outfile.mp4 \
-ovc libx264 -ovcopts preset=medium,crf=23,profile=baseline \
-oac aac -oacopts ab=128k
Typical VP8 encoding, WebM (restricted Matroska) container::
mplayer infile -o outfile.mkv \
-of webm \
-ovc libvpx -ovcopts qmin=6,b=1000000k \
-oac libvorbis -oacopts qscale=3
Device targets
==============
As the options for various devices can get complex, profiles can be used.
An example profile file for encoding is provided in
etc/encoding-example-profiles.conf in the source tree. You can include it into
your configuration by doing, from the mplayer2-build directory::
mkdir -p ~/.mplayer
echo "include = $PWD/mplayer/etc/encoding-example-profiles.conf" >> ~/.mplayer/config
Refer to the top of that file for more comments - in a nutshell, the following
options are added by it::
-profile enc-to-dvdpal DVD-Video PAL, use dvdauthor -v pal+4:3 -a ac3+en
-profile enc-to-dvdntsc DVD-Video NTSC, use dvdauthor -v ntsc+4:3 -a ac3+en
-profile enc-to-bb-9000 MP4 for Blackberry Bold 9000
-profile enc-to-nok-6300 3GP for Nokia 6300
-profile enc-to-psp MP4 for PlayStation Portable
-profile enc-to-iphone MP4 for iPhone
-profile enc-to-iphone4 MP4 for iPhone 4 (double res)
You can encode using these with a command line like::
mplayer infile -o outfile.mp4 -profile enc-to-bb-9000
Of course, you are free to override options set by these profiles by specifying
them after the -profile option.
What works
==========
* Encoding at variable frame rate (default)
* Encoding at constant frame rate using -ofps framerate -oharddup
* 2-pass encoding (specify flags=+pass1 in the first pass's -ovcopts, specify
flags=+pass2 in the second pass)
* Hardcoding subtitles using vobsub, ass or srt subtitle rendering (just
configure mplayer for the subtitles as usual)
* Hardcoding any other mplayer OSD (e.g. time codes, using -osdlevel 3 and -vf
expand=::::1)
* Encoding directly from a DVD, network stream, webcam, or any other source
mplayer supports
* Using x264 presets/tunings/profiles (by using profile=, tune=, preset= in the
-ovcopts)
* Deinterlacing/Inverse Telecine with any of mplayer's filters for that
* Audio file converting: mplayer -o outfile.mp3 infile.flac -novideo -oac
libmp3lame -oacopts ab=320k
* inverse telecine filters (confirmed working: detc, pullup, filmdint)
What does not work yet
* 3-pass encoding (ensuring constant total size and bitrate constraints while
having VBR audio; mencoder calls this "frameno")
* Direct stream copy

View File

@ -63,6 +63,7 @@ General changes for mplayer2 to mplayer3
* Do not lose settings when playing a new file in the same player instance
* New location for config files, new name for the binary. (Planned change.)
* Slave mode compatibility broken (see below)
* Encoding functionality (replacement for mencoder)
* General code cleanups
* Many more changes

134
DOCS/man/en/encode.rst Normal file
View File

@ -0,0 +1,134 @@
.. _encode:
ENCODING
========
You can encode files from one format/codec to another using this facility.
-o <filename>
Enables encoding mode and specifies the output file name.
--of=<format>
Specifies the output format (overrides autodetection by the extension of
the file specified by -o).
See --of=help for a full list of supported formats.
--ofopts=<options>
Specifies the output format options for libavformat.
See --ofopts=help for a full list of supported options.
Options are managed in lists. There are a few commands to manage the
options list.
--ofopts-add=<options1[,options2,...]>
Appends the options given as arguments to the options list.
--ofopts-pre=<options1[,options2,...]>
Prepends the options given as arguments to the options list.
--ofopts-del=<index1[,index2,...]>
Deletes the options at the given indexes. Index numbers start at 0,
negative numbers address the end of the list (-1 is the last).
--ofopts-clr
Completely empties the options list.
--ofps=<float value>
Specifies the output format time base (default: 24000). Low values like 25
limit video fps by dropping frames.
--oautofps
Sets the output format time base to the guessed frame rate of the input
video (simulates mencoder behaviour, useful for AVI; may cause frame
drops). Note that not all codecs and not all formats support VFR
encoding, and some which do have bugs when a target bitrate is
specified - use --ofps or --oautofps to force CFR encoding in these
cases.
--oharddup
If set, the frame rate given by --ofps is attained not by skipping time
codes, but by duplicating frames (constant frame rate mode).
--oneverdrop
If set, frames are never dropped. Instead, time codes of video are
readjusted to always increase. This may cause AV desync, though; to
work around this, use a high-fps time base using --ofps and absolutely
avoid --oautofps.
--oac=<codec>
Specifies the output audio codec.
See --oac=help for a full list of supported codecs.
--oaoffset=<value>
Shifts audio data by the given time (in seconds) by adding/removing
samples at the start.
--oacopts=<options>
Specifies the output audio codec options for libavcodec.
See --oacopts=help for a full list of supported options.
EXAMPLE: "--oac=libmp3lame --oacopts=b=128000" selects 128kbps MP3
encoding.
Options are managed in lists. There are a few commands to manage the
options list.
--oacopts-add=<options1[,options2,...]>
Appends the options given as arguments to the options list.
--oacopts-pre=<options1[,options2,...]>
Prepends the options given as arguments to the options list.
--oacopts-del=<index1[,index2,...]>
Deletes the options at the given indexes. Index numbers start at 0,
negative numbers address the end of the list (-1 is the last).
--oacopts-clr
Completely empties the options list.
--ovc=<codec>
Specifies the output video codec.
See --ovc=help for a full list of supported codecs.
--ovoffset=<value>
Shifts video data by the given time (in seconds) by shifting the pts
values.
--ocopyts
Copies input pts to the output video (not supported by some output
container formats, e.g. avi). Discontinuities are still fixed.
By default, audio pts are set to playback time and video pts are
synchronized to match audio pts, as some output formats do not support
anything else.
--orawts
Copies input pts to the output video (not supported by some output
container formats, e.g. avi). In this modem discontinuities are not fixed
and all pts are passed through as-is. Never seek backwards or use multiple
input files in this mode!
--ovcopts <options>
Specifies the output video codec options for libavcodec.
See --ovcopts=help for a full list of supported options.
EXAMPLE: "--ovc=mpeg4 --oacopts=qscale=5" selects constant quantizer scale
5 for MPEG-4 encoding.
EXAMPLE: "--ovc=libx264 --ovcopts=crf=23" selects VBR quality factor 23 for
H.264 encoding.
Options are managed in lists. There are a few commands to manage the
options list.
--ovcopts-add=<options1[,options2,...]>
Appends the options given as arguments to the options list.
--ovcopts-pre=<options1[,options2,...]>
Prepends the options given as arguments to the options list.
--ovcopts-del=<index1[,index2,...]>
Deletes the options at the given indexes. Index numbers start at 0,
negative numbers address the end of the list (-1 is the last).
--ovcopts-clr
Completely empties the options list.

View File

@ -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

View File

@ -407,6 +407,8 @@ OPTIONS
.. include:: vf.rst
.. include:: encode.rst
Taking screenshots
==================

View File

@ -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

View File

@ -245,6 +245,7 @@ const m_option_t msgl_config[]={
{ "lirc", &mp_msg_levels[MSGT_LIRC], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
{ "stream", &mp_msg_levels[MSGT_STREAM], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
{ "cache", &mp_msg_levels[MSGT_CACHE], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
{ "encode", &mp_msg_levels[MSGT_ENCODE], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
{ "xacodec", &mp_msg_levels[MSGT_XACODEC], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
{ "tv", &mp_msg_levels[MSGT_TV], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
{ "radio", &mp_msg_levels[MSGT_RADIO], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
@ -289,6 +290,7 @@ const m_option_t msgl_config[]={
" lirc - lirc_mp.c and input lirc driver\n"
" stream - stream.c\n"
" cache - cache2.c\n"
" encode - encode_lavc.c and associated vo/ao drivers\n"
" xacodec - XAnim codecs\n"
" tv - TV input subsystem\n"
" osdep - OS-dependent parts\n"
@ -739,6 +741,22 @@ const m_option_t mplayer_opts[]={
{"help", (void *) help_text, CONF_TYPE_PRINT, CONF_NOCFG|CONF_GLOBAL, 0, 0, NULL},
{"h", (void *) help_text, CONF_TYPE_PRINT, CONF_NOCFG|CONF_GLOBAL, 0, 0, NULL},
OPT_STRING("o", encode_output.file, CONF_GLOBAL),
OPT_STRING("of", encode_output.format, CONF_GLOBAL),
OPT_STRINGLIST("ofopts*", encode_output.fopts, CONF_GLOBAL),
OPT_FLOATRANGE("ofps", encode_output.fps, CONF_GLOBAL, 0.0, 1000000.0),
OPT_STRING("ovc", encode_output.vcodec, CONF_GLOBAL),
OPT_STRINGLIST("ovcopts*", encode_output.vopts, CONF_GLOBAL),
OPT_STRING("oac", encode_output.acodec, CONF_GLOBAL),
OPT_STRINGLIST("oacopts*", encode_output.aopts, CONF_GLOBAL),
OPT_MAKE_FLAGS("oharddup", encode_output.harddup, CONF_GLOBAL),
OPT_FLOATRANGE("ovoffset", encode_output.voffset, CONF_GLOBAL, -1000000.0, 1000000.0),
OPT_FLOATRANGE("oaoffset", encode_output.aoffset, CONF_GLOBAL, -1000000.0, 1000000.0),
OPT_MAKE_FLAGS("ocopyts", encode_output.copyts, CONF_GLOBAL),
OPT_MAKE_FLAGS("orawts", encode_output.rawts, CONF_GLOBAL),
OPT_MAKE_FLAGS("oautofps", encode_output.autofps, CONF_GLOBAL),
OPT_MAKE_FLAGS("oneverdrop", encode_output.neverdrop, CONF_GLOBAL),
{NULL, NULL, 0, 0, 0, 0, NULL}
};

14
configure vendored
View File

@ -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

View File

@ -22,7 +22,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
.vo_gamma_contrast = 1000,
.vo_gamma_saturation = 1000,
.vo_gamma_hue = 1000,
.osd_level = 1,
.osd_level = -1,
.osd_duration = 1000,
.loop_times = -1,
.ordered_chapters = 1,

21
encode.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef MPLAYER_ENCODE_H
#define MPLAYER_ENCODE_H
#include <stdbool.h>
#include <libavutil/avutil.h>
struct MPOpts;
struct encode_lavc_context;
struct encode_output_conf;
// interface for mplayer.c
struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options);
void encode_lavc_finish(struct encode_lavc_context *ctx);
void encode_lavc_free(struct encode_lavc_context *ctx);
void encode_lavc_discontinuity(struct encode_lavc_context *ctx);
bool encode_lavc_showhelp(struct MPOpts *opts);
int encode_lavc_getstatus(struct encode_lavc_context *ctx, char *buf, int bufsize, float relative_position, float playback_time);
void encode_lavc_expect_stream(struct encode_lavc_context *ctx, enum AVMediaType mt);
bool encode_lavc_didfail(struct encode_lavc_context *ctx); // check if encoding failed
#endif

1062
encode_lavc.c Normal file

File diff suppressed because it is too large Load Diff

93
encode_lavc.h Normal file
View File

@ -0,0 +1,93 @@
/*
* This file is part of MPlayer.
* Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_ENCODE_LAVC_H
#define MPLAYER_ENCODE_LAVC_H
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avstring.h>
#include <libavutil/pixfmt.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include "encode.h"
#include "libvo/csputils.h"
struct encode_lavc_context {
struct encode_output_conf *options;
// these are processed from the options
AVFormatContext *avc;
AVRational timebase;
AVCodec *vc;
AVCodec *ac;
AVDictionary *foptions;
AVDictionary *aoptions;
AVDictionary *voptions;
// values created during encoding
int header_written; // -1 means currently writing
// sync to audio mode
double audio_pts_offset;
double last_video_in_pts;
// anti discontinuity mode
double next_in_pts;
double discontinuity_pts_offset;
long long abytes;
long long vbytes;
struct stream *twopass_bytebuffer_a;
struct stream *twopass_bytebuffer_v;
unsigned int t0;
unsigned int frames;
bool expect_video;
bool expect_audio;
// has encoding failed?
bool failed;
bool finished;
};
// interface for vo/ao drivers
AVStream *encode_lavc_alloc_stream(struct encode_lavc_context *ctx, enum AVMediaType mt);
void encode_lavc_write_stats(struct encode_lavc_context *ctx, AVStream *stream);
int encode_lavc_write_frame(struct encode_lavc_context *ctx, AVPacket *packet);
int encode_lavc_supports_pixfmt(struct encode_lavc_context *ctx, enum PixelFormat format);
AVCodec *encode_lavc_get_codec(struct encode_lavc_context *ctx, AVStream *stream);
int encode_lavc_open_codec(struct encode_lavc_context *ctx, AVStream *stream);
int encode_lavc_available(struct encode_lavc_context *ctx);
int encode_lavc_timesyncfailed(struct encode_lavc_context *ctx);
int encode_lavc_start(struct encode_lavc_context *ctx); // returns 1 on success
int encode_lavc_oformat_flags(struct encode_lavc_context *ctx);
double encode_lavc_getoffset(struct encode_lavc_context *ctx, AVStream *stream);
void encode_lavc_fail(struct encode_lavc_context *ctx, const char *format, ...); // report failure of encoding
bool encode_lavc_set_csp(struct encode_lavc_context *ctx,
AVStream *stream, enum mp_csp csp);
bool encode_lavc_set_csp_levels(struct encode_lavc_context *ctx,
AVStream *stream, enum mp_csp_levels lev);
enum mp_csp encode_lavc_get_csp(struct encode_lavc_context *ctx,
AVStream *stream);
enum mp_csp_levels encode_lavc_get_csp_levels(struct encode_lavc_context *ctx,
AVStream *stream);
#endif

View File

@ -0,0 +1,189 @@
#
# MPlayer configuration file
#
#########################
# encoding profile file #
#########################
#
# Usage of this file: copy/symlink it to a fixed location, and add
# include = /path/to/this/encoding-example-profiles.conf
# to your ~/.mplayer/config
#
# Then, list all profiles by
# mplayer -profile help | grep enc-
#
# The following kinds of encoding profiles exist:
# enc-a-*: initialize an audio codec including good defaults
# enc-v-*: initialize a video codec including good defaults
# enc-f-*: initialize a file format including good defaults, including
# selecting and initializing a good audio and video codec
# enc-to-*: load known good settings for a target device; this typically
# includes selecting an enc-f-* profile, then adjusting some
# settings like frame rate, resolution and codec parameters
#
# AFTER including a profile of these, you can of course still change
# options, or even switch to another codec.
#
# You can view the exact options a profile sets by
# mplayer -show-profile enc-to-bb-9000
#
# Examples:
# mplayer -profile enc-to-dvdpal -o outfile.mpg infile.mkv
# mplayer -profile enc-f-avi -ofps 30 -o outfile.avi infile.mkv
# mplayer -profile enc-v-mpeg4 -ovcopts-add global_quality=7 -profile enc-a-mp3 -oacopts-add b=320k -o outfile.avi infile.mkv
################
# audio codecs #
################
[enc-a-aac]
profile-desc = "AAC (libfaac or FFmpeg)"
oac = libfaac,aac
oacopts = b=128k
[enc-a-ac3]
profile-desc = "AC3 (FFmpeg)"
oac = ac3
oacopts = b=448k
[enc-a-mp3]
profile-desc = "MP3 (LAME)"
oac = libmp3lame
oacopts = b=128k
[enc-a-vorbis]
profile-desc = "Vorbis (libvorbis)"
oac = libvorbis
oacopts = global_quality=3
################
# video codecs #
################
[enc-v-h263]
profile-desc = "H.263 (FFmpeg)"
ovc = h263
ovcopts = global_quality=4
[enc-v-h264]
profile-desc = "H.264 (x264)"
ovc = libx264
ovcopts = preset=medium,crf=23,threads=0
[enc-v-mpeg2]
profile-desc = "MPEG-2 Video (FFmpeg)"
ovc = mpeg2video
ovcopts-clr = yes
[enc-v-mpeg4]
profile-desc = "MPEG-4 Part 2 (FFmpeg)"
ovc = mpeg4
ovcopts = global_quality=4
[enc-v-vp8]
profile-desc = "VP8 (libvpx)"
oac = libvpx
oacopts = qmin=4,b=10000000k # ought to be enough for anyone; for CBR use, set b=; for VBR use, set qmin= to quality
###########
# formats #
###########
[enc-f-3gp]
profile-desc = "H.263 + AAC (for 3GP)"
of = 3gp
ocopyts = yes
profile = enc-v-h263
profile = enc-a-aac
ofopts-clr = yes
[enc-f-avi]
profile-desc = "MPEG-4 + MP3 (for AVI)"
of = avi
ocopyts = no
oautofps = yes
profile = enc-v-mpeg4
profile = enc-a-mp3
ofopts-clr = yes
[enc-f-mp4]
profile-desc = "H.264 + AAC (for MP4)"
of = mp4
ocopyts = yes
profile = enc-v-h264
profile = enc-a-aac
ofopts-clr = yes
[enc-f-webm]
profile-desc = "VP8 + Vorbis (for WebM)"
of = webm
ocopyts = yes
profile = enc-v-vp8
profile = enc-a-vorbis
ofopts-clr = yes
##################
# target devices #
##################
[enc-to-dvdpal]
profile-desc = "DVD-Video PAL, use dvdauthor -v pal+4:3 -a ac3+en"
profile = enc-v-mpeg2
profile = enc-a-ac3
of = dvd
ofopts-add = packetsize=2048,muxrate=10080000
ofps = 25
oharddup = yes
vf = expand=aspect=4/3,scale=720:576
srate = 48000
ovcopts-add = g=15,b=6000000,maxrate=9000000,minrate=0,bufsize=1835008
[enc-to-dvdntsc]
profile-desc = "DVD-Video NTSC, use dvdauthor -v ntsc+4:3 -a ac3+en"
profile = enc-v-mpeg2
profile = enc-a-ac3
of = dvd
ofopts-add = packetsize=2048,muxrate=10080000
ofps = 24000/1001
oharddup = yes
vf-add = expand=aspect=4/3,scale=720:480
srate = 48000
ovcopts-add = g=18,b=6000000,maxrate=9000000,minrate=0,bufsize=1835008
[enc-to-bb-9000]
profile-desc = "MP4 for Blackberry Bold 9000"
profile = enc-f-mp4
vf-add = scale=480:-2
ovcopts-add = maxrate=1500k,bufsize=1000k,rc_init_occupancy=900k,refs=1,profile=baseline
oacopts-add = b=96k
[enc-to-nok-6300]
profile-desc = "3GP for Nokia 6300"
profile = enc-f-3gp
ofps = 25
vf-add = scale=176:144
srate = 16000
channels = 1
oacopts-add = b=32k
[enc-to-psp]
profile-desc = "MP4 for PlayStation Portable"
profile = enc-f-mp4
ofps = 30000/1001
vf-add = scale=480:272,dsize=480:270
srate = 48000
channels = 2
ovcopts-add = b=512k,profile=baseline
[enc-to-iphone]
profile-desc = "MP4 for iPhone"
profile = enc-f-mp4
oautofps = yes # iphone supports 30fps max
vf-add = scale=-2:320 # half native screen res is probably best here, full res is no fun on tiny display anyway
ovcopts-add = maxrate=2500k,bufsize=1000k,rc_init_occupancy=900k,level=30,profile=baseline
oacopts-add = b=128k
[enc-to-iphone-4]
profile-desc = "MP4 for iPhone 4 (960x640)"
profile = enc-f-mp4
oautofps = yes # iphone supports 30fps max
vf-add = scale=-2:640 # native screen res
ovcopts-add = maxrate=2500k,bufsize=1000k,rc_init_occupancy=900k,level=30,profile=baseline
oacopts-add = b=128k

View File

@ -53,6 +53,7 @@ PGDWN seek -600
} speed_mult 2.0
BS speed_set 1.0 # reset speed to normal
q quit
q {encode} quit
ESC quit
p pause # toggle pause/playback mode
. frame_step # advance one frame and pause
@ -122,6 +123,7 @@ VOLUME_UP volume 1
VOLUME_DOWN volume -1
MUTE mute
CLOSE_WIN quit
CLOSE_WIN {encode} quit
! seek_chapter -1 # skip to previous chapter
@ seek_chapter 1 # next
E step_property_osd edition # next edition

588
libao2/ao_lavc.c Normal file
View File

@ -0,0 +1,588 @@
/*
* audio encoding using libavformat
* Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
* NOTE: this file is partially based on ao_pcm.c by Atmosfear
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <libavutil/common.h>
#include <libavutil/audioconvert.h>
#include "config.h"
#include "options.h"
#include "mpcommon.h"
#include "fmt-conversion.h"
#include "libaf/format.h"
#include "libaf/reorder_ch.h"
#include "talloc.h"
#include "audio_out.h"
#include "mp_msg.h"
#include "encode_lavc.h"
static const char *sample_padding_signed = "\x00\x00\x00\x00";
static const char *sample_padding_u8 = "\x80";
static const char *sample_padding_float = "\x00\x00\x00\x00";
struct priv {
uint8_t *buffer;
size_t buffer_size;
AVStream *stream;
int pcmhack;
int aframesize;
int aframecount;
int offset;
int offset_left;
int64_t savepts;
int framecount;
int64_t lastpts;
int sample_size;
const void *sample_padding;
AVRational worst_time_base;
int worst_time_base_is_stream;
};
// open & setup audio device
static int init(struct ao *ao, char *params)
{
struct priv *ac = talloc_zero(ao, struct priv);
const enum AVSampleFormat *sampleformat;
AVCodec *codec;
if (!encode_lavc_available(ao->encode_lavc_ctx)) {
mp_msg(MSGT_ENCODE, MSGL_ERR,
"ao-lavc: the option -o (output file) must be specified\n");
return -1;
}
if (ac->stream) {
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: rejecting reinitialization\n");
return -1;
}
ac->stream = encode_lavc_alloc_stream(ao->encode_lavc_ctx,
AVMEDIA_TYPE_AUDIO);
if (!ac->stream) {
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: could not get a new audio stream\n");
return -1;
}
codec = encode_lavc_get_codec(ao->encode_lavc_ctx, ac->stream);
// ac->stream->time_base.num = 1;
// ac->stream->time_base.den = ao->samplerate;
// doing this breaks mpeg2ts in ffmpeg
// which doesn't properly force the time base to be 90000
// furthermore, ffmpeg.c doesn't do this either and works
ac->stream->codec->time_base.num = 1;
ac->stream->codec->time_base.den = ao->samplerate;
ac->stream->codec->sample_rate = ao->samplerate;
ac->stream->codec->channels = ao->channels;
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
{
// first check if the selected format is somewhere in the list of
// supported formats by the codec
for (sampleformat = codec->sample_fmts;
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
++sampleformat) {
switch (*sampleformat) {
case AV_SAMPLE_FMT_U8:
if (ao->format == AF_FORMAT_U8)
goto out_search;
break;
case AV_SAMPLE_FMT_S16:
if (ao->format == AF_FORMAT_S16_BE)
goto out_search;
if (ao->format == AF_FORMAT_S16_LE)
goto out_search;
break;
case AV_SAMPLE_FMT_S32:
if (ao->format == AF_FORMAT_S32_BE)
goto out_search;
if (ao->format == AF_FORMAT_S32_LE)
goto out_search;
break;
case AV_SAMPLE_FMT_FLT:
if (ao->format == AF_FORMAT_FLOAT_BE)
goto out_search;
if (ao->format == AF_FORMAT_FLOAT_LE)
goto out_search;
break;
default:
break;
}
}
out_search:
;
}
if (!sampleformat || *sampleformat == AV_SAMPLE_FMT_NONE) {
// if the selected format is not supported, we have to pick the first
// one we CAN support
// note: not needing to select endianness here, as the switch() below
// does that anyway for us
for (sampleformat = codec->sample_fmts;
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
++sampleformat) {
switch (*sampleformat) {
case AV_SAMPLE_FMT_U8:
ao->format = AF_FORMAT_U8;
goto out_takefirst;
case AV_SAMPLE_FMT_S16:
ao->format = AF_FORMAT_S16_NE;
goto out_takefirst;
case AV_SAMPLE_FMT_S32:
ao->format = AF_FORMAT_S32_NE;
goto out_takefirst;
case AV_SAMPLE_FMT_FLT:
ao->format = AF_FORMAT_FLOAT_NE;
goto out_takefirst;
default:
break;
}
}
out_takefirst:
;
}
switch (ao->format) {
// now that we have chosen a format, set up the fields for it, boldly
// switching endianness if needed (mplayer code will convert for us
// anyway, but ffmpeg always expects native endianness)
case AF_FORMAT_U8:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_U8;
ac->sample_size = 1;
ac->sample_padding = sample_padding_u8;
ao->format = AF_FORMAT_U8;
break;
default:
case AF_FORMAT_S16_BE:
case AF_FORMAT_S16_LE:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S16;
ac->sample_size = 2;
ac->sample_padding = sample_padding_signed;
ao->format = AF_FORMAT_S16_NE;
break;
case AF_FORMAT_S32_BE:
case AF_FORMAT_S32_LE:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S32;
ac->sample_size = 4;
ac->sample_padding = sample_padding_signed;
ao->format = AF_FORMAT_S32_NE;
break;
case AF_FORMAT_FLOAT_BE:
case AF_FORMAT_FLOAT_LE:
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_FLT;
ac->sample_size = 4;
ac->sample_padding = sample_padding_float;
ao->format = AF_FORMAT_FLOAT_NE;
break;
}
ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
switch (ao->channels) {
case 1:
ac->stream->codec->channel_layout = AV_CH_LAYOUT_MONO;
break;
case 2:
ac->stream->codec->channel_layout = AV_CH_LAYOUT_STEREO;
break;
/* someone please check if these are what mplayer normally assumes
case 3:
ac->stream->codec->channel_layout = AV_CH_LAYOUT_SURROUND;
break;
case 4:
ac->stream->codec->channel_layout = AV_CH_LAYOUT_2_2;
break;
*/
case 5:
ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT0;
break;
case 6:
ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT1;
break;
case 8:
ac->stream->codec->channel_layout = AV_CH_LAYOUT_7POINT1;
break;
default:
mp_msg(MSGT_ENCODE, MSGL_ERR,
"ao-lavc: unknown channel layout; hoping for the best\n");
break;
}
if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
return -1;
ac->pcmhack = 0;
if (ac->stream->codec->frame_size <= 1)
ac->pcmhack = av_get_bits_per_sample(ac->stream->codec->codec_id) / 8;
if (ac->pcmhack) {
ac->aframesize = 16384; // "enough"
ac->buffer_size = ac->aframesize * ac->pcmhack * ao->channels * 2 + 200;
} else {
ac->aframesize = ac->stream->codec->frame_size;
ac->buffer_size = ac->aframesize * ac->sample_size * ao->channels * 2 +
200;
}
if (ac->buffer_size < FF_MIN_BUFFER_SIZE)
ac->buffer_size = FF_MIN_BUFFER_SIZE;
ac->buffer = talloc_size(ac, ac->buffer_size);
// enough frames for at least 0.25 seconds
ac->framecount = ceil(ao->samplerate * 0.25 / ac->aframesize);
// but at least one!
ac->framecount = FFMAX(ac->framecount, 1);
ac->savepts = MP_NOPTS_VALUE;
ac->lastpts = MP_NOPTS_VALUE;
ac->offset = ac->stream->codec->sample_rate *
encode_lavc_getoffset(ao->encode_lavc_ctx, ac->stream);
ac->offset_left = ac->offset;
//fill_ao_data:
ao->outburst = ac->aframesize * ac->sample_size * ao->channels *
ac->framecount;
ao->buffersize = ao->outburst * 2;
ao->bps = ao->channels * ao->samplerate * ac->sample_size;
ao->untimed = true;
ao->priv = ac;
return 0;
}
static void fill_with_padding(void *buf, int cnt, int sz, const void *padding)
{
int i;
if (sz == 1) {
memset(buf, cnt, *(char *)padding);
return;
}
for (i = 0; i < cnt; ++i)
memcpy((char *) buf + i * sz, padding, sz);
}
// close audio device
static int encode(struct ao *ao, int ptsvalid, double apts, void *data);
static void uninit(struct ao *ao, bool cut_audio)
{
struct priv *ac = ao->priv;
if (ac->buffer) {
double pts = ao->pts + ac->offset / (double) ao->samplerate;
if (ao->buffer.len > 0) {
void *paddingbuf = talloc_size(ao,
ac->aframesize * ao->channels * ac->sample_size);
memcpy(paddingbuf, ao->buffer.start, ao->buffer.len);
fill_with_padding((char *) paddingbuf + ao->buffer.len,
(ac->aframesize * ao->channels * ac->sample_size
- ao->buffer.len) / ac->sample_size,
ac->sample_size, ac->sample_padding);
encode(ao, ao->pts != MP_NOPTS_VALUE, pts, paddingbuf);
pts += ac->aframesize / (double) ao->samplerate;
talloc_free(paddingbuf);
ao->buffer.len = 0;
}
while (encode(ao, true, pts, NULL) > 0) ;
}
ao->priv = NULL;
}
// return: how many bytes can be played without blocking
static int get_space(struct ao *ao)
{
return ao->outburst;
}
// must get exactly ac->aframesize amount of data
static int encode(struct ao *ao, int ptsvalid, double apts, void *data)
{
AVFrame *frame;
AVPacket packet;
struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
double realapts = ac->aframecount * (double) ac->aframesize /
ao->samplerate;
int status, gotpacket;
ac->aframecount++;
if (data && (ao->channels == 5 || ao->channels == 6 || ao->channels == 8)) {
reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
ao->channels,
ac->aframesize * ao->channels, ac->sample_size);
}
if (data && ptsvalid)
ectx->audio_pts_offset = realapts - apts;
av_init_packet(&packet);
packet.data = ac->buffer;
packet.size = ac->buffer_size;
if(data)
{
frame = avcodec_alloc_frame();
frame->nb_samples = ac->aframesize;
if(avcodec_fill_audio_frame(frame, ao->channels, ac->stream->codec->sample_fmt, data, ac->aframesize * ao->channels * ac->sample_size, 1))
{
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error filling\n");
return -1;
}
if (ao->encode_lavc_ctx->options->rawts) {
// raw audio pts
frame->pts = floor(apts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
} else if (ectx->options->copyts) {
// real audio pts
frame->pts = floor((apts + ectx->discontinuity_pts_offset) * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
} else {
// audio playback time
frame->pts = floor(realapts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
}
int64_t frame_pts = av_rescale_q(frame->pts, ac->stream->codec->time_base, ac->worst_time_base);
if (ac->lastpts != MP_NOPTS_VALUE && frame_pts <= ac->lastpts) {
// this indicates broken video
// (video pts failing to increase fast enough to match audio)
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: audio frame pts went backwards "
"(%d <- %d), autofixed\n", (int)frame->pts,
(int)ac->lastpts);
frame_pts = ac->lastpts + 1;
frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, ac->stream->codec->time_base);
}
ac->lastpts = frame_pts;
frame->quality = ac->stream->codec->global_quality;
status = avcodec_encode_audio2(ac->stream->codec, &packet, frame, &gotpacket);
if (!status) {
if (ac->savepts == MP_NOPTS_VALUE)
ac->savepts = frame->pts;
}
av_free(frame);
}
else
{
status = avcodec_encode_audio2(ac->stream->codec, &packet, NULL, &gotpacket);
}
if(status)
{
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error encoding\n");
return -1;
}
if(!gotpacket)
return 0;
mp_msg(MSGT_ENCODE, MSGL_DBG2,
"ao-lavc: got pts %f (playback time: %f); out size: %d\n",
apts, realapts, packet.size);
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->stream);
// Do we need this at all? Better be safe than sorry...
if (packet.pts == AV_NOPTS_VALUE) {
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: encoder lost pts, why?\n");
if (ac->savepts != MP_NOPTS_VALUE)
packet.pts = ac->savepts;
}
if (packet.pts != AV_NOPTS_VALUE)
packet.pts = av_rescale_q(packet.pts, ac->stream->codec->time_base,
ac->stream->time_base);
if (packet.dts != AV_NOPTS_VALUE)
packet.dts = av_rescale_q(packet.dts, ac->stream->codec->time_base,
ac->stream->time_base);
if(packet.duration > 0)
packet.duration = av_rescale_q(packet.duration, ac->stream->codec->time_base,
ac->stream->time_base);
ac->savepts = MP_NOPTS_VALUE;
if (encode_lavc_write_frame(ao->encode_lavc_ctx, &packet) < 0) {
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error writing at %f %f/%f\n",
realapts, (double) ac->stream->time_base.num,
(double) ac->stream->time_base.den);
return -1;
}
return packet.size;
}
// plays 'len' bytes of 'data'
// it should round it down to outburst*n
// return: number of bytes played
static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
int bufpos = 0;
int64_t ptsoffset;
void *paddingbuf = NULL;
double nextpts;
len /= ac->sample_size * ao->channels;
if (!encode_lavc_start(ectx)) {
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: NOTE: deferred initial audio frame (probably because video is not there yet)\n");
return 0;
}
if (ac->worst_time_base.den == 0) {
//if (ac->stream->codec->time_base.num / ac->stream->codec->time_base.den >= ac->stream->time_base.num / ac->stream->time_base.den)
if (ac->stream->codec->time_base.num * (double) ac->stream->time_base.den >=
ac->stream->time_base.num * (double) ac->stream->codec->time_base.den) {
mp_msg(MSGT_ENCODE, MSGL_V, "ao-lavc: NOTE: using codec time base "
"(%d/%d) for pts adjustment; the stream base (%d/%d) is "
"not worse.\n", (int)ac->stream->codec->time_base.num,
(int)ac->stream->codec->time_base.den, (int)ac->stream->time_base.num,
(int)ac->stream->time_base.den);
ac->worst_time_base = ac->stream->codec->time_base;
ac->worst_time_base_is_stream = 0;
} else {
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: NOTE: not using codec time "
"base (%d/%d) for pts adjustment; the stream base (%d/%d) "
"is worse.\n", (int)ac->stream->codec->time_base.num,
(int)ac->stream->codec->time_base.den, (int)ac->stream->time_base.num,
(int)ac->stream->time_base.den);
ac->worst_time_base = ac->stream->time_base;
ac->worst_time_base_is_stream = 1;
}
// NOTE: we use the following "axiom" of av_rescale_q:
// if time base A is worse than time base B, then
// av_rescale_q(av_rescale_q(x, A, B), B, A) == x
// this can be proven as long as av_rescale_q rounds to nearest, which
// it currently does
// av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B"
// and:
// av_rescale_q(av_rescale_q(x, A, B), B, A) * A
// == "round av_rescale_q(x, A, B)*B to nearest multiple of A"
// == "round 'round x*A to nearest multiple of B' to nearest multiple of A"
//
// assume this fails. Then there is a value of x*A, for which the
// nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[.
// Absurd, as this range MUST contain at least one multiple of B.
}
ptsoffset = ac->offset;
// this basically just edits ao->apts for syncing purposes
if (ectx->options->copyts || ectx->options->rawts) {
// we do not send time sync data to the video side,
// but we always need the exact pts, even if zero
} else {
// here we must "simulate" the pts editing
// 1. if we have to skip stuff, we skip it
// 2. if we have to add samples, we add them
// 3. we must still adjust ptsoffset appropriately for AV sync!
// invariant:
// if no partial skipping is done, the first frame gets ao->apts passed as pts!
if (ac->offset_left < 0) {
if (ac->offset_left <= -len) {
// skip whole frame
ac->offset_left += len;
return len * ac->sample_size * ao->channels;
} else {
// skip part of this frame, buffer/encode the rest
bufpos -= ac->offset_left;
ptsoffset += ac->offset_left;
ac->offset_left = 0;
}
} else if (ac->offset_left > 0) {
// make a temporary buffer, filled with zeroes at the start
// (don't worry, only happens once)
paddingbuf = talloc_size(ac, ac->sample_size * ao->channels *
(ac->offset_left + len));
fill_with_padding(paddingbuf, ac->offset_left, ac->sample_size,
ac->sample_padding);
data = (char *) paddingbuf + ac->sample_size * ao->channels *
ac->offset_left;
bufpos -= ac->offset_left; // yes, negative!
ptsoffset += ac->offset_left;
ac->offset_left = 0;
// now adjust the bufpos so the final value of bufpos is positive!
/*
int cnt = (len - bufpos) / ac->aframesize;
int finalbufpos = bufpos + cnt * ac->aframesize;
*/
int finalbufpos = len - (len - bufpos) % ac->aframesize;
if (finalbufpos < 0) {
mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: cannot attain the "
"exact requested audio sync; shifting by %d frames\n",
-finalbufpos);
bufpos -= finalbufpos;
}
}
}
// fix the discontinuity pts offset
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
nextpts = ao->pts + ptsoffset / (double) ao->samplerate;
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
while (len - bufpos >= ac->aframesize) {
encode(ao, ao->pts != MP_NOPTS_VALUE,
ao->pts + (bufpos + ptsoffset) / (double) ao->samplerate +
encode_lavc_getoffset(ectx, ac->stream),
(char *) data + ac->sample_size * bufpos * ao->channels);
bufpos += ac->aframesize;
}
talloc_free(paddingbuf);
// set next allowed output pts value
nextpts = ao->pts + ectx->discontinuity_pts_offset + (bufpos + ptsoffset) / (double) ao->samplerate;
if (nextpts > ectx->next_in_pts)
ectx->next_in_pts = nextpts;
return bufpos * ac->sample_size * ao->channels;
}
const struct ao_driver audio_out_lavc = {
.is_new = true,
.info = &(const struct ao_info) {
"audio encoding using libavcodec",
"lavc",
"Rudolf Polzer <divVerent@xonotic.org>",
""
},
.init = init,
.uninit = uninit,
.get_space = get_space,
.play = play,
};

View File

@ -43,6 +43,7 @@ extern const struct ao_driver audio_out_alsa;
extern const struct ao_driver audio_out_dsound;
extern const struct ao_driver audio_out_pcm;
extern const struct ao_driver audio_out_pss;
extern const struct ao_driver audio_out_lavc;
extern const struct ao_driver audio_out_portaudio;
static const struct ao_driver * const audio_out_drivers[] = {
@ -71,6 +72,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
#endif
#ifdef CONFIG_OPENAL
&audio_out_openal,
#endif
#ifdef CONFIG_ENCODING
&audio_out_lavc,
#endif
&audio_out_null,
// should not be auto-selected:

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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,

View File

@ -249,6 +249,7 @@ struct vo {
struct vo_x11_state *x11;
struct vo_w32_state *w32;
struct mp_fifo *key_fifo;
struct encode_lavc_context *encode_lavc_ctx;
struct input_ctx *input_ctx;
int event_fd; // check_events() should be called when this has input
int registered_fd; // set to event_fd when registered in input system
@ -278,7 +279,8 @@ struct vo {
struct vo *init_best_video_out(struct MPOpts *opts,
struct mp_fifo *key_fifo,
struct input_ctx *input_ctx);
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx);
int vo_config(struct vo *vo, uint32_t width, uint32_t height,
uint32_t d_width, uint32_t d_height, uint32_t flags,
uint32_t format);

590
libvo/vo_lavc.c Normal file
View File

@ -0,0 +1,590 @@
/*
* video encoding using libavformat
* Copyright (C) 2010 Nicolas George <george@nsup.org>
* Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include "mpcommon.h"
#include "options.h"
#include "fmt-conversion.h"
#include "libmpcodecs/mp_image.h"
#include "libmpcodecs/vfcap.h"
#include "subopt-helper.h"
#include "talloc.h"
#include "video_out.h"
#include "encode_lavc.h"
#include "sub/sub.h"
#include "libvo/osd.h"
struct priv {
uint8_t *buffer;
size_t buffer_size;
AVStream *stream;
int have_first_packet;
int harddup;
double lastpts;
int64_t lastipts;
int64_t lastframeipts;
mp_image_t *lastimg;
int lastdisplaycount;
AVRational worst_time_base;
int worst_time_base_is_stream;
struct osd_state *osd;
struct mp_csp_details colorspace;
};
static int preinit(struct vo *vo, const char *arg)
{
struct priv *vc;
if (!encode_lavc_available(vo->encode_lavc_ctx)) {
mp_msg(MSGT_ENCODE, MSGL_ERR,
"vo-lavc: the option -o (output file) must be specified\n");
return -1;
}
vo->priv = talloc_zero(vo, struct priv);
vc = vo->priv;
vc->harddup = vo->encode_lavc_ctx->options->harddup;
vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
return 0;
}
static void draw_image(struct vo *vo, mp_image_t *mpi, double pts);
static void uninit(struct vo *vo)
{
struct priv *vc = vo->priv;
if (!vc)
return;
if (vc->lastipts >= 0 && vc->stream)
draw_image(vo, NULL, MP_NOPTS_VALUE);
if (vc->lastimg) {
// palette hack
if (vc->lastimg->imgfmt == IMGFMT_RGB8
|| vc->lastimg->imgfmt == IMGFMT_BGR8)
vc->lastimg->planes[1] = NULL;
free_mp_image(vc->lastimg);
vc->lastimg = NULL;
}
vo->priv = NULL;
}
static int config(struct vo *vo, uint32_t width, uint32_t height,
uint32_t d_width, uint32_t d_height, uint32_t flags,
uint32_t format)
{
struct priv *vc = vo->priv;
enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
AVRational display_aspect_ratio, image_aspect_ratio;
AVRational aspect;
if (!vc)
return -1;
display_aspect_ratio.num = d_width;
display_aspect_ratio.den = d_height;
image_aspect_ratio.num = width;
image_aspect_ratio.den = height;
aspect = av_div_q(display_aspect_ratio, image_aspect_ratio);
if (vc->stream) {
/* NOTE:
* in debug builds we get a "comparison between signed and unsigned"
* warning here. We choose to ignore that; just because ffmpeg currently
* uses a plain 'int' for these struct fields, it doesn't mean it always
* will */
if (width == vc->stream->codec->width &&
height == vc->stream->codec->height) {
if (aspect.num != vc->stream->codec->sample_aspect_ratio.num ||
aspect.den != vc->stream->codec->sample_aspect_ratio.den) {
/* aspect-only changes are not critical */
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: unsupported pixel aspect "
"ratio change from %d:%d to %d:%d\n",
vc->stream->codec->sample_aspect_ratio.num,
vc->stream->codec->sample_aspect_ratio.den,
aspect.num, aspect.den);
}
return 0;
}
/* FIXME Is it possible with raw video? */
mp_msg(MSGT_ENCODE, MSGL_ERR,
"vo-lavc: resolution changes not supported.\n");
goto error;
}
vc->lastipts = MP_NOPTS_VALUE;
vc->lastframeipts = MP_NOPTS_VALUE;
if (pix_fmt == PIX_FMT_NONE)
goto error; /* imgfmt2pixfmt already prints something */
vc->stream = encode_lavc_alloc_stream(vo->encode_lavc_ctx,
AVMEDIA_TYPE_VIDEO);
vc->stream->sample_aspect_ratio = vc->stream->codec->sample_aspect_ratio =
aspect;
vc->stream->codec->width = width;
vc->stream->codec->height = height;
vc->stream->codec->pix_fmt = pix_fmt;
encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->stream) < 0)
goto error;
vc->buffer_size = 6 * width * height + 200;
if (vc->buffer_size < FF_MIN_BUFFER_SIZE)
vc->buffer_size = FF_MIN_BUFFER_SIZE;
if (vc->buffer_size < sizeof(AVPicture))
vc->buffer_size = sizeof(AVPicture);
vc->buffer = talloc_size(vc, vc->buffer_size);
vc->lastimg = alloc_mpi(width, height, format);
// palette hack
if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
vc->lastimg->imgfmt == IMGFMT_BGR8)
vc->lastimg->planes[1] = talloc_zero_size(vc, 1024);
return 0;
error:
uninit(vo);
return -1;
}
static int query_format(struct vo *vo, uint32_t format)
{
enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
if (!vo->encode_lavc_ctx)
return 0;
return encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt) ?
VFCAP_CSP_SUPPORTED : 0;
}
static void write_packet(struct vo *vo, int size, AVPacket *packet)
{
struct priv *vc = vo->priv;
if (size < 0) {
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error encoding\n");
return;
}
if (size > 0) {
packet->stream_index = vc->stream->index;
if (packet->pts != AV_NOPTS_VALUE) {
packet->pts = av_rescale_q(packet->pts,
vc->stream->codec->time_base,
vc->stream->time_base);
} else {
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: codec did not provide pts\n");
packet->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
vc->stream->time_base);
}
if (packet->dts != AV_NOPTS_VALUE) {
packet->dts = av_rescale_q(packet->dts,
vc->stream->codec->time_base,
vc->stream->time_base);
}
if (packet->duration > 0) {
packet->duration = av_rescale_q(packet->duration,
vc->stream->codec->time_base,
vc->stream->time_base);
} else {
// HACK: libavformat calculates dts wrong if the initial packet
// duration is not set, but ONLY if the time base is "high" and if we
// have b-frames!
if (!packet->duration)
if (!vc->have_first_packet)
if (vc->stream->codec->has_b_frames
|| vc->stream->codec->max_b_frames)
if (vc->stream->time_base.num * 1000LL <=
vc->stream->time_base.den)
packet->duration = FFMAX(1, av_rescale_q(1,
vc->stream->codec->time_base, vc->stream->time_base));
}
if (encode_lavc_write_frame(vo->encode_lavc_ctx, packet) < 0) {
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error writing\n");
return;
}
vc->have_first_packet = 1;
}
}
static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet)
{
struct priv *vc = vo->priv;
if (encode_lavc_oformat_flags(vo->encode_lavc_ctx) & AVFMT_RAWPICTURE) {
if (!frame)
return 0;
memcpy(vc->buffer, frame, sizeof(AVPicture));
mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f\n",
frame->pts * (double) vc->stream->codec->time_base.num /
(double) vc->stream->codec->time_base.den);
packet->size = sizeof(AVPicture);
return packet->size;
} else {
int got_packet = 0;
int status = avcodec_encode_video2(vc->stream->codec, packet,
frame, &got_packet);
int size = (status < 0) ? status : got_packet ? packet->size : 0;
if (frame)
mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f; out size: %d\n",
frame->pts * (double) vc->stream->codec->time_base.num /
(double) vc->stream->codec->time_base.den, size);
encode_lavc_write_stats(vo->encode_lavc_ctx, vc->stream);
return size;
}
}
static void add_osd_to_lastimg_draw_func(void *ctx, int x0,int y0, int w,int h,unsigned char* src, unsigned char *srca, int stride){
struct priv *vc = ctx;
unsigned char* dst;
if(w<=0 || h<=0) return; // nothing to do...
// printf("OSD redraw: %d;%d %dx%d \n",x0,y0,w,h);
dst=vc->lastimg->planes[0]+
vc->lastimg->stride[0]*y0+
(vc->lastimg->bpp>>3)*x0;
switch(vc->lastimg->imgfmt){
case IMGFMT_BGR12:
case IMGFMT_RGB12:
vo_draw_alpha_rgb12(w, h, src, srca, stride, dst, vc->lastimg->stride[0]);
break;
case IMGFMT_BGR15:
case IMGFMT_RGB15:
vo_draw_alpha_rgb15(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
break;
case IMGFMT_BGR16:
case IMGFMT_RGB16:
vo_draw_alpha_rgb16(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
break;
case IMGFMT_BGR24:
case IMGFMT_RGB24:
vo_draw_alpha_rgb24(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
break;
case IMGFMT_BGR32:
case IMGFMT_RGB32:
vo_draw_alpha_rgb32(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
break;
case IMGFMT_YV12:
case IMGFMT_I420:
case IMGFMT_IYUV:
case IMGFMT_YVU9:
case IMGFMT_IF09:
case IMGFMT_Y800:
case IMGFMT_Y8:
vo_draw_alpha_yv12(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
break;
case IMGFMT_YUY2:
vo_draw_alpha_yuy2(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
break;
case IMGFMT_UYVY:
vo_draw_alpha_yuy2(w,h,src,srca,stride,dst+1,vc->lastimg->stride[0]);
break;
default:
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: tried to draw OSD on an usnupported pixel format\n");
}
}
static void add_osd_to_lastimg(struct vo *vo)
{
struct priv *vc = vo->priv;
if(vc->osd) {
osd_draw_text(vc->osd, vc->lastimg->w, vc->lastimg->h, add_osd_to_lastimg_draw_func, vc);
}
}
static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
{
struct priv *vc = vo->priv;
struct encode_lavc_context *ectx = vo->encode_lavc_ctx;
int i, size;
AVFrame *frame;
AVCodecContext *avc;
int64_t frameipts;
double nextpts;
if (!vc)
return;
if (!encode_lavc_start(ectx)) {
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: skipped initial video frame (probably because audio is not there yet)\n");
return;
}
avc = vc->stream->codec;
if (vc->worst_time_base.den == 0) {
//if (avc->time_base.num / avc->time_base.den >= vc->stream->time_base.num / vc->stream->time_base.den)
if (avc->time_base.num * (double) vc->stream->time_base.den >=
vc->stream->time_base.num * (double) avc->time_base.den) {
mp_msg(MSGT_ENCODE, MSGL_V, "vo-lavc: NOTE: using codec time base "
"(%d/%d) for frame dropping; the stream base (%d/%d) is "
"not worse.\n", (int)avc->time_base.num,
(int)avc->time_base.den, (int)vc->stream->time_base.num,
(int)vc->stream->time_base.den);
vc->worst_time_base = avc->time_base;
vc->worst_time_base_is_stream = 0;
} else {
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: not using codec time "
"base (%d/%d) for frame dropping; the stream base (%d/%d) "
"is worse.\n", (int)avc->time_base.num,
(int)avc->time_base.den, (int)vc->stream->time_base.num,
(int)vc->stream->time_base.den);
vc->worst_time_base = vc->stream->time_base;
vc->worst_time_base_is_stream = 1;
}
// NOTE: we use the following "axiom" of av_rescale_q:
// if time base A is worse than time base B, then
// av_rescale_q(av_rescale_q(x, A, B), B, A) == x
// this can be proven as long as av_rescale_q rounds to nearest, which
// it currently does
// av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B"
// and:
// av_rescale_q(av_rescale_q(x, A, B), B, A) * A
// == "round av_rescale_q(x, A, B)*B to nearest multiple of A"
// == "round 'round x*A to nearest multiple of B' to nearest multiple of A"
//
// assume this fails. Then there is a value of x*A, for which the
// nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[.
// Absurd, as this range MUST contain at least one multiple of B.
}
double timeunit = (double)vc->worst_time_base.num / vc->worst_time_base.den;
// fix the discontinuity pts offset
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
nextpts = pts;
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
}
// set next allowed output pts value
nextpts = pts + ectx->discontinuity_pts_offset + timeunit;
if (nextpts > ectx->next_in_pts)
ectx->next_in_pts = nextpts;
// vc->lastipts is MP_NOPTS_VALUE, or the start time of vc->lastframe
if (mpi) {
if (pts == MP_NOPTS_VALUE) {
// NOTE: this even applies to ectx->options->copyts!
if (vc->lastipts == MP_NOPTS_VALUE)
frameipts = 0;
else
frameipts = vc->lastipts + 1;
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: pts was missing, using %d - "
"consider using -ofps or -vf fixpts\n", (int) frameipts);
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
ectx->last_video_in_pts += timeunit;
// calculate backwards to set vc->lastpts matchingly
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
} else {
double outpts;
if (ectx->options->rawts)
outpts = pts;
else if (ectx->options->copyts)
outpts = pts + ectx->discontinuity_pts_offset;
else {
double duration = 0;
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
duration = pts - ectx->last_video_in_pts;
if (duration < 0)
duration = timeunit; // XXX warn about discontinuity?
outpts = vc->lastpts + duration;
if (ectx->audio_pts_offset != MP_NOPTS_VALUE) {
double adj = outpts - pts - ectx->audio_pts_offset;
adj = FFMIN(adj, duration * 0.1);
adj = FFMAX(adj, -duration * 0.1);
outpts -= adj;
}
}
vc->lastpts = outpts;
ectx->last_video_in_pts = pts;
frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->stream))
/ timeunit + 0.5);
}
} else {
if (vc->lastipts == MP_NOPTS_VALUE)
frameipts = 0;
else
frameipts = vc->lastipts + 1;
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
}
// never-drop mode
if (ectx->options->neverdrop && frameipts <= vc->lastipts) {
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: -oneverdrop increased pts by %d\n",
(int) (vc->lastipts - frameipts + 1));
frameipts = vc->lastipts + 1;
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
}
if (vc->lastipts != MP_NOPTS_VALUE) {
frame = avcodec_alloc_frame();
// we have a valid image in lastimg
while (vc->lastipts < frameipts) {
int64_t thisduration = vc->harddup ? 1 : (frameipts - vc->lastipts);
AVPacket packet;
avcodec_get_frame_defaults(frame);
// this is a nop, unless the worst time base is the STREAM time base
frame->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
avc->time_base);
for (i = 0; i < 4; i++) {
frame->data[i] = vc->lastimg->planes[i];
frame->linesize[i] = vc->lastimg->stride[i];
}
frame->quality = avc->global_quality;
av_init_packet(&packet);
packet.data = vc->buffer;
packet.size = vc->buffer_size;
size = encode_video(vo, frame, &packet);
write_packet(vo, size, &packet);
vc->lastipts += thisduration;
++vc->lastdisplaycount;
}
av_free(frame);
}
if (!mpi) {
// finish encoding
do {
AVPacket packet;
av_init_packet(&packet);
packet.data = vc->buffer;
packet.size = vc->buffer_size;
size = encode_video(vo, NULL, &packet);
write_packet(vo, size, &packet);
} while (size > 0);
} else {
if (frameipts >= vc->lastframeipts) {
if (vc->lastframeipts != MP_NOPTS_VALUE && vc->lastdisplaycount != 1)
mp_msg(MSGT_ENCODE, MSGL_INFO,
"vo-lavc: Frame at pts %d got displayed %d times\n",
(int) vc->lastframeipts, vc->lastdisplaycount);
copy_mpi(vc->lastimg, mpi);
add_osd_to_lastimg(vo);
// palette hack
if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
vc->lastimg->imgfmt == IMGFMT_BGR8)
memcpy(vc->lastimg->planes[1], mpi->planes[1], 1024);
vc->lastframeipts = vc->lastipts = frameipts;
if (ectx->options->rawts && vc->lastipts < 0) {
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: why does this happen? DEBUG THIS! vc->lastipts = %lld\n", (long long) vc->lastipts);
vc->lastipts = -1;
}
vc->lastdisplaycount = 0;
} else
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: Frame at pts %d got dropped "
"entirely because pts went backwards\n", (int) frameipts);
}
}
static int control(struct vo *vo, uint32_t request, void *data)
{
struct priv *vc = vo->priv;
switch (request) {
case VOCTRL_QUERY_FORMAT:
return query_format(vo, *((uint32_t *)data));
case VOCTRL_DRAW_IMAGE:
draw_image(vo, (mp_image_t *)data, vo->next_pts);
return 0;
case VOCTRL_SET_YUV_COLORSPACE:
vc->colorspace = *(struct mp_csp_details *)data;
if (vc->stream) {
encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
}
return 1;
case VOCTRL_GET_YUV_COLORSPACE:
*(struct mp_csp_details *)data = vc->colorspace;
return 1;
}
return VO_NOTIMPL;
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct priv *vc = vo->priv;
vc->osd = osd;
if(vc->lastimg)
osd_update(vc->osd, vc->lastimg->w, vc->lastimg->h);
}
static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
{
}
static void check_events(struct vo *vo)
{
}
const struct vo_driver video_out_lavc = {
.is_new = true,
.buffer_frames = false,
.info = &(const struct vo_info_s){
"video encoding using libavcodec",
"lavc",
"Nicolas George <george@nsup.org>, Rudolf Polzer <divVerent@xonotic.org>",
""
},
.preinit = preinit,
.config = config,
.control = control,
.uninit = uninit,
.check_events = check_events,
.draw_osd = draw_osd,
.flip_page_timed = flip_page_timed,
};
// vim: sw=4 ts=4 et

View File

@ -252,6 +252,8 @@ typedef struct MPContext {
struct screenshot_ctx *screenshot_ctx;
char *track_layout_hash;
struct encode_lavc_context *encode_lavc_ctx;
} MPContext;

View File

@ -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

View File

@ -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

View File

@ -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