1
mirror of https://github.com/mpv-player/mpv synced 2024-08-04 14:59:58 +02:00

Merge branch 'osd_changes' into master

Conflicts:
	DOCS/man/en/options.rst
This commit is contained in:
wm4 2012-11-01 02:12:47 +01:00
commit 84829a4ea1
80 changed files with 3347 additions and 5092 deletions

View File

@ -94,7 +94,7 @@ dsound (Windows only)
null
Produces no audio output but maintains video playback speed. Use
``--nosound`` for benchmarking.
``--no-audio`` for benchmarking.
pcm
raw PCM/wave file writer audio output

View File

@ -43,24 +43,27 @@ General changes for mplayer2 to mpv
decades old hardware
* Removal of support for dead platforms
* Generally improved MS Windows support (dealing with unicode filenames,
improved ``vo_direct3d``, improve window handling)
improved ``--vo=direct3d``, improved window handling)
* Better OSD rendering (using libass). This has full unicode support, and
languages like Arabic should be better supported.
* Cleaned up terminal output (nicer status line, less useless noise)
* Support for playing URLs of popular streaming sites directly
(e.g. ``mpv https://www.youtube.com/watch?v=...``)
* Improved OpenGL output (``vo_opengl``)
* Improved OpenGL output (``--vo=opengl-hq``)
* Make ``--softvol`` default (**mpv** is not a mixer control panel)
* Improved support for .cue files
* Screenshot improvements (can save screenshots as JPG, configurable filenames)
* Screenshot improvements (can save screenshots as JPG or PNG, configurable
filenames, support for taking screenshots with or without subtitles)
* Removal of teletext support
* Replace image VOs (``vo_jpeg`` etc.) with ``vo_image``
* Remove ``vo_gif89a``, ``vo_md5sum``, ``vo_yuv4mpeg`` (the plan is to merge
divverent's encoding branch, which provides support for all of these)
* Replace image VOs (``--vo=jpeg`` etc.) with ``--vo=image``
* 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)
(Remove ``--vo=gif89a``, ``--vo=md5sum``, ``--vo=yuv4mpeg``, as encoding can
handle these use cases.)
* Image subtitles (DVDs etc.) are rendered in color and use more correct
positioning
* General code cleanups
* Many more changes
@ -80,7 +83,7 @@ Command line switches
know about this change.
(The new syntax was introduced in mplayer2.)
* In general, negating a switch like ``-noopt`` now has to be written as
* In general, negating switches like ``-noopt`` now have to be written as
``-no-opt``, or better ``--no-opt``.
* Per-file options are not the default anymore. You can explicitly specify
file local options. See ``Usage`` section.
@ -151,8 +154,8 @@ Other
mplayer. A proper slave mode application needed tons of code and hacks to get
it right. The main problem is that slave mode is a bad and incomplete
interface, and to get around that, applications parsed output messages
intended for users. It's hard to know just which messages are parsed by some
slave mode application, and as such it's virtually impossible to improve
intended for users. It's hard to know which messages exactly are parsed by
slave mode applications. This makes it virtually impossible to improve
terminal output intended for users without possibly breaking something.
This is absolutely insane, and **mpv** will not try to keep slave mode
@ -164,7 +167,7 @@ Policy for removed features
Features are a good thing, because they make users happy. As such, it is
attempted to preserve useful features as far as possible. But if a feature is
likely to be not used by many, and causes otherwise problems, it will be
likely to be not used by many, and causes problems otherwise, it will be
removed. Developers should not be burdened with fixing or cleaning up code that
has no actual use.

View File

@ -14,16 +14,24 @@ with shift.
A list of special keys can be obtained with
| **mplayer** --input=keylist
| **mpv** --input=keylist
In general, keys can be combined with ``Shift``, ``Ctrl`` and ``Alt``:
| ctrl+q quit
**mpv** can be started in input test mode, which displays key bindings and the
commands they're bound to on the OSD, instead of running the commands:
| **mpv** --input=test --demuxer=rawvideo --rawvideo=w=1280:h=720 /dev/zero
(Commands which normally close the player will not work in this mode, and you
must kill **mpv** externally to make it exit.)
General input command syntax
----------------------------
`[Shift+][Ctrl+][Alt+][Meta+]<key> [<prefix>] <command> (<argument>)*`
`[Shift+][Ctrl+][Alt+][Meta+]<key> [<prefixes>] <command> (<argument>)*`
Newlines always start a new binding. ``#`` starts a comment (outside of quoted
string arguments). To bind commands to the ``#`` key, ``SHARP`` can be used.
@ -45,7 +53,7 @@ ignore
disabling default bindings, without disabling all bindings with
``--input=default-bindings=no``.
seek <seconds> [relative|absolute|absolute-percent] [default-precise|exact|keyframes]
seek <seconds> [relative|absolute|absolute-percent|- [default-precise|exact|keyframes]]
Change the playback position. By default, seeks by a relative amount of
seconds.
@ -56,7 +64,7 @@ seek <seconds> [relative|absolute|absolute-percent] [default-precise|exact|keyfr
absolute
Seek to a given time.
absolute-percent
Seek to agiven percent position.
Seek to a given percent position.
The third argument defines how exact the seek is:
@ -87,28 +95,31 @@ cycle <property> [up|down]
speed_mult <value>
Multiply the ``speed`` property by the given value.
screenshot [single|each-frame] [video|window]
screenshot [subtitles|video|window|- [single|each-frame]]
Take a screenshot.
First argument:
<subtitles> (default)
Save the video image, in its original resolution, and with subtitles.
Some video outputs may still include the OSD in the output under certain
circumstances.
<video>
Like ``subtitles``, but typically without OSD or subtitles. The exact
behavior depends on the selected video output.
<window>
Save the contents of the mpv window. Typically scaled, with OSD and
subtitles. The exact behavior depends on the selected video output, and
if no support is available, this will act like ``video``.
Second argument:
<single> (default)
Take a single screenshot.
<each-frame>
Take a screenshot each frame. Issue this command again to stop taking
screenshots.
Second argument:
<video> (default)
Save the video image, in its original resolution. Typically without
OSD or subtitles, but the exact behavior depends on the selected video
output.
<window>
Save the contents of the mplayer window. Typically scaled, with OSD and
subtitles. The exact behavior depends on the selected video output, and
if not support is available, this will act like ``video``.
playlist_next [weak|force]
Go to the next entry on the playlist.
@ -164,7 +175,7 @@ print_text "<string>"
Print text to stdout. The string can contain properties, which are expanded
like in ``--playing-msg``.
show_text "<string>" [<duration>] [<level>]
show_text "<string>" [<duration>|- [<level>]]
Show text on the OSD. The string can contain properties, which are expanded
like in ``--playing-msg``. This can be used to show playback time, filename,
and so on.
@ -204,9 +215,9 @@ osd-bar
If possible, show a bar with this command. Seek commands will show the
progress bar, property changing commands may show the newly set value.
osd-msg
If possible, show an OSD message with this command. The seek command shows
the current playback time (like ``show_progress``), property changing
commands show the newly set value as text.
If possible, show an OSD message with this command. Seek command show
the current playback time, property changing commands show the newly set
value as text.
osd-msg-bar
Combine osd-bar and osd-msg.
@ -220,7 +231,7 @@ pausing_keep_force. (Should these be made official?)
Properties
----------
Properties are used to set mplayer options during runtime, or to query arbitrary
Properties are used to set mpv options during runtime, or to query arbitrary
information. They can be manipulated with the ``set``/``add``/``cycle``
commands, and retrieved with ``show_text``, or anything else that uses property
expansion. (See ``--playing-msg`` how properties are expanded.)
@ -253,7 +264,7 @@ edition x current MKV edition number
titles number of DVD titles
chapters number of chapters
editions number of MKV editions
angle current DVD angle
angle x current DVD angle
metadata metadata key/value pairs
metadata/<key> value of metedata entry <key>
pause x pause status (bool)
@ -303,8 +314,8 @@ sub-scale x subtitle font size multiplicator
ass-use-margins x see ``--ass-use-margins``
ass-vsfilter-aspect-compat x see ``--ass-vsfilter-aspect-compat``
ass-style-override x see ``--ass-style-override``
tv-brightness
tv-contrast
tv-saturation
tv-hue
tv-brightness x
tv-contrast x
tv-saturation x
tv-hue x
=========================== = ==================================================

View File

@ -18,9 +18,6 @@ Synopsis
| **mpv** \mf://[filemask|\@listfile] [-mf options] [options]
| **mpv** [cdda|cddb]://track[-endtrack][:speed][/device] [options]
| **mpv** [file|mms[t]|http|http\_proxy|rt[s]p|ftp|udp|unsv|icyx|noicyx|smb]:// [user:pass\@]URL[:port] [options]
| **mpv** \sdp://file [options]
| **mpv** \mpst://host[:port]/URL [options]
| **mpv** \tivo://host/[list|llist|fsid] [options]
DESCRIPTION
@ -48,13 +45,11 @@ keyboard control
LEFT and RIGHT
Seek backward/forward 10 seconds. Shift+arrow does a 1 second exact seek
(see ``--hr-seek``; currently modifier keys like shift only work if used in
an X output window).
(see ``--hr-seek``).
UP and DOWN
Seek forward/backward 1 minute. Shift+arrow does a 5 second exact seek (see
``--hr-seek``; currently modifier keys like shift only work if used in an X
output window).
``--hr-seek``).
PGUP and PGDWN
Seek forward/backward 10 minutes.
@ -149,20 +144,15 @@ V
Toggle subtitle VSFilter aspect compatibility mode. See
``--ass-vsfilter-aspect-compat`` for more info.
C (``--capture`` only)
Start/stop capturing the primary stream.
r and t
Move subtitles up/down.
i (``--edlout`` mode only)
Set start or end of an EDL skip and write it out to the given file.
s
Take a screenshot.
S
Start/stop taking screenshots.
Take a screenshot, without subtitles. (Whether this works depends on VO
driver support.)
I
Show filename on the OSD.
@ -173,7 +163,7 @@ P
! and @
Seek to the beginning of the previous/next chapter.
D (``--vo=vdpau``, ``--vf=yadif``, ``--vf=kerndeint`` only)
D (``--vo=vdpau``, ``--vf=yadif`` only)
Activate/deactivate deinterlacer.
A
@ -183,8 +173,7 @@ c
Change YUV colorspace.
(The following keys are valid only when using a video output that supports the
corresponding adjustment, the software equalizer (``--vf=eq`` or ``--vf=eq2``)
or hue filter (``--vf=hue``).)
corresponding adjustment, or the software equalizer (``--vf=eq2``).)
1 and 2
Adjust contrast.
@ -304,7 +293,7 @@ affects all files. Example:
| file2.mkv | --a --b --c |
+-----------+-------------------------+
Also, if any option is changed at runtime (via slave commands), they aren't
Also, if any option is changed at runtime (via input commands), they aren't
reset when a new file is played.
Sometimes, it's useful to change options per-file. This can be achieved by
@ -340,9 +329,8 @@ your configuration directory (e.g. ``/etc/mpv`` or
User specific options override system-wide options and options given on the
command line override either. The syntax of the configuration files is
``option=<value>``, everything after a *#* is considered a comment. Options
that work without values can be enabled by setting them to *yes* or *1* or
*true* and disabled by setting them to *no* or *0* or *false*. Even suboptions
can be specified in this way.
that work without values can be enabled by setting them to *yes* and disabled by
setting them to *no*. Even suboptions can be specified in this way.
You can also write file-specific configuration files. If you wish to have a
configuration file for a file called 'movie.avi', create a file named
@ -358,8 +346,8 @@ as the file played and then tries to load any file-specific configuration.
*EXAMPLE MPV CONFIGURATION FILE:*
| # Use gl3 video output by default.
| vo=gl3
| # Use opengl video output by default.
| vo=opengl
| # I love practicing handstands while watching videos.
| flip=yes
| # Decode multiple files from PNG,
@ -415,12 +403,13 @@ Taking screenshots
==================
Screenshots of the currently played file can be taken using the 'screenshot'
slave mode command, which is by default bound to the ``s`` key. Files named
``shotNNNN.png`` will be saved in the working directory, using the first
input mode command, which is by default bound to the ``s`` key. Files named
``shotNNNN.jpg`` will be saved in the working directory, using the first
available number - no files will be overwritten.
A screenshot will usually contain the unscaled video contents at the end of the
video filter chain. Some video output drivers will include subtitles and OSD in
video filter chain and subtitles. By default the ``S`` takes screenshots without
subtitles. Some video output drivers will always include subtitles and OSD in
the video frame as well - this is because of technical restrictions.
The ``screenshot`` video filter is normally not required when using a
@ -442,10 +431,6 @@ ENVIRONMENT VARIABLES
There are a number of environment variables that can be used to control the
behavior of mpv.
``MPV_CHARSET`` (see also ``--msgcharset``)
Convert console messages to the specified charset (default: autodetect). A
value of "noconv" means no conversion.
``MPV_HOME``
Directory where mpv looks for user settings.
@ -519,29 +504,6 @@ libdvdcss:
``HOME``
FIXME: Document this.
libao2:
``AUDIOSERVER``
Specifies the Network Audio System server to which the nas audio
output driver should connect and the transport that should be used. If
unset DISPLAY is used instead. The transport can be one of tcp and
unix. Syntax is ``tcp/<somehost>:<someport>``,
``<somehost>:<instancenumber>`` or ``[unix]:<instancenumber>``. The
NAS base port is 8000 and <instancenumber> is added to that.
*EXAMPLES*:
``AUDIOSERVER=somehost:0``
Connect to NAS server on somehost using default port and
transport.
``AUDIOSERVER=tcp/somehost:8000``
Connect to NAS server on somehost listening on TCP port 8000.
``AUDIOSERVER=(unix)?:0``
Connect to NAS server instance 0 on localhost using unix domain
sockets.
``DISPLAY``
FIXME: Document this.
osdep:
``TERM``
FIXME: Document this.
@ -550,9 +512,6 @@ libvo:
``DISPLAY``
FIXME: Document this.
``FRAMEBUFFER``
FIXME: Document this.
``HOME``
FIXME: Document this.
@ -639,7 +598,7 @@ Play DVD video from a directory with VOB files:
``mpv dvd://1 --dvd-device=/path/to/directory/``
Stream from HTTP:
``mpv http://mpv.hq/example.avi``
``mpv http://example.com/example.avi``
Stream using RTSP:
``mpv rtsp://server.example.com/streamName``
@ -647,24 +606,6 @@ Stream using RTSP:
input from standard V4L:
``mpv tv:// --tv=driver=v4l:width=640:height=480:outfmt=i420 --vc=rawi420 --vo=xv``
Play DTS-CD with passthrough:
``mpv --ac=hwdts --rawaudio=format=0x2001 --cdrom-device=/dev/cdrom cdda://``
You can also use ``--afm=hwac3`` instead of ``--ac=hwdts``. Adjust
``/dev/cdrom`` to match the CD-ROM device on your system. If your external
receiver supports decoding raw DTS streams, you can directly play it via
``cdda://`` without setting format, hwac3 or hwdts.
Play a 6-channel AAC file with only two speakers:
``mpv --rawaudio=format=0xff --demuxer=rawaudio --af=pan=2:.32:.32:.39:.06:.06:.39:.17:-.17:-.17:.17:.33:.33 adts_he-aac160_51.aac``
You might want to play a bit with the pan values (e.g multiply with a
value) to increase volume or avoid clipping.
checkerboard invert with geq filter:
``mpv --vf=geq='128+(p(X\,Y)-128)*(0.5-gt(mod(X/SW\,128)\,64))*(0.5-gt(mod(Y/SH\,128)\,64))*4'``
AUTHORS
=======

View File

@ -19,16 +19,6 @@
*NOTE*: See ``--ac=help`` for a full list of available codecs.
*EXAMPLE*:
:``--ac=mp3acm``: Force the l3codeca.acm MP3 codec.
:``--ac=mad,``: Try libmad first, then fall back on others.
:``--ac=hwac3,a52,``: Try hardware AC-3 passthrough, software AC-3, then
others.
:``--ac=hwdts,``: Try hardware DTS passthrough, then fall back on
others.
:``--ac=-ffmp3,``: Skip FFmpeg's MP3 decoder.
--af=<filter1[=parameter1:parameter2:...],filter2,...>
Specify a list of audio filters to apply to the audio stream. See
:ref:`audio_filters` for details and descriptions of the available filters.
@ -80,7 +70,6 @@
*EXAMPLE*:
:``--afm=ffmpeg``: Try FFmpeg's libavcodec codecs first.
:``--afm=acm,dshow``: Try Win32 codecs first.
--aid=<ID|auto|no>
Select audio channel. ``auto`` selects the default, ``no`` disables audio.
@ -125,9 +114,9 @@
features in other subtitle formats by conversion to ASS markup. Enabled by
default if the player was compiled with libass support.
*NOTE*: Some of the other subtitle options were written for the old
non-libass subtitle rendering system and may not work the same way or at
all with libass rendering enabled.
*NOTE*: With ``--ass``, some of the ASS subtitle options work for non-ASS
text subtitles only, because ASS subtitles include their own styling
information.
--ass-border-color=<value>
Sets the border (outline) color for text subtitles. The color format is
@ -158,8 +147,6 @@
:1: FreeType autohinter, light mode
:2: FreeType autohinter, normal mode
:3: font native hinter
:0-3 + 4: The same, but hinting will only be performed if the OSD is
rendered at screen resolution and will therefore not be scaled.
The default value is 0 (no hinting).
@ -242,6 +229,8 @@
is also used to set the maximum delivery bandwidth allowing faster cache
filling and stream dumping.
*NOTE*: probably broken/useless.
--untimed
Do not sleep when outputting video frames. Useful for benchmarks when used
with --no-audio.
@ -319,7 +308,7 @@
Add <value> sectors to the values reported when addressing tracks. May
be negative.
(no)skip
(no-)skip
(Never) accept imperfect data reconstruction.
--cdrom-device=<path>
@ -392,12 +381,6 @@
:BT.601: ITU-R BT.601 (SD)
:BT.709: ITU-R BT.709 (HD)
:SMPTE-240M: SMPTE-240M
:sd: alias for BT.601
:hd: alias for BT.709
:0: compatibility alias for auto (do not use)
:1: compatibility alias for BT.601 (do not use)
:2: compatibility alias for BT.709 (do not use)
:3: compatibility alias for SMPTE-240M (do not use)
--colormatrix-input-range=<color-range>
YUV color levels used with YUV to RGB conversion. This option is only
@ -489,10 +472,6 @@
``--display=xtest.localdomain:0``
--double, --no-double
Double buffering. The option to disable this exists mostly for debugging
purposes and should not normally be used.
--doubleclick-time
Time in milliseconds to recognize two consecutive button presses as a
double-click (default: 300).
@ -542,6 +521,8 @@
EDL entries later. See http://www.mplayerhq.hu/DOCS/HTML/en/edl.html for
details.
*NOTE*: broken.
--embeddedfonts, --no-embeddedfonts
Use fonts embedded in Matroska container files and ASS scripts (default:
enabled). These fonts can be used for SSA/ASS subtitle rendering
@ -569,14 +550,6 @@
reliable enough), the filename extension is used to select the demuxer.
Always falls back on content-based demuxer selection.
--ffactor=<number>
Resample the font alphamap. Can be:
:0: plain white fonts
:0.75: very narrow black outline (default)
:1: narrow black outline
:10: bold black outline
--field-dominance=<-1-1>
Set first field for interlaced content. Useful for deinterlacers that
double the framerate: ``--vf=tfields=1``, ``--vf=yadif=1`` and
@ -597,15 +570,6 @@
--flip
Flip image upside-down.
--flip-hebrew
Turns on flipping subtitles using FriBiDi.
--flip-hebrew-commas, --no-flip-hebrew-commas
Enabled by default.
Change FriBiDi's assumptions about the placements of commas in subtitles.
Use this if commas in subtitles are shown at the start of a sentence
instead of at the end.
--font=<pattern-or-filename>
Specify font to use for OSD and for subtitles that do not themselves
specify a particular font. See also ``--subfont``. With fontconfig enabled
@ -643,6 +607,8 @@
--fps=<float>
Override video framerate. Useful if the original value is wrong or missing.
*NOTE*: Works in ``--no-correct-pts`` mode only.
--framedrop=<no|yes|hard>
Skip displaying some frames to maintain A/V sync on slow systems. Video
filters are not applied to such frames. For B-frames even decoding is
@ -650,15 +616,11 @@
decoding and output of any frame can be skipped, and will lead to an even
worse playback experience.
Practical use of this feature is questionable. Disabled by default.
*NOTE*: Practical use of this feature is questionable. Disabled by default.
--frames=<number>
Play/convert only first <number> frames, then quit.
--fribidi-charset=<name>
Specifies the character set that will be passed to FriBiDi when decoding
non-UTF-8 subtitles (default: ISO8859-8).
--fullscreen
--fs
Fullscreen playback (centers movie, and paints black bands around it).
@ -761,7 +723,7 @@
Command that is executed every 30 seconds during playback via *system()* -
i.e. using the shell.
*NOTE*: mpv uses this command without any checking, it is your
*NOTE*: mpv uses this command without any checking. It is your
responsibility to ensure it does not cause security problems (e.g. make
sure to use full paths if "." is in your path like on Windows). It also
only works when playing video (i.e. not with ``--no-video`` but works with
@ -779,7 +741,7 @@
--heartbeat-cmd="gnome-screensaver-command -p" file``
--help
Show short summary of options and key bindings.
Show short summary of options.
--hr-seek=<no|absolute|yes>
Select when to use precise seeks that are not limited to keyframes. Such
@ -831,14 +793,7 @@
drivers.
--identify
Shorthand for ``--msglevel=identify=4``. Show file parameters in an easily
parseable format. Also prints more detailed information about subtitle and
audio track languages and IDs. In some cases you can get more information
by using ``--msglevel=identify=6``. For example, for a DVD or Blu-ray it
will list the chapters and time length of each title, as well as a disk
ID. Combine this with ``--frames=0`` to suppress all video output. The
wrapper script ``TOOLS/midentify.sh`` suppresses the other mpv output
and (hopefully) shellescapes the filenames.
Deprecated. Use ``TOOLS/mpv_identify.sh``.
--idle
Makes mpv wait idly instead of quitting when there is no file to play.
@ -898,7 +853,7 @@
ar-rate
Number of key presses to generate per second on autorepeat.
(no)default-bindings
(no-)default-bindings
Use the key bindings that mpv ships with by default.
keylist
@ -1308,7 +1263,7 @@
values mean multiples of the default range. Negative numbers mean you can
zoom in up to a factor of ``--panscanrange=+1``. E.g. ``--panscanrange=-3``
allows a zoom factor of up to 4. This feature is experimental. Do not
report bugs unless you are using ``--vo=gl``.
report bugs unless you are using ``--vo=opengl``.
--passwd=<password>
Used with some network protocols. Specify password for HTTP authentication.
@ -1337,7 +1292,7 @@
$$
Expands to ``$``.
$}
Expands to ``}``. (To produce this character inside rexursive
Expands to ``}``. (To produce this character inside recursive
expansion.)
$>
Disable property expansion and special handling of ``$`` for the rest
@ -1359,19 +1314,12 @@
sources. Do NOT use ``--playlist`` with random internet sources or files
you don't trust!
*NOTE*: This option is considered an entry so options found after it will
apply only to the elements of this playlist.
FIXME: This needs to be clarified and documented thoroughly.
--pp=<quality>
This option only works when decoding video with Win32 DirectShow DLLs with
internal postprocessing routines. See also ``--vf=pp``. Set the DLL
postprocess level. The valid range of ``--pp`` values varies by codec, it
is mostly 0-6, where 0=disable, 6=slowest/best.
See also ``--vf=pp``.
--pphelp
Show a summary about the available postprocess filters and their usage.
See also ``--vf=pp``.
--prefer-ipv4
@ -1573,18 +1521,6 @@
images may cover the movie window, though. May not work with all video
output drivers.
--rtsp-destination
Used with ``rtsp://`` URLs to force the destination IP address to be
bound. This option may be useful with some RTSP server which do not send
RTP packets to the right interface. If the connection to the RTSP server
fails, use ``-v`` to see which IP address mpv tries to bind to and try
to force it to one assigned to your computer instead.
--rtsp-port
Used with ``rtsp://`` URLs to force the client's port number. This option
may be useful if you are behind a router and want to forward the RTSP
stream from the server to a specific client.
--saturation=<-100-100>
Adjust the saturation of the video signal (default: 0). You can get
grayscale output with this option. Not supported by all video output
@ -1665,9 +1601,9 @@
insert the number of the current month as number. You have to use
multiple ``%tX`` specifiers to build a full date/time string.
``%{prop[:fallback text]}``
Insert the value of the slave property 'prop'. E.g. %{filename} is the
same as %f. If the property doesn't exist or is not available, nothing
is inserted, unless a fallback is specified.
Insert the value of the slave property 'prop'. E.g. ``%{filename}`` is
the same as ``%f``. If the property doesn't exist or is not available,
an error text is inserted, unless a fallback is specified.
``%%``
Replaced with the ``%`` character itself.
@ -1741,10 +1677,10 @@
:yes: always use the volume filter
:auto: prefer the volume filter if the audio driver uses the system mixer (default)
The intention with ``auto`` is to avoid changing system mixer settings with
default settings. mpv is a video player, not a mixer panel. On the other
hand, mixer controls should be used for sound servers like PulseAudio, which
provide per-application volume.
The intention of ``auto`` is to avoid changing system mixer settings from
within mpv with default settings. mpv is a video player, not a mixer panel.
On the other hand, mixer controls are enabled for sound servers like
PulseAudio, which provide per-application volume.
--softvol-max=<10.0-10000.0>
Set the maximum amplification level in percent (default: 200). A value of
@ -1755,30 +1691,6 @@
--speed=<0.01-100>
Slow down or speed up playback by the factor given as parameter.
--spuaa=<mode>
Antialiasing/scaling mode for DVD/VOBsub. A value of 16 may be added to
<mode> in order to force scaling even when original and scaled frame size
already match. This can be employed to e.g. smooth subtitles with gaussian
blur. Available modes are:
:0: none (fastest, very ugly)
:1: approximate (broken?)
:2: full (slow)
:3: bilinear (default, fast and not too bad)
:4: uses swscaler gaussian blur (looks very good)
--spualign=<-1-2>
Specify how SPU (DVD/VOBsub) subtitles should be aligned.
:-1: Original position
:0: Align at top (original behavior, default).
:1: Align at center.
:2: Align at bottom.
--spugauss=<0.0-3.0>
Variance parameter of gaussian used by ``--spuaa=4``. Higher means more
blur (default: 1.0).
--srate=<Hz>
Select the output sample rate to be used (of course sound cards have
limits on this). If the sample frequency selected is different from that
@ -1806,11 +1718,12 @@
:chs=<h>: chroma horizontal shifting
:cvs=<v>: chroma vertical shifting
*EXAMPLE*: ``--vf=scale=-ssf=lgb=3.0``
*EXAMPLE*: ``--vf=scale --ssf=lgb=3.0``
--sstep=<sec>
Skip <sec> seconds after every frame. Since mpv will only seek to
the next keyframe unless you use ``--hr-seek`` this may be inexact.
Skip <sec> seconds after every frame.
*NOTE*: without ``--hr-seek``, skipping will snap to keyframes.
--stereo=<mode>
Select type of MP2/MP3 stereo output.
@ -1829,15 +1742,6 @@
Use/display these subtitle files. Only one file can be displayed at the
same time.
--sub-bg-alpha=<0-255>
Specify the alpha channel value for subtitles and OSD backgrounds. Big
values mean more transparency. 0 means completely transparent.
--sub-bg-color=<0-255>
Specify the color value for subtitles and OSD backgrounds. Currently
subtitles are grayscale so this value is equivalent to the intensity of
the color. 255 means white and 0 black.
--sub-demuxer=<[+]name>
Force subtitle demuxer type for ``--subfile``. Using a '+' before the name
will force it, this will skip some checks! Give the demuxer name as
@ -1899,42 +1803,17 @@
Delays subtitles by <sec> seconds. Can be negative.
--subfile=<filename>
(BETA CODE)
Currently useless. Same as ``--audiofile``, but for subtitle streams
(OggDS?).
Open the given file with a demuxer, and use its subtitle streams. Same as
``--audiofile``, but for subtitle streams.
Use ``--sub`` for normal text subtitle files.
--subfont=<pattern-or-filename>
Sets the subtitle font (see ``--font``). If no ``--subfont`` is given,
``--font`` is used for subtitles too.
--subfont-autoscale=<0-3>
Sets the autoscale mode.
*NOTE*: 0 means that text scale and OSD scale are font heights in points.
The mode can be:
:0: no autoscale
:1: proportional to movie height
:2: proportional to movie width
:3: proportional to movie diagonal (default)
--subfont-blur=<0-8>
Sets the font blur radius (default: 2).
--subfont-encoding=<value>
Sets the font encoding. When set to 'unicode', all the glyphs from the
font file will be rendered and unicode will be used (default: unicode).
--subfont-osd-scale=<0-100>
Sets the autoscale coefficient of the OSD elements (default: 4).
--subfont-outline=<0-8>
Sets the font outline thickness (default: 2).
--subfont-text-scale=<0-100>
Sets the subtitle text autoscale coefficient as percentage of the screen
size (default: 3.5).
Factor for the text subtitle and OSD font size (default: 6).
--subfps=<rate>
Specify the framerate of the subtitle file (default: movie fps).
@ -1947,10 +1826,6 @@
position of the subtitle in % of the screen height.
Can be useful with ``--vf=expand``.
--subwidth=<10-100>
Specify the maximum width of subtitles on the screen. Useful for TV-out.
The value is the width of the subtitle in % of the screen width.
--sws=<n>
Specify the software scaler algorithm to be used with the ``--zoom``
option. This affects video output drivers which lack hardware
@ -1982,8 +1857,9 @@
console. The escape sequence should move the pointer to the beginning of
the line used for the OSD and clear it (default: ``^[[A\r^[[K``).
--title
Set the window title. Properties are expanded (see ``--playing-msg``).
--title=<string>
Set the window title. Properties are expanded on playback start
(see ``--playing-msg``).
--tv=<option1:option2:...>
This option tunes various properties of the TV capture module. For
@ -2196,9 +2072,6 @@
the device (default: 50). A signal strength higher than this value will
indicate that the currently scanning channel is active.
--unicode
Tells mpv to handle the subtitle file as unicode.
--use-filedir-conf
Look for a file-specific configuration file in the same directory as the
file that is being played.
@ -2228,13 +2101,6 @@
*NOTE*: See ``--vc=help`` for a full list of available codecs.
*EXAMPLE*:
:``--vc=divx``: Force Win32/VfW DivX codec, no fallback.
:``--vc=-divxds,-divx,``: Skip Win32 DivX codecs.
:``--vc=ffmpeg12,mpeg12,``: Try libavcodec's MPEG-1/2 codec, then
libmpeg2, then others.
--vf=<filter1[=parameter1:parameter2:...],filter2,...>
Specify a list of video filters to apply to the video stream. See
:ref:`video_filters` for details and descriptions of the available filters.
@ -2249,14 +2115,6 @@
*NOTE*: See ``--vfm=help`` for a full list of available codec families.
*EXAMPLE*:
:``--vfm=ffmpeg,dshow,vfw``:
Try the libavcodec, then Directshow, then VfW codecs and fall back on
others, if they do not work.
:``--vfm=xanim``:
Try XAnim codecs first.
--vid=<ID|auto|no>
Select video channel. ``auto`` selects the default, ``no`` disables video.
@ -2282,21 +2140,15 @@
(default: 3).
--volume=<-1-100>
Set the startup volume in the mixer, either hardware or software (if used
with ``--softvol``). A value of -1 (the default) will not change the
volume. See also ``--af=volume``.
Set the startup volume. A value of -1 (the default) will not change the
volume. See also ``--softvol``.
--no-vsync
Tries to disable vsync.
Tries to disable vsync. (Effective with some video outputs only.)
--wid=<ID>
(X11, OpenGL and DirectX only)
This tells mpv to attach to an existing window. Useful to embed
mpv in a browser (e.g. the plugger extension). Earlier this option
always filled the given window completely, thus aspect scaling, panscan,
etc were no longer handled by mpv but had to be managed by the
application that created the window. Now aspect is maintained by default.
If you don't want that use ``--no-keepaspect``.
(X11 and win32 only)
This tells mpv to attach to an existing window.See ``--slave-broken``.
--x=<width>
Scale image to width <width> (if software/hardware scaling is available).
@ -2349,6 +2201,4 @@
Disables aspect calculations.
--zoom
Allow software scaling, where available. This will allow scaling with
output drivers (like x11) that do not support hardware scaling,
where mpv disables scaling by default for performance reasons.
Useful for ``--vo=x11`` only. Enables scaling.

View File

@ -663,15 +663,15 @@ screenshot
not always safe to insert this filter by default. See the
``Taking screenshots`` section for details.
ass
Moves SSA/ASS subtitle rendering to an arbitrary point in the filter
sub
Moves subtitle rendering to an arbitrary point in the filter
chain, or force subtitle rendering in the video filter as opposed to using
video output EOSD support. See the ``--ass`` option.
video output OSD support.
*EXAMPLE*:
``--vf=ass,eq``
Moves SSA/ASS rendering before the eq filter. This will put both
``--vf=sub,eq``
Moves sub rendering before the eq filter. This will put both
subtitle colors and video under the influence of the video equalizer
settings.

View File

@ -58,6 +58,8 @@ x11 (X11 only)
Shared memory video output driver without hardware acceleration that works
whenever X11 is present.
*NOTE*: this is a fallback only, and shouldn't be normally used.
vdpau (X11 only)
Uses the VDPAU interface to display and optionally also decode video.
Hardware decoding is used with ``--vc=ffmpeg12vdpau``,
@ -171,8 +173,8 @@ direct3d_shaders (Windows only)
Never render YUV video with more than 8 bits per component.
(Using this flag will force software conversion to 8 bit.)
disable-eosd
Disable EOSD rendering for subtitles.
disable-osd
Disable OSD rendering for subtitles.
(Using this flag might force the insertion of the 'ass' video filter,
which will render the subtitles in software.)
@ -233,8 +235,9 @@ opengl
OpenGL video output driver. It supports extended scaling methods, dithering
and color management.
By default, it tries to use fast and fail-safe settings. Use the driver
``opengl-hq`` to use this driver with a high quality rendering preset.
By default, it tries to use fast and fail-safe settings. Use the alias
``opengl-hq`` to use this driver with defaults set for high quality
rendering.
Requires at least OpenGL 2.1 and the GL_ARB_texture_rg extension. For older
drivers, ``opengl-old`` may work.

View File

@ -150,8 +150,7 @@ libmpcodecs/:
vf_*.c and vf.c form the video filter chain. They are fed by the video
decoder, and output the filtered images to the VOs though vf_vo.c. By
default, no video filters (except vf_vo) are used, though sometimes
vf_ass.c is inserted for rendering ASS subtitles, when the VO can't.
default, no video filters (except vf_vo) are used.
ad_*.c and dec_audio.c/ad.c handle audio decoding. The audio filter chain is
separately in libaf.

View File

@ -38,9 +38,7 @@ SRCS_COMMON-$(FTP) += stream/stream_ftp.c
SRCS_COMMON-$(GIF) += libmpdemux/demux_gif.c
SRCS_COMMON-$(HAVE_SYS_MMAN_H) += libaf/af_export.c osdep/mmap_anon.c
SRCS_COMMON-$(LADSPA) += libaf/af_ladspa.c
SRCS_COMMON-$(LIBASS) += libmpcodecs/vf_ass.c \
sub/ass_mp.c \
sub/sd_ass.c \
SRCS_COMMON-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c
SRCS_COMMON-$(LIBBLURAY) += stream/stream_bluray.c
SRCS_COMMON-$(LIBBS2B) += libaf/af_bs2b.c
@ -144,6 +142,7 @@ SRCS_COMMON = asxparser.c \
libmpcodecs/img_format.c \
libmpcodecs/mp_image.c \
libmpcodecs/pullup.c \
libmpcodecs/sws_utils.c \
libmpcodecs/vd.c \
libmpcodecs/vd_ffmpeg.c \
libmpcodecs/vf.c \
@ -170,6 +169,7 @@ SRCS_COMMON = asxparser.c \
libmpcodecs/vf_screenshot.c \
libmpcodecs/vf_softpulldown.c \
libmpcodecs/vf_stereo3d.c \
libmpcodecs/vf_sub.c \
libmpcodecs/vf_swapuv.c \
libmpcodecs/vf_unsharp.c \
libmpcodecs/vf_vo.c \
@ -197,8 +197,6 @@ SRCS_COMMON = asxparser.c \
libmpdemux/mf.c \
libmpdemux/mp_taglists.c \
libmpdemux/video.c \
libvo/osd.c \
libvo/eosd_packer.c \
libvo/bitmap_packer.c \
osdep/numcores.c \
osdep/io.c \
@ -216,6 +214,8 @@ SRCS_COMMON = asxparser.c \
sub/sd_lavc.c \
sub/spudec.c \
sub/sub.c \
sub/img_convert.c \
sub/draw_bmp.c \
sub/subassconvert.c \
sub/subreader.c \
sub/vobsub.c \
@ -234,7 +234,7 @@ SRCS_MPLAYER-$(COREVIDEO) += libvo/vo_corevideo.m
SRCS_MPLAYER-$(DIRECT3D) += libvo/vo_direct3d.c libvo/w32_common.c
SRCS_MPLAYER-$(DSOUND) += libao2/ao_dsound.c
SRCS_MPLAYER-$(GL) += libvo/gl_common.c libvo/vo_opengl.c \
libvo/vo_opengl_old.c pnm_loader.c
libvo/gl_osd.c libvo/vo_opengl_old.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

@ -502,31 +502,18 @@ const m_option_t common_opts[] = {
{"sub-delay", &sub_delay, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
{"subfps", &sub_fps, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
OPT_MAKE_FLAGS("autosub", sub_auto, 0),
{"unicode", &sub_unicode, CONF_TYPE_FLAG, 0, 0, 1, NULL},
{"utf8", &sub_utf8, CONF_TYPE_FLAG, 0, 0, 1, NULL},
{"sub-forced-only", &forced_subs_only, CONF_TYPE_FLAG, 0, 0, 1, NULL},
// specify IFO file for VOBSUB subtitle
{"ifo", &spudec_ifo, CONF_TYPE_STRING, 0, 0, 0, NULL},
// enable Closed Captioning display
{"overlapsub", &suboverlap_enabled, CONF_TYPE_FLAG, 0, 0, 2, NULL},
{"sub-bg-color", &sub_bg_color, CONF_TYPE_INT, CONF_RANGE, 0, 255, NULL},
{"sub-bg-alpha", &sub_bg_alpha, CONF_TYPE_INT, CONF_RANGE, 0, 255, NULL},
{"sub-no-text-pp", &sub_no_text_pp, CONF_TYPE_FLAG, 0, 0, 1, NULL},
{"sub-fuzziness", &sub_match_fuzziness, CONF_TYPE_INT, CONF_RANGE, 0, 2, NULL},
{"font", &font_name, CONF_TYPE_STRING, 0, 0, 0, NULL},
{"subfont", &sub_font_name, CONF_TYPE_STRING, 0, 0, 0, NULL},
{"ffactor", &font_factor, CONF_TYPE_FLOAT, CONF_RANGE, 0.0, 10.0, NULL},
{"sub-pos", &sub_pos, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL},
{"subwidth", &sub_width_p, CONF_TYPE_INT, CONF_RANGE, 10, 100, NULL},
{"spualign", &spu_alignment, CONF_TYPE_INT, CONF_RANGE, -1, 2, NULL},
{"spuaa", &spu_aamode, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL},
{"spugauss", &spu_gaussvar, CONF_TYPE_FLOAT, CONF_RANGE, 0.0, 3.0, NULL},
{"subfont-encoding", &subtitle_font_encoding, CONF_TYPE_STRING, 0, 0, 0, NULL},
{"subfont-text-scale", &text_font_scale_factor, CONF_TYPE_FLOAT, CONF_RANGE, 0, 100, NULL},
{"subfont-osd-scale", &osd_font_scale_factor, CONF_TYPE_FLOAT, CONF_RANGE, 0, 100, NULL},
{"subfont-blur", &subtitle_font_radius, CONF_TYPE_FLOAT, CONF_RANGE, 0, 8, NULL},
{"subfont-outline", &subtitle_font_thickness, CONF_TYPE_FLOAT, CONF_RANGE, 0, 8, NULL},
{"subfont-autoscale", &subtitle_autoscale, CONF_TYPE_INT, CONF_RANGE, 0, 3, NULL},
OPT_MAKE_FLAGS("ass", ass_enabled, 0),
OPT_FLOATRANGE("ass-font-scale", ass_font_scale, 0, 0, 100),
OPT_FLOATRANGE("ass-line-spacing", ass_line_spacing, 0, -1000, 1000),
@ -618,7 +605,6 @@ const m_option_t mplayer_opts[]={
OPT_INTRANGE("bpp", vo_dbpp, 0, 0, 32),
{"colorkey", &vo_colorkey, CONF_TYPE_INT, 0, 0, 0, NULL},
{"no-colorkey", &vo_colorkey, CONF_TYPE_FLAG, 0, 0, 0x1000000, NULL},
{"double", &vo_doublebuffering, CONF_TYPE_FLAG, 0, 0, 1, NULL},
// wait for v-sync (gl)
{"vsync", &vo_vsync, CONF_TYPE_FLAG, 0, 0, 1, NULL},
{"panscan", &vo_panscan, CONF_TYPE_FLOAT, CONF_RANGE, 0, 1.0, NULL},

View File

@ -1302,7 +1302,8 @@ static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
switch (action) {
case M_PROPERTY_SET:
*pscale = *(float *) arg;
vo_osd_resized();
vo_osd_changed(OSDTYPE_SUBTITLE);
vo_osd_changed(OSDTYPE_OSD);
return M_PROPERTY_OK;
case M_PROPERTY_GET:
*(float *)arg = *pscale;

View File

@ -102,10 +102,9 @@ TAB cycle program
i edl_mark # for use with --edlout mode
T cycle ontop # toggle video window ontop of other windows
f cycle fullscreen # toggle fullscreen
s screenshot # take a png screenshot
S screenshot each-frame # ...on every frame
Alt+s screenshot - window # take a screenshot of window contents
Alt+S screenshot each-frame window # ...on every frame
s screenshot # take a screenshot
S screenshot video # ...without subtitles
Alt+s screenshot - each-frame # automatically screenshot every frame
w add panscan -0.1 # zoom out with -panscan 0 -fs
e add panscan +0.1 # in
POWER quit

View File

@ -39,9 +39,8 @@
#include "libmpcodecs/vf.h"
#include "fmt-conversion.h"
//for sws_getContextFromCmdLine_hq and mp_sws_set_colorspace
#include "libmpcodecs/vf_scale.h"
#include "libvo/csputils.h"
#include "libmpcodecs/sws_utils.h"
#include "libmpcodecs/vf.h"
#include "m_option.h"
@ -260,12 +259,14 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts)
return get_writer(opts)->file_ext;
}
int write_image(struct mp_image *image, const struct mp_csp_details *csp,
const struct image_writer_opts *opts, const char *filename)
int write_image(struct mp_image *image, const struct image_writer_opts *opts,
const char *filename)
{
struct mp_image *allocated_image = NULL;
struct image_writer_opts defs = image_writer_opts_defaults;
bool is_anamorphic = image->w != image->width || image->h != image->height;
int d_w = image->display_w ? image->display_w : image->w;
int d_h = image->display_h ? image->display_h : image->h;
bool is_anamorphic = image->w != d_w || image->h != d_h;
if (!opts)
opts = &defs;
@ -284,28 +285,17 @@ int write_image(struct mp_image *image, const struct mp_csp_details *csp,
}
}
// Caveat: - no colorspace/levels conversion done if pixel formats equal
// - RGB->YUV assumes BT.601
// - color levels broken in various ways thanks to libswscale
if (image->imgfmt != destfmt || is_anamorphic) {
struct mp_image *dst = alloc_mpi(image->w, image->h, destfmt);
struct mp_image *dst = alloc_mpi(d_w, d_h, destfmt);
vf_clone_mpi_attributes(dst, image);
struct SwsContext *sws = sws_getContextFromCmdLine_hq(image->width,
image->height,
image->imgfmt,
dst->width,
dst->height,
dst->imgfmt);
int flags = SWS_LANCZOS | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP |
SWS_ACCURATE_RND | SWS_BITEXACT;
struct mp_csp_details colorspace = MP_CSP_DETAILS_DEFAULTS;
if (csp)
colorspace = *csp;
// This is a property of the output device; images always use
// full-range RGB.
colorspace.levels_out = MP_CSP_LEVELS_PC;
mp_sws_set_colorspace(sws, &colorspace);
sws_scale(sws, (const uint8_t **)image->planes, image->stride, 0,
image->height, dst->planes, dst->stride);
sws_freeContext(sws);
mp_image_swscale(dst, image, flags);
allocated_image = dst;
image = dst;
@ -328,3 +318,10 @@ int write_image(struct mp_image *image, const struct mp_csp_details *csp,
return success;
}
void dump_png(struct mp_image *image, const char *filename)
{
struct image_writer_opts opts = image_writer_opts_defaults;
opts.format = "png";
write_image(image, &opts, filename);
}

View File

@ -46,5 +46,8 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts);
* accordingly. Setting w and width or h and height to different values
* can be used to store snapshots of anamorphic video.
*/
int write_image(struct mp_image *image, const struct mp_csp_details *csp,
const struct image_writer_opts *opts, const char *filename);
int write_image(struct mp_image *image, const struct image_writer_opts *opts,
const char *filename);
// Debugging helper.
void dump_png(struct mp_image *image, const char *filename);

View File

@ -159,10 +159,11 @@ static const mp_cmd_t mp_cmds[] = {
{ MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", { ARG_INT, ARG_INT } },
{ MP_CMD_SCREENSHOT, "screenshot", {
OARG_CHOICE(0, ({"single", 0}, {"0", 0},
{"each-frame", 1}, {"1", 1})),
OARG_CHOICE(0, ({"video", 0}, {"0", 0},
{"window", 1}, {"1", 1})),
OARG_CHOICE(2, ({"video", 0},
{"window", 1},
{"subtitles", 2})),
OARG_CHOICE(0, ({"single", 0},
{"each-frame", 1})),
}},
{ MP_CMD_LOADFILE, "loadfile", {
ARG_STRING,

View File

@ -26,11 +26,30 @@
#include "libmpcodecs/img_format.h"
#include "libmpcodecs/mp_image.h"
#include "libmpcodecs/sws_utils.h"
#include "libvo/fastmemcpy.h"
#include "libavutil/mem.h"
#include "libavutil/common.h"
void mp_image_alloc_planes(mp_image_t *mpi) {
if (mpi->imgfmt == IMGFMT_BGRA) {
mpi->stride[0]=FFALIGN(mpi->width*4,SWS_MIN_BYTE_ALIGN);
mpi->planes[0]=av_malloc(mpi->stride[0]*mpi->height);
mpi->flags|=MP_IMGFLAG_ALLOCATED;
return;
}
if (mpi->imgfmt == IMGFMT_444P16 || mpi->imgfmt == IMGFMT_444P) {
int bp = mpi->imgfmt == IMGFMT_444P16 ? 2 : 1;
mpi->stride[0]=FFALIGN(mpi->width*bp,SWS_MIN_BYTE_ALIGN);
mpi->stride[1]=mpi->stride[2]=mpi->stride[0];
int imgsize = mpi->stride[0] * mpi->height;
mpi->planes[0]=av_malloc(imgsize*3);
mpi->planes[1]=mpi->planes[0]+imgsize;
mpi->planes[2]=mpi->planes[1]+imgsize;
mpi->flags|=MP_IMGFLAG_ALLOCATED;
return;
}
// IF09 - allocate space for 4. plane delta info - unused
if (mpi->imgfmt == IMGFMT_IF09) {
mpi->planes[0]=av_malloc(mpi->bpp*mpi->width*(mpi->height+2)/8+
@ -40,7 +59,9 @@ void mp_image_alloc_planes(mp_image_t *mpi) {
if (!mpi->planes[0])
abort(); //out of memory
if (mpi->flags&MP_IMGFLAG_PLANAR) {
int bpp = IMGFMT_IS_YUVP16(mpi->imgfmt)? 2 : 1;
// FIXME this code only supports same bpp for all planes, and bpp divisible
// by 8. Currently the case for all planar formats.
int bpp = MP_IMAGE_PLANAR_BITS_PER_PIXEL_ON_PLANE(mpi, 0) / 8;
// YV12/I420/YVU9/IF09. feel free to add other planar formats here...
mpi->stride[0]=mpi->stride[3]=bpp*mpi->width;
if(mpi->num_planes > 2){
@ -82,15 +103,15 @@ mp_image_t* alloc_mpi(int w, int h, unsigned long int fmt) {
void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi) {
if(mpi->flags&MP_IMGFLAG_PLANAR){
memcpy_pic(dmpi->planes[0],mpi->planes[0], mpi->w, mpi->h,
memcpy_pic(dmpi->planes[0],mpi->planes[0], MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 0), mpi->h,
dmpi->stride[0],mpi->stride[0]);
memcpy_pic(dmpi->planes[1],mpi->planes[1], mpi->chroma_width, mpi->chroma_height,
memcpy_pic(dmpi->planes[1],mpi->planes[1], MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 1), mpi->chroma_height,
dmpi->stride[1],mpi->stride[1]);
memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height,
memcpy_pic(dmpi->planes[2], mpi->planes[2], MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 2), mpi->chroma_height,
dmpi->stride[2],mpi->stride[2]);
} else {
memcpy_pic(dmpi->planes[0],mpi->planes[0],
mpi->w*(dmpi->bpp/8), mpi->h,
MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, 0), mpi->h,
dmpi->stride[0],mpi->stride[0]);
}
}
@ -180,6 +201,9 @@ void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt){
mpi->flags|=MP_IMGFLAG_SWAPPED;
case IMGFMT_YUY2:
mpi->chroma_x_shift = 1;
mpi->chroma_y_shift = 1;
mpi->chroma_width=(mpi->width>>1);
mpi->chroma_height=(mpi->height>>1);
mpi->bpp=16;
mpi->num_planes=1;
return;
@ -225,3 +249,32 @@ void free_mp_image(mp_image_t* mpi){
talloc_free(mpi);
}
enum mp_csp mp_image_csp(struct mp_image *img)
{
if (img->colorspace != MP_CSP_AUTO)
return img->colorspace;
return (img->flags & MP_IMGFLAG_YUV) ? MP_CSP_BT_601 : MP_CSP_RGB;
}
enum mp_csp_levels mp_image_levels(struct mp_image *img)
{
if (img->levels != MP_CSP_LEVELS_AUTO)
return img->levels;
return (img->flags & MP_IMGFLAG_YUV) ? MP_CSP_LEVELS_TV : MP_CSP_LEVELS_PC;
}
void mp_image_set_colorspace_details(struct mp_image *image,
struct mp_csp_details *csp)
{
if (image->flags & MP_IMGFLAG_YUV) {
image->colorspace = csp->format;
if (image->colorspace == MP_CSP_AUTO)
image->colorspace = MP_CSP_BT_601;
image->levels = csp->levels_in;
if (image->levels == MP_CSP_LEVELS_AUTO)
image->levels = MP_CSP_LEVELS_TV;
} else {
image->colorspace = MP_CSP_RGB;
image->levels = MP_CSP_LEVELS_PC;
}
}

View File

@ -22,7 +22,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "mp_msg.h"
#include "libvo/csputils.h"
//--------- codec's requirements (filled by the codec/vf) ---------
@ -109,9 +111,10 @@ typedef struct mp_image {
int number;
unsigned char bpp; // bits/pixel. NOT depth! for RGB it will be n*8
unsigned int imgfmt;
int width,height; // stored dimensions
int width,height; // internal to vf.c, do not use (stored dimensions)
int w,h; // visible dimensions
unsigned char* planes[MP_MAX_PLANES];
int display_w,display_h; // if set (!= 0), anamorphic size
uint8_t *planes[MP_MAX_PLANES];
int stride[MP_MAX_PLANES];
char * qscale;
int qstride;
@ -124,6 +127,8 @@ typedef struct mp_image {
int chroma_height;
int chroma_x_shift; // horizontal
int chroma_y_shift; // vertical
enum mp_csp colorspace;
enum mp_csp_levels levels;
int usage_count;
/* for private use by filter or vo driver (to store buffer id or dmpi) */
void* priv;
@ -137,4 +142,21 @@ mp_image_t* alloc_mpi(int w, int h, unsigned long int fmt);
void mp_image_alloc_planes(mp_image_t *mpi);
void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi);
enum mp_csp mp_image_csp(struct mp_image *img);
enum mp_csp_levels mp_image_levels(struct mp_image *img);
struct mp_csp_details;
void mp_image_set_colorspace_details(struct mp_image *image,
struct mp_csp_details *csp);
// this macro requires img_format.h to be included too:
#define MP_IMAGE_PLANAR_BITS_PER_PIXEL_ON_PLANE(mpi, p) \
(IMGFMT_IS_YUVP16((mpi)->imgfmt) ? 16 : 8)
#define MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(mpi, p) \
(((mpi)->flags & MP_IMGFLAG_PLANAR) \
? MP_IMAGE_PLANAR_BITS_PER_PIXEL_ON_PLANE(mpi, p) \
: (mpi)->bpp)
#define MP_IMAGE_BYTES_PER_ROW_ON_PLANE(mpi, p) \
((MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(mpi, p) * ((mpi)->w >> (p ? mpi->chroma_x_shift : 0)) + 7) / 8)
#endif /* MPLAYER_MP_IMAGE_H */

199
libmpcodecs/sws_utils.c Normal file
View File

@ -0,0 +1,199 @@
/*
* 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 <assert.h>
#include <libavutil/opt.h>
#include "libmpcodecs/sws_utils.h"
#include "libmpcodecs/mp_image.h"
#include "libmpcodecs/img_format.h"
#include "fmt-conversion.h"
#include "libvo/csputils.h"
#include "mp_msg.h"
//global sws_flags from the command line
int sws_flags = 2;
float sws_lum_gblur = 0.0;
float sws_chr_gblur = 0.0;
int sws_chr_vshift = 0;
int sws_chr_hshift = 0;
float sws_chr_sharpen = 0.0;
float sws_lum_sharpen = 0.0;
//global srcFilter
static SwsFilter *src_filter = NULL;
void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam,
SwsFilter **dstFilterParam)
{
static int firstTime = 1;
*flags = 0;
if (firstTime) {
firstTime = 0;
*flags = SWS_PRINT_INFO;
} else if (mp_msg_test(MSGT_VFILTER, MSGL_DBG2))
*flags = SWS_PRINT_INFO;
if (src_filter)
sws_freeFilter(src_filter);
src_filter = sws_getDefaultFilter(
sws_lum_gblur, sws_chr_gblur,
sws_lum_sharpen, sws_chr_sharpen,
sws_chr_hshift, sws_chr_vshift, verbose > 1);
switch (sws_flags) {
case 0: *flags |= SWS_FAST_BILINEAR;
break;
case 1: *flags |= SWS_BILINEAR;
break;
case 2: *flags |= SWS_BICUBIC;
break;
case 3: *flags |= SWS_X;
break;
case 4: *flags |= SWS_POINT;
break;
case 5: *flags |= SWS_AREA;
break;
case 6: *flags |= SWS_BICUBLIN;
break;
case 7: *flags |= SWS_GAUSS;
break;
case 8: *flags |= SWS_SINC;
break;
case 9: *flags |= SWS_LANCZOS;
break;
case 10: *flags |= SWS_SPLINE;
break;
default: *flags |= SWS_BILINEAR;
break;
}
*srcFilterParam = src_filter;
*dstFilterParam = NULL;
}
// will use sws_flags & src_filter (from cmd line)
static struct SwsContext *sws_getContextFromCmdLine2(int srcW, int srcH,
int srcFormat, int dstW,
int dstH, int dstFormat,
int extraflags)
{
int flags;
SwsFilter *dstFilterParam, *srcFilterParam;
enum PixelFormat dfmt, sfmt;
dfmt = imgfmt2pixfmt(dstFormat);
sfmt = imgfmt2pixfmt(srcFormat);
if (srcFormat == IMGFMT_RGB8 || srcFormat == IMGFMT_BGR8)
sfmt = PIX_FMT_PAL8;
sws_getFlagsAndFilterFromCmdLine(&flags, &srcFilterParam, &dstFilterParam);
return sws_getContext(srcW, srcH, sfmt, dstW, dstH, dfmt, flags |
extraflags, srcFilterParam, dstFilterParam,
NULL);
}
struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat,
int dstW, int dstH,
int dstFormat)
{
return sws_getContextFromCmdLine2(srcW, srcH, srcFormat, dstW, dstH,
dstFormat,
0);
}
struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH,
int srcFormat, int dstW,
int dstH,
int dstFormat)
{
return sws_getContextFromCmdLine2(
srcW, srcH, srcFormat, dstW, dstH, dstFormat,
SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP |
SWS_ACCURATE_RND | SWS_BITEXACT);
}
bool mp_sws_supported_format(int imgfmt)
{
enum PixelFormat av_format = imgfmt2pixfmt(imgfmt);
return av_format != PIX_FMT_NONE && sws_isSupportedInput(av_format)
&& sws_isSupportedOutput(av_format);
}
static int mp_csp_to_sws_colorspace(enum mp_csp csp)
{
switch (csp) {
case MP_CSP_BT_601: return SWS_CS_ITU601;
case MP_CSP_BT_709: return SWS_CS_ITU709;
case MP_CSP_SMPTE_240M: return SWS_CS_SMPTE240M;
default: return SWS_CS_DEFAULT;
}
}
void mp_image_swscale(struct mp_image *dst, struct mp_image *src,
int my_sws_flags)
{
enum PixelFormat s_fmt = imgfmt2pixfmt(src->imgfmt);
if (src->imgfmt == IMGFMT_RGB8 || src->imgfmt == IMGFMT_BGR8)
s_fmt = PIX_FMT_PAL8;
int s_csp = mp_csp_to_sws_colorspace(mp_image_csp(src));
int s_range = mp_image_levels(src) == MP_CSP_LEVELS_PC;
enum PixelFormat d_fmt = imgfmt2pixfmt(dst->imgfmt);
int d_csp = mp_csp_to_sws_colorspace(mp_image_csp(dst));
int d_range = mp_image_levels(dst) == MP_CSP_LEVELS_PC;
// Work around libswscale bug #1852 (fixed in ffmpeg commit 8edf9b1fa):
// setting range flags for RGB gives random bogus results.
// Newer libswscale always ignores range flags for RGB.
bool s_yuv = src->flags & MP_IMGFLAG_YUV;
bool d_yuv = dst->flags & MP_IMGFLAG_YUV;
s_range = s_range && s_yuv;
d_range = d_range && d_yuv;
struct SwsContext *sws = sws_alloc_context();
av_opt_set_int(sws, "sws_flags", my_sws_flags, 0);
av_opt_set_int(sws, "srcw", src->w, 0);
av_opt_set_int(sws, "srch", src->h, 0);
av_opt_set_int(sws, "src_format", s_fmt, 0);
av_opt_set_int(sws, "dstw", dst->w, 0);
av_opt_set_int(sws, "dsth", dst->h, 0);
av_opt_set_int(sws, "dst_format", d_fmt, 0);
sws_setColorspaceDetails(sws, sws_getCoefficients(s_csp), s_range,
sws_getCoefficients(d_csp), d_range,
0, 1 << 16, 1 << 16);
int res = sws_init_context(sws, NULL, NULL);
assert(res >= 0);
sws_scale(sws, (const uint8_t *const *) src->planes, src->stride,
0, src->h, dst->planes, dst->stride);
sws_freeContext(sws);
}
// vim: ts=4 sw=4 et tw=80

33
libmpcodecs/sws_utils.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef MPLAYER_SWS_UTILS_H
#define MPLAYER_SWS_UTILS_H
#include <stdbool.h>
#include <libswscale/swscale.h>
struct mp_image;
struct mp_csp_details;
// libswscale currently requires 16 bytes alignment for row pointers and
// strides. Otherwise, it will print warnings and use slow codepaths.
// Guaranteed to be a power of 2 and > 1.
#define SWS_MIN_BYTE_ALIGN 16
void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam,
SwsFilter **dstFilterParam);
struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat,
int dstW, int dstH,
int dstFormat);
struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH,
int srcFormat, int dstW,
int dstH,
int dstFormat);
int mp_sws_set_colorspace(struct SwsContext *sws, struct mp_csp_details *csp);
bool mp_sws_supported_format(int imgfmt);
void mp_image_swscale(struct mp_image *dst, struct mp_image *src,
int my_sws_flags);
#endif /* MP_SWS_UTILS_H */
// vim: ts=4 sw=4 et tw=80

View File

@ -774,6 +774,8 @@ static struct mp_image *decode(struct sh_video *sh, struct demux_packet *packet,
swap_palette(mpi->planes[1]);
#endif
mpi->colorspace = sh->colorspace;
mpi->levels = sh->color_range;
mpi->qscale = pic->qscale_table;
mpi->qstride = pic->qstride;
mpi->pict_type = pic->pict_type;

View File

@ -64,7 +64,7 @@ extern const vf_info_t vf_info_divtc;
extern const vf_info_t vf_info_softskip;
extern const vf_info_t vf_info_screenshot;
extern const vf_info_t vf_info_screenshot_force;
extern const vf_info_t vf_info_ass;
extern const vf_info_t vf_info_sub;
extern const vf_info_t vf_info_yadif;
extern const vf_info_t vf_info_stereo3d;
extern const vf_info_t vf_info_dlopen;
@ -102,9 +102,7 @@ static const vf_info_t *const filter_list[] = {
&vf_info_delogo,
&vf_info_phase,
&vf_info_divtc,
#ifdef CONFIG_ASS
&vf_info_ass,
#endif
&vf_info_sub,
&vf_info_yadif,
&vf_info_stereo3d,
&vf_info_dlopen,
@ -541,6 +539,12 @@ void vf_clone_mpi_attributes(mp_image_t *dst, mp_image_t *src)
if (dst->width == src->width && dst->height == src->height) {
dst->qstride = src->qstride;
dst->qscale = src->qscale;
dst->display_w = src->display_w;
dst->display_h = src->display_h;
}
if ((dst->flags & MP_IMGFLAG_YUV) == (src->flags & MP_IMGFLAG_YUV)) {
dst->colorspace = src->colorspace;
dst->levels = src->levels;
}
}

View File

@ -99,16 +99,14 @@ struct vf_ctrl_screenshot {
#define VFCTRL_SET_PP_LEVEL 5 // set postprocessing level
#define VFCTRL_SET_EQUALIZER 6 // set color options (brightness,contrast etc)
#define VFCTRL_GET_EQUALIZER 8 // get color options (brightness,contrast etc)
#define VFCTRL_DRAW_OSD 7
#define VFCTRL_DUPLICATE_FRAME 11 // For encoding - encode zero-change frame
#define VFCTRL_SKIP_NEXT_FRAME 12 // For encoding - drop the next frame that passes thru
#define VFCTRL_FLUSH_FRAMES 13 // For encoding - flush delayed frames
#define VFCTRL_SCREENSHOT 14 // Take screenshot, arg is vf_ctrl_screenshot
#define VFCTRL_INIT_EOSD 15 // Select EOSD renderer
#define VFCTRL_DRAW_EOSD 16 // Render EOSD */
#define VFCTRL_INIT_OSD 15 // Filter OSD renderer present?
#define VFCTRL_SET_DEINTERLACE 18 // Set deinterlacing status
#define VFCTRL_GET_DEINTERLACE 19 // Get deinterlacing status
/* Hack to make the OSD state object available to vf_expand and vf_ass which
/* Hack to make the OSD state object available to vf_sub which
* access OSD/subtitle state outside of normal OSD draw time. */
#define VFCTRL_SET_OSD_OBJ 20
#define VFCTRL_SET_YUV_COLORSPACE 22 // arg is struct mp_csp_details*

View File

@ -32,8 +32,7 @@
#include "fmt-conversion.h"
#include "mpbswap.h"
#include "libswscale/swscale.h"
#include "vf_scale.h"
#include "libmpcodecs/sws_utils.h"
#include "libvo/csputils.h"
// VOFLAG_SWSCALE
@ -68,8 +67,6 @@ static struct vf_priv_s {
//===========================================================================//
void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam, SwsFilter **dstFilterParam);
static const unsigned int outfmt_list[]={
// YUV:
IMGFMT_444P,
@ -647,84 +644,6 @@ static int vf_open(vf_instance_t *vf, char *args){
return 1;
}
//global sws_flags from the command line
int sws_flags=2;
//global srcFilter
static SwsFilter *src_filter= NULL;
float sws_lum_gblur= 0.0;
float sws_chr_gblur= 0.0;
int sws_chr_vshift= 0;
int sws_chr_hshift= 0;
float sws_chr_sharpen= 0.0;
float sws_lum_sharpen= 0.0;
void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam, SwsFilter **dstFilterParam)
{
static int firstTime=1;
*flags=0;
if(firstTime)
{
firstTime=0;
*flags= SWS_PRINT_INFO;
}
else if( mp_msg_test(MSGT_VFILTER,MSGL_DBG2) ) *flags= SWS_PRINT_INFO;
if(src_filter) sws_freeFilter(src_filter);
src_filter= sws_getDefaultFilter(
sws_lum_gblur, sws_chr_gblur,
sws_lum_sharpen, sws_chr_sharpen,
sws_chr_hshift, sws_chr_vshift, verbose>1);
switch(sws_flags)
{
case 0: *flags|= SWS_FAST_BILINEAR; break;
case 1: *flags|= SWS_BILINEAR; break;
case 2: *flags|= SWS_BICUBIC; break;
case 3: *flags|= SWS_X; break;
case 4: *flags|= SWS_POINT; break;
case 5: *flags|= SWS_AREA; break;
case 6: *flags|= SWS_BICUBLIN; break;
case 7: *flags|= SWS_GAUSS; break;
case 8: *flags|= SWS_SINC; break;
case 9: *flags|= SWS_LANCZOS; break;
case 10:*flags|= SWS_SPLINE; break;
default:*flags|= SWS_BILINEAR; break;
}
*srcFilterParam= src_filter;
*dstFilterParam= NULL;
}
// will use sws_flags & src_filter (from cmd line)
static struct SwsContext *sws_getContextFromCmdLine2(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat, int extraflags)
{
int flags;
SwsFilter *dstFilterParam, *srcFilterParam;
enum PixelFormat dfmt, sfmt;
dfmt = imgfmt2pixfmt(dstFormat);
sfmt = imgfmt2pixfmt(srcFormat);
if (srcFormat == IMGFMT_RGB8 || srcFormat == IMGFMT_BGR8) sfmt = PIX_FMT_PAL8;
sws_getFlagsAndFilterFromCmdLine(&flags, &srcFilterParam, &dstFilterParam);
return sws_getContext(srcW, srcH, sfmt, dstW, dstH, dfmt, flags | extraflags, srcFilterParam, dstFilterParam, NULL);
}
struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat)
{
return sws_getContextFromCmdLine2(srcW, srcH, srcFormat, dstW, dstH, dstFormat, 0);
}
struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat)
{
return sws_getContextFromCmdLine2(srcW, srcH, srcFormat, dstW, dstH, dstFormat,
SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | SWS_ACCURATE_RND | SWS_BITEXACT);
}
/// An example of presets usage
static const struct size_preset {
char* name;

View File

@ -1,28 +0,0 @@
/*
* 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.
*/
#ifndef MPLAYER_VF_SCALE_H
#define MPLAYER_VF_SCALE_H
struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat);
struct SwsContext *sws_getContextFromCmdLine_hq(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat);
struct mp_csp_details;
int mp_sws_set_colorspace(struct SwsContext *sws, struct mp_csp_details *csp);
#endif /* MPLAYER_VF_SCALE_H */

View File

@ -27,7 +27,7 @@
#include "img_format.h"
#include "mp_image.h"
#include "vf.h"
#include "vf_scale.h"
#include "libmpcodecs/sws_utils.h"
#include "fmt-conversion.h"
#include "libvo/fastmemcpy.h"
@ -141,6 +141,7 @@ static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
image = *vf->priv->image;
image.w = vf->priv->image->w;
image.h = vf->priv->image->h;
vf_clone_mpi_attributes(&image, mpi);
vf->priv->image_callback(vf->priv->image_callback_ctx, &image);
vf->priv->store_slices = 0;
}

View File

@ -35,41 +35,25 @@
#include "mp_image.h"
#include "vf.h"
#include "sub/sub.h"
#include "sub/dec_sub.h"
#include "libvo/fastmemcpy.h"
#include "libvo/csputils.h"
#include "m_option.h"
#include "m_struct.h"
#include "sub/ass_mp.h"
#define _r(c) ((c)>>24)
#define _g(c) (((c)>>16)&0xFF)
#define _b(c) (((c)>>8)&0xFF)
#define _a(c) ((c)&0xFF)
#define rgba2y(c) ( (( 263*_r(c) + 516*_g(c) + 100*_b(c)) >> 10) + 16 )
#define rgba2u(c) ( ((-152*_r(c) - 298*_g(c) + 450*_b(c)) >> 10) + 128 )
#define rgba2v(c) ( (( 450*_r(c) - 376*_g(c) - 73*_b(c)) >> 10) + 128 )
static const struct vf_priv_s {
int outh, outw;
unsigned int outfmt;
// 1 = auto-added filter: insert only if chain does not support EOSD already
// 0 = insert always
int auto_insert;
struct mp_csp_details csp;
struct osd_state *osd;
double aspect_correction;
unsigned char *planes[3];
struct line_limits {
uint16_t start;
uint16_t end;
} *line_limits;
} vf_priv_dflt;
struct mp_osd_res dim;
} vf_priv_dflt = {
.csp = MP_CSP_DETAILS_DEFAULTS,
};
static int config(struct vf_instance *vf,
int width, int height, int d_width, int d_height,
@ -87,11 +71,17 @@ static int config(struct vf_instance *vf,
d_height = d_height * vf->priv->outh / height;
}
vf->priv->planes[1] = malloc(vf->priv->outw * vf->priv->outh);
vf->priv->planes[2] = malloc(vf->priv->outw * vf->priv->outh);
vf->priv->line_limits = malloc((vf->priv->outh + 1) / 2 * sizeof(*vf->priv->line_limits));
double dar = (double)d_width / d_height;
double sar = (double)width / height;
vf->priv->aspect_correction = (double)width / height * d_height / d_width;
vf->priv->dim = (struct mp_osd_res) {
.w = vf->priv->outw,
.h = vf->priv->outh,
.mt = opts->ass_top_margin,
.mb = opts->ass_bottom_margin,
.display_par = sar / dar,
.video_par = dar / sar,
};
return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width,
d_height, flags, outfmt);
@ -225,153 +215,16 @@ static int prepare_image(struct vf_instance *vf, mp_image_t *mpi)
return 0;
}
static void update_limits(struct vf_instance *vf, int starty, int endy,
int startx, int endx)
{
starty >>= 1;
endy = (endy + 1) >> 1;
startx >>= 1;
endx = (endx + 1) >> 1;
for (int i = starty; i < endy; i++) {
struct line_limits *ll = vf->priv->line_limits + i;
if (startx < ll->start)
ll->start = startx;
if (endx > ll->end)
ll->end = endx;
}
}
/**
* \brief Copy specified rows from render_context.dmpi to render_context.planes, upsampling to 4:4:4
*/
static void copy_from_image(struct vf_instance *vf)
{
int pl;
for (pl = 1; pl < 3; ++pl) {
int dst_stride = vf->priv->outw;
int src_stride = vf->dmpi->stride[pl];
unsigned char *src = vf->dmpi->planes[pl];
unsigned char *dst = vf->priv->planes[pl];
for (int i = 0; i < (vf->priv->outh + 1) / 2; i++) {
struct line_limits *ll = vf->priv->line_limits + i;
unsigned char *dst_next = dst + dst_stride;
for (int j = ll->start; j < ll->end; j++) {
unsigned char val = src[j];
dst[j << 1] = val;
dst[(j << 1) + 1] = val;
dst_next[j << 1] = val;
dst_next[(j << 1) + 1] = val;
}
src += src_stride;
dst = dst_next + dst_stride;
}
}
}
/**
* \brief Copy all previously copied rows back to render_context.dmpi
*/
static void copy_to_image(struct vf_instance *vf)
{
int pl;
int i, j;
for (pl = 1; pl < 3; ++pl) {
int dst_stride = vf->dmpi->stride[pl];
int src_stride = vf->priv->outw;
unsigned char *dst = vf->dmpi->planes[pl];
unsigned char *src = vf->priv->planes[pl];
unsigned char *src_next = vf->priv->planes[pl] + src_stride;
for (i = 0; i < vf->priv->outh / 2; ++i) {
for (j = vf->priv->line_limits[i].start; j < vf->priv->line_limits[i].end; j++) {
unsigned val = 0;
val += src[j << 1];
val += src[(j << 1) + 1];
val += src_next[j << 1];
val += src_next[(j << 1) + 1];
dst[j] = val >> 2;
}
dst += dst_stride;
src = src_next + src_stride;
src_next = src + src_stride;
}
}
}
static void my_draw_bitmap(struct vf_instance *vf, unsigned char *bitmap,
int bitmap_w, int bitmap_h, int stride,
int dst_x, int dst_y, unsigned color)
{
unsigned char y = rgba2y(color);
unsigned char u = rgba2u(color);
unsigned char v = rgba2v(color);
unsigned char opacity = 255 - _a(color);
unsigned char *src, *dsty, *dstu, *dstv;
int i, j;
mp_image_t *dmpi = vf->dmpi;
src = bitmap;
dsty = dmpi->planes[0] + dst_x + dst_y * dmpi->stride[0];
dstu = vf->priv->planes[1] + dst_x + dst_y * vf->priv->outw;
dstv = vf->priv->planes[2] + dst_x + dst_y * vf->priv->outw;
for (i = 0; i < bitmap_h; ++i) {
for (j = 0; j < bitmap_w; ++j) {
unsigned k = (src[j] * opacity + 255) >> 8;
dsty[j] = (k * y + (255 - k) * dsty[j] + 255) >> 8;
dstu[j] = (k * u + (255 - k) * dstu[j] + 255) >> 8;
dstv[j] = (k * v + (255 - k) * dstv[j] + 255) >> 8;
}
src += stride;
dsty += dmpi->stride[0];
dstu += vf->priv->outw;
dstv += vf->priv->outw;
}
}
static int render_frame(struct vf_instance *vf, mp_image_t *mpi,
const ASS_Image *img)
{
if (img) {
for (int i = 0; i < (vf->priv->outh + 1) / 2; i++)
vf->priv->line_limits[i] = (struct line_limits){65535, 0};
for (const ASS_Image *im = img; im; im = im->next)
update_limits(vf, im->dst_y, im->dst_y + im->h,
im->dst_x, im->dst_x + im->w);
copy_from_image(vf);
while (img) {
my_draw_bitmap(vf, img->bitmap, img->w, img->h, img->stride,
img->dst_x, img->dst_y, img->color);
img = img->next;
}
copy_to_image(vf);
}
return 0;
}
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
{
struct vf_priv_s *priv = vf->priv;
struct MPOpts *opts = vf->opts;
struct osd_state *osd = priv->osd;
ASS_Image *images = 0;
if (pts != MP_NOPTS_VALUE) {
osd->dim = (struct mp_eosd_res){ .w = vf->priv->outw,
.h = vf->priv->outh,
.mt = opts->ass_top_margin,
.mb = opts->ass_bottom_margin };
osd->normal_scale = vf->priv->aspect_correction;
osd->vsfilter_scale = 1;
osd->sub_pts = pts - osd->sub_offset;
osd->support_rgba = false;
struct sub_bitmaps b;
sub_get_bitmaps(osd, &b);
images = b.imgs;
}
prepare_image(vf, mpi);
render_frame(vf, mpi, images);
mp_image_set_colorspace_details(mpi, &priv->csp);
if (pts != MP_NOPTS_VALUE)
osd_draw_on_image(osd, priv->dim, pts, OSD_DRAW_SUB_FILTER, vf->dmpi);
return vf_next_put_image(vf, vf->dmpi, pts);
}
@ -393,19 +246,19 @@ static int control(vf_instance_t *vf, int request, void *data)
case VFCTRL_SET_OSD_OBJ:
vf->priv->osd = data;
break;
case VFCTRL_INIT_EOSD:
return CONTROL_TRUE;
case VFCTRL_DRAW_EOSD:
case VFCTRL_INIT_OSD:
return CONTROL_TRUE;
case VFCTRL_SET_YUV_COLORSPACE: {
struct mp_csp_details colorspace = *(struct mp_csp_details *)data;
vf->priv->csp = colorspace;
break;
}
}
return vf_next_control(vf, request, data);
}
static void uninit(struct vf_instance *vf)
{
free(vf->priv->planes[1]);
free(vf->priv->planes[2]);
free(vf->priv->line_limits);
free(vf->priv);
}
@ -418,47 +271,37 @@ static const unsigned int fmt_list[] = {
static int vf_open(vf_instance_t *vf, char *args)
{
int flags;
vf->priv->outfmt = vf_match_csp(&vf->next, fmt_list, IMGFMT_YV12);
if (vf->priv->outfmt)
flags = vf_next_query_format(vf, vf->priv->outfmt);
if (!vf->priv->outfmt) {
uninit(vf);
return 0;
} else if (vf->priv->auto_insert && flags & VFCAP_EOSD) {
uninit(vf);
return -1;
}
if (vf->priv->auto_insert)
mp_msg(MSGT_ASS, MSGL_INFO, "[ass] auto-open\n");
vf->config = config;
vf->query_format = query_format;
vf->uninit = uninit;
vf->control = control;
vf->get_image = get_image;
vf->put_image = put_image;
vf->default_caps = VFCAP_EOSD | VFCAP_EOSD_FILTER;
vf->default_caps = VFCAP_OSD;
return 1;
}
#define ST_OFF(f) M_ST_OFF(struct vf_priv_s, f)
static const m_option_t vf_opts_fields[] = {
{"auto", ST_OFF(auto_insert), CONF_TYPE_FLAG, 0, 0, 1, NULL},
{NULL, NULL, 0, 0, 0, 0, NULL}
};
static const m_struct_t vf_opts = {
"ass",
"sub",
sizeof(struct vf_priv_s),
&vf_priv_dflt,
vf_opts_fields
};
const vf_info_t vf_info_ass = {
"Render ASS/SSA subtitles",
"ass",
const vf_info_t vf_info_sub = {
"Render subtitles",
"sub",
"Evgeniy Stepanov",
"",
vf_open,

View File

@ -34,11 +34,9 @@
struct vf_priv_s {
struct vo *vo;
double scale_ratio;
};
#define video_out (vf->priv->vo)
static int query_format(struct vf_instance *vf, unsigned int fmt);
static void draw_slice(struct vf_instance *vf, unsigned char **src,
int *stride, int w, int h, int x, int y);
@ -67,14 +65,12 @@ static int config(struct vf_instance *vf,
if (info->comment && strlen(info->comment) > 0)
mp_msg(MSGT_CPLAYER, MSGL_V, "VO: Comment: %s\n", info->comment);
// save vo's stride capability for the wanted colorspace:
vf->default_caps = query_format(vf, outfmt);
vf->draw_slice = (vf->default_caps & VOCAP_NOSLICES) ? NULL : draw_slice;
if (vo_config(video_out, width, height, d_width, d_height, flags, outfmt))
return 0;
vf->priv->scale_ratio = (double) d_width / d_height * height / width;
// save vo's stride capability for the wanted colorspace:
vf->default_caps = video_out->default_caps;
vf->draw_slice = (vf->default_caps & VOCAP_NOSLICES) ? NULL : draw_slice;
return 1;
}
@ -94,11 +90,6 @@ static int control(struct vf_instance *vf, int request, void *data)
return vo_control(video_out, VOCTRL_GET_YUV_COLORSPACE, data) == true;
case VFCTRL_SET_YUV_COLORSPACE:
return vo_control(video_out, VOCTRL_SET_YUV_COLORSPACE, data) == true;
case VFCTRL_DRAW_OSD:
if (!video_out->config_ok)
return CONTROL_FALSE; // vo not configured?
vo_draw_osd(video_out, data);
return CONTROL_TRUE;
case VFCTRL_SET_EQUALIZER: {
vf_equalizer_t *eq = data;
if (!video_out->config_ok)
@ -117,20 +108,6 @@ static int control(struct vf_instance *vf, int request, void *data)
};
return vo_control(video_out, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
}
case VFCTRL_DRAW_EOSD: {
struct osd_state *osd = data;
osd->support_rgba = vf->default_caps & VFCAP_EOSD_RGBA;
osd->dim = (struct mp_eosd_res){0};
if (!video_out->config_ok ||
vo_control(video_out, VOCTRL_GET_EOSD_RES, &osd->dim) != true)
return CONTROL_FALSE;
osd->normal_scale = 1;
osd->vsfilter_scale = vf->priv->scale_ratio;
osd->unscaled = vf->default_caps & VFCAP_EOSD_UNSCALED;
struct sub_bitmaps images;
sub_get_bitmaps(osd, &images);
return vo_control(video_out, VOCTRL_DRAW_EOSD, &images) == VO_TRUE;
}
}
return CONTROL_UNKNOWN;
}

View File

@ -40,14 +40,7 @@
#define VFCAP_ACCEPT_STRIDE 0x400
// filter does postprocessing (so you shouldn't scale/filter image before it)
#define VFCAP_POSTPROC 0x800
// filter can draw EOSD
#define VFCAP_EOSD 0x2000
// filter will draw EOSD at screen resolution (without scaling)
#define VFCAP_EOSD_UNSCALED 0x4000
// used by libvo and vf_vo, indicates the VO does not support draw_slice for this format
#define VOCAP_NOSLICES 0x8000
#define VFCAP_OSD_FILTER 0x10000 // OSD is drawn in filter chain
#define VFCAP_EOSD_FILTER 0x20000 // EOSD is drawn in filter chain
#define VFCAP_EOSD_RGBA 0x40000
#endif /* MPLAYER_VFCAP_H */

View File

@ -120,8 +120,6 @@ typedef struct demux_stream {
off_t dpos; // position in the demuxed stream
int pack_no; // serial number of packet
bool keyframe; // keyframe flag of current packet
int non_interleaved; // 1 if this stream is not properly interleaved,
// so e.g. subtitle handling must do explicit reads.
//---------------
int packs; // number of packets in buffer
int bytes; // total bytes of packets in buffer

View File

@ -25,18 +25,13 @@
#include "video_out.h"
void aspect_save_orig(struct vo *vo, int orgw, int orgh)
void aspect_save_videores(struct vo *vo, int w, int h, int d_w, int d_h)
{
mp_msg(MSGT_VO, MSGL_DBG2, "aspect_save_orig %dx%d\n", orgw, orgh);
vo->aspdat.orgw = orgw;
vo->aspdat.orgh = orgh;
}
void aspect_save_prescale(struct vo *vo, int prew, int preh)
{
mp_msg(MSGT_VO, MSGL_DBG2, "aspect_save_prescale %dx%d\n", prew, preh);
vo->aspdat.prew = prew;
vo->aspdat.preh = preh;
vo->aspdat.orgw = w;
vo->aspdat.orgh = h;
vo->aspdat.prew = d_w;
vo->aspdat.preh = d_h;
vo->aspdat.par = (double)d_w / d_h * h / w;
}
void aspect_save_screenres(struct vo *vo, int scrw, int scrh)
@ -52,9 +47,9 @@ void aspect_save_screenres(struct vo *vo, int scrw, int scrh)
vo->aspdat.scrw = scrw;
vo->aspdat.scrh = scrh;
if (opts->force_monitor_aspect)
vo->monitor_aspect = opts->force_monitor_aspect;
vo->monitor_par = opts->force_monitor_aspect * scrh / scrw;
else
vo->monitor_aspect = opts->monitor_pixel_aspect * scrw / scrh;
vo->monitor_par = 1.0 / opts->monitor_pixel_aspect;
}
/* aspect is called with the source resolution and the
@ -64,17 +59,17 @@ void aspect_save_screenres(struct vo *vo, int scrw, int scrh)
void aspect_fit(struct vo *vo, int *srcw, int *srch, int fitw, int fith)
{
struct aspect_data *aspdat = &vo->aspdat;
float pixelaspect = vo->monitor_aspect * aspdat->scrh / aspdat->scrw;
float pixelaspect = vo->monitor_par;
mp_msg(MSGT_VO, MSGL_DBG2, "aspect(0) fitin: %dx%d screenaspect: %.2f\n",
fitw, fith, vo->monitor_aspect);
mp_msg(MSGT_VO, MSGL_DBG2, "aspect(0) fitin: %dx%d monitor_par: %.2f\n",
fitw, fith, vo->monitor_par);
*srcw = fitw;
*srch = (float)fitw / aspdat->prew * aspdat->preh * pixelaspect;
*srch = (float)fitw / aspdat->prew * aspdat->preh / pixelaspect;
*srch += *srch % 2; // round
mp_msg(MSGT_VO, MSGL_DBG2, "aspect(1) wh: %dx%d (org: %dx%d)\n",
*srcw, *srch, aspdat->prew, aspdat->preh);
if (*srch > fith || *srch < aspdat->orgh) {
int tmpw = (float)fith / aspdat->preh * aspdat->prew / pixelaspect;
int tmpw = (float)fith / aspdat->preh * aspdat->prew * pixelaspect;
tmpw += tmpw % 2; // round
if (tmpw <= fitw) {
*srch = fith;

View File

@ -24,10 +24,7 @@ struct vo;
void panscan_init(struct vo *vo);
void panscan_calc_windowed(struct vo *vo);
void aspect_save_orig(struct vo *vo, int orgw, int orgh);
void aspect_save_prescale(struct vo *vo, int prew, int preh);
void aspect_save_videores(struct vo *vo, int w, int h, int d_w, int d_h);
void aspect_save_screenres(struct vo *vo, int scrw, int scrh);
#define A_WINZOOM 2 ///< zoom to fill window size

View File

@ -20,6 +20,7 @@
*/
#include <stdlib.h>
#include <assert.h>
#include <libavutil/common.h>
@ -27,9 +28,29 @@
#include "bitmap_packer.h"
#include "mp_msg.h"
#include "mpcommon.h"
#include "sub/ass_mp.h"
#include "sub/dec_sub.h"
#include "fastmemcpy.h"
#define IS_POWER_OF_2(x) (((x) > 0) && !(((x) - 1) & (x)))
void packer_reset(struct bitmap_packer *packer)
{
struct bitmap_packer old = *packer;
*packer = (struct bitmap_packer) {
.w_max = old.w_max,
.h_max = old.h_max,
};
talloc_free_children(packer);
}
void packer_get_bb(struct bitmap_packer *packer, struct pos out_bb[2])
{
out_bb[0] = (struct pos) {0};
out_bb[1] = (struct pos) {
FFMIN(packer->used_width + packer->padding, packer->w),
FFMIN(packer->used_height + packer->padding, packer->h),
};
}
#define HEIGHT_SORT_BITS 4
static int size_index(int s)
@ -142,6 +163,8 @@ int packer_pack(struct bitmap_packer *packer)
// No padding at edges
packer->used_width = FFMIN(used_width, packer->w);
packer->used_height = FFMIN(y, packer->h);
assert(packer->w == 0 || IS_POWER_OF_2(packer->w));
assert(packer->h == 0 || IS_POWER_OF_2(packer->h));
return packer->w != w_orig || packer->h != h_orig;
}
if (packer->w <= packer->h && packer->w != packer->w_max)
@ -171,36 +194,34 @@ void packer_set_size(struct bitmap_packer *packer, int size)
packer->asize + 16);
}
static int packer_pack_from_assimg(struct bitmap_packer *packer,
struct ass_image *imglist)
{
int count = 0;
struct ass_image *img = imglist;
while (img) {
if (count >= packer->asize)
packer_set_size(packer, FFMAX(packer->asize * 2, 32));
packer->in[count].x = img->w;
packer->in[count].y = img->h;
img = img->next;
count++;
}
packer->count = count;
return packer_pack(packer);
}
int packer_pack_from_subbitmaps(struct bitmap_packer *packer,
struct sub_bitmaps *b, int padding_pixels)
struct sub_bitmaps *b)
{
packer->padding = 0;
packer->count = 0;
if (b->type == SUBBITMAP_EMPTY)
if (b->format == SUBBITMAP_EMPTY)
return 0;
if (b->type == SUBBITMAP_LIBASS)
return packer_pack_from_assimg(packer, b->imgs);
packer->padding = padding_pixels;
packer_set_size(packer, b->part_count);
packer_set_size(packer, b->num_parts);
int a = packer->padding;
for (int i = 0; i < b->part_count; i++)
for (int i = 0; i < b->num_parts; i++)
packer->in[i] = (struct pos){b->parts[i].w + a, b->parts[i].h + a};
return packer_pack(packer);
}
void packer_copy_subbitmaps(struct bitmap_packer *packer, struct sub_bitmaps *b,
void *data, int pixel_stride, int stride)
{
assert(packer->count == b->num_parts);
if (packer->padding) {
struct pos bb[2];
packer_get_bb(packer, bb);
memset_pic(data, 0, bb[1].x * pixel_stride, bb[1].y, stride);
}
for (int n = 0; n < packer->count; n++) {
struct sub_bitmap *s = &b->parts[n];
struct pos p = packer->result[n];
void *pdata = (uint8_t *)data + p.y * stride + p.x * pixel_stride;
memcpy_pic(pdata, s->bitmap, s->w * pixel_stride, s->h,
stride, s->stride);
}
}

View File

@ -26,6 +26,13 @@ struct bitmap_packer {
struct ass_image;
struct sub_bitmaps;
// Clear all internal state. Leave the following fields: w_max, h_max
void packer_reset(struct bitmap_packer *packer);
// Get the bounding box used for bitmap data (including padding).
// The bounding box doesn't exceed (0,0)-(packer->w,packer->h).
void packer_get_bb(struct bitmap_packer *packer, struct pos out_bb[2]);
/* Reallocate packer->in for at least to desired number of items.
* Also sets packer->count to the same value.
*/
@ -36,6 +43,7 @@ void packer_set_size(struct bitmap_packer *packer, int size);
* Write input sizes in packer->in.
* Resulting packing will be written in packer->result.
* w and h will be increased if necessary for successful packing.
* There is a strong guarantee that w and h will be powers of 2 (or set to 0).
* Return value is -1 if packing failed because w and h were set to max
* values but that wasn't enough, 1 if w or h was increased, and 0 otherwise.
*/
@ -46,6 +54,15 @@ int packer_pack(struct bitmap_packer *packer);
* given image list.
*/
int packer_pack_from_subbitmaps(struct bitmap_packer *packer,
struct sub_bitmaps *b, int padding_pixels);
struct sub_bitmaps *b);
// Copy the (already packed) sub-bitmaps from b to the image in data.
// data must point to an image that is at least (packer->w, packer->h) big.
// The image has the given stride (bytes between (x, y) to (x, y + 1)), and the
// pixel format used by both the sub-bitmaps and the image uses pixel_stride
// bytes per pixel (bytes between (x, y) to (x + 1, y)).
// If packer->padding is set, the padding borders are cleared with 0.
void packer_copy_subbitmaps(struct bitmap_packer *packer, struct sub_bitmaps *b,
void *data, int pixel_stride, int stride);
#endif

View File

@ -3,6 +3,8 @@
*
* Copyleft (C) 2009 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
*
* mp_invert_yuv2rgb based on DarkPlaces engine, original code (GPL2 or later)
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
@ -37,6 +39,7 @@ char * const mp_csp_names[MP_CSP_COUNT] = {
"BT.601 (SD)",
"BT.709 (HD)",
"SMPTE-240M",
"RGB",
};
char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
@ -54,6 +57,7 @@ enum mp_csp avcol_spc_to_mp_csp(enum AVColorSpace colorspace)
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;
case AVCOL_SPC_RGB: return MP_CSP_RGB;
default: return MP_CSP_AUTO;
}
}
@ -73,7 +77,8 @@ enum AVColorSpace mp_csp_to_avcol_spc(enum mp_csp 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;
case MP_CSP_RGB: return AVCOL_SPC_RGB;
default: return AVCOL_SPC_UNSPECIFIED;
}
}
@ -228,6 +233,16 @@ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4])
m[i][COL_Y] *= params->contrast;
m[i][COL_C] += (rgblev.max-rgblev.min) * (1 - params->contrast)/2;
}
int in_bits = FFMAX(params->int_bits_in, 1);
int out_bits = FFMAX(params->int_bits_out, 1);
double in_scale = (1 << in_bits) - 1.0;
double out_scale = (1 << out_bits) - 1.0;
for (int i = 0; i < 3; i++) {
m[i][COL_C] *= out_scale; // constant is 1.0
for (int x = 0; x < 3; x++)
m[i][x] *= out_scale / in_scale;
}
}
//! size of gamma map use to avoid slow exp function in gen_yuv2rgb_map
@ -317,3 +332,60 @@ int mp_csp_equalizer_set(struct mp_csp_equalizer *eq, const char *property,
return 1;
}
void mp_invert_yuv2rgb(float out[3][4], float in[3][4])
{
float m00 = in[0][0], m01 = in[0][1], m02 = in[0][2], m03 = in[0][3],
m10 = in[1][0], m11 = in[1][1], m12 = in[1][2], m13 = in[1][3],
m20 = in[2][0], m21 = in[2][1], m22 = in[2][2], m23 = in[2][3];
// calculate the adjoint
out[0][0] = (m11 * m22 - m21 * m12);
out[0][1] = -(m01 * m22 - m21 * m02);
out[0][2] = (m01 * m12 - m11 * m02);
out[1][0] = -(m10 * m22 - m20 * m12);
out[1][1] = (m00 * m22 - m20 * m02);
out[1][2] = -(m00 * m12 - m10 * m02);
out[2][0] = (m10 * m21 - m20 * m11);
out[2][1] = -(m00 * m21 - m20 * m01);
out[2][2] = (m00 * m11 - m10 * m01);
// calculate the determinant (as inverse == 1/det * adjoint,
// adjoint * m == identity * det, so this calculates the det)
float det = m00 * out[0][0] + m10 * out[0][1] + m20 * out[0][2];
det = 1.0f / det;
out[0][0] *= det;
out[0][1] *= det;
out[0][2] *= det;
out[1][0] *= det;
out[1][1] *= det;
out[1][2] *= det;
out[2][0] *= det;
out[2][1] *= det;
out[2][2] *= det;
// fix the constant coefficient
// rgb = M * yuv + C
// M^-1 * rgb = yuv + M^-1 * C
// yuv = M^-1 * rgb - M^-1 * C
// ^^^^^^^^^^
out[0][3] = -(out[0][0] * m03 + out[0][1] * m13 + out[0][2] * m23);
out[1][3] = -(out[1][0] * m03 + out[1][1] * m13 + out[1][2] * m23);
out[2][3] = -(out[2][0] * m03 + out[2][1] * m13 + out[2][2] * m23);
}
// Multiply the color in c with the given matrix.
// c is {R, G, B} or {Y, U, V} (depending on input/output and matrix).
// Output is clipped to the given number of bits.
void mp_map_int_color(float matrix[3][4], int clip_bits, int c[3])
{
int in[3] = {c[0], c[1], c[2]};
for (int i = 0; i < 3; i++) {
double val = matrix[i][3];
for (int x = 0; x < 3; x++)
val += matrix[i][x] * in[x];
int ival = lrint(val);
c[i] = av_clip(ival, 0, (1 << clip_bits) - 1);
}
}

View File

@ -24,6 +24,7 @@
#ifndef MPLAYER_CSPUTILS_H
#define MPLAYER_CSPUTILS_H
#include <stdbool.h>
#include <stdint.h>
#include "libavcodec/avcodec.h"
@ -38,6 +39,7 @@ enum mp_csp {
MP_CSP_BT_601,
MP_CSP_BT_709,
MP_CSP_SMPTE_240M,
MP_CSP_RGB,
MP_CSP_COUNT
};
@ -69,10 +71,20 @@ struct mp_csp_params {
float rgamma;
float ggamma;
float bgamma;
// texture_bits/input_bits is for rescaling fixed point input to range [0,1]
int texture_bits;
int input_bits;
// for scaling integer input and output (if 0, assume range [0,1])
int int_bits_in;
int int_bits_out;
};
#define MP_CSP_PARAMS_DEFAULTS { \
.colorspace = MP_CSP_DETAILS_DEFAULTS, \
.brightness = 0, .contrast = 1, .hue = 0, .saturation = 1, \
.rgamma = 1, .ggamma = 1, .bgamma = 1, \
.texture_bits = 8, .input_bits = 8}
enum mp_csp_equalizer_param {
MP_CSP_EQ_BRIGHTNESS,
MP_CSP_EQ_CONTRAST,
@ -133,4 +145,7 @@ void mp_gen_gamma_map(unsigned char *map, int size, float gamma);
void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float yuv2rgb[3][4]);
void mp_gen_yuv2rgb_map(struct mp_csp_params *params, uint8_t *map, int size);
void mp_invert_yuv2rgb(float out[3][4], float in[3][4]);
void mp_map_int_color(float matrix[3][4], int clip_bits, int c[3]);
#endif /* MPLAYER_CSPUTILS_H */

View File

@ -1,255 +0,0 @@
/*
* Common code for packing EOSD images into larger surfaces.
*
* This file is part of mplayer2.
*
* mplayer2 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.
*
* mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <libavutil/common.h>
#include "talloc.h"
#include "mp_msg.h"
#include "eosd_packer.h"
// Initial size of EOSD surface in pixels (x*x)
#define EOSD_SURFACE_INITIAL_SIZE 256
// Allocate an eosd_packer, which can be used to layout and cache the list of
// EOSD images contained in a mp_eosd_images_t into a flat surface.
// It can be free'd with talloc_free().
// Don't forget to call eosd_init() before using it.
struct eosd_packer *eosd_packer_create(void *talloc_ctx) {
return talloc_zero(talloc_ctx, struct eosd_packer);
}
// Call this when you need to completely reinitialize the EOSD state, e.g. when
// when your EOSD surface was deleted.
// max_width and max_height are the maximum surface sizes that should be
// allowed.
void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width,
uint32_t max_height)
{
state->max_surface_width = max_width;
state->max_surface_height = max_height;
state->surface.w = 0;
state->surface.h = 0;
state->targets_count = 0;
}
#define HEIGHT_SORT_BITS 4
static int size_index(struct eosd_target *r)
{
unsigned int h = r->source.y1;
int n = av_log2_16bit(h);
return (n << HEIGHT_SORT_BITS)
+ (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
}
/* Pack the given rectangles into an area of size w * h.
* The size of each rectangle is read from .source.x1/.source.y1.
* The height of each rectangle must be at least 1 and less than 65536.
* The .source rectangle is then set corresponding to the packed position.
* 'scratch' must point to work memory for num_rects+16 ints.
* Return 0 on success, -1 if the rectangles did not fit in w*h.
*
* The rectangles are placed in rows in order approximately sorted by
* height (the approximate sorting is simpler than a full one would be,
* and allows the algorithm to work in linear time). Additionally, to
* reduce wasted space when there are a few tall rectangles, empty
* lower-right parts of rows are filled recursively when the size of
* rectangles in the row drops past a power-of-two threshold. So if a
* row starts with rectangles of size 3x50, 10x40 and 5x20 then the
* free rectangle with corners (13, 20)-(w, 50) is filled recursively.
*/
static int pack_rectangles(struct eosd_target *rects, int num_rects,
int w, int h, int *scratch)
{
int bins[16 << HEIGHT_SORT_BITS];
int sizes[16 << HEIGHT_SORT_BITS] = {};
for (int i = 0; i < num_rects; i++)
sizes[size_index(rects + i)]++;
int idx = 0;
for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
bins[i + j] = idx;
idx += sizes[i + j];
}
scratch[idx++] = -1;
}
for (int i = 0; i < num_rects; i++)
scratch[bins[size_index(rects + i)]++] = i;
for (int i = 0; i < 16; i++)
bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
struct {
int size, x, bottom;
} stack[16] = {{15, 0, h}}, s = {};
int stackpos = 1;
int y;
while (stackpos) {
y = s.bottom;
s = stack[--stackpos];
s.size++;
while (s.size--) {
int maxy = -1;
int obj;
while ((obj = scratch[bins[s.size]]) >= 0) {
int bottom = y + rects[obj].source.y1;
if (bottom > s.bottom)
break;
int right = s.x + rects[obj].source.x1;
if (right > w)
break;
bins[s.size]++;
rects[obj].source.x0 = s.x;
rects[obj].source.x1 += s.x;
rects[obj].source.y0 = y;
rects[obj].source.y1 += y;
num_rects--;
if (maxy <= 0)
stack[stackpos++] = s;
s.x = right;
maxy = FFMAX(maxy, bottom);
}
if (maxy > 0)
s.bottom = maxy;
}
}
return num_rects ? -1 : 0;
}
// padding to reduce interpolation artifacts when doing scaling & filtering
#define EOSD_PADDING 0
// Release all previous images, and packs the images in imgs into state. The
// caller must check the change variables:
// *out_need_reposition == true: sub-image positions changed
// *out_need_upload == true: upload all sub-images again
// *out_need_reallocate == true: resize the EOSD texture to state->surface.w/h
// Logical implications: need_reallocate => need_upload => need_reposition
void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs,
bool *out_need_reposition, bool *out_need_upload,
bool *out_need_reallocate)
{
int i;
ASS_Image *img = imgs->imgs;
ASS_Image *p;
struct eosd_surface *sfc = &state->surface;
*out_need_reposition = imgs->bitmap_pos_id != state->last_bitmap_pos_id;
*out_need_upload = imgs->bitmap_id != state->last_bitmap_id;
*out_need_reallocate = false;
state->last_bitmap_pos_id = imgs->bitmap_pos_id;
state->last_bitmap_id = imgs->bitmap_id;
// eosd_reinit() was probably called, force full reupload.
if (state->targets_count == 0 && img)
*out_need_upload = true;
if (!(*out_need_reposition) && !(*out_need_upload))
return; // Nothing changed, no need to redraw
state->targets_count = 0;
*out_need_reposition = true;
if (!img)
return; // There's nothing to render!
if (!(*out_need_upload))
goto eosd_skip_upload;
*out_need_upload = true;
while (1) {
for (p = img, i = 0; p; p = p->next) {
if (p->w <= 0 || p->h <= 0)
continue;
// Allocate new space for surface/target arrays
if (i >= state->targets_size) {
state->targets_size = FFMAX(state->targets_size * 2, 512);
state->targets =
talloc_realloc_size(state, state->targets,
state->targets_size
* sizeof(*state->targets));
state->scratch =
talloc_realloc_size(state, state->scratch,
(state->targets_size + 16)
* sizeof(*state->scratch));
}
state->targets[i].source.x1 = p->w + EOSD_PADDING;
state->targets[i].source.y1 = p->h + EOSD_PADDING;
i++;
}
if (pack_rectangles(state->targets, i, sfc->w, sfc->h,
state->scratch) >= 0)
break;
int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE),
state->max_surface_width);
int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE),
state->max_surface_height);
if (w == sfc->w && h == sfc->h) {
mp_msg(MSGT_VO, MSGL_ERR, "[eosd] EOSD bitmaps do not fit on "
"a surface with the maximum supported size\n");
return;
}
sfc->w = w;
sfc->h = h;
*out_need_reallocate = true;
}
if (*out_need_reallocate) {
mp_msg(MSGT_VO, MSGL_V, "[eosd] Allocate a %dx%d surface for "
"EOSD bitmaps.\n", sfc->w, sfc->h);
}
eosd_skip_upload:
for (p = img; p; p = p->next) {
if (p->w <= 0 || p->h <= 0)
continue;
struct eosd_target *target = &state->targets[state->targets_count];
target->source.x1 -= EOSD_PADDING;
target->source.y1 -= EOSD_PADDING;
target->dest.x0 = p->dst_x;
target->dest.y0 = p->dst_y;
target->dest.x1 = p->w + p->dst_x;
target->dest.y1 = p->h + p->dst_y;
target->color = p->color;
target->ass_img = p;
state->targets_count++;
}
}
// Calculate the bounding box of all sub-rectangles in the EOSD surface that
// will be used for EOSD rendering.
// If the bounding box is empty, return false.
bool eosd_packer_calculate_source_bb(struct eosd_packer *state,
struct eosd_rect *out_bb)
{
struct eosd_rect bb = { state->surface.w, state->surface.h, 0, 0 };
for (int n = 0; n < state->targets_count; n++) {
struct eosd_rect s = state->targets[n].source;
bb.x0 = FFMIN(bb.x0, s.x0);
bb.y0 = FFMIN(bb.y0, s.y0);
bb.x1 = FFMAX(bb.x1, s.x1);
bb.y1 = FFMAX(bb.y1, s.y1);
}
// avoid degenerate bounding box if empty
bb.x0 = FFMIN(bb.x0, bb.x1);
bb.y0 = FFMIN(bb.y0, bb.y1);
*out_bb = bb;
return state->targets_count > 0;
}

View File

@ -1,74 +0,0 @@
/*
* This file is part of mplayer2.
*
* mplayer2 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.
*
* mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_EOSD_PACKER_H
#define MPLAYER_EOSD_PACKER_H
#include <inttypes.h>
#include <stdbool.h>
#include "sub/ass_mp.h"
#include "sub/dec_sub.h"
// Pool of surfaces
struct eosd_surface {
//void *native_surface;
int w;
int h;
};
struct eosd_rect {
int x0, y0, x1, y1;
};
// List of surfaces to be rendered
struct eosd_target {
struct eosd_rect source; // position in EOSD surface
struct eosd_rect dest; // position on screen
uint32_t color; // libass-style color of the image
// NOTE: This must not be accessed after you return from your VO's
// VOCTRL_DRAW_EOSD call - libass will free or reuse the associated
// memory. Feel free to set this to NULL to make erroneous accesses to
// this member fail early.
ASS_Image *ass_img;
};
struct eosd_packer {
struct eosd_surface surface;
struct eosd_target *targets;
int targets_count; // number of valid elements in targets
int targets_size; // number of allocated elements in targets
uint32_t max_surface_width;
uint32_t max_surface_height;
int *scratch;
int last_bitmap_id;
int last_bitmap_pos_id;
};
struct eosd_packer *eosd_packer_create(void *talloc_ctx);
void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width,
uint32_t max_height);
void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs,
bool *out_need_reposition, bool *out_need_upload,
bool *out_need_reallocate);
bool eosd_packer_calculate_source_bb(struct eosd_packer *state,
struct eosd_rect *out_bb);
#endif /* MPLAYER_EOSD_PACKER_H */

View File

@ -64,4 +64,17 @@ static inline void * memcpy_pic2(void * dst, const void * src,
return retval;
}
static inline void memset_pic(void *dst, int fill, int bytesPerLine, int height,
int stride)
{
if (bytesPerLine == stride) {
memset(dst, fill, stride * height);
} else {
for (int i = 0; i < height; i++) {
memset(dst, fill, bytesPerLine);
dst = (uint8_t *)dst + stride;
}
}
}
#endif /* MPLAYER_FASTMEMCPY_H */

View File

@ -46,6 +46,8 @@
#include "aspect.h"
#include "pnm_loader.h"
#include "options.h"
#include "sub/sub.h"
#include "bitmap_packer.h"
//! \defgroup glgeneral OpenGL general helper functions
@ -904,6 +906,31 @@ void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type,
gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
}
// Like glUploadTex, but upload a byte array with all elements set to val.
// If scratch is not NULL, points to a resizeable talloc memory block than can
// be freely used by the function (for avoiding temporary memory allocations).
void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type,
int x, int y, int w, int h, uint8_t val, void **scratch)
{
int bpp = glFmt2bpp(format, type);
int stride = w * bpp;
int size = h * stride;
if (size < 1)
return;
void *data = scratch ? *scratch : NULL;
if (talloc_get_size(data) < size)
data = talloc_realloc(NULL, data, char *, size);
memset(data, val, size);
glAdjustAlignment(gl, stride);
gl->PixelStorei(GL_UNPACK_ROW_LENGTH, w);
gl->TexSubImage2D(target, 0, x, y, w, h, format, type, data);
if (scratch) {
*scratch = data;
} else {
talloc_free(data);
}
}
/**
* \brief download a texture, handling things like stride and slices
* \param target texture target, usually GL_TEXTURE_2D
@ -1853,7 +1880,7 @@ void glDisable3D(GL *gl, int type)
gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
break;
case GL_3D_QUADBUFFER:
gl->DrawBuffer(vo_doublebuffering ? GL_BACK : GL_FRONT);
gl->DrawBuffer(GL_BACK);
gl->GetIntegerv(GL_DRAW_BUFFER, &buffer);
switch (buffer) {
case GL_FRONT:
@ -1938,6 +1965,24 @@ void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h,
gl->End();
}
mp_image_t *glGetWindowScreenshot(GL *gl)
{
GLint vp[4]; //x, y, w, h
gl->GetIntegerv(GL_VIEWPORT, vp);
mp_image_t *image = alloc_mpi(vp[2], vp[3], IMGFMT_RGB24);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
gl->PixelStorei(GL_PACK_ALIGNMENT, 0);
gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
gl->ReadBuffer(GL_FRONT);
//flip image while reading
for (int y = 0; y < vp[3]; y++) {
gl->ReadPixels(vp[0], vp[1] + vp[3] - y - 1, vp[2], 1,
GL_RGB, GL_UNSIGNED_BYTE,
image->planes[0] + y * image->stride[0]);
}
return image;
}
#ifdef CONFIG_GL_COCOA
#include "cocoa_common.h"

View File

@ -33,6 +33,8 @@
#include "video_out.h"
#include "csputils.h"
#include "libmpcodecs/mp_image.h"
#if defined(CONFIG_GL_COCOA) && !defined(CONFIG_GL_X11)
#ifdef GL_VERSION_3_0
#include <OpenGL/gl3.h>
@ -63,6 +65,8 @@ int glCreatePPMTex(GL *gl, GLenum target, GLenum fmt, GLint filter,
void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type,
const void *dataptr, int stride,
int x, int y, int w, int h, int slice);
void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type,
int x, int y, int w, int h, uint8_t val, void **scratch);
void glDownloadTex(GL *gl, GLenum target, GLenum format, GLenum type,
void *dataptr, int stride);
void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h,
@ -70,6 +74,7 @@ void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h,
int sx, int sy, int rect_tex, int is_yv12, int flip);
int loadGPUProgram(GL *gl, GLenum target, char *prog);
void glCheckError(GL *gl, const char *info);
mp_image_t *glGetWindowScreenshot(GL *gl);
/** \addtogroup glconversion
* \{ */
@ -380,6 +385,7 @@ struct GL {
void (GLAPIENTRY *Uniform1f)(GLint, GLfloat);
void (GLAPIENTRY *Uniform2f)(GLint, GLfloat, GLfloat);
void (GLAPIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat);
void (GLAPIENTRY *Uniform4f)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);
void (GLAPIENTRY *Uniform1i)(GLint, GLint);
void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean,
const GLfloat *);

324
libvo/gl_osd.c Normal file
View File

@ -0,0 +1,324 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include <libavutil/common.h>
#include "bitmap_packer.h"
#include "gl_osd.h"
struct osd_fmt_entry {
GLint internal_format;
GLint format;
GLenum type;
};
// glBlendFunc() arguments
static const int blend_factors[SUBBITMAP_COUNT][2] = {
[SUBBITMAP_LIBASS] = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
[SUBBITMAP_RGBA] = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
};
static const struct osd_fmt_entry osd_to_gl3_formats[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = {GL_RED, GL_RED, GL_UNSIGNED_BYTE},
[SUBBITMAP_RGBA] = {GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE},
};
static const struct osd_fmt_entry osd_to_gl_legacy_formats[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = {GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE},
[SUBBITMAP_RGBA] = {GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE},
};
struct mpgl_osd *mpgl_osd_init(GL *gl, bool legacy)
{
GLint max_texture_size;
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
struct mpgl_osd *ctx = talloc_ptrtype(NULL, ctx);
*ctx = (struct mpgl_osd) {
.gl = gl,
.fmt_table = legacy ? osd_to_gl_legacy_formats : osd_to_gl3_formats,
.scratch = talloc_zero_size(ctx, 1),
};
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct mpgl_osd_part *p = talloc_ptrtype(ctx, p);
*p = (struct mpgl_osd_part) {
.packer = talloc_struct(p, struct bitmap_packer, {
.w_max = max_texture_size,
.h_max = max_texture_size,
}),
};
ctx->parts[n] = p;
}
for (int n = 0; n < SUBBITMAP_COUNT; n++)
ctx->formats[n] = ctx->fmt_table[n].type != 0;
return ctx;
}
void mpgl_osd_destroy(struct mpgl_osd *ctx)
{
GL *gl = ctx->gl;
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct mpgl_osd_part *p = ctx->parts[n];
gl->DeleteTextures(1, &p->texture);
if (gl->DeleteBuffers)
gl->DeleteBuffers(1, &p->buffer);
}
talloc_free(ctx);
}
static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
struct sub_bitmaps *imgs)
{
GL *gl = ctx->gl;
bool success = true;
struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
int pix_stride = glFmt2bpp(fmt.format, fmt.type);
if (!osd->buffer) {
gl->GenBuffers(1, &osd->buffer);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer);
gl->BufferData(GL_PIXEL_UNPACK_BUFFER, osd->w * osd->h * pix_stride,
NULL, GL_DYNAMIC_COPY);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer);
char *data = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if (!data) {
success = false;
} else {
struct pos bb[2];
packer_get_bb(osd->packer, bb);
size_t stride = osd->w * pix_stride;
packer_copy_subbitmaps(osd->packer, imgs, data, pix_stride, stride);
if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
success = false;
glUploadTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, NULL, stride,
bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y,
0);
}
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
if (!success) {
mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error: can't upload subtitles! "
"Remove the 'pbo' suboption.\n");
}
return success;
}
static void upload_tex(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
struct sub_bitmaps *imgs)
{
struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
if (osd->packer->padding) {
struct pos bb[2];
packer_get_bb(osd->packer, bb);
glClearTex(ctx->gl, GL_TEXTURE_2D, fmt.format, fmt.type,
bb[0].x, bb[0].y, bb[1].x - bb[0].y, bb[1].y - bb[0].y,
0, &ctx->scratch);
}
for (int n = 0; n < osd->packer->count; n++) {
struct sub_bitmap *s = &imgs->parts[n];
struct pos p = osd->packer->result[n];
glUploadTex(ctx->gl, GL_TEXTURE_2D, fmt.format, fmt.type,
s->bitmap, s->stride, p.x, p.y, s->w, s->h, 0);
}
}
static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
struct sub_bitmaps *imgs)
{
GL *gl = ctx->gl;
// assume 2x2 filter on scaling
osd->packer->padding = ctx->scaled || imgs->scaled;
int r = packer_pack_from_subbitmaps(osd->packer, imgs);
if (r < 0) {
mp_msg(MSGT_VO, MSGL_ERR, "[gl] OSD bitmaps do not fit on "
"a surface with the maximum supported size %dx%d.\n",
osd->packer->w_max, osd->packer->h_max);
return false;
}
struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
assert(fmt.type != 0);
if (!osd->texture)
gl->GenTextures(1, &osd->texture);
gl->BindTexture(GL_TEXTURE_2D, osd->texture);
if (osd->packer->w > osd->w || osd->packer->h > osd->h
|| osd->format != imgs->format)
{
osd->format = imgs->format;
osd->w = FFMAX(32, osd->packer->w);
osd->h = FFMAX(32, osd->packer->h);
gl->TexImage2D(GL_TEXTURE_2D, 0, fmt.internal_format, osd->w, osd->h,
0, fmt.format, fmt.type, NULL);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (gl->DeleteBuffers)
gl->DeleteBuffers(1, &osd->buffer);
osd->buffer = 0;
}
bool uploaded = false;
if (ctx->use_pbo)
uploaded = upload_pbo(ctx, osd, imgs);
if (!uploaded)
upload_tex(ctx, osd, imgs);
gl->BindTexture(GL_TEXTURE_2D, 0);
return true;
}
struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
struct sub_bitmaps *imgs)
{
if (imgs->num_parts == 0 || !ctx->formats[imgs->format])
return NULL;
struct mpgl_osd_part *osd = ctx->parts[imgs->render_index];
if (imgs->bitmap_pos_id != osd->bitmap_pos_id) {
if (imgs->bitmap_id != osd->bitmap_id) {
if (!upload_osd(ctx, osd, imgs))
osd->packer->count = 0;
}
osd->bitmap_id = imgs->bitmap_id;
osd->bitmap_pos_id = imgs->bitmap_pos_id;
osd->num_vertices = 0;
}
return osd->packer->count ? osd : NULL;
}
void mpgl_osd_set_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p)
{
GL *gl = ctx->gl;
gl->BindTexture(GL_TEXTURE_2D, p->texture);
gl->Enable(GL_BLEND);
gl->BlendFunc(blend_factors[p->format][0], blend_factors[p->format][1]);
}
void mpgl_osd_unset_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p)
{
GL *gl = ctx->gl;
gl->Disable(GL_BLEND);
gl->BindTexture(GL_TEXTURE_2D, 0);
}
struct vertex {
float position[2];
uint8_t color[4];
float texcoord[2];
};
static void draw_legacy_cb(void *pctx, struct sub_bitmaps *imgs)
{
struct mpgl_osd *ctx = pctx;
struct mpgl_osd_part *osd = mpgl_osd_generate(ctx, imgs);
if (!osd)
return;
if (!osd->num_vertices) {
// 2 triangles primitives per quad = 6 vertices per quad
// not using GL_QUADS, as it is deprecated in OpenGL 3.x and later
osd->vertices = talloc_realloc(osd, osd->vertices, struct vertex,
osd->packer->count * 6);
struct vertex *va = osd->vertices;
float tex_w = osd->w;
float tex_h = osd->h;
for (int n = 0; n < osd->packer->count; n++) {
struct sub_bitmap *b = &imgs->parts[n];
struct pos p = osd->packer->result[n];
uint32_t c = imgs->format == SUBBITMAP_LIBASS
? b->libass.color : 0xFFFFFF00;
uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
(c >> 8) & 0xff, 255 - (c & 0xff) };
float x0 = b->x;
float y0 = b->y;
float x1 = b->x + b->dw;
float y1 = b->y + b->dh;
float tx0 = p.x / tex_w;
float ty0 = p.y / tex_h;
float tx1 = (p.x + b->w) / tex_w;
float ty1 = (p.y + b->h) / tex_h;
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
struct vertex *v = &va[osd->num_vertices];
v[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} };
v[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} };
v[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} };
v[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} };
v[4] = v[2];
v[5] = v[1];
#undef COLOR_INIT
osd->num_vertices += 6;
}
}
GL *gl = ctx->gl;
struct vertex *va = osd->vertices;
size_t stride = sizeof(va[0]);
gl->VertexPointer(2, GL_FLOAT, stride, &va[0].position[0]);
gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]);
gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].texcoord[0]);
gl->EnableClientState(GL_VERTEX_ARRAY);
gl->EnableClientState(GL_TEXTURE_COORD_ARRAY);
gl->EnableClientState(GL_COLOR_ARRAY);
mpgl_osd_set_gl_state(ctx, osd);
gl->DrawArrays(GL_TRIANGLES, 0, osd->num_vertices);
mpgl_osd_unset_gl_state(ctx, osd);
gl->DisableClientState(GL_VERTEX_ARRAY);
gl->DisableClientState(GL_TEXTURE_COORD_ARRAY);
gl->DisableClientState(GL_COLOR_ARRAY);
}
void mpgl_osd_draw_legacy(struct mpgl_osd *ctx, struct osd_state *osd,
struct mp_osd_res res)
{
osd_draw(osd, res, osd->vo_pts, 0, ctx->formats, draw_legacy_cb, ctx);
}

43
libvo/gl_osd.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef MPLAYER_GL_OSD_H
#define MPLAYER_GL_OSD_H
#include <stdbool.h>
#include <inttypes.h>
#include "gl_common.h"
#include "sub/sub.h"
struct mpgl_osd_part {
enum sub_bitmap_format format;
int bitmap_id, bitmap_pos_id;
GLuint texture;
int w, h;
GLuint buffer;
int num_vertices;
void *vertices;
struct bitmap_packer *packer;
};
struct mpgl_osd {
GL *gl;
bool use_pbo;
bool scaled;
struct mpgl_osd_part *parts[MAX_OSD_PARTS];
const struct osd_fmt_entry *fmt_table;
bool formats[SUBBITMAP_COUNT];
void *scratch;
};
struct mpgl_osd *mpgl_osd_init(GL *gl, bool legacy);
void mpgl_osd_destroy(struct mpgl_osd *ctx);
struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
struct sub_bitmaps *b);
void mpgl_osd_set_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p);
void mpgl_osd_unset_gl_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p);
void mpgl_osd_draw_legacy(struct mpgl_osd *ctx, struct osd_state *osd,
struct mp_osd_res res);
#endif

View File

@ -1,224 +0,0 @@
/*
* generic alpha renderers for all YUV modes and RGB depths
* These are "reference implementations", should be optimized later (MMX, etc).
* templating code by Michael Niedermayer (michaelni@gmx.at)
*
* 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 "config.h"
#include "osd.h"
#include "mp_msg.h"
#include <inttypes.h>
#include <sys/types.h>
#include "cpudetect.h"
#if ARCH_X86
static const uint64_t bFF __attribute__((aligned(8))) = 0xFFFFFFFFFFFFFFFFULL;
static const unsigned long long mask24lh __attribute__((aligned(8))) = 0xFFFF000000000000ULL;
static const unsigned long long mask24hl __attribute__((aligned(8))) = 0x0000FFFFFFFFFFFFULL;
#endif
//Note: we have C, X86-nommx, MMX, MMX2
//Plain C versions
#define COMPILE_C
#if ARCH_X86
#define COMPILE_MMX
#define COMPILE_MMX2
#endif /* ARCH_X86 */
#undef HAVE_MMX
#undef HAVE_MMX2
#define HAVE_MMX 0
#define HAVE_MMX2 0
#if ! ARCH_X86
#ifdef COMPILE_C
#undef HAVE_MMX
#undef HAVE_MMX2
#define HAVE_MMX 0
#define HAVE_MMX2 0
#define RENAME(a) a ## _C
#include "osd_template.c"
#endif
#else
//X86 noMMX versions
#ifdef COMPILE_C
#undef RENAME
#undef HAVE_MMX
#undef HAVE_MMX2
#define HAVE_MMX 0
#define HAVE_MMX2 0
#define RENAME(a) a ## _X86
#include "osd_template.c"
#endif
//MMX versions
#ifdef COMPILE_MMX
#undef RENAME
#undef HAVE_MMX
#undef HAVE_MMX2
#define HAVE_MMX 1
#define HAVE_MMX2 0
#define RENAME(a) a ## _MMX
#include "osd_template.c"
#endif
//MMX2 versions
#ifdef COMPILE_MMX2
#undef RENAME
#undef HAVE_MMX
#undef HAVE_MMX2
#define HAVE_MMX 1
#define HAVE_MMX2 1
#define RENAME(a) a ## _MMX2
#include "osd_template.c"
#endif
#endif /* ARCH_X86 */
void vo_draw_alpha_yv12(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
#if ARCH_X86
// ordered by speed / fastest first
if(gCpuCaps.hasMMX2)
vo_draw_alpha_yv12_MMX2(w, h, src, srca, srcstride, dstbase, dststride);
else if(gCpuCaps.hasMMX)
vo_draw_alpha_yv12_MMX(w, h, src, srca, srcstride, dstbase, dststride);
else
vo_draw_alpha_yv12_X86(w, h, src, srca, srcstride, dstbase, dststride);
#else
vo_draw_alpha_yv12_C(w, h, src, srca, srcstride, dstbase, dststride);
#endif
}
void vo_draw_alpha_yuy2(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
#if ARCH_X86
// ordered by speed / fastest first
if(gCpuCaps.hasMMX2)
vo_draw_alpha_yuy2_MMX2(w, h, src, srca, srcstride, dstbase, dststride);
else if(gCpuCaps.hasMMX)
vo_draw_alpha_yuy2_MMX(w, h, src, srca, srcstride, dstbase, dststride);
else
vo_draw_alpha_yuy2_X86(w, h, src, srca, srcstride, dstbase, dststride);
#else
vo_draw_alpha_yuy2_C(w, h, src, srca, srcstride, dstbase, dststride);
#endif
}
void vo_draw_alpha_rgb24(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
#if ARCH_X86
// ordered by speed / fastest first
if(gCpuCaps.hasMMX2)
vo_draw_alpha_rgb24_MMX2(w, h, src, srca, srcstride, dstbase, dststride);
else if(gCpuCaps.hasMMX)
vo_draw_alpha_rgb24_MMX(w, h, src, srca, srcstride, dstbase, dststride);
else
vo_draw_alpha_rgb24_X86(w, h, src, srca, srcstride, dstbase, dststride);
#else
vo_draw_alpha_rgb24_C(w, h, src, srca, srcstride, dstbase, dststride);
#endif
}
void vo_draw_alpha_rgb32(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
#if ARCH_X86
// ordered by speed / fastest first
if(gCpuCaps.hasMMX2)
vo_draw_alpha_rgb32_MMX2(w, h, src, srca, srcstride, dstbase, dststride);
else if(gCpuCaps.hasMMX)
vo_draw_alpha_rgb32_MMX(w, h, src, srca, srcstride, dstbase, dststride);
else
vo_draw_alpha_rgb32_X86(w, h, src, srca, srcstride, dstbase, dststride);
#else
vo_draw_alpha_rgb32_C(w, h, src, srca, srcstride, dstbase, dststride);
#endif
}
void vo_draw_alpha_rgb12(int w, int h, unsigned char* src, unsigned char *srca,
int srcstride, unsigned char* dstbase, int dststride) {
int y;
for (y = 0; y < h; y++) {
register unsigned short *dst = (unsigned short*) dstbase;
register int x;
for (x = 0; x < w; x++) {
if(srca[x]){
unsigned char r = dst[x] & 0x0F;
unsigned char g = (dst[x] >> 4) & 0x0F;
unsigned char b = (dst[x] >> 8) & 0x0F;
r = (((r*srca[x]) >> 4) + src[x]) >> 4;
g = (((g*srca[x]) >> 4) + src[x]) >> 4;
b = (((b*srca[x]) >> 4) + src[x]) >> 4;
dst[x] = (b << 8) | (g << 4) | r;
}
}
src += srcstride;
srca += srcstride;
dstbase += dststride;
}
return;
}
void vo_draw_alpha_rgb15(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
int y;
for(y=0;y<h;y++){
register unsigned short *dst = (unsigned short*) dstbase;
register int x;
for(x=0;x<w;x++){
if(srca[x]){
unsigned char r=dst[x]&0x1F;
unsigned char g=(dst[x]>>5)&0x1F;
unsigned char b=(dst[x]>>10)&0x1F;
r=(((r*srca[x])>>5)+src[x])>>3;
g=(((g*srca[x])>>5)+src[x])>>3;
b=(((b*srca[x])>>5)+src[x])>>3;
dst[x]=(b<<10)|(g<<5)|r;
}
}
src+=srcstride;
srca+=srcstride;
dstbase+=dststride;
}
return;
}
void vo_draw_alpha_rgb16(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
int y;
for(y=0;y<h;y++){
register unsigned short *dst = (unsigned short*) dstbase;
register int x;
for(x=0;x<w;x++){
if(srca[x]){
unsigned char r=dst[x]&0x1F;
unsigned char g=(dst[x]>>5)&0x3F;
unsigned char b=(dst[x]>>11)&0x1F;
r=(((r*srca[x])>>5)+src[x])>>3;
g=(((g*srca[x])>>6)+src[x])>>2;
b=(((b*srca[x])>>5)+src[x])>>3;
dst[x]=(b<<11)|(g<<5)|r;
}
}
src+=srcstride;
srca+=srcstride;
dstbase+=dststride;
}
return;
}

View File

@ -1,34 +0,0 @@
/*
* generic alpha renderers for all YUV modes and RGB depths
* These are "reference implementations", should be optimized later (MMX, etc).
*
* 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.
*/
#ifndef MPLAYER_OSD_H
#define MPLAYER_OSD_H
void vo_draw_alpha_yv12(int w, int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase, int dststride);
void vo_draw_alpha_yuy2(int w, int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase, int dststride);
void vo_draw_alpha_rgb24(int w, int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase, int dststride);
void vo_draw_alpha_rgb32(int w, int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase, int dststride);
void vo_draw_alpha_rgb12(int w, int h, unsigned char* src, unsigned char *srca,
int srcstride, unsigned char* dstbase, int dststride);
void vo_draw_alpha_rgb15(int w, int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase, int dststride);
void vo_draw_alpha_rgb16(int w, int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase, int dststride);
#endif /* MPLAYER_OSD_H */

View File

@ -1,383 +0,0 @@
/*
* generic alpha renderers for all YUV modes and RGB depths
* Optimized by Nick and Michael.
*
* 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.
*/
#undef PREFETCH
#undef EMMS
#undef PREFETCHW
#undef PAVGB
#if HAVE_MMX2
#define PREFETCH "prefetchnta"
#define PREFETCHW "prefetcht0"
#define PAVGB "pavgb"
#else
#define PREFETCH " # nop"
#define PREFETCHW " # nop"
#endif
#define EMMS "emms"
static inline void RENAME(vo_draw_alpha_yv12)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
int y;
#if HAVE_MMX
__asm__ volatile(
"pcmpeqb %%mm5, %%mm5\n\t" // F..F
"movq %%mm5, %%mm4\n\t"
"movq %%mm5, %%mm7\n\t"
"psllw $8, %%mm5\n\t" //FF00FF00FF00
"psrlw $8, %%mm4\n\t" //00FF00FF00FF
::);
#endif
for(y=0;y<h;y++){
register int x;
#if HAVE_MMX
__asm__ volatile(
PREFETCHW" %0\n\t"
PREFETCH" %1\n\t"
PREFETCH" %2\n\t"
::"m"(*dstbase),"m"(*srca),"m"(*src):"memory");
for(x=0;x<w;x+=8){
__asm__ volatile(
"movl %1, %%eax\n\t"
"orl 4%1, %%eax\n\t"
" jz 1f\n\t"
PREFETCHW" 32%0\n\t"
PREFETCH" 32%1\n\t"
PREFETCH" 32%2\n\t"
"movq %0, %%mm0\n\t" // dstbase
"movq %%mm0, %%mm1\n\t"
"pand %%mm4, %%mm0\n\t" //0Y0Y0Y0Y
"psrlw $8, %%mm1\n\t" //0Y0Y0Y0Y
"movq %1, %%mm2\n\t" //srca HGFEDCBA
"paddb %%mm7, %%mm2\n\t"
"movq %%mm2, %%mm3\n\t"
"pand %%mm4, %%mm2\n\t" //0G0E0C0A
"psrlw $8, %%mm3\n\t" //0H0F0D0B
"pmullw %%mm2, %%mm0\n\t"
"pmullw %%mm3, %%mm1\n\t"
"psrlw $8, %%mm0\n\t"
"pand %%mm5, %%mm1\n\t"
"por %%mm1, %%mm0\n\t"
"paddb %2, %%mm0\n\t"
"movq %%mm0, %0\n\t"
"1:\n\t"
:: "m" (dstbase[x]), "m" (srca[x]), "m" (src[x])
: "%eax");
}
#else
for(x=0;x<w;x++){
if(srca[x]) dstbase[x]=((dstbase[x]*srca[x])>>8)+src[x];
}
#endif
src+=srcstride;
srca+=srcstride;
dstbase+=dststride;
}
#if HAVE_MMX
__asm__ volatile(EMMS:::"memory");
#endif
return;
}
static inline void RENAME(vo_draw_alpha_yuy2)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
int y;
#if HAVE_MMX
__asm__ volatile(
"pxor %%mm7, %%mm7\n\t"
"pcmpeqb %%mm5, %%mm5\n\t" // F..F
"movq %%mm5, %%mm6\n\t"
"movq %%mm5, %%mm4\n\t"
"psllw $8, %%mm5\n\t" //FF00FF00FF00
"psrlw $8, %%mm4\n\t" //00FF00FF00FF
::);
#endif
for(y=0;y<h;y++){
register int x;
#if HAVE_MMX
__asm__ volatile(
PREFETCHW" %0\n\t"
PREFETCH" %1\n\t"
PREFETCH" %2\n\t"
::"m"(*dstbase),"m"(*srca),"m"(*src));
for(x=0;x<w;x+=4){
__asm__ volatile(
"movl %1, %%eax\n\t"
"orl %%eax, %%eax\n\t"
" jz 1f\n\t"
PREFETCHW" 32%0\n\t"
PREFETCH" 32%1\n\t"
PREFETCH" 32%2\n\t"
"movq %0, %%mm0\n\t" // dstbase
"movq %%mm0, %%mm1\n\t"
"pand %%mm4, %%mm0\n\t" //0Y0Y0Y0Y
"movd %%eax, %%mm2\n\t" //srca 0000DCBA
"paddb %%mm6, %%mm2\n\t"
"punpcklbw %%mm7, %%mm2\n\t" //srca 0D0C0B0A
"pmullw %%mm2, %%mm0\n\t"
"psrlw $8, %%mm0\n\t"
"pand %%mm5, %%mm1\n\t" //U0V0U0V0
"movd %2, %%mm2\n\t" //src 0000DCBA
"punpcklbw %%mm7, %%mm2\n\t" //srca 0D0C0B0A
"por %%mm1, %%mm0\n\t"
"paddb %%mm2, %%mm0\n\t"
"movq %%mm0, %0\n\t"
"1:\n\t"
:: "m" (dstbase[x*2]), "m" (srca[x]), "m" (src[x])
: "%eax");
}
#else
for(x=0;x<w;x++){
if(srca[x]) {
dstbase[2*x]=((dstbase[2*x]*srca[x])>>8)+src[x];
dstbase[2*x+1]=((((signed)dstbase[2*x+1]-128)*srca[x])>>8)+128;
}
}
#endif
src+=srcstride;
srca+=srcstride;
dstbase+=dststride;
}
#if HAVE_MMX
__asm__ volatile(EMMS:::"memory");
#endif
return;
}
static inline void RENAME(vo_draw_alpha_rgb24)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
int y;
#if HAVE_MMX
__asm__ volatile(
"pxor %%mm7, %%mm7\n\t"
"pcmpeqb %%mm6, %%mm6\n\t" // F..F
::);
#endif
for(y=0;y<h;y++){
register unsigned char *dst = dstbase;
register int x;
#if ARCH_X86 && (!ARCH_X86_64 || HAVE_MMX)
#if HAVE_MMX
__asm__ volatile(
PREFETCHW" %0\n\t"
PREFETCH" %1\n\t"
PREFETCH" %2\n\t"
::"m"(*dst),"m"(*srca),"m"(*src):"memory");
for(x=0;x<w;x+=2){
if(srca[x] || srca[x+1])
__asm__ volatile(
PREFETCHW" 32%0\n\t"
PREFETCH" 32%1\n\t"
PREFETCH" 32%2\n\t"
"movq %0, %%mm0\n\t" // dstbase
"movq %%mm0, %%mm1\n\t"
"movq %%mm0, %%mm5\n\t"
"punpcklbw %%mm7, %%mm0\n\t"
"punpckhbw %%mm7, %%mm1\n\t"
"movd %1, %%mm2\n\t" // srca ABCD0000
"paddb %%mm6, %%mm2\n\t"
"punpcklbw %%mm2, %%mm2\n\t" // srca AABBCCDD
"punpcklbw %%mm2, %%mm2\n\t" // srca AAAABBBB
"psrlq $8, %%mm2\n\t" // srca AAABBBB0
"movq %%mm2, %%mm3\n\t"
"punpcklbw %%mm7, %%mm2\n\t" // srca 0A0A0A0B
"punpckhbw %%mm7, %%mm3\n\t" // srca 0B0B0B00
"pmullw %%mm2, %%mm0\n\t"
"pmullw %%mm3, %%mm1\n\t"
"psrlw $8, %%mm0\n\t"
"psrlw $8, %%mm1\n\t"
"packuswb %%mm1, %%mm0\n\t"
"movd %2, %%mm2 \n\t" // src ABCD0000
"punpcklbw %%mm2, %%mm2\n\t" // src AABBCCDD
"punpcklbw %%mm2, %%mm2\n\t" // src AAAABBBB
"psrlq $8, %%mm2\n\t" // src AAABBBB0
"paddb %%mm2, %%mm0\n\t"
"pand %4, %%mm5\n\t"
"pand %3, %%mm0\n\t"
"por %%mm0, %%mm5\n\t"
"movq %%mm5, %0\n\t"
:: "m" (dst[0]), "m" (srca[x]), "m" (src[x]), "m"(mask24hl), "m"(mask24lh));
dst += 6;
}
#else /* HAVE_MMX */
for(x=0;x<w;x++){
if(srca[x]){
__asm__ volatile(
"movzbl (%0), %%ecx\n\t"
"movzbl 1(%0), %%eax\n\t"
"imull %1, %%ecx\n\t"
"imull %1, %%eax\n\t"
"addl %2, %%ecx\n\t"
"addl %2, %%eax\n\t"
"movb %%ch, (%0)\n\t"
"movb %%ah, 1(%0)\n\t"
"movzbl 2(%0), %%eax\n\t"
"imull %1, %%eax\n\t"
"addl %2, %%eax\n\t"
"movb %%ah, 2(%0)\n\t"
:
:"D" (dst),
"r" ((unsigned)srca[x]),
"r" (((unsigned)src[x])<<8)
:"%eax", "%ecx"
);
}
dst += 3;
}
#endif /* !HAVE_MMX */
#else /*non x86 arch or x86_64 with MMX disabled */
for(x=0;x<w;x++){
if(srca[x]){
dst[0]=((dst[0]*srca[x])>>8)+src[x];
dst[1]=((dst[1]*srca[x])>>8)+src[x];
dst[2]=((dst[2]*srca[x])>>8)+src[x];
}
dst+=3; // 24bpp
}
#endif /* arch_x86 */
src+=srcstride;
srca+=srcstride;
dstbase+=dststride;
}
#if HAVE_MMX
__asm__ volatile(EMMS:::"memory");
#endif
return;
}
static inline void RENAME(vo_draw_alpha_rgb32)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
int y;
#if BYTE_ORDER == BIG_ENDIAN
dstbase++;
#endif
#if HAVE_MMX
__asm__ volatile(
"pxor %%mm7, %%mm7\n\t"
"pcmpeqb %%mm5, %%mm5\n\t" // F..F
"movq %%mm5, %%mm4\n\t"
"psllw $8, %%mm5\n\t" //FF00FF00FF00
"psrlw $8, %%mm4\n\t" //00FF00FF00FF
::);
#endif /* HAVE_MMX */
for(y=0;y<h;y++){
register int x;
#if ARCH_X86 && (!ARCH_X86_64 || HAVE_MMX)
#if HAVE_MMX
__asm__ volatile(
PREFETCHW" %0\n\t"
PREFETCH" %1\n\t"
PREFETCH" %2\n\t"
::"m"(*dstbase),"m"(*srca),"m"(*src):"memory");
for(x=0;x<w;x+=4){
__asm__ volatile(
"movl %1, %%eax\n\t"
"orl %%eax, %%eax\n\t"
" jz 1f\n\t"
PREFETCHW" 32%0\n\t"
PREFETCH" 32%1\n\t"
PREFETCH" 32%2\n\t"
"movq %0, %%mm0\n\t" // dstbase
"movq %%mm0, %%mm1\n\t"
"pand %%mm4, %%mm0\n\t" //0R0B0R0B
"psrlw $8, %%mm1\n\t" //0?0G0?0G
"movd %%eax, %%mm2\n\t" //srca 0000DCBA
"paddb %3, %%mm2\n\t"
"punpcklbw %%mm2, %%mm2\n\t" //srca DDCCBBAA
"movq %%mm2, %%mm3\n\t"
"punpcklbw %%mm7, %%mm2\n\t" //srca 0B0B0A0A
"pmullw %%mm2, %%mm0\n\t"
"pmullw %%mm2, %%mm1\n\t"
"psrlw $8, %%mm0\n\t"
"pand %%mm5, %%mm1\n\t"
"por %%mm1, %%mm0\n\t"
"movd %2, %%mm2 \n\t" //src 0000DCBA
"punpcklbw %%mm2, %%mm2\n\t" //src DDCCBBAA
"movq %%mm2, %%mm6\n\t"
"punpcklbw %%mm2, %%mm2\n\t" //src BBBBAAAA
"paddb %%mm2, %%mm0\n\t"
"movq %%mm0, %0\n\t"
"movq 8%0, %%mm0\n\t" // dstbase
"movq %%mm0, %%mm1\n\t"
"pand %%mm4, %%mm0\n\t" //0R0B0R0B
"psrlw $8, %%mm1\n\t" //0?0G0?0G
"punpckhbw %%mm7, %%mm3\n\t" //srca 0D0D0C0C
"pmullw %%mm3, %%mm0\n\t"
"pmullw %%mm3, %%mm1\n\t"
"psrlw $8, %%mm0\n\t"
"pand %%mm5, %%mm1\n\t"
"por %%mm1, %%mm0\n\t"
"punpckhbw %%mm6, %%mm6\n\t" //src DDDDCCCC
"paddb %%mm6, %%mm0\n\t"
"movq %%mm0, 8%0\n\t"
"1:\n\t"
:: "m" (dstbase[4*x]), "m" (srca[x]), "m" (src[x]), "m" (bFF)
: "%eax");
}
#else /* HAVE_MMX */
for(x=0;x<w;x++){
if(srca[x]){
__asm__ volatile(
"movzbl (%0), %%ecx\n\t"
"movzbl 1(%0), %%eax\n\t"
"movzbl 2(%0), %%edx\n\t"
"imull %1, %%ecx\n\t"
"imull %1, %%eax\n\t"
"imull %1, %%edx\n\t"
"addl %2, %%ecx\n\t"
"addl %2, %%eax\n\t"
"addl %2, %%edx\n\t"
"movb %%ch, (%0)\n\t"
"movb %%ah, 1(%0)\n\t"
"movb %%dh, 2(%0)\n\t"
:
:"r" (&dstbase[4*x]),
"r" ((unsigned)srca[x]),
"r" (((unsigned)src[x])<<8)
:"%eax", "%ecx", "%edx"
);
}
}
#endif /* HAVE_MMX */
#else /*non x86 arch or x86_64 with MMX disabled */
for(x=0;x<w;x++){
if(srca[x]){
dstbase[4*x+0]=((dstbase[4*x+0]*srca[x])>>8)+src[x];
dstbase[4*x+1]=((dstbase[4*x+1]*srca[x])>>8)+src[x];
dstbase[4*x+2]=((dstbase[4*x+2]*srca[x])>>8)+src[x];
}
}
#endif /* arch_x86 */
src+=srcstride;
srca+=srcstride;
dstbase+=dststride;
}
#if HAVE_MMX
__asm__ volatile(EMMS:::"memory");
#endif
return;
}

View File

@ -38,6 +38,8 @@
#include "mp_fifo.h"
#include "m_config.h"
#include "mp_msg.h"
#include "libmpcodecs/vfcap.h"
#include "sub/sub.h"
#include "osdep/shmem.h"
#ifdef CONFIG_X11
@ -50,7 +52,6 @@ int xinerama_y;
int vo_nomouse_input = 0;
int vo_grabpointer = 1;
int vo_doublebuffering = 1;
int vo_vsync = 1;
int vo_fs = 0;
int vo_fsmode = 0;
@ -224,9 +225,8 @@ void vo_new_frame_imminent(struct vo *vo)
void vo_draw_osd(struct vo *vo, struct osd_state *osd)
{
if (!vo->config_ok)
return;
vo->driver->draw_osd(vo, osd);
if (vo->config_ok && (vo->default_caps & VFCAP_OSD))
vo->driver->draw_osd(vo, osd);
}
void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration)
@ -370,8 +370,7 @@ int vo_config(struct vo *vo, uint32_t width, uint32_t height,
{
struct MPOpts *opts = vo->opts;
panscan_init(vo);
aspect_save_orig(vo, width, height);
aspect_save_prescale(vo, d_width, d_height);
aspect_save_videores(vo, width, height, d_width, d_height);
if (vo_control(vo, VOCTRL_UPDATE_SCREENINFO, NULL) == VO_TRUE) {
aspect(vo, &d_width, &d_height, A_NOZOOM);
@ -386,10 +385,14 @@ int vo_config(struct vo *vo, uint32_t width, uint32_t height,
vo->dheight = d_height;
}
vo->default_caps = vo_control(vo, VOCTRL_QUERY_FORMAT, &format);
int ret = vo->driver->config(vo, width, height, d_width, d_height, flags,
format);
vo->config_ok = (ret == 0);
vo->config_count += vo->config_ok;
if (!vo->config_ok)
vo->default_caps = 0;
if (vo->registered_fd == -1 && vo->event_fd != -1 && vo->config_ok) {
mp_input_add_key_fd(vo->input_ctx, vo->event_fd, 1, event_fd_callback,
NULL, vo);
@ -413,76 +416,91 @@ int lookup_keymap_table(const struct mp_keymap *map, int key) {
return map->to;
}
/**
* \brief helper function for the kind of panscan-scaling that needs a source
* and destination rectangle like Direct3D and VDPAU
*/
static void src_dst_split_scaling(int src_size, int dst_size, int scaled_src_size,
int *src_start, int *src_end, int *dst_start, int *dst_end) {
if (scaled_src_size > dst_size) {
int border = src_size * (scaled_src_size - dst_size) / scaled_src_size;
// round to a multiple of 2, this is at least needed for vo_direct3d and ATI cards
border = (border / 2 + 1) & ~1;
*src_start = border;
*src_end = src_size - border;
*dst_start = 0;
*dst_end = dst_size;
} else {
*src_start = 0;
*src_end = src_size;
*dst_start = (dst_size - scaled_src_size) / 2;
*dst_end = *dst_start + scaled_src_size;
}
static void print_video_rect(struct vo *vo, struct mp_rect src,
struct mp_rect dst, struct mp_osd_res osd)
{
int lv = MSGL_V;
int sw = src.x1 - src.x0, sh = src.y1 - src.y0;
int dw = dst.x1 - dst.x0, dh = dst.y1 - dst.y0;
mp_msg(MSGT_VO, lv, "[vo] Window size: %dx%d\n",
vo->dwidth, vo->dheight);
mp_msg(MSGT_VO, lv, "[vo] Video source: %dx%d (%dx%d)\n",
vo->aspdat.orgw, vo->aspdat.orgh,
vo->aspdat.prew, vo->aspdat.preh);
mp_msg(MSGT_VO, lv, "[vo] Video display: (%d, %d) %dx%d -> (%d, %d) %dx%d\n",
src.x0, src.y0, sw, sh, dst.x0, dst.y0, dw, dh);
mp_msg(MSGT_VO, lv, "[vo] Video scale: %f/%f\n",
(double)dw / sw, (double)dh / sh);
mp_msg(MSGT_VO, lv, "[vo] OSD borders: l=%d t=%d r=%d b=%d\n",
osd.ml, osd.mt, osd.mr, osd.mb);
mp_msg(MSGT_VO, lv, "[vo] Video borders: l=%d t=%d r=%d b=%d\n",
dst.x0, dst.y0, vo->dwidth - dst.x1, vo->dheight - dst.y1);
}
/**
* Calculate the appropriate source and destination rectangle to
* get a correctly scaled picture, including pan-scan.
* Can be extended to take future cropping support into account.
*
* \param crop specifies the cropping border size in the left, right, top and bottom members, may be NULL
* \param borders the border values as e.g. EOSD (ASS) and properly placed DVD highlight support requires,
* may be NULL and only left and top are currently valid.
*/
void calc_src_dst_rects(struct vo *vo, int src_width, int src_height,
struct vo_rect *src, struct vo_rect *dst,
struct vo_rect *borders, const struct vo_rect *crop)
static void src_dst_split_scaling(int src_size, int dst_size,
int scaled_src_size, int *src_start,
int *src_end, int *dst_start, int *dst_end)
{
static const struct vo_rect no_crop = {0, 0, 0, 0, 0, 0};
int scaled_width = 0;
int scaled_height = 0;
if (!crop) crop = &no_crop;
src_width -= crop->left + crop->right;
src_height -= crop->top + crop->bottom;
if (src_width < 2) src_width = 2;
if (src_height < 2) src_height = 2;
dst->left = 0; dst->right = vo->dwidth;
dst->top = 0; dst->bottom = vo->dheight;
src->left = 0; src->right = src_width;
src->top = 0; src->bottom = src_height;
if (borders) {
borders->left = 0; borders->top = 0;
}
if (aspect_scaling()) {
aspect(vo, &scaled_width, &scaled_height, A_WINZOOM);
panscan_calc_windowed(vo);
scaled_width += vo->panscan_x;
scaled_height += vo->panscan_y;
if (borders) {
borders->left = (vo->dwidth - scaled_width ) / 2;
borders->top = (vo->dheight - scaled_height) / 2;
if (scaled_src_size > dst_size) {
int border = src_size * (scaled_src_size - dst_size) / scaled_src_size;
// round to a multiple of 2, this is at least needed for vo_direct3d
// and ATI cards
border = (border / 2 + 1) & ~1;
*src_start = border;
*src_end = src_size - border;
*dst_start = 0;
*dst_end = dst_size;
} else {
*src_start = 0;
*src_end = src_size;
*dst_start = (dst_size - scaled_src_size) / 2;
*dst_end = *dst_start + scaled_src_size;
}
src_dst_split_scaling(src_width, vo->dwidth, scaled_width,
&src->left, &src->right, &dst->left, &dst->right);
src_dst_split_scaling(src_height, vo->dheight, scaled_height,
&src->top, &src->bottom, &dst->top, &dst->bottom);
}
src->left += crop->left; src->right += crop->left;
src->top += crop->top; src->bottom += crop->top;
src->width = src->right - src->left;
src->height = src->bottom - src->top;
dst->width = dst->right - dst->left;
dst->height = dst->bottom - dst->top;
}
// Calculate the appropriate source and destination rectangle to
// get a correctly scaled picture, including pan-scan.
// out_src: visible part of the video
// out_dst: area of screen covered by the video source rectangle
// out_osd: OSD size, OSD margins, etc.
void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
struct mp_rect *out_dst, struct mp_osd_res *out_osd)
{
int src_w = vo->aspdat.orgw;
int src_h = vo->aspdat.orgh;
struct mp_rect dst = {0, 0, vo->dwidth, vo->dheight};
struct mp_rect src = {0, 0, src_w, src_h};
struct mp_osd_res osd = {
.w = vo->dwidth,
.h = vo->dheight,
.display_par = vo->monitor_par,
.video_par = vo->aspdat.par,
};
if (aspect_scaling()) {
int scaled_width = 0, scaled_height = 0;
aspect(vo, &scaled_width, &scaled_height, A_WINZOOM);
panscan_calc_windowed(vo);
scaled_width += vo->panscan_x;
scaled_height += vo->panscan_y;
int border_w = vo->dwidth - scaled_width;
int border_h = vo->dheight - scaled_height;
osd.ml = border_w / 2;
osd.mt = border_h / 2;
osd.mr = border_w - osd.ml;
osd.mb = border_h - osd.mt;
src_dst_split_scaling(src_w, vo->dwidth, scaled_width,
&src.x0, &src.x1, &dst.x0, &dst.x1);
src_dst_split_scaling(src_h, vo->dheight, scaled_height,
&src.y0, &src.y1, &dst.y0, &dst.y1);
}
*out_src = src;
*out_dst = dst;
*out_osd = osd;
print_video_rect(vo, src, dst, osd);
}
// Return the window title the VO should set. Always returns a null terminated

View File

@ -64,8 +64,6 @@ enum mp_voctrl {
VOCTRL_ONTOP,
VOCTRL_ROOTWIN,
VOCTRL_BORDER,
VOCTRL_DRAW_EOSD,
VOCTRL_GET_EOSD_RES, // struct mp_eosd_res
VOCTRL_SET_DEINTERLACE,
VOCTRL_GET_DEINTERLACE,
@ -107,6 +105,8 @@ struct voctrl_screenshot_args {
// image data directly.
// Is never NULL. (Failure has to be indicated by returning VO_FALSE.)
struct mp_image *out_image;
// Whether the VO rendered OSD/subtitles into out_image
bool has_osd;
};
typedef struct {
@ -233,6 +233,7 @@ struct vo_driver {
struct vo {
int config_ok; // Last config call was successful?
int config_count; // Total number of successful config calls
int default_caps; // query_format() result for configured video format
bool frame_loaded; // Is there a next frame the VO could flip to?
struct mp_image *waiting_mpi;
@ -256,7 +257,7 @@ struct vo {
int event_fd; // check_events() should be called when this has input
int registered_fd; // set to event_fd when registered in input system
// requested position/resolution
// requested position/resolution (usually window position/window size)
int dx;
int dy;
int dwidth;
@ -265,12 +266,13 @@ struct vo {
int panscan_x;
int panscan_y;
float panscan_amount;
float monitor_aspect;
float monitor_par;
struct aspect_data {
int orgw; // real width
int orgh; // real height
int prew; // prescaled width
int preh; // prescaled height
float par; // pixel aspect ratio out of orgw/orgh and prew/preh
int scrw; // horizontal resolution
int scrh; // vertical resolution
float asp;
@ -311,7 +313,6 @@ extern int xinerama_x;
extern int xinerama_y;
extern int vo_grabpointer;
extern int vo_doublebuffering;
extern int vo_vsync;
extern int vo_fs;
extern int vo_fsmode;
@ -336,14 +337,13 @@ struct mp_keymap {
int to;
};
int lookup_keymap_table(const struct mp_keymap *map, int key);
struct vo_rect {
int left, right, top, bottom, width, height;
};
void calc_src_dst_rects(struct vo *vo, int src_width, int src_height,
struct vo_rect *src, struct vo_rect *dst,
struct vo_rect *borders, const struct vo_rect *crop);
void vo_mouse_movement(struct vo *vo, int posx, int posy);
struct mp_osd_res;
void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
struct mp_rect *out_dst, struct mp_osd_res *out_osd);
static inline int aspect_scaling(void)
{
return vo_keepaspect || vo_fs;

View File

@ -325,9 +325,9 @@ static void uninit(struct vo *vo)
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
if (vo_osd_progbar_type != -1)
osdpercent(MESSAGE_DURATION, 0, 255, vo_osd_progbar_value,
sub_osd_names[vo_osd_progbar_type], "");
if (osd->progbar_type != -1)
osdpercent(MESSAGE_DURATION, 0, 255, osd->progbar_value,
sub_osd_names[osd->progbar_type], "");
}
static int preinit(struct vo *vo, const char *arg)

View File

@ -19,6 +19,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <assert.h>
#import "vo_corevideo.h"
// mplayer includes
@ -32,9 +34,9 @@
#import "csputils.h"
#import "libmpcodecs/vfcap.h"
#import "libmpcodecs/mp_image.h"
#import "osd.h"
#import "gl_common.h"
#import "gl_osd.h"
#import "cocoa_common.h"
struct quad {
@ -44,44 +46,31 @@ struct quad {
GLfloat upperLeft[2];
};
#define CV_VERTICES_PER_QUAD 6
#define CV_MAX_OSD_PARTS 20
struct osd_p {
GLuint tex[CV_MAX_OSD_PARTS];
NSRect tex_rect[CV_MAX_OSD_PARTS];
int tex_cnt;
};
struct priv {
MPGLContext *mpglctx;
OSType pixelFormat;
unsigned int image_width;
unsigned int image_height;
struct mp_csp_details colorspace;
int ass_border_x, ass_border_y;
CVPixelBufferRef pixelBuffer;
CVOpenGLTextureCacheRef textureCache;
CVOpenGLTextureRef texture;
struct quad *quad;
struct osd_p *osd;
struct mpgl_osd *osd;
};
static void resize(struct vo *vo, int width, int height)
{
struct priv *p = vo->priv;
GL *gl = p->mpglctx->gl;
p->image_width = width;
p->image_height = height;
mp_msg(MSGT_VO, MSGL_V, "[vo_corevideo] New OpenGL Viewport (0, 0, %d, "
"%d)\n", p->image_width, p->image_height);
gl->Viewport(0, 0, p->image_width, p->image_height);
gl->Viewport(0, 0, width, height);
gl->MatrixMode(GL_PROJECTION);
gl->LoadIdentity();
p->ass_border_x = p->ass_border_y = 0;
if (aspect_scaling()) {
int new_w, new_h;
GLdouble scale_x, scale_y;
@ -90,17 +79,17 @@ static void resize(struct vo *vo, int width, int height)
panscan_calc_windowed(vo);
new_w += vo->panscan_x;
new_h += vo->panscan_y;
scale_x = (GLdouble)new_w / (GLdouble)p->image_width;
scale_y = (GLdouble)new_h / (GLdouble)p->image_height;
scale_x = (GLdouble)new_w / (GLdouble)width;
scale_y = (GLdouble)new_h / (GLdouble)height;
gl->Scaled(scale_x, scale_y, 1);
p->ass_border_x = (vo->dwidth - new_w) / 2;
p->ass_border_y = (vo->dheight - new_h) / 2;
}
gl->Ortho(0, p->image_width, p->image_height, 0, -1.0, 1.0);
gl->MatrixMode(GL_MODELVIEW);
gl->LoadIdentity();
vo_osd_resized();
gl->Clear(GL_COLOR_BUFFER_BIT);
vo->want_redraw = true;
}
@ -125,6 +114,9 @@ static int init_gl(struct vo *vo, uint32_t d_width, uint32_t d_height)
gl->DrawBuffer(GL_BACK);
gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
if (!p->osd)
p->osd = mpgl_osd_init(gl, true);
resize(vo, d_width, d_height);
gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@ -142,7 +134,6 @@ static void release_cv_entities(struct vo *vo) {
p->texture = NULL;
CVOpenGLTextureCacheRelease(p->textureCache);
p->textureCache = NULL;
}
static int config(struct vo *vo, uint32_t width, uint32_t height,
@ -171,90 +162,6 @@ static void check_events(struct vo *vo)
resize(vo, vo->dwidth, vo->dheight);
}
static void create_osd_texture(void *ctx, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride)
{
struct priv *p = ((struct vo *) ctx)->priv;
struct osd_p *osd = p->osd;
GL *gl = p->mpglctx->gl;
if (w <= 0 || h <= 0 || stride < w) {
mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n");
return;
}
if (osd->tex_cnt >= CV_MAX_OSD_PARTS) {
mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the"
" developers!\n");
return;
}
gl->GenTextures(1, &osd->tex[osd->tex_cnt]);
gl->BindTexture(GL_TEXTURE_2D, osd->tex[osd->tex_cnt]);
glCreateClearTex(gl, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE, GL_LINEAR, w, h, 0);
{
int i;
unsigned char *tmp = malloc(stride * h * 2);
// convert alpha from weird MPlayer scale.
for (i = 0; i < h * stride; i++) {
tmp[i*2+0] = src[i];
tmp[i*2+1] = -srca[i];
}
glUploadTex(gl, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
tmp, stride * 2, 0, 0, w, h, 0);
free(tmp);
}
osd->tex_rect[osd->tex_cnt] = NSMakeRect(x0, y0, w, h);
gl->BindTexture(GL_TEXTURE_2D, 0);
osd->tex_cnt++;
}
static void clearOSD(struct vo *vo)
{
struct priv *p = vo->priv;
struct osd_p *osd = p->osd;
GL *gl = p->mpglctx->gl;
if (!osd->tex_cnt)
return;
gl->DeleteTextures(osd->tex_cnt, osd->tex);
osd->tex_cnt = 0;
}
static void draw_osd(struct vo *vo, struct osd_state *osd_s)
{
struct priv *p = vo->priv;
struct osd_p *osd = p->osd;
GL *gl = p->mpglctx->gl;
if (vo_osd_has_changed(osd_s)) {
clearOSD(vo);
osd_draw_text_ext(osd_s, vo->dwidth, vo->dheight, 0, 0, 0, 0,
p->image_width, p->image_height, create_osd_texture,
vo);
}
if (osd->tex_cnt > 0) {
gl->Enable(GL_BLEND);
gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
for (int n = 0; n < osd->tex_cnt; n++) {
NSRect tr = osd->tex_rect[n];
gl->BindTexture(GL_TEXTURE_2D, osd->tex[n]);
glDrawTex(gl, tr.origin.x, tr.origin.y,
tr.size.width, tr.size.height,
0, 0, 1.0, 1.0, 1, 1, 0, 0, 0);
}
gl->Disable(GL_BLEND);
gl->BindTexture(GL_TEXTURE_2D, 0);
}
}
static void prepare_texture(struct vo *vo)
{
struct priv *p = vo->priv;
@ -366,8 +273,10 @@ static int query_format(struct vo *vo, uint32_t format)
static void uninit(struct vo *vo)
{
struct priv *p = vo->priv;
mpgl_uninit(p->mpglctx);
if (p->osd)
mpgl_osd_destroy(p->osd);
release_cv_entities(vo);
mpgl_uninit(p->mpglctx);
}
@ -379,16 +288,39 @@ static int preinit(struct vo *vo, const char *arg)
.mpglctx = mpgl_init(GLTYPE_COCOA, vo),
.colorspace = MP_CSP_DETAILS_DEFAULTS,
.quad = talloc_ptrtype(p, p->quad),
.osd = talloc_ptrtype(p, p->osd),
};
*p->osd = (struct osd_p) {
.tex_cnt = 0,
};
return 0;
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct priv *p = vo->priv;
GL *gl = p->mpglctx->gl;
assert(p->osd);
gl->MatrixMode(GL_PROJECTION);
gl->PushMatrix();
gl->LoadIdentity();
gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1);
struct mp_osd_res res = {
.w = vo->dwidth,
.h = vo->dheight,
.display_par = vo->monitor_par,
.video_par = vo->aspdat.par,
};
if (aspect_scaling()) {
res.ml = res.mr = p->ass_border_x;
res.mt = res.mb = p->ass_border_y;
}
mpgl_osd_draw_legacy(p->osd, osd, res);
gl->PopMatrix();
}
static CFStringRef get_cv_csp_matrix(struct vo *vo)
{
struct priv *p = vo->priv;
@ -412,6 +344,45 @@ static void set_yuv_colorspace(struct vo *vo)
vo->want_redraw = true;
}
static int get_image_fmt(struct vo *vo)
{
struct priv *p = vo->priv;
switch (p->pixelFormat) {
case kYUVSPixelFormat: return IMGFMT_YUY2;
case k24RGBPixelFormat: return IMGFMT_RGB24;
case k32ARGBPixelFormat: return IMGFMT_ARGB;
case k32BGRAPixelFormat: return IMGFMT_BGRA;
}
mp_msg(MSGT_VO, MSGL_ERR, "[vo_corevideo] Failed to convert pixel format. "
"Please contact the developers. PixelFormat: %d\n", p->pixelFormat);
return -1;
}
static mp_image_t *get_screenshot(struct vo *vo)
{
int img_fmt = get_image_fmt(vo);
if (img_fmt < 0) return NULL;
struct priv *p = vo->priv;
void *base = CVPixelBufferGetBaseAddress(p->pixelBuffer);
size_t width = CVPixelBufferGetWidth(p->pixelBuffer);
size_t height = CVPixelBufferGetHeight(p->pixelBuffer);
size_t stride = CVPixelBufferGetBytesPerRow(p->pixelBuffer);
size_t image_size = stride * height;
mp_image_t *image = alloc_mpi(width, height, img_fmt);
memcpy(image->planes[0], base, image_size);
image->stride[0] = stride;
image->display_w = vo->aspdat.prew;
image->display_h = vo->aspdat.preh;
mp_image_set_colorspace_details(image, &p->colorspace);
return image;
}
static int control(struct vo *vo, uint32_t request, void *data)
{
struct priv *p = vo->priv;
@ -455,6 +426,14 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_GET_YUV_COLORSPACE:
*(struct mp_csp_details *)data = p->colorspace;
return VO_TRUE;
case VOCTRL_SCREENSHOT: {
struct voctrl_screenshot_args *args = data;
if (args->full_window)
args->out_image = glGetWindowScreenshot(p->mpglctx->gl);
else
args->out_image = get_screenshot(vo);
return VO_TRUE;
}
}
return VO_NOTIMPL;
}

View File

@ -41,7 +41,7 @@
#include "w32_common.h"
#include "libavutil/common.h"
#include "sub/sub.h"
#include "eosd_packer.h"
#include "bitmap_packer.h"
// shaders generated by fxc.exe from d3d_shader_yuv.hlsl
#include "d3d_shader_yuv.h"
@ -59,21 +59,13 @@
#define DEVTYPE D3DDEVTYPE_HAL
//#define DEVTYPE D3DDEVTYPE_REF
#define D3DFVF_OSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
typedef struct {
float x, y, z; /* Position of vertex in 3D space */
float tu, tv; /* Texture coordinates */
} vertex_osd;
#define D3DFVF_EOSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE)
#define D3DFVF_OSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE)
typedef struct {
float x, y, z;
D3DCOLOR color;
float tu, tv;
} vertex_eosd;
} vertex_osd;
#define D3DFVF_VIDEO_VERTEX (D3DFVF_XYZ | D3DFVF_TEX3)
@ -116,6 +108,15 @@ struct texplane {
uint8_t clearval;
};
struct osdpart {
enum sub_bitmap_format format;
int bitmap_id, bitmap_pos_id;
struct d3dtex texture;
int num_vertices;
vertex_osd *vertices;
struct bitmap_packer *packer;
};
/* Global variables "priv" structure. I try to keep their count low.
*/
typedef struct d3d_priv {
@ -124,7 +125,7 @@ typedef struct d3d_priv {
int opt_disable_stretchrect;
int opt_disable_shaders;
int opt_only_8bit;
int opt_disable_eosd;
int opt_disable_osd;
int opt_disable_texture_align;
// debugging
int opt_force_power_of_2;
@ -144,8 +145,7 @@ typedef struct d3d_priv {
fullscreen */
int src_width; /**< Source (movie) width */
int src_height; /**< Source (movie) heigth */
int border_x; /**< horizontal border value for OSD */
int border_y; /**< vertical border value for OSD */
struct mp_osd_res osd_res;
int image_format; /**< mplayer image format */
bool use_textures; /**< use 3D texture rendering, instead of
StretchRect */
@ -173,14 +173,10 @@ typedef struct d3d_priv {
IDirect3DSurface9 *d3d_surface; /**< Offscreen Direct3D Surface. MPlayer
renders inside it. Uses colorspace
priv->movie_src_fmt */
struct d3dtex texture_osd; /**< RGBA */
IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to
display next frame) */
struct d3dtex texture_eosd; /**< A8 */
int cur_backbuf_width; /**< Current backbuffer width */
int cur_backbuf_height; /**< Current backbuffer height */
int is_osd_populated; /**< 1 = OSD texture has something to display,
0 = OSD texture is clear */
int device_caps_power2_only; /**< 1 = texture sizes have to be power 2
0 = texture sizes can be anything */
int device_caps_square_only; /**< 1 = textures have to be square
@ -195,8 +191,7 @@ typedef struct d3d_priv {
struct mp_csp_details colorspace;
struct mp_csp_equalizer video_eq;
struct eosd_packer *eosd; /**< EOSD packer (image positions etc.) */
vertex_eosd *eosd_vb; /**< temporary memory for D3D when rendering EOSD */
struct osdpart *osd[MAX_OSD_PARTS];
} d3d_priv;
struct fmt_entry {
@ -230,9 +225,16 @@ static const struct fmt_entry fmt_table[] = {
{0},
};
static const D3DFORMAT osd_fmt_table[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = D3DFMT_A8,
[SUBBITMAP_RGBA] = D3DFMT_A8R8G8B8,
};
static const bool osd_fmt_supported[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = true,
[SUBBITMAP_RGBA] = true,
};
static void generate_eosd(d3d_priv *priv, mp_eosd_images_t *);
static void draw_eosd(d3d_priv *priv);
static void update_colorspace(d3d_priv *priv);
static void d3d_clear_video_textures(d3d_priv *priv);
static bool resize_d3d(d3d_priv *priv);
@ -240,7 +242,6 @@ static uint32_t d3d_draw_frame(d3d_priv *priv);
static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
int x, int y);
static void uninit(struct vo *vo);
static void draw_osd(struct vo *vo, struct osd_state *osd);
static void flip_page(struct vo *vo);
static mp_image_t *get_screenshot(d3d_priv *priv);
static mp_image_t *get_window_screenshot(d3d_priv *priv);
@ -292,22 +293,18 @@ static bool d3d_begin_scene(d3d_priv *priv)
*/
static void calc_fs_rect(d3d_priv *priv)
{
struct vo_rect src_rect;
struct vo_rect dst_rect;
struct vo_rect borders;
calc_src_dst_rects(priv->vo, priv->src_width, priv->src_height, &src_rect,
&dst_rect, &borders, NULL);
struct mp_rect src_rect;
struct mp_rect dst_rect;
vo_get_src_dst_rects(priv->vo, &src_rect, &dst_rect, &priv->osd_res);
priv->fs_movie_rect.left = dst_rect.left;
priv->fs_movie_rect.right = dst_rect.right;
priv->fs_movie_rect.top = dst_rect.top;
priv->fs_movie_rect.bottom = dst_rect.bottom;
priv->fs_panscan_rect.left = src_rect.left;
priv->fs_panscan_rect.right = src_rect.right;
priv->fs_panscan_rect.top = src_rect.top;
priv->fs_panscan_rect.bottom = src_rect.bottom;
priv->border_x = borders.left;
priv->border_y = borders.top;
priv->fs_movie_rect.left = dst_rect.x0;
priv->fs_movie_rect.right = dst_rect.x1;
priv->fs_movie_rect.top = dst_rect.y0;
priv->fs_movie_rect.bottom = dst_rect.y1;
priv->fs_panscan_rect.left = src_rect.x0;
priv->fs_panscan_rect.right = src_rect.x1;
priv->fs_panscan_rect.top = src_rect.y0;
priv->fs_panscan_rect.bottom = src_rect.y1;
mp_msg(MSGT_VO, MSGL_V,
"<vo_direct3d>Video rectangle: t: %ld, l: %ld, r: %ld, b:%ld\n",
@ -478,17 +475,16 @@ static void destroy_d3d_surfaces(d3d_priv *priv)
d3d_destroy_video_objects(priv);
d3dtex_release(priv, &priv->texture_osd);
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct osdpart *osd = priv->osd[n];
d3dtex_release(priv, &osd->texture);
osd->bitmap_id = osd->bitmap_pos_id = -1;
}
if (priv->d3d_backbuf)
IDirect3DSurface9_Release(priv->d3d_backbuf);
priv->d3d_backbuf = NULL;
d3dtex_release(priv, &priv->texture_eosd);
if (priv->eosd)
eosd_packer_reinit(priv->eosd, 0, 0);
priv->d3d_in_scene = false;
}
@ -596,8 +592,6 @@ static void d3d_clear_video_textures(d3d_priv *priv)
// done.
static bool create_d3d_surfaces(d3d_priv *priv)
{
int osd_width = priv->vo->dwidth, osd_height = priv->vo->dheight;
int tex_width = osd_width, tex_height = osd_height;
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>create_d3d_surfaces called.\n");
if (!priv->d3d_backbuf &&
@ -611,40 +605,6 @@ static bool create_d3d_surfaces(d3d_priv *priv)
if (!d3d_configure_video_objects(priv))
return 0;
/* create OSD */
d3d_fix_texture_size(priv, &tex_width, &tex_height);
while (tex_width > priv->max_texture_width
|| tex_height > priv->max_texture_height)
{
osd_width >>= 1;
osd_height >>= 1;
tex_width >>= 1;
tex_height >>= 1;
}
if (priv->texture_osd.tex_w < tex_width
|| priv->texture_osd.tex_h < tex_height)
{
d3dtex_release(priv, &priv->texture_osd);
mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>OSD texture size (%dx%d),"
" requested (%dx%d).\n", osd_width, osd_height, tex_width,
tex_height);
if (!d3dtex_allocate(priv, &priv->texture_osd, D3DFMT_A8L8, tex_width,
tex_height))
{
mp_msg(MSGT_VO, MSGL_ERR,
"<vo_direct3d>Allocating OSD texture failed.\n");
return 0;
}
}
priv->texture_osd.w = osd_width;
priv->texture_osd.h = osd_height;
/* setup default renderstate */
IDirect3DDevice9_SetRenderState(priv->d3d_device,
D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
@ -669,10 +629,6 @@ static bool create_d3d_surfaces(d3d_priv *priv)
D3DTADDRESS_CLAMP);
}
if (priv->eosd && !priv->texture_eosd.system)
eosd_packer_reinit(priv->eosd, priv->max_texture_width,
priv->max_texture_height);
return 1;
}
@ -890,8 +846,6 @@ static bool resize_d3d(d3d_priv *priv)
calc_fs_rect(priv);
vo_osd_resized();
priv->vo->want_redraw = true;
return 1;
@ -1266,11 +1220,11 @@ static int query_format(d3d_priv *priv, uint32_t movie_fmt)
if (!init_rendering_mode(priv, movie_fmt, false))
return 0;
int eosd_caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
if (priv->eosd)
eosd_caps |= VFCAP_EOSD | VFCAP_EOSD_UNSCALED;
return eosd_caps;
int osd_caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
if (!priv->opt_disable_osd)
osd_caps |= VFCAP_OSD;
return osd_caps;
}
/****************************************************************************
@ -1338,7 +1292,7 @@ static void update_colorspace(d3d_priv *priv)
}
const char *options_help_text = "-vo direct3d command line help:\n"
"Example: -vo direct3d:disable-eosd:disable-textures\n"
"Example: -vo direct3d:disable-osd:disable-textures\n"
"Options:\n"
" prefer-stretchrect\n"
" Use IDirect3DDevice9::StretchRect over other methods if possible.\n"
@ -1353,8 +1307,8 @@ const char *options_help_text = "-vo direct3d command line help:\n"
" only-8bit\n"
" Never render YUV video with more than 8 bits per component.\n"
" (Using this flag will force software conversion to 8 bit.)\n"
" disable-eosd\n"
" Disable EOSD rendering for subtitles.\n"
" disable-osd\n"
" Disable OSD rendering.\n"
" (Using this flag might force the insertion of the 'ass' video filter,\n"
" which will render the subtitles in software.)\n"
" disable-texture-align\n"
@ -1410,6 +1364,14 @@ static int preinit_internal(struct vo *vo, const char *arg, bool allow_shaders)
.video_eq = { MP_CSP_EQ_CAPS_COLORMATRIX },
};
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct osdpart *osd = talloc_ptrtype(priv, osd);
*osd = (struct osdpart) {
.packer = talloc_zero(osd, struct bitmap_packer),
};
priv->osd[n] = osd;
}
if (!allow_shaders) {
priv->opt_disable_shaders = priv->opt_disable_textures = true;
}
@ -1420,7 +1382,7 @@ static int preinit_internal(struct vo *vo, const char *arg, bool allow_shaders)
{"disable-stretchrect", OPT_ARG_BOOL, &priv->opt_disable_stretchrect},
{"disable-shaders", OPT_ARG_BOOL, &priv->opt_disable_shaders},
{"only-8bit", OPT_ARG_BOOL, &priv->opt_only_8bit},
{"disable-eosd", OPT_ARG_BOOL, &priv->opt_disable_eosd},
{"disable-osd", OPT_ARG_BOOL, &priv->opt_disable_osd},
{"force-power-of-2", OPT_ARG_BOOL, &priv->opt_force_power_of_2},
{"disable-texture-align", OPT_ARG_BOOL, &priv->opt_disable_texture_align},
{"texture-memory", OPT_ARG_INT, &priv->opt_texture_memory},
@ -1434,9 +1396,6 @@ static int preinit_internal(struct vo *vo, const char *arg, bool allow_shaders)
return -1;
}
if (!priv->opt_disable_eosd)
priv->eosd = eosd_packer_create(priv);
priv->d3d9_dll = LoadLibraryA("d3d9.dll");
if (!priv->d3d9_dll) {
mp_msg(MSGT_VO, MSGL_ERR,
@ -1544,22 +1503,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
return VO_TRUE;
case VOCTRL_GET_PANSCAN:
return VO_TRUE;
case VOCTRL_DRAW_EOSD:
if (!data)
return VO_FALSE;
assert(priv->eosd);
generate_eosd(priv, data);
draw_eosd(priv);
return VO_TRUE;
case VOCTRL_GET_EOSD_RES: {
assert(priv->eosd);
struct mp_eosd_res *r = data;
r->w = vo->dwidth;
r->h = vo->dheight;
r->ml = r->mr = priv->border_x;
r->mt = r->mb = priv->border_y;
return VO_TRUE;
}
case VOCTRL_SCREENSHOT: {
struct voctrl_screenshot_args *args = data;
if (args->full_window)
@ -1853,8 +1796,10 @@ static mp_image_t *get_screenshot(d3d_priv *priv)
return NULL;
}
image->w = priv->vo->aspdat.prew;
image->h = priv->vo->aspdat.preh;
image->display_w = priv->vo->aspdat.prew;
image->display_h = priv->vo->aspdat.preh;
mp_image_set_colorspace_details(image, &priv->colorspace);
return image;
}
@ -1929,142 +1874,86 @@ error_exit:
return NULL;
}
/** @brief Maps MPlayer alpha to D3D
* 0x0 -> transparent and discarded by alpha test
* 0x1 -> 0xFF to become opaque
* other alpha values are inverted +1 (2 = -2)
* These values are then inverted again with
the texture filter D3DBLEND_INVSRCALPHA
*/
static void vo_draw_alpha_l8a8(int w, int h, unsigned char* src,
unsigned char *srca, int srcstride,
unsigned char* dstbase, int dststride)
static bool upload_osd(d3d_priv *priv, struct osdpart *osd,
struct sub_bitmaps *imgs)
{
int y;
for (y = 0; y < h; y++) {
unsigned short *dst = (unsigned short*)dstbase;
int x;
for (x = 0; x < w; x++) {
dst[x] = (-srca[x] << 8) | src[x];
}
src += srcstride;
srca += srcstride;
dstbase += dststride;
}
}
D3DFORMAT fmt = osd_fmt_table[imgs->format];
osd->packer->w_max = priv->max_texture_width;
osd->packer->h_max = priv->max_texture_height;
osd->packer->padding = imgs->scaled; // assume 2x2 filter on scaling
int r = packer_pack_from_subbitmaps(osd->packer, imgs);
if (r < 0) {
mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>OSD bitmaps do not fit on "
"a surface with the maximum supported size %dx%d.\n",
osd->packer->w_max, osd->packer->h_max);
return false;
}
if (osd->packer->w > osd->texture.tex_w
|| osd->packer->h > osd->texture.tex_h
|| osd->format != imgs->format)
{
osd->format = imgs->format;
int new_w = osd->packer->w;
int new_h = osd->packer->h;
d3d_fix_texture_size(priv, &new_w, &new_h);
mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>reallocate OSD surface.\n");
d3dtex_release(priv, &osd->texture);
d3dtex_allocate(priv, &osd->texture, fmt, new_w, new_h);
if (!osd->texture.system)
return false; // failed to allocate
}
struct pos bb[2];
packer_get_bb(osd->packer, bb);
RECT dirty_rc = { bb[0].x, bb[0].y, bb[1].x, bb[1].y };
struct draw_osd_closure {
d3d_priv *priv;
D3DLOCKED_RECT locked_rect;
};
/** @brief Callback function to render the OSD to the texture
*/
static void draw_alpha(void *pctx, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca, int stride)
{
struct draw_osd_closure *ctx = pctx;
D3DLOCKED_RECT locked_rect = ctx->locked_rect;
vo_draw_alpha_l8a8(w, h, src, srca, stride,
(unsigned char *)locked_rect.pBits + locked_rect.Pitch * y0 + 2 * x0,
locked_rect.Pitch);
ctx->priv->is_osd_populated = 1;
}
/** @brief libvo Callback: Draw OSD/Subtitles,
*/
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
d3d_priv *priv = vo->priv;
if (!priv->d3d_device)
return;
if (vo_osd_has_changed(osd)) {
struct draw_osd_closure ctx = { priv };
/* clear the OSD */
if (FAILED(IDirect3DTexture9_LockRect(priv->texture_osd.system, 0,
&ctx.locked_rect, NULL, 0))) {
mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture lock failed.\n");
return;
}
/* clear the whole texture to avoid issues due to interpolation */
memset(ctx.locked_rect.pBits, 0,
ctx.locked_rect.Pitch * priv->texture_osd.tex_h);
priv->is_osd_populated = 0;
/* required for if subs are in the boarder region */
priv->is_clear_needed = 1;
osd_draw_text_ext(osd, priv->texture_osd.w, priv->texture_osd.h,
priv->border_x, priv->border_y,
priv->border_x, priv->border_y,
priv->src_width, priv->src_height, draw_alpha, &ctx);
if (FAILED(IDirect3DTexture9_UnlockRect(priv->texture_osd.system, 0))) {
mp_msg(MSGT_VO,MSGL_ERR,
"<vo_direct3d>OSD texture unlock failed.\n");
return;
}
d3dtex_update(priv, &priv->texture_osd);
if (FAILED(IDirect3DTexture9_LockRect(osd->texture.system, 0, &locked_rect,
&dirty_rc, 0)))
{
mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture lock failed.\n");
return false;
}
if (priv->is_osd_populated) {
float tw = (float)priv->texture_osd.w / priv->texture_osd.tex_w;
float th = (float)priv->texture_osd.h / priv->texture_osd.tex_h;
int ps = fmt == D3DFMT_A8 ? 1 : 4;
packer_copy_subbitmaps(osd->packer, imgs, locked_rect.pBits, ps,
locked_rect.Pitch);
vertex_osd osd_quad_vb[] = {
{ 0, 0, 0.0f, 0, 0 },
{ vo->dwidth, 0, 0.0f, tw, 0 },
{ 0, vo->dheight, 0.0f, 0, th },
{ vo->dwidth, vo->dheight, 0.0f, tw, th }
};
d3d_begin_scene(priv);
IDirect3DDevice9_SetRenderState(priv->d3d_device,
D3DRS_ALPHABLENDENABLE, TRUE);
IDirect3DDevice9_SetRenderState(priv->d3d_device,
D3DRS_ALPHATESTENABLE, TRUE);
IDirect3DDevice9_SetTexture(priv->d3d_device, 0,
d3dtex_get_render_texture(priv, &priv->texture_osd));
IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_OSD_VERTEX);
IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP,
2, osd_quad_vb, sizeof(vertex_osd));
IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL);
IDirect3DDevice9_SetRenderState(priv->d3d_device,
D3DRS_ALPHATESTENABLE, FALSE);
IDirect3DDevice9_SetRenderState(priv->d3d_device,
D3DRS_ALPHABLENDENABLE, FALSE);
if (FAILED(IDirect3DTexture9_UnlockRect(osd->texture.system, 0))) {
mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture unlock failed.\n");
return false;
}
return d3dtex_update(priv, &osd->texture);
}
static void d3d_realloc_eosd_texture(d3d_priv *priv)
static struct osdpart *generate_osd(d3d_priv *priv, struct sub_bitmaps *imgs)
{
int new_w = priv->eosd->surface.w;
int new_h = priv->eosd->surface.h;
if (imgs->num_parts == 0 || !osd_fmt_table[imgs->format])
return NULL;
d3d_fix_texture_size(priv, &new_w, &new_h);
struct osdpart *osd = priv->osd[imgs->render_index];
if (new_w == priv->texture_eosd.tex_w && new_h == priv->texture_eosd.tex_h)
return;
if (imgs->bitmap_pos_id != osd->bitmap_pos_id) {
if (imgs->bitmap_id != osd->bitmap_id) {
if (!upload_osd(priv, osd, imgs))
osd->packer->count = 0;
}
mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>reallocate EOSD surface.\n");
osd->bitmap_id = imgs->bitmap_id;
osd->bitmap_pos_id = imgs->bitmap_pos_id;
osd->num_vertices = 0;
}
// fortunately, we don't need to keep the old image data
// we can always free it
d3dtex_release(priv, &priv->texture_eosd);
d3dtex_allocate(priv, &priv->texture_eosd, D3DFMT_A8, new_w, new_h);
return osd->packer->count ? osd : NULL;
}
static D3DCOLOR ass_to_d3d_color(uint32_t color)
@ -2076,129 +1965,85 @@ static D3DCOLOR ass_to_d3d_color(uint32_t color)
return D3DCOLOR_ARGB(a, r, g, b);
}
static void generate_eosd(d3d_priv *priv, mp_eosd_images_t *imgs)
static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
{
if (!priv->d3d_device)
d3d_priv *priv = ctx;
struct osdpart *osd = generate_osd(priv, imgs);
if (!osd)
return;
bool need_reposition, need_upload, need_resize;
eosd_packer_generate(priv->eosd, imgs, &need_reposition, &need_upload,
&need_resize);
if (osd->packer->count && !osd->num_vertices) {
// We need 2 primitives per quad which makes 6 vertices (we could reduce
// the number of vertices by using an indexed vertex array, but it's
// probably not worth doing)
osd->num_vertices = osd->packer->count * 6;
osd->vertices = talloc_realloc(osd, osd->vertices, vertex_osd,
osd->num_vertices);
if (!need_reposition)
return;
// even if the texture size is unchanged, the texture might have been free'd
d3d_realloc_eosd_texture(priv);
if (!priv->texture_eosd.system)
return; // failed to allocate
float tex_w = osd->texture.tex_w;
float tex_h = osd->texture.tex_h;
// reupload all EOSD images
for (int n = 0; n < osd->packer->count; n++) {
struct sub_bitmap *b = &imgs->parts[n];
struct pos p = osd->packer->result[n];
// we need 2 primitives per quad which makes 6 vertices (we could reduce the
// number of vertices by using an indexed vertex array, but it's probably
// not worth doing)
priv->eosd_vb = talloc_realloc_size(priv->eosd, priv->eosd_vb,
priv->eosd->targets_count
* sizeof(vertex_eosd) * 6);
D3DCOLOR color = imgs->format == SUBBITMAP_LIBASS
? ass_to_d3d_color(b->libass.color)
: D3DCOLOR_ARGB(255, 255, 255, 255);
if (need_upload) {
struct eosd_rect rc;
eosd_packer_calculate_source_bb(priv->eosd, &rc);
RECT dirty_rc = { rc.x0, rc.y0, rc.x1, rc.y1 };
float x0 = b->x;
float y0 = b->y;
float x1 = b->x + b->dw;
float y1 = b->y + b->dh;
float tx0 = p.x / tex_w;
float ty0 = p.y / tex_h;
float tx1 = (p.x + b->w) / tex_w;
float ty1 = (p.y + b->h) / tex_h;
D3DLOCKED_RECT locked_rect;
if (FAILED(IDirect3DTexture9_LockRect(priv->texture_eosd.system, 0,
&locked_rect, &dirty_rc, 0)))
{
mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>EOSD texture lock failed.\n");
return;
vertex_osd *v = &osd->vertices[n * 6];
v[0] = (vertex_osd) { x0, y0, 0, color, tx0, ty0 };
v[1] = (vertex_osd) { x1, y0, 0, color, tx1, ty0 };
v[2] = (vertex_osd) { x0, y1, 0, color, tx0, ty1 };
v[3] = (vertex_osd) { x1, y1, 0, color, tx1, ty1 };
v[4] = v[2];
v[5] = v[1];
}
//memset(locked_rect.pBits, 0, locked_rect.Pitch * priv->texture_eosd.tex_h);
for (int i = 0; i < priv->eosd->targets_count; i++) {
struct eosd_target *target = &priv->eosd->targets[i];
ASS_Image *img = target->ass_img;
char *src = img->bitmap;
char *dst = (char*)locked_rect.pBits + target->source.x0
+ locked_rect.Pitch * target->source.y0;
for (int y = 0; y < img->h; y++) {
memcpy(dst, src, img->w);
src += img->stride;
dst += locked_rect.Pitch;
}
}
if (FAILED(IDirect3DTexture9_UnlockRect(priv->texture_eosd.system, 0))) {
mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>EOSD texture unlock failed.\n");
return;
}
d3dtex_update(priv, &priv->texture_eosd);
}
float eosd_w = priv->texture_eosd.tex_w;
float eosd_h = priv->texture_eosd.tex_h;
for (int i = 0; i < priv->eosd->targets_count; i++) {
struct eosd_target *target = &priv->eosd->targets[i];
D3DCOLOR color = ass_to_d3d_color(target->ass_img->color);
float x0 = target->dest.x0;
float y0 = target->dest.y0;
float x1 = target->dest.x1;
float y1 = target->dest.y1;
float tx0 = target->source.x0 / eosd_w;
float ty0 = target->source.y0 / eosd_h;
float tx1 = target->source.x1 / eosd_w;
float ty1 = target->source.y1 / eosd_h;
vertex_eosd *v = &priv->eosd_vb[i*6];
v[0] = (vertex_eosd) { x0, y0, 0, color, tx0, ty0 };
v[1] = (vertex_eosd) { x1, y0, 0, color, tx1, ty0 };
v[2] = (vertex_eosd) { x0, y1, 0, color, tx0, ty1 };
v[3] = (vertex_eosd) { x1, y1, 0, color, tx1, ty1 };
v[4] = v[2];
v[5] = v[1];
}
}
static void draw_eosd(d3d_priv *priv)
{
if (!priv->d3d_device)
return;
if (!priv->eosd->targets_count)
return;
d3d_begin_scene(priv);
IDirect3DDevice9_SetRenderState(priv->d3d_device,
D3DRS_ALPHABLENDENABLE, TRUE);
IDirect3DDevice9_SetTexture(priv->d3d_device, 0,
d3dtex_get_render_texture(priv, &priv->texture_eosd));
d3dtex_get_render_texture(priv, &osd->texture));
// do not use the color value from the A8 texture, because that is black
IDirect3DDevice9_SetRenderState(priv->d3d_device,D3DRS_TEXTUREFACTOR,
0xFFFFFFFF);
IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0,
D3DTSS_COLORARG1, D3DTA_TFACTOR);
if (imgs->format == SUBBITMAP_LIBASS) {
// do not use the color value from the A8 texture, because that is black
IDirect3DDevice9_SetRenderState(priv->d3d_device,D3DRS_TEXTUREFACTOR,
0xFFFFFFFF);
IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0,
D3DTSS_COLORARG1, D3DTA_TFACTOR);
IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0,
D3DTSS_ALPHAOP, D3DTOP_MODULATE);
IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0,
D3DTSS_ALPHAOP, D3DTOP_MODULATE);
} else {
IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_SRCBLEND,
D3DBLEND_ONE);
}
IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_EOSD_VERTEX);
IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_OSD_VERTEX);
IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLELIST,
priv->eosd->targets_count * 2,
priv->eosd_vb, sizeof(vertex_eosd));
osd->num_vertices / 3,
osd->vertices, sizeof(vertex_osd));
IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0,
D3DTSS_COLORARG1, D3DTA_TEXTURE);
IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0,
D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
IDirect3DDevice9_SetRenderState(priv->d3d_device,
D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL);
@ -2206,6 +2051,17 @@ static void draw_eosd(d3d_priv *priv)
D3DRS_ALPHABLENDENABLE, FALSE);
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
d3d_priv *priv = vo->priv;
if (!priv->d3d_device)
return;
osd_draw(osd, priv->osd_res, osd->vo_pts, 0, osd_fmt_supported,
draw_osd_cb, priv);
}
#define AUTHOR "Georgi Petrov (gogothebee) <gogothebee@gmail.com> and others"
const struct vo_driver video_out_direct3d = {

View File

@ -104,6 +104,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
mp_image_t img = *mpi;
img.width = p->d_width;
img.height = p->d_height;
mp_image_set_colorspace_details(&img, &p->colorspace);
void *t = talloc_new(NULL);
char *filename = talloc_asprintf(t, "%08d.%s", p->frame,
@ -113,7 +114,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
filename = mp_path_join(t, bstr0(p->outdir), bstr0(filename));
mp_msg(MSGT_VO, MSGL_STATUS, "\nSaving %s\n", filename);
write_image(&img, &p->colorspace, p->opts, filename);
write_image(&img, p->opts, filename);
talloc_free(t);

View File

@ -34,7 +34,7 @@
#include "encode_lavc.h"
#include "sub/sub.h"
#include "libvo/osd.h"
#include "sub/dec_sub.h"
struct priv {
uint8_t *buffer;
@ -49,13 +49,12 @@ struct priv {
int64_t lastframeipts;
double expected_next_pts;
mp_image_t *lastimg;
int lastimg_wants_osd;
int lastdisplaycount;
AVRational worst_time_base;
int worst_time_base_is_stream;
struct osd_state *osd;
struct mp_csp_details colorspace;
};
@ -156,12 +155,13 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
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->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);
vc->buffer_size = 6 * width * height + 200;
if (vc->buffer_size < FF_MIN_BUFFER_SIZE)
vc->buffer_size = FF_MIN_BUFFER_SIZE;
@ -191,8 +191,18 @@ static int query_format(struct vo *vo, uint32_t format)
if (!vo->encode_lavc_ctx)
return 0;
return encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt) ?
VFCAP_CSP_SUPPORTED : 0;
if (!encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt))
return 0;
return
VFCAP_CSP_SUPPORTED |
// we can do it
VFCAP_CSP_SUPPORTED_BY_HW |
// we don't convert colorspaces here
VFCAP_OSD |
// we have OSD
VOCAP_NOSLICES;
// we don't use slices
}
static void write_packet(struct vo *vo, int size, AVPacket *packet)
@ -275,63 +285,6 @@ static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet)
}
}
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;
@ -503,7 +456,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
"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);
vc->lastimg_wants_osd = true;
// palette hack
if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
@ -516,9 +469,41 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
vc->lastipts = -1;
}
vc->lastdisplaycount = 0;
} else
} else {
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: Frame at pts %d got dropped "
"entirely because pts went backwards\n", (int) frameipts);
vc->lastimg_wants_osd = false;
}
}
}
static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
{
}
static void check_events(struct vo *vo)
{
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct priv *vc = vo->priv;
if (vc->lastimg && vc->lastimg_wants_osd) {
struct aspect_data asp = vo->aspdat;
double sar = (double)asp.orgw / asp.orgh;
double dar = (double)asp.prew / asp.preh;
struct mp_osd_res dim = {
.w = asp.orgw,
.h = asp.orgh,
.display_par = sar / dar,
.video_par = dar / sar,
};
mp_image_set_colorspace_details(vc->lastimg, &vc->colorspace);
osd_draw_on_image(osd, dim, osd->vo_pts, OSD_DRAW_SUB_ONLY, vc->lastimg);
}
}
@ -547,22 +532,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
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,
@ -581,4 +550,4 @@ const struct vo_driver video_out_lavc = {
.flip_page_timed = flip_page_timed,
};
// vim: sw=4 ts=4 et
// vim: sw=4 ts=4 et tw=80

View File

@ -37,6 +37,7 @@
#endif
#include "talloc.h"
#include "mpcommon.h"
#include "bstr.h"
#include "mp_msg.h"
#include "subopt-helper.h"
@ -44,24 +45,20 @@
#include "libmpcodecs/vfcap.h"
#include "libmpcodecs/mp_image.h"
#include "geometry.h"
#include "osd.h"
#include "sub/sub.h"
#include "eosd_packer.h"
#include "bitmap_packer.h"
#include "gl_common.h"
#include "gl_osd.h"
#include "filter_kernels.h"
#include "aspect.h"
#include "fastmemcpy.h"
#include "sub/ass_mp.h"
static const char vo_opengl_shaders[] =
// Generated from libvo/vo_opengl_shaders.glsl
#include "libvo/vo_opengl_shaders.h"
;
// How many parts the OSD may consist of at most.
#define MAX_OSD_PARTS 20
// Pixel width of 1D lookup textures.
#define LOOKUP_TEXTURE_SIZE 256
@ -114,7 +111,7 @@ struct vertex {
#define VERTEX_ATTRIB_TEXCOORD 2
// 2 triangles primitives per quad = 6 vertices per quad
// (GL_QUAD is deprecated, strips can't be used with EOSD image lists)
// (GL_QUAD is deprecated, strips can't be used with OSD image lists)
#define VERTICES_PER_QUAD 6
struct texplane {
@ -173,18 +170,10 @@ struct gl_priv {
GLuint vertex_buffer;
GLuint vao;
GLuint osd_program, eosd_program;
GLuint osd_programs[SUBBITMAP_COUNT];
GLuint indirect_program, scale_sep_program, final_program;
GLuint osd_textures[MAX_OSD_PARTS];
int osd_textures_count;
struct vertex osd_va[MAX_OSD_PARTS * VERTICES_PER_QUAD];
GLuint eosd_texture;
int eosd_texture_width, eosd_texture_height;
GLuint eosd_buffer;
struct vertex *eosd_va;
struct eosd_packer *eosd;
struct mpgl_osd *osd;
GLuint lut_3d_texture;
int lut_3d_w, lut_3d_h, lut_3d_d;
@ -230,12 +219,14 @@ struct gl_priv {
int mpi_flipped;
int vo_flipped;
struct vo_rect src_rect; // displayed part of the source video
struct vo_rect dst_rect; // video rectangle on output window
int border_x, border_y; // OSD borders
struct mp_rect src_rect; // displayed part of the source video
struct mp_rect dst_rect; // video rectangle on output window
struct mp_osd_res osd_rect; // OSD size/margins
int vp_x, vp_y, vp_w, vp_h; // GL viewport
int frames_rendered;
void *scratch;
};
struct fmt_entry {
@ -259,6 +250,11 @@ static const struct fmt_entry mp_to_gl_formats[] = {
{0},
};
static const char *osd_shaders[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = "frag_osd_libass",
[SUBBITMAP_RGBA] = "frag_osd_rgba",
};
static const char help_text[];
@ -489,6 +485,15 @@ static void update_uniforms(struct gl_priv *p, GLuint program)
gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"),
isnan(sparam1) ? 0.5f : sparam1);
loc = gl->GetUniformLocation(program, "osd_color");
if (loc >= 0) {
int r = (p->osd_color >> 16) & 0xff;
int g = (p->osd_color >> 8) & 0xff;
int b = p->osd_color & 0xff;
int a = 0xff - (p->osd_color >> 24);
gl->Uniform4f(loc, r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
}
gl->UseProgram(0);
debug_check_gl(p, "update_uniforms()");
@ -496,8 +501,8 @@ static void update_uniforms(struct gl_priv *p, GLuint program)
static void update_all_uniforms(struct gl_priv *p)
{
update_uniforms(p, p->osd_program);
update_uniforms(p, p->eosd_program);
for (int n = 0; n < SUBBITMAP_COUNT; n++)
update_uniforms(p, p->osd_programs[n]);
update_uniforms(p, p->indirect_program);
update_uniforms(p, p->scale_sep_program);
update_uniforms(p, p->final_program);
@ -661,20 +666,21 @@ static void compile_shaders(struct gl_priv *p)
char *vertex_shader = get_section(tmp, src, "vertex_all");
char *shader_prelude = get_section(tmp, src, "prelude");
char *s_video = get_section(tmp, src, "frag_video");
char *s_eosd = get_section(tmp, src, "frag_eosd");
char *s_osd = get_section(tmp, src, "frag_osd");
char *header = talloc_asprintf(tmp, "#version %d\n%s", gl->glsl_version,
shader_prelude);
char *header_eosd = talloc_strdup(tmp, header);
shader_def_opt(&header_eosd, "USE_3DLUT", p->use_lut_3d);
char *header_osd = talloc_strdup(tmp, header);
shader_def_opt(&header_osd, "USE_3DLUT", p->use_lut_3d);
p->eosd_program =
create_program(gl, "eosd", header_eosd, vertex_shader, s_eosd);
p->osd_program =
create_program(gl, "osd", header, vertex_shader, s_osd);
for (int n = 0; n < SUBBITMAP_COUNT; n++) {
const char *name = osd_shaders[n];
if (name) {
char *s_osd = get_section(tmp, src, name);
p->osd_programs[n] =
create_program(gl, name, header_osd, vertex_shader, s_osd);
}
}
char *header_conv = talloc_strdup(tmp, "");
char *header_final = talloc_strdup(tmp, "");
@ -775,8 +781,8 @@ static void delete_shaders(struct gl_priv *p)
{
GL *gl = p->gl;
delete_program(gl, &p->osd_program);
delete_program(gl, &p->eosd_program);
for (int n = 0; n < SUBBITMAP_COUNT; n++)
delete_program(gl, &p->osd_programs[n]);
delete_program(gl, &p->indirect_program);
delete_program(gl, &p->scale_sep_program);
delete_program(gl, &p->final_program);
@ -784,8 +790,10 @@ static void delete_shaders(struct gl_priv *p)
static double get_scale_factor(struct gl_priv *p)
{
double sx = p->dst_rect.width / (double)p->src_rect.width;
double sy = p->dst_rect.height / (double)p->src_rect.height;
double sx = (p->dst_rect.x1 - p->dst_rect.x0) /
(double)(p->src_rect.x1 - p->src_rect.x0);
double sy = (p->dst_rect.y1 - p->dst_rect.y0) /
(double)(p->src_rect.y1 - p->src_rect.y0);
// xxx: actually we should use different scalers in X/Y directions if the
// scale factors are different due to anamorphic content
return FFMIN(sx, sy);
@ -939,6 +947,11 @@ static void reinit_rendering(struct gl_priv *p)
if (p->indirect_program && !p->indirect_fbo.fbo)
fbotex_init(p, &p->indirect_fbo, p->texture_width, p->texture_height);
if (!p->osd) {
p->osd = mpgl_osd_init(p->gl, false);
p->osd->use_pbo = p->use_pbo;
}
}
static void uninit_rendering(struct gl_priv *p)
@ -956,6 +969,10 @@ static void uninit_rendering(struct gl_priv *p)
gl->DeleteTextures(1, &p->dither_texture);
p->dither_texture = 0;
if (p->osd)
mpgl_osd_destroy(p->osd);
p->osd = NULL;
}
static void init_lut_3d(struct gl_priv *p)
@ -1111,16 +1128,16 @@ static void do_render(struct gl_priv *p)
gl->Enable(GL_FRAMEBUFFER_SRGB);
if (p->stereo_mode) {
int w = p->src_rect.width;
int w = p->src_rect.x1 - p->src_rect.x0;
int imgw = p->image_width;
glEnable3DLeft(gl, p->stereo_mode);
write_quad(vb,
p->dst_rect.left, p->dst_rect.top,
p->dst_rect.right, p->dst_rect.bottom,
p->src_rect.left / 2, p->src_rect.top,
p->src_rect.left / 2 + w / 2, p->src_rect.bottom,
p->dst_rect.x0, p->dst_rect.y0,
p->dst_rect.x1, p->dst_rect.y1,
p->src_rect.x0 / 2, p->src_rect.y0,
p->src_rect.x0 / 2 + w / 2, p->src_rect.y1,
final_texw, final_texh,
NULL, is_flipped);
draw_triangles(p, vb, VERTICES_PER_QUAD);
@ -1128,10 +1145,10 @@ static void do_render(struct gl_priv *p)
glEnable3DRight(gl, p->stereo_mode);
write_quad(vb,
p->dst_rect.left, p->dst_rect.top,
p->dst_rect.right, p->dst_rect.bottom,
p->src_rect.left / 2 + imgw / 2, p->src_rect.top,
p->src_rect.left / 2 + imgw / 2 + w / 2, p->src_rect.bottom,
p->dst_rect.x0, p->dst_rect.y0,
p->dst_rect.x1, p->dst_rect.y1,
p->src_rect.x0 / 2 + imgw / 2, p->src_rect.y0,
p->src_rect.x0 / 2 + imgw / 2 + w / 2, p->src_rect.y1,
final_texw, final_texh,
NULL, is_flipped);
draw_triangles(p, vb, VERTICES_PER_QUAD);
@ -1139,10 +1156,10 @@ static void do_render(struct gl_priv *p)
glDisable3D(gl, p->stereo_mode);
} else {
write_quad(vb,
p->dst_rect.left, p->dst_rect.top,
p->dst_rect.right, p->dst_rect.bottom,
p->src_rect.left, p->src_rect.top,
p->src_rect.right, p->src_rect.bottom,
p->dst_rect.x0, p->dst_rect.y0,
p->dst_rect.x1, p->dst_rect.y1,
p->src_rect.x0, p->src_rect.y0,
p->src_rect.x1, p->src_rect.y1,
final_texw, final_texh,
NULL, is_flipped);
draw_triangles(p, vb, VERTICES_PER_QUAD);
@ -1159,15 +1176,16 @@ static void do_render(struct gl_priv *p)
static void update_window_sized_objects(struct gl_priv *p)
{
if (p->scale_sep_program) {
if (p->dst_rect.height > p->scale_sep_fbo.tex_h) {
int h = p->dst_rect.y1 - p->dst_rect.y0;
if (h > p->scale_sep_fbo.tex_h) {
fbotex_uninit(p, &p->scale_sep_fbo);
// Round up to an arbitrary alignment to make window resizing or
// panscan controls smoother (less texture reallocations).
int height = FFALIGN(p->dst_rect.height, 256);
int height = FFALIGN(h, 256);
fbotex_init(p, &p->scale_sep_fbo, p->image_width, height);
}
p->scale_sep_fbo.vp_w = p->image_width;
p->scale_sep_fbo.vp_h = p->dst_rect.height;
p->scale_sep_fbo.vp_h = h;
}
}
@ -1178,20 +1196,10 @@ static void resize(struct gl_priv *p)
mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", vo->dwidth, vo->dheight);
p->vp_x = 0, p->vp_y = 0;
if (WinID >= 0) {
int w = vo->dwidth, h = vo->dheight;
int old_y = vo->dheight;
geometry(&p->vp_x, &p->vp_y, &w, &h, vo->dwidth, vo->dheight);
p->vp_y = old_y - h - p->vp_y;
}
p->vp_w = vo->dwidth, p->vp_h = vo->dheight;
gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h);
struct vo_rect borders;
calc_src_dst_rects(vo, p->image_width, p->image_height, &p->src_rect,
&p->dst_rect, &borders, NULL);
p->border_x = borders.left;
p->border_y = borders.top;
vo_get_src_dst_rects(vo, &p->src_rect, &p->dst_rect, &p->osd_rect);
bool need_scaler_reinit = false; // filter size change needed
bool need_scaler_update = false; // filter LUT change needed
@ -1219,7 +1227,6 @@ static void resize(struct gl_priv *p)
update_window_sized_objects(p);
update_all_uniforms(p);
vo_osd_resized();
gl->Clear(GL_COLOR_BUFFER_BIT);
vo->want_redraw = true;
}
@ -1234,9 +1241,9 @@ static void flip_page(struct vo *vo)
p->glctx->swapGlBuffers(p->glctx);
if (p->dst_rect.left > p->vp_x || p->dst_rect.top > p->vp_y
|| p->dst_rect.right < p->vp_x + p->vp_w
|| p->dst_rect.bottom < p->vp_y + p->vp_h)
if (p->dst_rect.x0 > p->vp_x || p->dst_rect.y0 > p->vp_y
|| p->dst_rect.x1 < p->vp_x + p->vp_w
|| p->dst_rect.y1 < p->vp_y + p->vp_h)
{
gl->Clear(GL_COLOR_BUFFER_BIT);
}
@ -1380,236 +1387,68 @@ static mp_image_t *get_screenshot(struct gl_priv *p)
}
gl->ActiveTexture(GL_TEXTURE0);
image->width = p->image_width;
image->height = p->image_height;
image->w = p->image_width;
image->h = p->image_height;
image->display_w = p->vo->aspdat.prew;
image->display_h = p->vo->aspdat.preh;
image->w = p->vo->aspdat.prew;
image->h = p->vo->aspdat.preh;
mp_image_set_colorspace_details(image, &p->colorspace);
return image;
}
static mp_image_t *get_window_screenshot(struct gl_priv *p)
{
GL *gl = p->gl;
mp_image_t *image = alloc_mpi(p->vp_w, p->vp_h, IMGFMT_RGB24);
gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
gl->ReadBuffer(GL_FRONT);
// flip image while reading
for (int y = 0; y < p->vp_h; y++) {
gl->ReadPixels(p->vp_x, p->vp_y + p->vp_h - y - 1, p->vp_w, 1,
GL_RGB, GL_UNSIGNED_BYTE,
image->planes[0] + y * image->stride[0]);
}
return image;
}
static void clear_osd(struct gl_priv *p)
{
GL *gl = p->gl;
if (!p->osd_textures_count)
return;
gl->DeleteTextures(p->osd_textures_count, p->osd_textures);
p->osd_textures_count = 0;
}
static void create_osd_texture(void *ctx, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride)
static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
{
struct gl_priv *p = ctx;
GL *gl = p->gl;
if (w <= 0 || h <= 0 || stride < w) {
mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n");
struct mpgl_osd_part *osd = mpgl_osd_generate(p->osd, imgs);
if (!osd)
return;
assert(osd->format != SUBBITMAP_EMPTY);
if (!osd->num_vertices) {
osd->vertices = talloc_realloc(osd, osd->vertices, struct vertex,
osd->packer->count * VERTICES_PER_QUAD);
struct vertex *va = osd->vertices;
for (int n = 0; n < osd->packer->count; n++) {
struct sub_bitmap *b = &imgs->parts[n];
struct pos p = osd->packer->result[n];
// NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
// doesn't matter that we upload garbage for the other formats
uint32_t c = b->libass.color;
uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
(c >> 8) & 0xff, 255 - (c & 0xff) };
write_quad(&va[osd->num_vertices],
b->x, b->y, b->x + b->dw, b->y + b->dh,
p.x, p.y, p.x + b->w, p.y + b->h,
osd->w, osd->h, color, false);
osd->num_vertices += VERTICES_PER_QUAD;
}
}
if (p->osd_textures_count >= MAX_OSD_PARTS) {
mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n");
return;
}
debug_check_gl(p, "before drawing osd");
int sx, sy;
tex_size(p, w, h, &sx, &sy);
gl->UseProgram(p->osd_programs[osd->format]);
mpgl_osd_set_gl_state(p->osd, osd);
draw_triangles(p, osd->vertices, osd->num_vertices);
mpgl_osd_unset_gl_state(p->osd, osd);
gl->UseProgram(0);
gl->GenTextures(1, &p->osd_textures[p->osd_textures_count]);
gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[p->osd_textures_count]);
gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RG, sx, sy, 0, GL_RG, GL_UNSIGNED_BYTE,
NULL);
default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST);
unsigned char *tmp = malloc(stride * h * 2);
// Convert alpha from weird MPlayer scale.
for (int i = 0; i < h * stride; i++) {
tmp[i*2+0] = src[i];
tmp[i*2+1] = -srca[i];
}
glUploadTex(gl, GL_TEXTURE_2D, GL_RG, GL_UNSIGNED_BYTE, tmp, stride * 2,
0, 0, w, h, 0);
free(tmp);
gl->BindTexture(GL_TEXTURE_2D, 0);
uint8_t color[4] = {(p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff,
p->osd_color & 0xff, 0xff - (p->osd_color >> 24)};
write_quad(&p->osd_va[p->osd_textures_count * VERTICES_PER_QUAD],
x0, y0, x0 + w, y0 + h, 0, 0, w, h,
sx, sy, color, false);
p->osd_textures_count++;
debug_check_gl(p, "after drawing osd");
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
assert(p->osd);
if (vo_osd_has_changed(osd)) {
clear_osd(p);
osd_draw_text_ext(osd, vo->dwidth, vo->dheight, p->border_x,
p->border_y, p->border_x,
p->border_y, p->image_width,
p->image_height, create_osd_texture, p);
}
if (p->osd_textures_count > 0) {
gl->Enable(GL_BLEND);
// OSD bitmaps use premultiplied alpha.
gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
gl->UseProgram(p->osd_program);
for (int n = 0; n < p->osd_textures_count; n++) {
gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[n]);
draw_triangles(p, &p->osd_va[n * VERTICES_PER_QUAD],
VERTICES_PER_QUAD);
}
gl->UseProgram(0);
gl->Disable(GL_BLEND);
gl->BindTexture(GL_TEXTURE_2D, 0);
}
}
static void gen_eosd(struct gl_priv *p, mp_eosd_images_t *imgs)
{
GL *gl = p->gl;
bool need_repos, need_upload, need_allocate;
eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload,
&need_allocate);
if (!need_repos)
return;
if (!p->eosd_texture) {
gl->GenTextures(1, &p->eosd_texture);
gl->GenBuffers(1, &p->eosd_buffer);
}
gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture);
if (need_allocate) {
tex_size(p, p->eosd->surface.w, p->eosd->surface.h,
&p->eosd_texture_width, &p->eosd_texture_height);
gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED,
p->eosd_texture_width, p->eosd_texture_height, 0,
GL_RED, GL_UNSIGNED_BYTE, NULL);
default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer);
gl->BufferData(GL_PIXEL_UNPACK_BUFFER,
p->eosd->surface.w * p->eosd->surface.h,
NULL,
GL_DYNAMIC_COPY);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va,
p->eosd->targets_count
* sizeof(struct vertex)
* VERTICES_PER_QUAD);
if (need_upload && p->use_pbo && p->eosd->targets_count) {
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer);
char *data = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if (!data) {
mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error: can't upload subtitles! "
"Subtitles will look corrupted.\n");
} else {
for (int n = 0; n < p->eosd->targets_count; n++) {
struct eosd_target *target = &p->eosd->targets[n];
ASS_Image *i = target->ass_img;
void *pdata = data + target->source.y0 * p->eosd->surface.w
+ target->source.x0;
memcpy_pic(pdata, i->bitmap, i->w, i->h,
p->eosd->surface.w, i->stride);
}
if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
mp_msg(MSGT_VO, MSGL_FATAL, "[gl] EOSD PBO upload failed. "
"Remove the 'pbo' suboption.\n");
struct eosd_rect rc;
eosd_packer_calculate_source_bb(p->eosd, &rc);
glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, NULL,
p->eosd->surface.w, rc.x0, rc.y0,
rc.x1 - rc.x0, rc.y1 - rc.y0, 0);
}
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
} else if (need_upload) {
// non-PBO upload
for (int n = 0; n < p->eosd->targets_count; n++) {
struct eosd_target *target = &p->eosd->targets[n];
ASS_Image *i = target->ass_img;
glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, i->bitmap,
i->stride, target->source.x0, target->source.y0,
i->w, i->h, 0);
}
}
gl->BindTexture(GL_TEXTURE_2D, 0);
debug_check_gl(p, "EOSD upload");
for (int n = 0; n < p->eosd->targets_count; n++) {
struct eosd_target *target = &p->eosd->targets[n];
ASS_Image *i = target->ass_img;
uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff,
(i->color >> 8) & 0xff, 255 - (i->color & 0xff) };
write_quad(&p->eosd_va[n * VERTICES_PER_QUAD],
target->dest.x0, target->dest.y0,
target->dest.x1, target->dest.y1,
target->source.x0, target->source.y0,
target->source.x1, target->source.y1,
p->eosd_texture_width, p->eosd_texture_height,
color, false);
}
}
static void draw_eosd(struct gl_priv *p, mp_eosd_images_t *imgs)
{
GL *gl = p->gl;
gen_eosd(p, imgs);
if (p->eosd->targets_count == 0)
return;
gl->Enable(GL_BLEND);
gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture);
gl->UseProgram(p->eosd_program);
draw_triangles(p, p->eosd_va, p->eosd->targets_count * VERTICES_PER_QUAD);
gl->UseProgram(0);
gl->BindTexture(GL_TEXTURE_2D, 0);
gl->Disable(GL_BLEND);
osd_draw(osd, p->osd_rect, osd->vo_pts, 0, p->osd->formats, draw_osd_cb, p);
}
// Disable features that are not supported with the current OpenGL version.
@ -1732,10 +1571,6 @@ static int init_gl(struct gl_priv *p)
gl->BindBuffer(GL_ARRAY_BUFFER, 0);
GLint max_texture_size;
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size);
gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl->Clear(GL_COLOR_BUFFER_BIT);
@ -1760,13 +1595,6 @@ static void uninit_gl(struct gl_priv *p)
gl->DeleteBuffers(1, &p->vertex_buffer);
p->vertex_buffer = 0;
clear_osd(p);
gl->DeleteTextures(1, &p->eosd_texture);
p->eosd_texture = 0;
gl->DeleteBuffers(1, &p->eosd_buffer);
p->eosd_buffer = 0;
eosd_packer_reinit(p->eosd, 0, 0);
gl->DeleteTextures(1, &p->lut_3d_texture);
p->lut_3d_texture = 0;
}
@ -1847,7 +1675,7 @@ static int query_format(uint32_t format)
{
int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP |
VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE |
VFCAP_OSD | VFCAP_EOSD | VFCAP_EOSD_UNSCALED;
VFCAP_OSD;
if (!init_format(format, NULL))
return 0;
return caps;
@ -1923,19 +1751,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
return query_format(*(uint32_t *)data);
case VOCTRL_DRAW_IMAGE:
return draw_image(p, data);
case VOCTRL_DRAW_EOSD:
if (!data)
return VO_FALSE;
draw_eosd(p, data);
return VO_TRUE;
case VOCTRL_GET_EOSD_RES: {
mp_eosd_res_t *r = data;
r->w = vo->dwidth;
r->h = vo->dheight;
r->ml = r->mr = p->border_x;
r->mt = r->mb = p->border_y;
return VO_TRUE;
}
case VOCTRL_ONTOP:
if (!p->glctx->ontop)
break;
@ -2003,7 +1818,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SCREENSHOT: {
struct voctrl_screenshot_args *args = data;
if (args->full_window)
args->out_image = get_window_screenshot(p);
args->out_image = glGetWindowScreenshot(p->gl);
else
args->out_image = get_screenshot(p);
return true;
@ -2351,6 +2166,7 @@ static int preinit(struct vo *vo, const char *arg)
{ .index = 1, .name = "bilinear" },
},
.scaler_params = {NAN, NAN},
.scratch = talloc_zero_array(p, char *, 1),
};
p->defaults = talloc(p, struct gl_priv);
@ -2430,8 +2246,6 @@ static int preinit(struct vo *vo, const char *arg)
p->orig_cmdline = talloc(p, struct gl_priv);
*p->orig_cmdline = *p;
p->eosd = eosd_packer_create(vo);
p->glctx = mpgl_init(backend, vo);
if (!p->glctx)
goto err_out;

View File

@ -26,6 +26,7 @@
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <assert.h>
#include "config.h"
#include "talloc.h"
@ -35,29 +36,18 @@
#include "libmpcodecs/vfcap.h"
#include "libmpcodecs/mp_image.h"
#include "geometry.h"
#include "osd.h"
#include "sub/sub.h"
#include "eosd_packer.h"
#include "gl_common.h"
#include "gl_osd.h"
#include "aspect.h"
#include "fastmemcpy.h"
#include "sub/ass_mp.h"
//! How many parts the OSD may consist of at most
#define MAX_OSD_PARTS 20
//for gl_priv.use_yuv
#define MASK_ALL_YUV (~(1 << YUV_CONVERSION_NONE))
#define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS)))
#define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT))
struct vertex_eosd {
float x, y;
uint8_t color[4];
float u, v;
};
struct gl_priv {
MPGLContext *glctx;
GL *gl;
@ -66,19 +56,7 @@ struct gl_priv {
int use_osd;
int scaled_osd;
//! Textures for OSD
GLuint osdtex[MAX_OSD_PARTS];
//! Alpha textures for OSD
GLuint osdatex[MAX_OSD_PARTS];
GLuint eosd_texture;
int eosd_texture_width, eosd_texture_height;
struct eosd_packer *eosd;
struct vertex_eosd *eosd_va;
//! Display lists that draw the OSD parts
GLuint osdDispList[MAX_OSD_PARTS];
GLuint osdaDispList[MAX_OSD_PARTS];
//! How many parts the OSD currently consists of
int osdtexCnt;
struct mpgl_osd *osd;
int osd_color;
int use_ycbcr;
@ -137,13 +115,7 @@ static void resize(struct vo *vo, int x, int y)
GL *gl = p->gl;
mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", x, y);
if (WinID >= 0) {
int left = 0, top = 0, w = x, h = y;
geometry(&left, &top, &w, &h, vo->dwidth, vo->dheight);
top = y - h - top;
gl->Viewport(left, top, w, h);
} else
gl->Viewport(0, 0, x, y);
gl->Viewport(0, 0, x, y);
gl->MatrixMode(GL_PROJECTION);
gl->LoadIdentity();
@ -166,7 +138,6 @@ static void resize(struct vo *vo, int x, int y)
gl->MatrixMode(GL_MODELVIEW);
gl->LoadIdentity();
vo_osd_resized();
gl->Clear(GL_COLOR_BUFFER_BIT);
vo->want_redraw = true;
}
@ -251,133 +222,43 @@ static void update_yuvconv(struct vo *vo)
}
}
/**
* \brief remove all OSD textures and display-lists, thus clearing it.
*/
static void clearOSD(struct vo *vo)
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
assert(p->osd);
int i;
if (!p->osdtexCnt)
return;
gl->DeleteTextures(p->osdtexCnt, p->osdtex);
gl->DeleteTextures(p->osdtexCnt, p->osdatex);
for (i = 0; i < p->osdtexCnt; i++)
gl->DeleteLists(p->osdaDispList[i], 1);
for (i = 0; i < p->osdtexCnt; i++)
gl->DeleteLists(p->osdDispList[i], 1);
p->osdtexCnt = 0;
}
/**
* \brief construct display list from ass image list
* \param img image list to create OSD from.
*/
static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
bool need_repos, need_upload, need_allocate;
eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload,
&need_allocate);
if (!need_repos)
if (!p->use_osd)
return;
if (!p->eosd_texture)
gl->GenTextures(1, &p->eosd_texture);
gl->BindTexture(p->target, p->eosd_texture);
if (need_allocate) {
texSize(vo, p->eosd->surface.w, p->eosd->surface.h,
&p->eosd_texture_width, &p->eosd_texture_height);
// xxx it doesn't need to be cleared, that's a waste of time
glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE,
GL_NEAREST, p->eosd_texture_width,
p->eosd_texture_height, 0);
if (!p->scaled_osd) {
gl->MatrixMode(GL_PROJECTION);
gl->PushMatrix();
gl->LoadIdentity();
gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1);
}
// 2 triangles primitives per quad = 6 vertices per quad
// not using GL_QUADS, as it is deprecated in OpenGL 3.x and later
p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va,
p->eosd->targets_count
* sizeof(struct vertex_eosd) * 6);
gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff,
p->osd_color & 0xff, 0xff - (p->osd_color >> 24));
float eosd_w = p->eosd_texture_width;
float eosd_h = p->eosd_texture_height;
if (p->use_rectangle == 1)
eosd_w = eosd_h = 1.0f;
for (int n = 0; n < p->eosd->targets_count; n++) {
struct eosd_target *target = &p->eosd->targets[n];
ASS_Image *i = target->ass_img;
if (need_upload) {
glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap,
i->stride, target->source.x0, target->source.y0,
i->w, i->h, 0);
}
uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff,
(i->color >> 8) & 0xff, 255 - (i->color & 0xff) };
float x0 = target->dest.x0;
float y0 = target->dest.y0;
float x1 = target->dest.x1;
float y1 = target->dest.y1;
float tx0 = target->source.x0 / eosd_w;
float ty0 = target->source.y0 / eosd_h;
float tx1 = target->source.x1 / eosd_w;
float ty1 = target->source.y1 / eosd_h;
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
struct vertex_eosd *va = &p->eosd_va[n * 6];
va[0] = (struct vertex_eosd) { x0, y0, COLOR_INIT, tx0, ty0 };
va[1] = (struct vertex_eosd) { x0, y1, COLOR_INIT, tx0, ty1 };
va[2] = (struct vertex_eosd) { x1, y0, COLOR_INIT, tx1, ty0 };
va[3] = (struct vertex_eosd) { x1, y1, COLOR_INIT, tx1, ty1 };
va[4] = va[2];
va[5] = va[1];
#undef COLOR_INIT
struct mp_osd_res res = {
.w = vo->dwidth,
.h = vo->dheight,
.display_par = vo->monitor_par,
.video_par = vo->aspdat.par,
};
if (p->scaled_osd) {
res.w = p->image_width;
res.h = p->image_height;
} else if (aspect_scaling()) {
res.ml = res.mr = p->ass_border_x;
res.mt = res.mb = p->ass_border_y;
}
gl->BindTexture(p->target, 0);
}
mpgl_osd_draw_legacy(p->osd, osd, res);
// Note: relies on state being setup, like projection matrix and blending
static void drawEOSD(struct vo *vo)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
if (p->eosd->targets_count == 0)
return;
gl->BindTexture(p->target, p->eosd_texture);
struct vertex_eosd *va = p->eosd_va;
size_t stride = sizeof(struct vertex_eosd);
gl->VertexPointer(2, GL_FLOAT, stride, &va[0].x);
gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]);
gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].u);
gl->EnableClientState(GL_VERTEX_ARRAY);
gl->EnableClientState(GL_TEXTURE_COORD_ARRAY);
gl->EnableClientState(GL_COLOR_ARRAY);
gl->DrawArrays(GL_TRIANGLES, 0, p->eosd->targets_count * 6);
gl->DisableClientState(GL_VERTEX_ARRAY);
gl->DisableClientState(GL_TEXTURE_COORD_ARRAY);
gl->DisableClientState(GL_COLOR_ARRAY);
gl->BindTexture(p->target, 0);
if (!p->scaled_osd)
gl->PopMatrix();
}
/**
@ -400,13 +281,9 @@ static void uninitGl(struct vo *vo)
if (i)
gl->DeleteTextures(i, p->default_texs);
p->default_texs[0] = 0;
clearOSD(vo);
if (p->eosd_texture)
gl->DeleteTextures(1, &p->eosd_texture);
eosd_packer_reinit(p->eosd, 0, 0);
p->eosd_texture = 0;
if (gl->DeleteBuffers && p->buffer)
gl->DeleteBuffers(1, &p->buffer);
if (p->osd)
mpgl_osd_destroy(p->osd);
p->osd = NULL;
p->buffer = 0;
p->buffersize = 0;
p->bufferptr = NULL;
@ -519,7 +396,7 @@ static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height)
gl->DepthMask(GL_FALSE);
gl->Disable(GL_CULL_FACE);
gl->Enable(p->target);
gl->DrawBuffer(vo_doublebuffering ? GL_BACK : GL_FRONT);
gl->DrawBuffer(GL_BACK);
gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n",
@ -576,9 +453,8 @@ static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height)
update_yuvconv(vo);
}
GLint max_texture_size;
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size);
p->osd = mpgl_osd_init(gl, true);
p->osd->scaled = p->scaled_osd;
resize(vo, d_width, d_height);
@ -646,141 +522,6 @@ static void check_events(struct vo *vo)
vo->want_redraw = true;
}
/**
* Creates the textures and the display list needed for displaying
* an OSD part.
* Callback function for osd_draw_text_ext().
*/
static void create_osd_texture(void *ctx, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride)
{
struct vo *vo = ctx;
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
// initialize to 8 to avoid special-casing on alignment
int sx = 8, sy = 8;
GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST;
if (w <= 0 || h <= 0 || stride < w) {
mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n");
return;
}
texSize(vo, w, h, &sx, &sy);
if (p->osdtexCnt >= MAX_OSD_PARTS) {
mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n");
return;
}
// create Textures for OSD part
gl->GenTextures(1, &p->osdtex[p->osdtexCnt]);
gl->BindTexture(p->target, p->osdtex[p->osdtexCnt]);
glCreateClearTex(gl, p->target, GL_LUMINANCE, GL_LUMINANCE,
GL_UNSIGNED_BYTE, scale_type, sx, sy, 0);
glUploadTex(gl, p->target, GL_LUMINANCE, GL_UNSIGNED_BYTE, src, stride,
0, 0, w, h, 0);
gl->GenTextures(1, &p->osdatex[p->osdtexCnt]);
gl->BindTexture(p->target, p->osdatex[p->osdtexCnt]);
glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE,
scale_type, sx, sy, 0);
{
int i;
char *tmp = malloc(stride * h);
// convert alpha from weird MPlayer scale.
// in-place is not possible since it is reused for future OSDs
for (i = h * stride - 1; i >= 0; i--)
tmp[i] = -srca[i];
glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, tmp, stride,
0, 0, w, h, 0);
free(tmp);
}
gl->BindTexture(p->target, 0);
// Create a list for rendering this OSD part
p->osdaDispList[p->osdtexCnt] = gl->GenLists(1);
gl->NewList(p->osdaDispList[p->osdtexCnt], GL_COMPILE);
// render alpha
gl->BindTexture(p->target, p->osdatex[p->osdtexCnt]);
glDrawTex(gl, x0, y0, w, h, 0, 0, w, h, sx, sy, p->use_rectangle == 1, 0, 0);
gl->EndList();
p->osdDispList[p->osdtexCnt] = gl->GenLists(1);
gl->NewList(p->osdDispList[p->osdtexCnt], GL_COMPILE);
// render OSD
gl->BindTexture(p->target, p->osdtex[p->osdtexCnt]);
glDrawTex(gl, x0, y0, w, h, 0, 0, w, h, sx, sy, p->use_rectangle == 1, 0, 0);
gl->EndList();
p->osdtexCnt++;
}
#define RENDER_OSD 1
#define RENDER_EOSD 2
/**
* \param type bit 0: render OSD, bit 1: render EOSD
*/
static void do_render_osd(struct vo *vo, int type)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
int draw_osd = (type & RENDER_OSD) && p->osdtexCnt > 0;
int draw_eosd = (type & RENDER_EOSD);
if (!draw_osd && !draw_eosd)
return;
// set special rendering parameters
if (!p->scaled_osd) {
gl->MatrixMode(GL_PROJECTION);
gl->PushMatrix();
gl->LoadIdentity();
gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1);
}
gl->Enable(GL_BLEND);
if (draw_eosd) {
gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawEOSD(vo);
}
if (draw_osd) {
gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff,
p->osd_color & 0xff, 0xff - (p->osd_color >> 24));
// draw OSD
gl->BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
gl->CallLists(p->osdtexCnt, GL_UNSIGNED_INT, p->osdaDispList);
gl->BlendFunc(GL_SRC_ALPHA, GL_ONE);
gl->CallLists(p->osdtexCnt, GL_UNSIGNED_INT, p->osdDispList);
}
// set rendering parameters back to defaults
gl->Disable(GL_BLEND);
if (!p->scaled_osd)
gl->PopMatrix();
gl->BindTexture(p->target, 0);
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct gl_priv *p = vo->priv;
if (!p->use_osd)
return;
if (vo_osd_has_changed(osd)) {
int osd_h, osd_w;
clearOSD(vo);
osd_w = p->scaled_osd ? p->image_width : vo->dwidth;
osd_h = p->scaled_osd ? p->image_height : vo->dheight;
osd_draw_text_ext(osd, osd_w, osd_h, p->ass_border_x,
p->ass_border_y, p->ass_border_x,
p->ass_border_y, p->image_width,
p->image_height, create_osd_texture, vo);
}
if (vo_doublebuffering)
do_render_osd(vo, RENDER_OSD);
}
static void do_render(struct vo *vo)
{
struct gl_priv *p = vo->priv;
@ -822,20 +563,11 @@ static void flip_page(struct vo *vo)
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
if (vo_doublebuffering) {
if (p->use_glFinish)
gl->Finish();
p->glctx->swapGlBuffers(p->glctx);
if (aspect_scaling())
gl->Clear(GL_COLOR_BUFFER_BIT);
} else {
do_render(vo);
do_render_osd(vo, RENDER_OSD | RENDER_EOSD);
if (p->use_glFinish)
gl->Finish();
else
gl->Flush();
}
if (p->use_glFinish)
gl->Finish();
p->glctx->swapGlBuffers(p->glctx);
if (aspect_scaling())
gl->Clear(GL_COLOR_BUFFER_BIT);
}
static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
@ -1059,8 +791,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
skip_upload:
if (vo_doublebuffering)
do_render(vo);
do_render(vo);
return VO_TRUE;
}
@ -1085,36 +816,16 @@ static mp_image_t *get_screenshot(struct vo *vo)
gl->ActiveTexture(GL_TEXTURE0);
}
image->width = p->image_width;
image->height = p->image_height;
image->w = p->image_width;
image->h = p->image_height;
image->display_w = vo->aspdat.prew;
image->display_h = vo->aspdat.preh;
image->w = vo->aspdat.prew;
image->h = vo->aspdat.preh;
mp_image_set_colorspace_details(image, &p->colorspace);
return image;
}
static mp_image_t *get_window_screenshot(struct vo *vo)
{
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
GLint vp[4]; //x, y, w, h
gl->GetIntegerv(GL_VIEWPORT, vp);
mp_image_t *image = alloc_mpi(vp[2], vp[3], IMGFMT_RGB24);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
gl->PixelStorei(GL_PACK_ALIGNMENT, 0);
gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
gl->ReadBuffer(GL_FRONT);
//flip image while reading
for (int y = 0; y < vp[3]; y++) {
gl->ReadPixels(vp[0], vp[1] + vp[3] - y - 1, vp[2], 1,
GL_RGB, GL_UNSIGNED_BYTE,
image->planes[0] + y * image->stride[0]);
}
return image;
}
static int query_format(struct vo *vo, uint32_t format)
{
struct gl_priv *p = vo->priv;
@ -1123,7 +834,7 @@ static int query_format(struct vo *vo, uint32_t format)
int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP |
VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE;
if (p->use_osd)
caps |= VFCAP_OSD | VFCAP_EOSD | (p->scaled_osd ? 0 : VFCAP_EOSD_UNSCALED);
caps |= VFCAP_OSD;
if (format == IMGFMT_RGB24 || format == IMGFMT_RGBA)
return caps;
if (p->use_yuv && mp_get_chroma_shift(format, NULL, NULL, &depth) &&
@ -1182,8 +893,6 @@ static int preinit(struct vo *vo, const char *arg)
.osd_color = 0xffffff,
};
p->eosd = eosd_packer_create(vo);
char *backend_arg = NULL;
//essentially unused; for legacy warnings only
@ -1359,27 +1068,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
return query_format(vo, *(uint32_t *)data);
case VOCTRL_DRAW_IMAGE:
return draw_image(vo, data);
case VOCTRL_DRAW_EOSD:
if (!data)
return VO_FALSE;
genEOSD(vo, data);
if (vo_doublebuffering)
do_render_osd(vo, RENDER_EOSD);
return VO_TRUE;
case VOCTRL_GET_EOSD_RES: {
mp_eosd_res_t *r = data;
r->w = vo->dwidth;
r->h = vo->dheight;
r->mt = r->mb = r->ml = r->mr = 0;
if (p->scaled_osd) {
r->w = p->image_width;
r->h = p->image_height;
} else if (aspect_scaling()) {
r->ml = r->mr = p->ass_border_x;
r->mt = r->mb = p->ass_border_y;
}
return VO_TRUE;
}
case VOCTRL_ONTOP:
if (!p->glctx->ontop)
break;
@ -1435,8 +1123,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
p->glctx->update_xinerama_info(vo);
return VO_TRUE;
case VOCTRL_REDRAW_FRAME:
if (vo_doublebuffering)
do_render(vo);
do_render(vo);
return true;
case VOCTRL_PAUSE:
if (!p->glctx->pause)
@ -1451,7 +1138,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SCREENSHOT: {
struct voctrl_screenshot_args *args = data;
if (args->full_window)
args->out_image = get_window_screenshot(vo);
args->out_image = glGetWindowScreenshot(p->gl);
else
args->out_image = get_screenshot(vo);
return true;

View File

@ -67,7 +67,7 @@ void main() {
texcoord = vertex_texcoord;
}
#!section frag_eosd
#!section frag_osd_libass
uniform sampler2D textures[3];
in vec2 texcoord;
@ -78,15 +78,14 @@ void main() {
out_color = vec4(color.rgb, color.a * texture(textures[0], texcoord).r);
}
#!section frag_osd
#!section frag_osd_rgba
uniform sampler2D textures[3];
in vec2 texcoord;
in vec4 color;
DECLARE_FRAGPARMS
void main() {
out_color = texture(textures[0], texcoord).rrrg * color;
out_color = texture(textures[0], texcoord);
}
#!section frag_video

View File

@ -51,7 +51,6 @@
#include "libmpcodecs/vfcap.h"
#include "libmpcodecs/mp_image.h"
#include "osdep/timer.h"
#include "sub/ass_mp.h"
#include "bitmap_packer.h"
#define WRAP_ADD(x, a, m) ((a) < 0 \
@ -92,8 +91,6 @@ struct vdp_functions {
#undef VDP_FUNCTION
};
#define MAX_OLD_OSD_BITMAPS 6
struct vdpctx {
struct vdp_functions *vdp;
@ -140,7 +137,7 @@ struct vdpctx {
VdpRect src_rect_vid;
VdpRect out_rect_vid;
int border_x, border_y;
struct mp_osd_res osd_rect;
struct vdpau_render_state surface_render[MAX_VIDEO_SURFACES];
int surface_num;
@ -159,35 +156,24 @@ struct vdpctx {
VdpChromaType vdp_chroma_type;
VdpYCbCrFormat vdp_pixel_format;
/* draw_osd */
struct old_osd {
int x0, y0, w, h;
unsigned char *src, *srca;
int stride;
} old_osd_elements[MAX_OLD_OSD_BITMAPS];
int old_osd_count;
unsigned char *osd_data_temp;
int osd_data_size;
// EOSD
struct eosd_bitmap_surface {
// OSD
struct osd_bitmap_surface {
VdpRGBAFormat format;
VdpBitmapSurface surface;
uint32_t max_width;
uint32_t max_height;
struct bitmap_packer *packer;
} eosd_surface, osd_surface;
// List of surfaces to be rendered
struct eosd_target {
VdpRect source;
VdpRect dest;
VdpColor color;
} *eosd_targets, osd_targets[MAX_OLD_OSD_BITMAPS][2];
int eosd_targets_size;
int eosd_render_count;
int bitmap_id;
int bitmap_pos_id;
// List of surfaces to be rendered
struct osd_target {
VdpRect source;
VdpRect dest;
VdpColor color;
} *targets;
int targets_size;
int render_count;
int bitmap_id;
int bitmap_pos_id;
} osd_surfaces[MAX_OSD_PARTS];
// Video equalizer
struct mp_csp_equalizer video_eq;
@ -196,6 +182,8 @@ struct vdpctx {
bool mode_switched;
};
static bool status_ok(struct vo *vo);
static int change_vdptime_sync(struct vdpctx *vc, unsigned int *t)
{
struct vdp_functions *vdp = vc->vdp;
@ -382,22 +370,18 @@ static void resize(struct vo *vo)
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
struct vo_rect src_rect;
struct vo_rect dst_rect;
struct vo_rect borders;
calc_src_dst_rects(vo, vc->vid_width, vc->vid_height, &src_rect, &dst_rect,
&borders, NULL);
vc->out_rect_vid.x0 = dst_rect.left;
vc->out_rect_vid.x1 = dst_rect.right;
vc->out_rect_vid.y0 = dst_rect.top;
vc->out_rect_vid.y1 = dst_rect.bottom;
vc->src_rect_vid.x0 = src_rect.left;
vc->src_rect_vid.x1 = src_rect.right;
vc->src_rect_vid.y0 = vc->flip ? src_rect.bottom : src_rect.top;
vc->src_rect_vid.y1 = vc->flip ? src_rect.top : src_rect.bottom;
vc->border_x = borders.left;
vc->border_y = borders.top;
vo_osd_resized();
struct mp_rect src_rect;
struct mp_rect dst_rect;
vo_get_src_dst_rects(vo, &src_rect, &dst_rect, &vc->osd_rect);
vc->out_rect_vid.x0 = dst_rect.x0;
vc->out_rect_vid.x1 = dst_rect.x1;
vc->out_rect_vid.y0 = dst_rect.y0;
vc->out_rect_vid.y1 = dst_rect.y1;
vc->src_rect_vid.x0 = src_rect.x0;
vc->src_rect_vid.x1 = src_rect.x1;
vc->src_rect_vid.y0 = vc->flip ? src_rect.y1 : src_rect.y0;
vc->src_rect_vid.y1 = vc->flip ? src_rect.y0 : src_rect.y1;
int flip_offset_ms = vo_fs ? vc->flip_offset_fs : vc->flip_offset_window;
vo->flip_queue_offset = flip_offset_ms / 1000.;
@ -816,14 +800,15 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo)
vc->output_surfaces[i] = VDP_INVALID_HANDLE;
vc->screenshot_surface = VDP_INVALID_HANDLE;
vc->vdp_device = VDP_INVALID_HANDLE;
talloc_free(vc->osd_surface.packer);
talloc_free(vc->eosd_surface.packer);
vc->bitmap_id = vc->bitmap_pos_id = 0;
vc->osd_surface = vc->eosd_surface = (struct eosd_bitmap_surface){
.surface = VDP_INVALID_HANDLE,
};
for (int i = 0; i < MAX_OSD_PARTS; i++) {
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
talloc_free(sfc->packer);
sfc->bitmap_id = sfc->bitmap_pos_id = 0;
*sfc = (struct osd_bitmap_surface){
.surface = VDP_INVALID_HANDLE,
};
}
vc->output_surface_width = vc->output_surface_height = -1;
vc->eosd_render_count = 0;
}
static int handle_preemption(struct vo *vo)
@ -922,9 +907,6 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
}
#endif
if ((flags & VOFLAG_FULLSCREEN) && WinID <= 0)
vo_fs = 1;
if (initialize_vdpau_objects(vo) < 0)
return -1;
@ -955,17 +937,18 @@ static struct bitmap_packer *make_packer(struct vo *vo, VdpRGBAFormat format)
VdpStatus vdp_st = vdp->
bitmap_surface_query_capabilities(vc->vdp_device, format,
&(VdpBool){0}, &w_max, &h_max);
CHECK_ST_WARNING("Query to get max EOSD surface size failed");
CHECK_ST_WARNING("Query to get max OSD surface size failed");
packer->w_max = w_max;
packer->h_max = h_max;
return packer;
}
static void draw_eosd(struct vo *vo)
static void draw_osd_part(struct vo *vo, int index)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[index];
VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
int i;
@ -983,42 +966,49 @@ static void draw_eosd(struct vo *vo)
.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
};
for (i = 0; i < vc->eosd_render_count; i++) {
VdpOutputSurfaceRenderBlendState blend_state_premultiplied = blend_state;
blend_state_premultiplied.blend_factor_source_color =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE;
for (i = 0; i < sfc->render_count; i++) {
VdpOutputSurfaceRenderBlendState *blend = &blend_state;
if (sfc->format == VDP_RGBA_FORMAT_B8G8R8A8)
blend = &blend_state_premultiplied;
vdp_st = vdp->
output_surface_render_bitmap_surface(output_surface,
&vc->eosd_targets[i].dest,
vc->eosd_surface.surface,
&vc->eosd_targets[i].source,
&vc->eosd_targets[i].color,
&blend_state,
&sfc->targets[i].dest,
sfc->surface,
&sfc->targets[i].source,
&sfc->targets[i].color,
blend,
VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
CHECK_ST_WARNING("EOSD: Error when rendering");
CHECK_ST_WARNING("OSD: Error when rendering");
}
}
static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
struct eosd_bitmap_surface *sfc = &vc->eosd_surface;
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[imgs->render_index];
bool need_upload = false;
if (imgs->bitmap_pos_id == vc->bitmap_pos_id)
if (imgs->bitmap_pos_id == sfc->bitmap_pos_id)
return; // Nothing changed and we still have the old data
vc->eosd_render_count = 0;
sfc->render_count = 0;
if (imgs->type == SUBBITMAP_EMPTY)
if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
return;
if (imgs->bitmap_id == vc->bitmap_id)
goto eosd_skip_upload;
if (imgs->bitmap_id == sfc->bitmap_id)
goto osd_skip_upload;
need_upload = true;
VdpRGBAFormat format;
int format_size;
switch (imgs->type) {
switch (imgs->format) {
case SUBBITMAP_LIBASS:
format = VDP_RGBA_FORMAT_A8;
format_size = 1;
@ -1037,9 +1027,10 @@ static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
sfc->format = format;
if (!sfc->packer)
sfc->packer = make_packer(vo, format);
int r = packer_pack_from_subbitmaps(sfc->packer, imgs, imgs->scaled);
sfc->packer->padding = imgs->scaled; // assume 2x2 filter on scaling
int r = packer_pack_from_subbitmaps(sfc->packer, imgs);
if (r < 0) {
mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on "
mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] OSD bitmaps do not fit on "
"a surface with the maximum supported size\n");
return;
} else if (r == 1) {
@ -1048,13 +1039,13 @@ static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
"EOSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
"OSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
vdp_st = vdp->bitmap_surface_create(vc->vdp_device, format,
sfc->packer->w, sfc->packer->h,
true, &sfc->surface);
if (vdp_st != VDP_STATUS_OK)
sfc->surface = VDP_INVALID_HANDLE;
CHECK_ST_WARNING("EOSD: error when creating surface");
CHECK_ST_WARNING("OSD: error when creating surface");
}
if (imgs->scaled) {
char zeros[sfc->packer->used_width * format_size];
@ -1065,218 +1056,66 @@ static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
sfc->packer->used_height});
}
eosd_skip_upload:
osd_skip_upload:
if (sfc->surface == VDP_INVALID_HANDLE)
return;
if (sfc->packer->count > vc->eosd_targets_size) {
talloc_free(vc->eosd_targets);
vc->eosd_targets_size = sfc->packer->count;
vc->eosd_targets = talloc_size(vc, vc->eosd_targets_size
* sizeof(*vc->eosd_targets));
}
if (imgs->type == SUBBITMAP_LIBASS) {
int i = 0;
for (ASS_Image *p = imgs->imgs; p; p = p->next, i++) {
if (p->w == 0 || p->h == 0)
continue;
struct eosd_target *target = vc->eosd_targets +
vc->eosd_render_count;
int x = sfc->packer->result[i].x;
int y = sfc->packer->result[i].y;
target->source = (VdpRect){x, y, x + p->w, y + p->h};
if (need_upload) {
vdp_st = vdp->
bitmap_surface_put_bits_native(sfc->surface,
(const void *) &p->bitmap,
&p->stride, &target->source);
CHECK_ST_WARNING("EOSD: putbits failed");
}
// Render dest, color, etc.
target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0;
target->color.blue = ((p->color >> 8) & 0xff) / 255.0;
target->color.green = ((p->color >> 16) & 0xff) / 255.0;
target->color.red = ((p->color >> 24) & 0xff) / 255.0;
target->dest.x0 = p->dst_x;
target->dest.y0 = p->dst_y;
target->dest.x1 = p->w + p->dst_x;
target->dest.y1 = p->h + p->dst_y;
vc->eosd_render_count++;
}
} else {
for (int i = 0 ;i < sfc->packer->count; i++) {
struct sub_bitmap *b = &imgs->parts[i];
struct eosd_target *target = vc->eosd_targets +
vc->eosd_render_count;
int x = sfc->packer->result[i].x;
int y = sfc->packer->result[i].y;
target->source = (VdpRect){x, y, x + b->w, y + b->h};
if (need_upload) {
vdp_st = vdp->
bitmap_surface_put_bits_native(sfc->surface,
&(const void *){b->bitmap},
&(uint32_t){b->w * 4},
&target->source);
CHECK_ST_WARNING("EOSD: putbits failed");
}
target->color = (VdpColor){1, 1, 1, 1};
target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh};
vc->eosd_render_count++;
}
if (sfc->packer->count > sfc->targets_size) {
talloc_free(sfc->targets);
sfc->targets_size = sfc->packer->count;
sfc->targets = talloc_size(vc, sfc->targets_size
* sizeof(*sfc->targets));
}
vc->bitmap_id = imgs->bitmap_id;
vc->bitmap_pos_id = imgs->bitmap_pos_id;
for (int i = 0 ;i < sfc->packer->count; i++) {
struct sub_bitmap *b = &imgs->parts[i];
struct osd_target *target = sfc->targets + sfc->render_count;
int x = sfc->packer->result[i].x;
int y = sfc->packer->result[i].y;
target->source = (VdpRect){x, y, x + b->w, y + b->h};
target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh};
target->color = (VdpColor){1, 1, 1, 1};
if (imgs->format == SUBBITMAP_LIBASS) {
uint32_t color = b->libass.color;
target->color.alpha = 1.0 - ((color >> 0) & 0xff) / 255.0;
target->color.blue = ((color >> 8) & 0xff) / 255.0;
target->color.green = ((color >> 16) & 0xff) / 255.0;
target->color.red = ((color >> 24) & 0xff) / 255.0;
}
if (need_upload) {
vdp_st = vdp->
bitmap_surface_put_bits_native(sfc->surface,
&(const void *){b->bitmap},
&(uint32_t){b->stride},
&target->source);
CHECK_ST_WARNING("OSD: putbits failed");
}
sfc->render_count++;
}
sfc->bitmap_id = imgs->bitmap_id;
sfc->bitmap_pos_id = imgs->bitmap_pos_id;
}
static void record_osd(void *ctx, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca, int stride)
static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
{
struct vo *vo = ctx;
struct vdpctx *vc = vo->priv;
assert(vc->old_osd_count < MAX_OLD_OSD_BITMAPS);
if (!w || !h)
return;
vc->old_osd_elements[vc->old_osd_count++] = (struct old_osd){
x0, y0, w, h, src, srca, stride};
}
static void render_old_osd(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
VdpStatus vdp_st;
struct eosd_bitmap_surface *sfc = &vc->osd_surface;
if (!sfc->packer)
sfc->packer = make_packer(vo, VDP_RGBA_FORMAT_A8);
packer_set_size(sfc->packer, vc->old_osd_count * 2);
for (int i = 0; i < vc->old_osd_count; i++) {
struct old_osd *o = &vc->old_osd_elements[i];
sfc->packer->in[i*2] = sfc->packer->in[i*2 + 1] =
(struct pos){o->w, o->h};
};
int r = packer_pack(sfc->packer);
if (r < 0) {
mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] OSD bitmaps do not fit on "
"a surface with the maximum supported size\n");
vc->old_osd_count = 0;
return;
} else if (r == 1) {
if (sfc->surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
"OSD bitmaps.\n", sfc->packer->w, sfc->packer->h);
vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8,
sfc->packer->w, sfc->packer->h,
true, &sfc->surface);
if (vdp_st != VDP_STATUS_OK)
sfc->surface = VDP_INVALID_HANDLE;
CHECK_ST_WARNING("OSD: error when creating surface");
}
for (int i = 0; i < vc->old_osd_count; i++) {
struct old_osd *o = &vc->old_osd_elements[i];
struct eosd_target *target1 = &vc->osd_targets[i][0];
struct eosd_target *target2 = &vc->osd_targets[i][1];
int w = o->w, h = o->h;
int sx = sfc->packer->result[i * 2].x;
int sy = sfc->packer->result[i * 2].y;
target1->source = (VdpRect){ sx, sy, sx + w, sy + h };
target1->dest = (VdpRect){ o->x0, o->y0, o->x0 + w, o->y0 + h };
sx = sfc->packer->result[i * 2 + 1].x;
sy = sfc->packer->result[i * 2 + 1].y;
target2->source = (VdpRect){ sx, sy, sx + w, sy + h };
target2->dest = target1->dest;
vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
&(const void *){o->src},
&(uint32_t){o->stride},
&target1->source);
CHECK_ST_WARNING("OSD: putbits failed");
int size_required = w * h;
if (vc->osd_data_size < size_required) {
talloc_free(vc->osd_data_temp);
vc->osd_data_temp = talloc_size(vc, size_required);
vc->osd_data_size = size_required;
}
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
vc->osd_data_temp[y * w + x] = -o->srca[y * o->stride + x];
vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
&(const void *){vc->osd_data_temp},
&(uint32_t){w},
&target2->source);
CHECK_ST_WARNING("OSD: putbits failed");
}
VdpOutputSurfaceRenderBlendState blend_state_alpha = {
.struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
.blend_factor_source_color =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
.blend_factor_source_alpha =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
.blend_factor_destination_color =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.blend_factor_destination_alpha =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
};
VdpOutputSurfaceRenderBlendState blend_state_gray = {
.struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
.blend_factor_source_color =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
.blend_factor_source_alpha =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
.blend_factor_destination_color =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
.blend_factor_destination_alpha =
VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
.blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
};
for (int i = 0; i < vc->old_osd_count; i++) {
struct eosd_target *target1 = &vc->osd_targets[i][0];
struct eosd_target *target2 = &vc->osd_targets[i][1];
vdp_st = vdp->
output_surface_render_bitmap_surface(output_surface,
&target2->dest,
vc->osd_surface.surface,
&target2->source,
&(VdpColor){1, 1, 1, 1},
&blend_state_alpha,
VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
CHECK_ST_WARNING("OSD: Error when rendering");
vdp_st = vdp->
output_surface_render_bitmap_surface(output_surface,
&target1->dest,
vc->osd_surface.surface,
&target1->source,
&(VdpColor){1, 1, 1, 1},
&blend_state_gray,
VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
CHECK_ST_WARNING("OSD: Error when rendering");
}
generate_osd_part(vo, imgs);
draw_osd_part(vo, imgs->render_index);
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct vdpctx *vc = vo->priv;
if (handle_preemption(vo) < 0)
if (!status_ok(vo))
return;
vc->old_osd_count = 0;
osd_draw_text_ext(osd, vo->dwidth, vo->dheight, vc->border_x, vc->border_y,
vc->border_x, vc->border_y, vc->vid_width,
vc->vid_height, record_osd, vo);
render_old_osd(vo);
static const bool formats[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = true,
[SUBBITMAP_RGBA] = true,
};
osd_draw(osd, vc->osd_rect, osd->vo_pts, 0, formats, draw_osd_cb, vo);
}
static int update_presentation_queue_status(struct vo *vo)
@ -1508,6 +1347,8 @@ static struct mp_image *read_output_surface(struct vdpctx *vc,
VdpStatus vdp_st;
struct vdp_functions *vdp = vc->vdp;
struct mp_image *image = alloc_mpi(width, height, IMGFMT_BGR32);
image->colorspace = MP_CSP_RGB;
image->levels = vc->colorspace.levels_out; // hardcoded with conv. matrix
void *dst_planes[] = { image->planes[0] };
uint32_t dst_pitches[] = { image->stride[0] };
@ -1538,10 +1379,8 @@ static struct mp_image *get_screenshot(struct vo *vo)
struct mp_image *image = read_output_surface(vc, vc->screenshot_surface,
vc->vid_width, vc->vid_height);
image->width = vc->vid_width;
image->height = vc->vid_height;
image->w = vo->aspdat.prew;
image->h = vo->aspdat.preh;
image->display_w = vo->aspdat.prew;
image->display_h = vo->aspdat.preh;
return image;
}
@ -1590,8 +1429,8 @@ static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
static int query_format(uint32_t format)
{
int default_flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_EOSD
| VFCAP_EOSD_UNSCALED | VFCAP_EOSD_RGBA | VFCAP_FLIP;
| VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD
| VFCAP_FLIP;
switch (format) {
case IMGFMT_YV12:
case IMGFMT_I420:
@ -1639,14 +1478,12 @@ static void destroy_vdpau_objects(struct vo *vo)
CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
}
if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
if (vc->osd_surface.surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(vc->osd_surface.surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
for (int i = 0; i < MAX_OSD_PARTS; i++) {
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
if (sfc->surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
}
vdp_st = vdp->device_destroy(vc->vdp_device);
@ -1810,22 +1647,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_UPDATE_SCREENINFO:
update_xinerama_info(vo);
return VO_TRUE;
case VOCTRL_DRAW_EOSD:
if (!data)
return VO_FALSE;
if (status_ok(vo)) {
generate_eosd(vo, data);
draw_eosd(vo);
}
return VO_TRUE;
case VOCTRL_GET_EOSD_RES: {
struct mp_eosd_res *r = data;
r->w = vo->dwidth;
r->h = vo->dheight;
r->ml = r->mr = vc->border_x;
r->mt = r->mb = vc->border_y;
return VO_TRUE;
}
case VOCTRL_NEWFRAME:
vc->deint_queue_pos = next_deint_queue_pos(vo, true);
if (status_ok(vo))

View File

@ -24,7 +24,7 @@
#include "config.h"
#include "video_out.h"
#include "aspect.h"
#include "osd.h"
#include "csputils.h"
#include "libmpcodecs/mp_image.h"
#include "libmpcodecs/vfcap.h"
@ -43,8 +43,7 @@
#include "sub/sub.h"
#include "libswscale/swscale.h"
#include "libmpcodecs/vf_scale.h"
#include "libmpcodecs/sws_utils.h"
#define MODE_RGB 0x1
#define MODE_BGR 0x2
@ -114,42 +113,6 @@ static void check_events(struct vo *vo)
flip_page(vo);
}
static void draw_alpha_32(int x0, int y0, int w, int h, unsigned char *src,
unsigned char *srca, int stride,
unsigned char *dst, uint32_t dst_width)
{
vo_draw_alpha_rgb32(w, h, src, srca, stride,
dst + 4 * (y0 * dst_width + x0),
4 * dst_width);
}
static void draw_alpha_24(int x0, int y0, int w, int h, unsigned char *src,
unsigned char *srca, int stride,
unsigned char *dst, uint32_t dst_width)
{
vo_draw_alpha_rgb24(w, h, src, srca, stride,
dst + 3 * (y0 * dst_width + x0),
3 * dst_width);
}
static void draw_alpha_16(int x0, int y0, int w, int h, unsigned char *src,
unsigned char *srca, int stride,
unsigned char *dst, uint32_t dst_width)
{
vo_draw_alpha_rgb16(w, h, src, srca, stride,
dst + 2 * (y0 * dst_width + x0),
2 * dst_width);
}
static void draw_alpha_15(int x0, int y0, int w, int h, unsigned char *src,
unsigned char *srca, int stride,
unsigned char *dst, uint32_t dst_width)
{
vo_draw_alpha_rgb15(w, h, src, srca, stride,
dst + 2 * (y0 * dst_width + x0),
2 * dst_width);
}
static void getMyXImage(struct priv *p)
{
struct vo *vo = p->vo;
@ -450,36 +413,33 @@ static void Display_Image(struct priv *p, XImage *myximage, uint8_t *ImageData)
p->myximage->data -= p->out_offset;
}
static void draw_osd_elem(void *ctx, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca, int stride)
static struct mp_image get_x_buffer(struct priv *p)
{
struct priv *p = ctx;
struct mp_image img = {0};
img.w = img.width = p->image_width;
img.h = img.height = p->image_height;
mp_image_setfmt(&img, p->out_format);
switch (p->myximage->bits_per_pixel) {
case 24:
draw_alpha_24(x0, y0, w, h, src, srca, stride, p->ImageData,
p->image_width);
break;
case 32:
draw_alpha_32(x0, y0, w, h, src, srca, stride, p->ImageData,
p->image_width);
break;
case 15:
draw_alpha_15(x0, y0, w, h, src, srca, stride, p->ImageData,
p->image_width);
break;
case 16:
draw_alpha_16(x0, y0, w, h, src, srca, stride, p->ImageData,
p->image_width);
break;
default:;
}
img.planes[0] = p->ImageData;
img.stride[0] = p->image_width * ((p->bpp + 7) / 8);
return img;
}
static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct priv *p = vo->priv;
osd_draw_text(osd, p->image_width, p->image_height, draw_osd_elem, p);
struct mp_image img = get_x_buffer(p);
struct mp_osd_res res = {
.w = img.w,
.h = img.h,
.display_par = vo->monitor_par,
.video_par = vo->aspdat.par,
};
osd_draw_on_image(osd, res, osd->vo_pts, 0, &img);
}
static void flip_page(struct vo *vo)

View File

@ -47,7 +47,6 @@
#include "video_out.h"
#include "libmpcodecs/vfcap.h"
#include "libmpcodecs/mp_image.h"
#include "osd.h"
#include "x11_common.h"
#include "fastmemcpy.h"
#include "sub/sub.h"
@ -78,14 +77,10 @@ struct xvctx {
uint32_t image_height;
uint32_t image_format;
int is_paused;
struct vo_rect src_rect;
struct vo_rect dst_rect;
struct mp_rect src_rect;
struct mp_rect dst_rect;
uint32_t max_width, max_height; // zero means: not set
int mode_switched;
int osd_objects_drawn;
void (*draw_alpha_fnc)(void *ctx, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride);
#ifdef HAVE_SHM
XShmSegmentInfo Shminfo[2 + 1];
int Shmem_Flag;
@ -93,82 +88,22 @@ struct xvctx {
};
static void allocate_xvimage(struct vo *, int);
static void fixup_osd_position(struct vo *vo, int *x0, int *y0, int *w, int *h)
{
struct xvctx *ctx = vo->priv;
*x0 += ctx->image_width * (vo->panscan_x >> 1)
/ (vo->dwidth + vo->panscan_x);
*w = av_clip(*w, 0, ctx->image_width);
*h = av_clip(*h, 0, ctx->image_height);
*x0 = FFMIN(*x0, ctx->image_width - *w);
*y0 = FFMIN(*y0, ctx->image_height - *h);
}
static void draw_alpha_yv12(void *p, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride)
{
struct vo *vo = p;
struct xvctx *ctx = vo->priv;
fixup_osd_position(vo, &x0, &y0, &w, &h);
vo_draw_alpha_yv12(w, h, src, srca, stride,
ctx->xvimage[ctx->current_buf]->data +
ctx->xvimage[ctx->current_buf]->offsets[0] +
ctx->xvimage[ctx->current_buf]->pitches[0] * y0 + x0,
ctx->xvimage[ctx->current_buf]->pitches[0]);
ctx->osd_objects_drawn++;
}
static void draw_alpha_yuy2(void *p, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride)
{
struct vo *vo = p;
struct xvctx *ctx = vo->priv;
fixup_osd_position(vo, &x0, &y0, &w, &h);
vo_draw_alpha_yuy2(w, h, src, srca, stride,
ctx->xvimage[ctx->current_buf]->data +
ctx->xvimage[ctx->current_buf]->offsets[0] +
ctx->xvimage[ctx->current_buf]->pitches[0] * y0 + 2 * x0,
ctx->xvimage[ctx->current_buf]->pitches[0]);
ctx->osd_objects_drawn++;
}
static void draw_alpha_uyvy(void *p, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride)
{
struct vo *vo = p;
struct xvctx *ctx = vo->priv;
fixup_osd_position(vo, &x0, &y0, &w, &h);
vo_draw_alpha_yuy2(w, h, src, srca, stride,
ctx->xvimage[ctx->current_buf]->data +
ctx->xvimage[ctx->current_buf]->offsets[0] +
ctx->xvimage[ctx->current_buf]->pitches[0] * y0 + 2 * x0 + 1,
ctx->xvimage[ctx->current_buf]->pitches[0]);
ctx->osd_objects_drawn++;
}
static void draw_alpha_null(void *p, int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca,
int stride)
{
}
static void deallocate_xvimage(struct vo *vo, int foo);
static void resize(struct vo *vo)
{
struct xvctx *ctx = vo->priv;
calc_src_dst_rects(vo, ctx->image_width, ctx->image_height, &ctx->src_rect,
&ctx->dst_rect, NULL, NULL);
struct vo_rect *dst = &ctx->dst_rect;
vo_x11_clearwindow_part(vo, vo->x11->window, dst->width, dst->height);
vo_xv_draw_colorkey(vo, dst->left, dst->top, dst->width, dst->height);
// Can't be used, because the function calculates screen-space coordinates,
// while we need video-space.
struct mp_osd_res unused;
vo_get_src_dst_rects(vo, &ctx->src_rect, &ctx->dst_rect, &unused);
struct mp_rect *dst = &ctx->dst_rect;
int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
vo_x11_clearwindow_part(vo, vo->x11->window, dw, dh);
vo_xv_draw_colorkey(vo, dst->x0, dst->y0, dw, dh);
}
/*
@ -257,23 +192,6 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
mp_msg(MSGT_VO, MSGL_V, "using Xvideo port %d for hw scaling\n",
x11->xv_port);
switch (ctx->xv_format) {
case IMGFMT_YV12:
case IMGFMT_I420:
case IMGFMT_IYUV:
ctx->draw_alpha_fnc = draw_alpha_yv12;
break;
case IMGFMT_YUY2:
case IMGFMT_YVYU:
ctx->draw_alpha_fnc = draw_alpha_yuy2;
break;
case IMGFMT_UYVY:
ctx->draw_alpha_fnc = draw_alpha_uyvy;
break;
default:
ctx->draw_alpha_fnc = draw_alpha_null;
}
// In case config has been called before
for (i = 0; i < ctx->total_buffers; i++)
deallocate_xvimage(vo, i);
@ -362,33 +280,55 @@ static inline void put_xvimage(struct vo *vo, XvImage *xvi)
{
struct xvctx *ctx = vo->priv;
struct vo_x11_state *x11 = vo->x11;
struct vo_rect *src = &ctx->src_rect;
struct vo_rect *dst = &ctx->dst_rect;
struct mp_rect *src = &ctx->src_rect;
struct mp_rect *dst = &ctx->dst_rect;
int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
int sw = src->x1 - src->x0, sh = src->y1 - src->y0;
#ifdef HAVE_SHM
if (ctx->Shmem_Flag) {
XvShmPutImage(x11->display, x11->xv_port, x11->window, x11->vo_gc, xvi,
src->left, src->top, src->width, src->height,
dst->left, dst->top, dst->width, dst->height,
src->x0, src->y0, sw, sh,
dst->x0, dst->y0, dw, dh,
False);
} else
#endif
{
XvPutImage(x11->display, x11->xv_port, x11->window, x11->vo_gc, xvi,
src->left, src->top, src->width, src->height,
dst->left, dst->top, dst->width, dst->height);
src->x0, src->y0, sw, sh,
dst->x0, dst->y0, dw, dh);
}
}
// Only copies luma for planar formats as draw_alpha doesn't change others */
static void copy_backup_image(struct vo *vo, int dest, int src)
static struct mp_image get_xv_buffer(struct vo *vo, int buf_index)
{
struct xvctx *ctx = vo->priv;
XvImage *xv_image = ctx->xvimage[buf_index];
XvImage *vb = ctx->xvimage[dest];
XvImage *cp = ctx->xvimage[src];
memcpy_pic(vb->data + vb->offsets[0], cp->data + cp->offsets[0],
vb->width, vb->height,
vb->pitches[0], cp->pitches[0]);
struct mp_image img = {0};
img.w = img.width = xv_image->width;
img.h = img.height = xv_image->height;
mp_image_setfmt(&img, ctx->image_format);
bool swapuv = ctx->image_format == IMGFMT_YV12;
for (int n = 0; n < img.num_planes; n++) {
int sn = n > 0 && swapuv ? (n == 1 ? 2 : 1) : n;
img.planes[n] = xv_image->data + xv_image->offsets[sn];
img.stride[n] = xv_image->pitches[sn];
}
struct mp_csp_details csp = {0};
vo_control(vo, VOCTRL_GET_YUV_COLORSPACE, &csp);
mp_image_set_colorspace_details(&img, &csp);
return img;
}
static void copy_backup_image(struct vo *vo, int dest, int src)
{
struct mp_image img_dest = get_xv_buffer(vo, dest);
struct mp_image img_src = get_xv_buffer(vo, src);
copy_mpi(&img_dest, &img_src);
}
static void check_events(struct vo *vo)
@ -405,13 +345,22 @@ static void draw_osd(struct vo *vo, struct osd_state *osd)
{
struct xvctx *ctx = vo->priv;
ctx->osd_objects_drawn = 0;
osd_draw_text(osd,
ctx->image_width -
ctx->image_width * vo->panscan_x / (vo->dwidth +
vo->panscan_x),
ctx->image_height, ctx->draw_alpha_fnc, vo);
if (ctx->osd_objects_drawn)
struct mp_image img = get_xv_buffer(vo, ctx->current_buf);
struct mp_rect *src = &ctx->src_rect;
struct mp_rect *dst = &ctx->dst_rect;
int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
int sw = src->x1 - src->x0, sh = src->y1 - src->y0;
double xvpar = (double)dw / dh * sh / sw;
struct mp_osd_res res = {
.w = ctx->image_width,
.h = ctx->image_height,
.display_par = vo->monitor_par / xvpar,
.video_par = vo->aspdat.par,
};
if (osd_draw_on_image(osd, res, osd->vo_pts, 0, &img))
ctx->unchanged_image = false;
}
@ -481,42 +430,12 @@ static mp_image_t *get_screenshot(struct vo *vo)
struct xvctx *ctx = vo->priv;
// try to get an image without OSD
if (ctx->have_image_copy)
copy_backup_image(vo, ctx->visible_buf, ctx->num_buffers);
int id = ctx->have_image_copy ? ctx->num_buffers : ctx->visible_buf;
struct mp_image img = get_xv_buffer(vo, id);
img.display_w = vo->aspdat.prew;
img.display_h = vo->aspdat.preh;
XvImage *xv_image = ctx->xvimage[ctx->visible_buf];
int w = xv_image->width;
int h = xv_image->height;
mp_image_t *image = alloc_mpi(w, h, ctx->image_format);
int bytes = 1;
if (!(image->flags & MP_IMGFLAG_PLANAR) && (image->flags & MP_IMGFLAG_YUV))
// packed YUV
bytes = image->bpp / 8;
memcpy_pic(image->planes[0], xv_image->data + xv_image->offsets[0],
bytes * w, h, image->stride[0], xv_image->pitches[0]);
if (image->flags & MP_IMGFLAG_PLANAR) {
int swap = ctx->image_format == IMGFMT_YV12;
int p1 = swap ? 2 : 1;
int p2 = swap ? 1 : 2;
w /= 2;
h /= 2;
memcpy_pic(image->planes[p1], xv_image->data + xv_image->offsets[1],
w, h, image->stride[p1], xv_image->pitches[1]);
memcpy_pic(image->planes[p2], xv_image->data + xv_image->offsets[2],
w, h, image->stride[p2], xv_image->pitches[2]);
}
image->w = vo->aspdat.prew;
image->h = vo->aspdat.preh;
return image;
return talloc_memdup(NULL, &img, sizeof(img));
}
static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
@ -766,6 +685,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SCREENSHOT: {
struct voctrl_screenshot_args *args = data;
args->out_image = get_screenshot(vo);
args->has_osd = !ctx->have_image_copy;
return true;
}
}

View File

@ -80,4 +80,9 @@ extern const char *mplayer_version;
char *mp_format_time(double time, bool fractions);
struct mp_rect {
int x0, y0;
int x1, y1;
};
#endif /* MPLAYER_MPCOMMON_H */

132
mplayer.c
View File

@ -995,7 +995,6 @@ void init_vo_spudec(struct MPContext *mpctx)
sh_sub_t *sh = mpctx->sh_sub;
vo_spudec = spudec_new_scaled(NULL, width, height, sh->extradata,
sh->extradata_len);
spudec_set_font_factor(vo_spudec, font_factor);
}
if (vo_spudec != NULL) {
@ -1313,7 +1312,7 @@ static mp_osd_msg_t *get_osd_msg(struct MPContext *mpctx)
// the difference is greater assume it's wrapped around from below 0
if (mpctx->osd_visible - now > 36000000) {
mpctx->osd_visible = 0;
vo_osd_progbar_type = -1; // disable
mpctx->osd->progbar_type = -1; // disable
vo_osd_changed(OSDTYPE_PROGBAR);
mpctx->osd_function = mpctx->paused ? OSD_PAUSE : OSD_PLAY;
}
@ -1373,8 +1372,8 @@ void set_osd_bar(struct MPContext *mpctx, int type, const char *name,
if (mpctx->sh_video && opts->term_osd != 1) {
mpctx->osd_visible = (GetTimerMS() + 1000) | 1;
vo_osd_progbar_type = type;
vo_osd_progbar_value = 256 * (val - min) / (max - min);
mpctx->osd->progbar_type = type;
mpctx->osd->progbar_value = 256 * (val - min) / (max - min);
vo_osd_changed(OSDTYPE_PROGBAR);
return;
}
@ -1661,6 +1660,8 @@ double playing_audio_pts(struct MPContext *mpctx)
static void reset_subtitles(struct MPContext *mpctx)
{
if (mpctx->sh_sub)
sub_reset(mpctx->sh_sub, mpctx->osd);
sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
if (vo_sub)
set_osd_subtitle(mpctx, NULL);
@ -1696,7 +1697,9 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
}
// DVD sub:
if (track->vobsub_id_plus_one || type == 'v') {
if ((track->vobsub_id_plus_one || type == 'v')
&& !(sh_sub && sh_sub->active))
{
int timestamp;
// Get a sub packet from the demuxer (or the vobsub.c thing, which
// should be a demuxer, but isn't).
@ -1745,7 +1748,8 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
spudec_assemble(vo_spudec, packet, len, timestamp);
}
} else if (d_sub && (is_text_sub(type) || (sh_sub && sh_sub->active))) {
if (d_sub->non_interleaved)
bool non_interleaved = track->is_external; // if demuxing subs only
if (non_interleaved)
ds_get_next_pts(d_sub);
while (d_sub->first) {
@ -1755,7 +1759,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
if (!opts->ass_enabled || !is_text_sub(type))
break;
// Try to avoid demuxing whole file at once
if (d_sub->non_interleaved && subpts_s > curpts_s + 1)
if (non_interleaved && subpts_s > curpts_s + 1)
break;
}
double duration = d_sub->first->duration;
@ -1791,7 +1795,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
sub_add_text(&mpctx->subs, packet, len, endpts_s);
set_osd_subtitle(mpctx, &mpctx->subs);
}
if (d_sub->non_interleaved)
if (non_interleaved)
ds_get_next_pts(d_sub);
}
if (!opts->ass_enabled)
@ -2255,45 +2259,11 @@ int reinit_video_chain(struct MPContext *mpctx)
sh_video->vfilter = vf_open_filter(opts, NULL, "vo", vf_arg);
}
#ifdef CONFIG_ASS
if (opts->ass_enabled) {
int i;
int insert = 1;
if (opts->vf_settings)
for (i = 0; opts->vf_settings[i].name; ++i)
if (strcmp(opts->vf_settings[i].name, "ass") == 0) {
insert = 0;
break;
}
if (insert) {
extern vf_info_t vf_info_ass;
const vf_info_t *libass_vfs[] = {
&vf_info_ass, NULL
};
char *vf_arg[] = {
"auto", "yes", NULL
};
int retcode = 0;
struct vf_instance *vf_ass = vf_open_plugin_noerr(opts, libass_vfs,
sh_video->vfilter,
"ass", vf_arg,
&retcode);
if (vf_ass)
sh_video->vfilter = vf_ass;
else if (retcode == -1) // vf_ass open() returns -1 VO has EOSD
mp_msg(MSGT_CPLAYER, MSGL_V, "[ass] vf_ass not needed\n");
else
mp_msg(MSGT_CPLAYER, MSGL_ERR,
"ASS: cannot add video filter\n");
}
}
#endif
sh_video->vfilter = append_filters(sh_video->vfilter, opts->vf_settings);
if (opts->ass_enabled)
sh_video->vfilter->control(sh_video->vfilter, VFCTRL_INIT_EOSD,
mpctx->ass_library);
struct vf_instance *vf = sh_video->vfilter;
mpctx->osd->render_subs_in_filter
= vf->control(vf, VFCTRL_INIT_OSD, NULL) == VO_TRUE;
init_best_video_codec(sh_video, video_codec_list, video_fm_list);
@ -2403,7 +2373,7 @@ static double update_video(struct MPContext *mpctx)
struct sh_video *sh_video = mpctx->sh_video;
struct vo *video_out = mpctx->video_out;
sh_video->vfilter->control(sh_video->vfilter, VFCTRL_SET_OSD_OBJ,
mpctx->osd); // for vf_ass
mpctx->osd); // for vf_sub
if (!mpctx->opts.correct_pts)
return update_video_nocorrect_pts(mpctx);
@ -2532,24 +2502,25 @@ void unpause_player(struct MPContext *mpctx)
(void)get_relative_time(mpctx); // ignore time that passed during pause
}
static int redraw_osd(struct MPContext *mpctx)
static void draw_osd(struct MPContext *mpctx)
{
struct sh_video *sh_video = mpctx->sh_video;
struct vf_instance *vf = sh_video->vfilter;
if (sh_video->output_flags & VFCAP_OSD_FILTER)
return -1;
if (vo_redraw_frame(mpctx->video_out) < 0)
return -1;
mpctx->osd->sub_pts = mpctx->video_pts;
if (mpctx->osd->sub_pts != MP_NOPTS_VALUE)
mpctx->osd->sub_pts += sub_delay - mpctx->osd->sub_offset;
struct vo *vo = mpctx->video_out;
if (!(sh_video->output_flags & VFCAP_EOSD_FILTER))
vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd);
vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd);
vo_osd_reset_changed();
vo_flip_page(mpctx->video_out, 0, -1);
return 0;
mpctx->osd->vo_pts = mpctx->video_pts;
vo_draw_osd(vo, mpctx->osd);
mpctx->osd->want_redraw = false;
}
static bool redraw_osd(struct MPContext *mpctx)
{
struct vo *vo = mpctx->video_out;
if (vo_redraw_frame(vo) < 0)
return false;
draw_osd(mpctx);
vo_flip_page(vo, 0, -1);
return true;
}
void add_step_frame(struct MPContext *mpctx)
@ -3154,13 +3125,7 @@ static void run_playloop(struct MPContext *mpctx)
mpctx->video_pts = sh_video->pts;
update_subtitles(mpctx, sh_video->pts);
update_osd_msg(mpctx);
struct vf_instance *vf = sh_video->vfilter;
mpctx->osd->sub_pts = mpctx->video_pts;
if (mpctx->osd->sub_pts != MP_NOPTS_VALUE)
mpctx->osd->sub_pts += sub_delay - mpctx->osd->sub_offset;
vf->control(vf, VFCTRL_DRAW_EOSD, mpctx->osd);
vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd);
vo_osd_reset_changed();
draw_osd(mpctx);
mpctx->time_frame -= get_relative_time(mpctx);
mpctx->time_frame -= vo->flip_queue_offset;
@ -3299,22 +3264,23 @@ static void run_playloop(struct MPContext *mpctx)
audio_sleep = 0.020;
}
sleeptime = FFMIN(sleeptime, audio_sleep);
if (sleeptime > 0) {
if (!mpctx->sh_video)
goto novideo;
if (vo_osd_has_changed(mpctx->osd) || mpctx->video_out->want_redraw)
{
if (redraw_osd(mpctx) < 0) {
if (mpctx->paused && video_left)
add_step_frame(mpctx);
else
goto novideo;
if (sleeptime > 0 && mpctx->sh_video) {
bool want_redraw = mpctx->video_out->want_redraw;
if (mpctx->video_out->default_caps & VFCAP_OSD)
want_redraw |= mpctx->osd->want_redraw;
mpctx->osd->want_redraw = false;
if (want_redraw) {
if (redraw_osd(mpctx)) {
sleeptime = 0;
} else if (mpctx->paused && video_left) {
// force redrawing OSD by framestepping
add_step_frame(mpctx);
sleeptime = 0;
}
} else {
novideo:
mp_input_get_cmd(mpctx->input, sleeptime * 1000, true);
}
}
if (sleeptime > 0)
mp_input_get_cmd(mpctx->input, sleeptime * 1000, true);
}
//================= Keyboard events, SEEKing ====================
@ -3587,6 +3553,8 @@ static void open_external_file(struct MPContext *mpctx, char *filename,
if (stream->type == filter) {
struct track *t = add_stream_track(mpctx, stream, false);
t->is_external = true;
t->title = talloc_strdup(t, filename);
num_added++;
}
}
if (num_added == 0) {

View File

@ -36,13 +36,17 @@
#include "libmpcodecs/vf.h"
#include "libvo/video_out.h"
#include "image_writer.h"
#include "sub/sub.h"
#include "libvo/csputils.h"
#define MODE_FULL_WINDOW 1
#define MODE_SUBTITLES 2
typedef struct screenshot_ctx {
struct MPContext *mpctx;
int full_window;
int mode;
int each_frame;
int using_vf_screenshot;
@ -230,31 +234,65 @@ static char *gen_fname(screenshot_ctx *ctx, const char *file_ext)
}
}
void screenshot_save(struct MPContext *mpctx, struct mp_image *image)
static struct mp_image *add_subs(struct MPContext *mpctx,
struct mp_image *image)
{
if (!(image->flags & MP_IMGFLAG_ALLOCATED)) {
struct mp_image *new_image = alloc_mpi(image->width, image->height,
image->imgfmt);
copy_mpi(new_image, image);
vf_clone_mpi_attributes(new_image, image);
image = new_image;
}
int d_w = image->display_w ? image->display_w : image->w;
int d_h = image->display_h ? image->display_h : image->h;
double sar = (double)image->width / image->height;
double dar = (double)d_w / d_h;
struct mp_osd_res res = {
.w = image->w,
.h = image->h,
.display_par = sar / dar,
.video_par = dar / sar,
};
osd_draw_on_image(mpctx->osd, res, mpctx->osd->vo_pts,
OSD_DRAW_SUB_ONLY, image);
return image;
}
static void screenshot_save(struct MPContext *mpctx, struct mp_image *image,
bool with_subs)
{
screenshot_ctx *ctx = mpctx->screenshot_ctx;
struct mp_csp_details colorspace;
get_detected_video_colorspace(mpctx->sh_video, &colorspace);
struct image_writer_opts *opts = mpctx->opts.screenshot_image_opts;
struct mp_image *new_image = image;
if (with_subs)
new_image = add_subs(mpctx, new_image);
char *filename = gen_fname(ctx, image_writer_file_ext(opts));
if (filename) {
mp_msg(MSGT_CPLAYER, MSGL_INFO, "*** screenshot '%s' ***\n", filename);
if (!write_image(image, &colorspace, opts, filename))
if (!write_image(new_image, opts, filename))
mp_msg(MSGT_CPLAYER, MSGL_ERR, "\nError writing screenshot!\n");
talloc_free(filename);
}
if (new_image != image)
free_mp_image(new_image);
}
static void vf_screenshot_callback(void *pctx, struct mp_image *image)
{
struct MPContext *mpctx = (struct MPContext *)pctx;
screenshot_ctx *ctx = mpctx->screenshot_ctx;
screenshot_save(mpctx, image);
screenshot_save(mpctx, image, ctx->mode);
if (ctx->each_frame)
screenshot_request(mpctx, 0, ctx->full_window);
screenshot_request(mpctx, ctx->mode, false);
}
static bool force_vf(struct MPContext *mpctx)
@ -270,26 +308,31 @@ static bool force_vf(struct MPContext *mpctx)
return false;
}
void screenshot_request(struct MPContext *mpctx, bool each_frame,
bool full_window)
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame)
{
if (mpctx->video_out && mpctx->video_out->config_ok) {
screenshot_ctx *ctx = mpctx->screenshot_ctx;
ctx->using_vf_screenshot = 0;
if (mode == MODE_SUBTITLES && mpctx->osd->render_subs_in_filter)
mode = 0;
if (each_frame) {
ctx->each_frame = !ctx->each_frame;
ctx->full_window = full_window;
ctx->mode = mode;
if (!ctx->each_frame)
return;
}
struct voctrl_screenshot_args args = { .full_window = full_window };
struct voctrl_screenshot_args args =
{ .full_window = (mode == MODE_FULL_WINDOW) };
if (!force_vf(mpctx)
&& vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args) == true)
{
screenshot_save(mpctx, args.out_image);
if (args.has_osd)
mode = 0;
screenshot_save(mpctx, args.out_image, mode == MODE_SUBTITLES);
free_mp_image(args.out_image);
} else {
mp_msg(MSGT_CPLAYER, MSGL_INFO, "No VO support for taking"
@ -322,5 +365,5 @@ void screenshot_flip(struct MPContext *mpctx)
if (ctx->using_vf_screenshot)
return;
screenshot_request(mpctx, 0, ctx->full_window);
screenshot_request(mpctx, ctx->mode, false);
}

View File

@ -22,21 +22,15 @@
#include <stdbool.h>
struct MPContext;
struct mp_image;
// One time initialization at program start.
void screenshot_init(struct MPContext *mpctx);
// Request a taking & saving a screenshot of the currently displayed frame.
// mode: 0: -, 1: save the actual output window contents, 2: with subtitles.
// each_frame: If set, this toggles per-frame screenshots, exactly like the
// screenshot slave command (MP_CMD_SCREENSHOT).
// full_window: If set, save the actual output window contents.
void screenshot_request(struct MPContext *mpctx, bool each_frame,
bool full_window);
// Save the screenshot contained in the image to disk.
// The image can be in any format supported by libswscale.
void screenshot_save(struct MPContext *mpctx, struct mp_image *image);
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame);
// Called by the playback core code when a new frame is displayed.
void screenshot_flip(struct MPContext *mpctx);

View File

@ -61,17 +61,6 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
style->treat_fontname_as_pattern = 1;
double fs = track->PlayResY * text_font_scale_factor / 100.;
/* The font size is always proportional to video height only;
* real -subfont-autoscale behavior is not implemented.
* Apply a correction that corresponds to about 4:3 aspect ratio
* video to get a size somewhat closer to what non-libass rendering
* would produce with the same text_font_scale_factor
* and subtitle_autoscale.
*/
if (subtitle_autoscale == 2)
fs *= 1.3;
else if (subtitle_autoscale == 3)
fs *= 1.7;
uint32_t c1 = 0xFFFFFF00;
uint32_t c2 = 0x00000000;
@ -228,7 +217,7 @@ ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname,
}
void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
struct mp_eosd_res *dim, bool unscaled)
struct mp_osd_res *dim)
{
ass_set_frame_size(priv, dim->w, dim->h);
ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr);
@ -243,10 +232,7 @@ void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
set_sub_pos = 100 - sub_pos;
set_line_spacing = opts->ass_line_spacing;
set_font_scale = opts->ass_font_scale;
if (!unscaled && (opts->ass_hinting & 4))
set_hinting = 0;
else
set_hinting = opts->ass_hinting & 3;
set_hinting = opts->ass_hinting & 3; // +4 was for no hinting if scaled
}
ass_set_use_margins(priv, set_use_margins);
@ -281,6 +267,41 @@ void mp_ass_configure_fonts(ASS_Renderer *priv)
free(family);
}
void mp_ass_render_frame(ASS_Renderer *renderer, ASS_Track *track, double time,
struct sub_bitmap **parts, struct sub_bitmaps *res)
{
int changed;
ASS_Image *imgs = ass_render_frame(renderer, track, time, &changed);
if (changed == 2)
res->bitmap_id = ++res->bitmap_pos_id;
else if (changed)
res->bitmap_pos_id++;
res->format = SUBBITMAP_LIBASS;
res->parts = *parts;
res->num_parts = 0;
int num_parts_alloc = MP_TALLOC_ELEMS(res->parts);
for (struct ass_image *img = imgs; img; img = img->next) {
if (img->w == 0 || img->h == 0)
continue;
if (res->num_parts >= num_parts_alloc) {
num_parts_alloc = FFMAX(num_parts_alloc * 2, 32);
res->parts = talloc_realloc(NULL, res->parts, struct sub_bitmap,
num_parts_alloc);
}
struct sub_bitmap *p = &res->parts[res->num_parts];
p->bitmap = img->bitmap;
p->stride = img->stride;
p->libass.color = img->color;
p->dw = p->w = img->w;
p->dh = p->h = img->h;
p->x = img->dst_x;
p->y = img->dst_y;
res->num_parts++;
}
*parts = res->parts;
}
static int map_ass_level[] = {
MSGL_ERR, // 0 "FATAL errors"
MSGL_WARN,

View File

@ -32,7 +32,7 @@
#include <ass/ass_types.h>
struct MPOpts;
struct mp_eosd_res;
struct mp_osd_res;
ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts);
ASS_Track *mp_ass_read_subdata(ASS_Library *library, struct MPOpts *opts,
@ -42,13 +42,18 @@ ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname,
struct MPOpts;
void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
struct mp_eosd_res *dim, bool unscaled);
struct mp_osd_res *dim);
void mp_ass_configure_fonts(ASS_Renderer *priv);
ASS_Library *mp_ass_init(struct MPOpts *opts);
struct sub_bitmap;
struct sub_bitmaps;
void mp_ass_render_frame(ASS_Renderer *renderer, ASS_Track *track, double time,
struct sub_bitmap **parts, struct sub_bitmaps *res);
#else /* CONFIG_ASS */
/* Needed for EOSD code using this type to compile */
/* Needed for OSD code using this type to compile */
typedef struct ass_image {
int w, h;

View File

@ -39,13 +39,13 @@ void sub_init(struct sh_sub *sh, struct osd_state *osd)
if (opts->ass_enabled && is_text_sub(sh->type))
sh->sd_driver = &sd_ass;
#endif
if (strchr("bpx", sh->type))
if (strchr("bpxv", sh->type))
sh->sd_driver = &sd_lavc;
if (sh->sd_driver) {
if (sh->sd_driver->init(sh, osd) < 0)
return;
osd->sh_sub = sh;
osd->bitmap_id = ++osd->bitmap_pos_id;
osd->switch_sub_id++;
sh->initialized = true;
sh->active = true;
}
@ -58,13 +58,12 @@ void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data,
sh->sd_driver->decode(sh, osd, data, data_len, pts, duration);
}
void sub_get_bitmaps(struct osd_state *osd, struct sub_bitmaps *res)
void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
struct MPOpts *opts = osd->opts;
*res = (struct sub_bitmaps){ .type = SUBBITMAP_EMPTY,
.bitmap_id = osd->bitmap_id,
.bitmap_pos_id = osd->bitmap_pos_id };
*res = (struct sub_bitmaps) {0};
if (!opts->sub_visibility || !osd->sh_sub || !osd->sh_sub->active) {
/* Change ID in case we just switched from visible subtitles
* to current state. Hopefully, unnecessarily claiming that
@ -72,14 +71,15 @@ void sub_get_bitmaps(struct osd_state *osd, struct sub_bitmaps *res)
* Increase osd-> values ahead so that _next_ returned id
* is also guaranteed to differ from this one.
*/
res->bitmap_id = ++res->bitmap_pos_id;
osd->bitmap_id = osd->bitmap_pos_id += 2;
return;
osd->switch_sub_id++;
} else {
if (osd->sh_sub->sd_driver->get_bitmaps)
osd->sh_sub->sd_driver->get_bitmaps(osd->sh_sub, osd, dim, pts, res);
}
if (osd->sh_sub->sd_driver->get_bitmaps)
osd->sh_sub->sd_driver->get_bitmaps(osd->sh_sub, osd, res);
osd->bitmap_id = res->bitmap_id;
osd->bitmap_pos_id = res->bitmap_pos_id;
res->bitmap_id += osd->switch_sub_id;
res->bitmap_pos_id += osd->switch_sub_id;
osd->switch_sub_id = 0;
}
void sub_reset(struct sh_sub *sh, struct osd_state *osd)

View File

@ -1,38 +1,14 @@
#ifndef MPLAYER_DEC_SUB_H
#define MPLAYER_DEC_SUB_H
#include <stdbool.h>
#include <stdint.h>
#include "sub/sub.h"
struct sh_sub;
struct osd_state;
struct ass_track;
enum sub_bitmap_type {
SUBBITMAP_EMPTY,
SUBBITMAP_LIBASS,
SUBBITMAP_RGBA,
};
typedef struct mp_eosd_res {
int w, h; // screen dimensions, including black borders
int mt, mb, ml, mr; // borders (top, bottom, left, right)
} mp_eosd_res_t;
typedef struct sub_bitmaps {
enum sub_bitmap_type type;
struct ass_image *imgs;
struct sub_bitmap {
int w, h;
int x, y;
// Note: not clipped, going outside the screen area is allowed
int dw, dh;
void *bitmap;
} *parts;
int part_count;
bool scaled;
int bitmap_id, bitmap_pos_id;
} mp_eosd_images_t;
struct MPOpts;
static inline bool is_text_sub(int type)
{
@ -41,7 +17,8 @@ static inline bool is_text_sub(int type)
void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data,
int data_len, double pts, double duration);
void sub_get_bitmaps(struct osd_state *osd, struct sub_bitmaps *res);
void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
void sub_init(struct sh_sub *sh, struct osd_state *osd);
void sub_reset(struct sh_sub *sh, struct osd_state *osd);
void sub_switchoff(struct sh_sub *sh, struct osd_state *osd);

530
sub/draw_bmp.c Normal file
View File

@ -0,0 +1,530 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stddef.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include <inttypes.h>
#include <libavutil/common.h>
#include "mpcommon.h"
#include "sub/draw_bmp.h"
#include "sub/sub.h"
#include "libmpcodecs/mp_image.h"
#include "libmpcodecs/sws_utils.h"
#include "libmpcodecs/img_format.h"
#include "libvo/csputils.h"
const bool mp_draw_sub_formats[SUBBITMAP_COUNT] = {
[SUBBITMAP_LIBASS] = true,
[SUBBITMAP_RGBA] = true,
};
struct sub_cache {
struct mp_image *i, *a;
};
struct part {
int bitmap_pos_id;
int imgfmt;
enum mp_csp colorspace;
enum mp_csp_levels levels;
int num_imgs;
struct sub_cache *imgs;
};
struct mp_draw_sub_cache
{
struct part *parts[MAX_OSD_PARTS];
};
static struct part *get_cache(struct mp_draw_sub_cache **cache,
struct sub_bitmaps *sbs, struct mp_image *format);
static bool get_sub_area(struct mp_rect bb, struct mp_image *temp,
struct sub_bitmap *sb, struct mp_image *out_area,
int *out_src_x, int *out_src_y);
#define ACCURATE
#define CONDITIONAL
static void blend_const16_alpha(void *dst, int dst_stride, uint16_t srcp,
uint8_t *srca, int srca_stride, uint8_t srcamul,
int w, int h)
{
if (!srcamul)
return;
for (int y = 0; y < h; y++) {
uint16_t *dst_r = (uint16_t *)((uint8_t *)dst + dst_stride * y);
uint8_t *srca_r = srca + srca_stride * y;
for (int x = 0; x < w; x++) {
uint32_t srcap = srca_r[x];
#ifdef CONDITIONAL
if (!srcap)
continue;
#endif
srcap *= srcamul; // now 0..65025
dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025;
}
}
}
static void blend_const8_alpha(void *dst, int dst_stride, uint16_t srcp,
uint8_t *srca, int srca_stride, uint8_t srcamul,
int w, int h)
{
if (!srcamul)
return;
for (int y = 0; y < h; y++) {
uint8_t *dst_r = (uint8_t *)dst + dst_stride * y;
uint8_t *srca_r = srca + srca_stride * y;
for (int x = 0; x < w; x++) {
uint32_t srcap = srca_r[x];
#ifdef CONDITIONAL
if (!srcap)
continue;
#endif
#ifdef ACCURATE
srcap *= srcamul; // now 0..65025
dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025;
#else
srcap = (srcap * srcamul + 255) >> 8;
dst_r[x] = (srcp * srcap + dst_r[x] * (255 - srcap) + 255) >> 8;
#endif
}
}
}
static void blend_const_alpha(void *dst, int dst_stride, int srcp,
uint8_t *srca, int srca_stride, uint8_t srcamul,
int w, int h, int bytes)
{
if (bytes == 2) {
blend_const16_alpha(dst, dst_stride, srcp, srca, srca_stride, srcamul,
w, h);
} else if (bytes == 1) {
blend_const8_alpha(dst, dst_stride, srcp, srca, srca_stride, srcamul,
w, h);
}
}
static void blend_src16_alpha(void *dst, int dst_stride, void *src,
int src_stride, uint8_t *srca, int srca_stride,
int w, int h)
{
for (int y = 0; y < h; y++) {
uint16_t *dst_r = (uint16_t *)((uint8_t *)dst + dst_stride * y);
uint16_t *src_r = (uint16_t *)((uint8_t *)src + src_stride * y);
uint8_t *srca_r = srca + srca_stride * y;
for (int x = 0; x < w; x++) {
uint32_t srcap = srca_r[x];
#ifdef CONDITIONAL
if (!srcap)
continue;
#endif
dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255;
}
}
}
static void blend_src8_alpha(void *dst, int dst_stride, void *src,
int src_stride, uint8_t *srca, int srca_stride,
int w, int h)
{
for (int y = 0; y < h; y++) {
uint8_t *dst_r = (uint8_t *)dst + dst_stride * y;
uint8_t *src_r = (uint8_t *)src + src_stride * y;
uint8_t *srca_r = srca + srca_stride * y;
for (int x = 0; x < w; x++) {
uint16_t srcap = srca_r[x];
#ifdef CONDITIONAL
if (!srcap)
continue;
#endif
#ifdef ACCURATE
dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255;
#else
dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 255) >> 8;
#endif
}
}
}
static void blend_src_alpha(void *dst, int dst_stride, void *src,
int src_stride, uint8_t *srca, int srca_stride,
int w, int h, int bytes)
{
if (bytes == 2) {
blend_src16_alpha(dst, dst_stride, src, src_stride, srca, srca_stride,
w, h);
} else if (bytes == 1) {
blend_src8_alpha(dst, dst_stride, src, src_stride, srca, srca_stride,
w, h);
}
}
static void unpremultiply_and_split_BGR32(struct mp_image *img,
struct mp_image *alpha)
{
for (int y = 0; y < img->h; ++y) {
uint32_t *irow = (uint32_t *) &img->planes[0][img->stride[0] * y];
uint8_t *arow = &alpha->planes[0][alpha->stride[0] * y];
for (int x = 0; x < img->w; ++x) {
uint32_t pval = irow[x];
uint8_t aval = (pval >> 24);
uint8_t rval = (pval >> 16) & 0xFF;
uint8_t gval = (pval >> 8) & 0xFF;
uint8_t bval = pval & 0xFF;
// multiplied = separate * alpha / 255
// separate = rint(multiplied * 255 / alpha)
// = floor(multiplied * 255 / alpha + 0.5)
// = floor((multiplied * 255 + 0.5 * alpha) / alpha)
// = floor((multiplied * 255 + floor(0.5 * alpha)) / alpha)
int div = (int) aval;
int add = div / 2;
if (aval) {
rval = FFMIN(255, (rval * 255 + add) / div);
gval = FFMIN(255, (gval * 255 + add) / div);
bval = FFMIN(255, (bval * 255 + add) / div);
irow[x] = bval + (gval << 8) + (rval << 16) + (aval << 24);
}
arow[x] = aval;
}
}
}
// dst_format merely contains the target colorspace/format information
static void scale_sb_rgba(struct sub_bitmap *sb, struct mp_image *dst_format,
struct mp_image **out_sbi, struct mp_image **out_sba)
{
struct mp_image *sbisrc = new_mp_image(sb->w, sb->h);
mp_image_setfmt(sbisrc, IMGFMT_BGR32);
sbisrc->planes[0] = sb->bitmap;
sbisrc->stride[0] = sb->stride;
struct mp_image *sbisrc2 = alloc_mpi(sb->dw, sb->dh, IMGFMT_BGR32);
mp_image_swscale(sbisrc2, sbisrc, SWS_BILINEAR);
struct mp_image *sba = alloc_mpi(sb->dw, sb->dh, IMGFMT_Y8);
unpremultiply_and_split_BGR32(sbisrc2, sba);
struct mp_image *sbi = alloc_mpi(sb->dw, sb->dh, dst_format->imgfmt);
sbi->colorspace = dst_format->colorspace;
sbi->levels = dst_format->levels;
mp_image_swscale(sbi, sbisrc2, SWS_BILINEAR);
free_mp_image(sbisrc);
free_mp_image(sbisrc2);
*out_sbi = sbi;
*out_sba = sba;
}
static void draw_rgba(struct mp_draw_sub_cache **cache, struct mp_rect bb,
struct mp_image *temp, int bits,
struct sub_bitmaps *sbs)
{
struct part *part = get_cache(cache, sbs, temp);
for (int i = 0; i < sbs->num_parts; ++i) {
struct sub_bitmap *sb = &sbs->parts[i];
if (sb->w < 1 || sb->h < 1)
continue;
struct mp_image dst;
int src_x, src_y;
if (!get_sub_area(bb, temp, sb, &dst, &src_x, &src_y))
continue;
struct mp_image *sbi = NULL;
struct mp_image *sba = NULL;
if (part) {
sbi = part->imgs[i].i;
sba = part->imgs[i].a;
}
if (!(sbi && sba))
scale_sb_rgba(sb, temp, &sbi, &sba);
int bytes = (bits + 7) / 8;
uint8_t *alpha_p = sba->planes[0] + src_y * sba->stride[0] + src_x;
for (int p = 0; p < 3; p++) {
void *src = sbi->planes[p] + src_y * sbi->stride[p] + src_x * bytes;
blend_src_alpha(dst.planes[p], dst.stride[p], src, sbi->stride[p],
alpha_p, sba->stride[0], dst.w, dst.h, bytes);
}
if (part) {
part->imgs[i].i = talloc_steal(part, sbi);
part->imgs[i].a = talloc_steal(part, sba);
} else {
free_mp_image(sbi);
free_mp_image(sba);
}
}
}
static void draw_ass(struct mp_draw_sub_cache **cache, struct mp_rect bb,
struct mp_image *temp, int bits, struct sub_bitmaps *sbs)
{
struct mp_csp_params cspar = MP_CSP_PARAMS_DEFAULTS;
cspar.colorspace.format = temp->colorspace;
cspar.colorspace.levels_in = temp->levels;
cspar.colorspace.levels_out = MP_CSP_LEVELS_PC; // RGB (libass.color)
cspar.int_bits_in = bits;
cspar.int_bits_out = 8;
float yuv2rgb[3][4], rgb2yuv[3][4];
mp_get_yuv2rgb_coeffs(&cspar, yuv2rgb);
mp_invert_yuv2rgb(rgb2yuv, yuv2rgb);
for (int i = 0; i < sbs->num_parts; ++i) {
struct sub_bitmap *sb = &sbs->parts[i];
struct mp_image dst;
int src_x, src_y;
if (!get_sub_area(bb, temp, sb, &dst, &src_x, &src_y))
continue;
int r = (sb->libass.color >> 24) & 0xFF;
int g = (sb->libass.color >> 16) & 0xFF;
int b = (sb->libass.color >> 8) & 0xFF;
int a = 255 - (sb->libass.color & 0xFF);
int color_yuv[3] = {r, g, b};
mp_map_int_color(rgb2yuv, bits, color_yuv);
int bytes = (bits + 7) / 8;
uint8_t *alpha_p = (uint8_t *)sb->bitmap + src_y * sb->stride + src_x;
for (int p = 0; p < 3; p++) {
blend_const_alpha(dst.planes[p], dst.stride[p], color_yuv[p],
alpha_p, sb->stride, a, dst.w, dst.h, bytes);
}
}
}
static void mp_image_crop(struct mp_image *img, struct mp_rect rc)
{
for (int p = 0; p < img->num_planes; ++p) {
int bits = MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(img, p);
img->planes[p] +=
(rc.y0 >> (p ? img->chroma_y_shift : 0)) * img->stride[p] +
(rc.x0 >> (p ? img->chroma_x_shift : 0)) * bits / 8;
}
img->w = rc.x1 - rc.x0;
img->h = rc.y1 - rc.y0;
img->chroma_width = img->w >> img->chroma_x_shift;
img->chroma_height = img->h >> img->chroma_y_shift;
img->display_w = img->display_h = 0;
}
static bool clip_to_bb(struct mp_rect bb, struct mp_rect *rc)
{
rc->x0 = FFMAX(bb.x0, rc->x0);
rc->y0 = FFMAX(bb.y0, rc->y0);
rc->x1 = FFMIN(bb.x1, rc->x1);
rc->y1 = FFMIN(bb.y1, rc->y1);
return rc->x1 > rc->x0 && rc->y1 > rc->y0;
}
static void get_swscale_alignment(const struct mp_image *img, int *out_xstep,
int *out_ystep)
{
int sx = (1 << img->chroma_x_shift);
int sy = (1 << img->chroma_y_shift);
// Hack for IMGFMT_Y8
if (img->chroma_x_shift == 31 && img->chroma_y_shift == 31) {
sx = 1;
sy = 1;
}
for (int p = 0; p < img->num_planes; ++p) {
int bits = MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(img, p);
// the * 2 fixes problems with writing past the destination width
while (((sx >> img->chroma_x_shift) * bits) % (SWS_MIN_BYTE_ALIGN * 8 * 2))
sx *= 2;
}
*out_xstep = sx;
*out_ystep = sy;
}
static void align_bbox(int xstep, int ystep, struct mp_rect *rc)
{
rc->x0 = rc->x0 & ~(xstep - 1);
rc->y0 = rc->y0 & ~(ystep - 1);
rc->x1 = FFALIGN(rc->x1, xstep);
rc->y1 = FFALIGN(rc->y1, ystep);
}
static bool align_bbox_for_swscale(struct mp_image *img, struct mp_rect *rc)
{
struct mp_rect img_rect = {0, 0, img->w, img->h};
// Get rid of negative coordinates
if (!clip_to_bb(img_rect, rc))
return false;
int xstep, ystep;
get_swscale_alignment(img, &xstep, &ystep);
align_bbox(xstep, ystep, rc);
return clip_to_bb(img_rect, rc);
}
// Try to find best/closest YUV 444 format for imgfmt
static void get_closest_y444_format(int imgfmt, int *out_format, int *out_bits)
{
#ifdef ACCURATE
struct mp_image tmp = {0};
mp_image_setfmt(&tmp, imgfmt);
if (tmp.flags & MP_IMGFLAG_YUV) {
int bits;
if (mp_get_chroma_shift(imgfmt, NULL, NULL, &bits)) {
switch (bits) {
case 8:
*out_format = IMGFMT_444P;
*out_bits = 8;
return;
case 9:
*out_format = IMGFMT_444P9;
*out_bits = 9;
return;
case 10:
*out_format = IMGFMT_444P10;
*out_bits = 10;
return;
}
}
}
*out_format = IMGFMT_444P16;
*out_bits = 16;
#else
*out_format = IMGFMT_444P;
*out_bits = 8;
#endif
}
static struct part *get_cache(struct mp_draw_sub_cache **cache,
struct sub_bitmaps *sbs, struct mp_image *format)
{
if (cache && !*cache)
*cache = talloc_zero(NULL, struct mp_draw_sub_cache);
struct part *part = NULL;
bool use_cache = sbs->format == SUBBITMAP_RGBA;
if (cache && use_cache) {
part = (*cache)->parts[sbs->render_index];
if (part) {
if (part->bitmap_pos_id != sbs->bitmap_pos_id
|| part->imgfmt != format->imgfmt
|| part->colorspace != format->colorspace
|| part->levels != format->levels)
{
talloc_free(part);
part = NULL;
}
}
if (!part) {
part = talloc(*cache, struct part);
*part = (struct part) {
.bitmap_pos_id = sbs->bitmap_pos_id,
.num_imgs = sbs->num_parts,
.imgfmt = format->imgfmt,
.levels = format->levels,
.colorspace = format->colorspace,
};
part->imgs = talloc_zero_array(part, struct sub_cache,
part->num_imgs);
}
assert(part->num_imgs == sbs->num_parts);
(*cache)->parts[sbs->render_index] = part;
}
return part;
}
// Return area of intersection between target and sub-bitmap as cropped image
static bool get_sub_area(struct mp_rect bb, struct mp_image *temp,
struct sub_bitmap *sb, struct mp_image *out_area,
int *out_src_x, int *out_src_y)
{
// coordinates are relative to the bbox
struct mp_rect dst = {sb->x - bb.x0, sb->y - bb.y0};
dst.x1 = dst.x0 + sb->dw;
dst.y1 = dst.y0 + sb->dh;
if (!clip_to_bb((struct mp_rect){0, 0, temp->w, temp->h}, &dst))
return false;
*out_src_x = (dst.x0 - sb->x) + bb.x0;
*out_src_y = (dst.y0 - sb->y) + bb.y0;
*out_area = *temp;
mp_image_crop(out_area, dst);
return true;
}
// cache: if not NULL, the function will set *cache to a talloc-allocated cache
// containing scaled versions of sbs contents - free the cache with
// talloc_free()
void mp_draw_sub_bitmaps(struct mp_draw_sub_cache **cache, struct mp_image *dst,
struct sub_bitmaps *sbs)
{
assert(mp_draw_sub_formats[sbs->format]);
if (!mp_sws_supported_format(dst->imgfmt))
return;
int format, bits;
get_closest_y444_format(dst->imgfmt, &format, &bits);
struct mp_rect bb;
if (!sub_bitmaps_bb(sbs, &bb))
return;
if (!align_bbox_for_swscale(dst, &bb))
return;
struct mp_image *temp;
struct mp_image dst_region = *dst;
mp_image_crop(&dst_region, bb);
if (dst->imgfmt == format) {
temp = &dst_region;
} else {
temp = alloc_mpi(bb.x1 - bb.x0, bb.y1 - bb.y0, format);
// temp is always YUV, dst_region not
// reduce amount of conversions in YUV case (upsampling/shifting only)
if (dst_region.flags & MP_IMGFLAG_YUV) {
temp->colorspace = dst_region.colorspace;
temp->levels = dst_region.levels;
}
mp_image_swscale(temp, &dst_region, SWS_POINT); // chroma up
}
if (sbs->format == SUBBITMAP_RGBA) {
draw_rgba(cache, bb, temp, bits, sbs);
} else if (sbs->format == SUBBITMAP_LIBASS) {
draw_ass(cache, bb, temp, bits, sbs);
}
if (temp != &dst_region) {
mp_image_swscale(&dst_region, temp, SWS_AREA); // chroma down
free_mp_image(temp);
}
}
// vim: ts=4 sw=4 et tw=80

17
sub/draw_bmp.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef MPLAYER_DRAW_BMP_H
#define MPLAYER_DRAW_BMP_H
#include "sub/sub.h"
struct mp_image;
struct sub_bitmaps;
struct mp_csp_details;
struct mp_draw_sub_cache;
void mp_draw_sub_bitmaps(struct mp_draw_sub_cache **cache, struct mp_image *dst,
struct sub_bitmaps *sbs);
extern const bool mp_draw_sub_formats[SUBBITMAP_COUNT];
#endif /* MPLAYER_DRAW_BMP_H */
// vim: ts=4 sw=4 et tw=80

View File

@ -12,6 +12,7 @@
#include "mpcommon.h"
#include "sub/find_subfiles.h"
#include "sub/sub.h"
#include "sub/subreader.h"
static struct bstr strip_ext(struct bstr str)
{

89
sub/img_convert.c Normal file
View File

@ -0,0 +1,89 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <assert.h>
#include <libavutil/mem.h>
#include <libavutil/common.h>
#include "talloc.h"
#include "img_convert.h"
#include "sub.h"
#include "spudec.h"
#include "libmpcodecs/img_format.h"
#include "libmpcodecs/mp_image.h"
#include "libmpcodecs/sws_utils.h"
struct osd_conv_cache {
struct sub_bitmap part;
struct sub_bitmap *parts;
};
struct osd_conv_cache *osd_conv_cache_new(void)
{
return talloc_zero(NULL, struct osd_conv_cache);
}
static void rgba_to_premultiplied_rgba(uint32_t *colors, size_t count)
{
for (int n = 0; n < count; n++) {
uint32_t c = colors[n];
int b = c & 0xFF;
int g = (c >> 8) & 0xFF;
int r = (c >> 16) & 0xFF;
int a = (c >> 24) & 0xFF;
b = b * a / 255;
g = g * a / 255;
r = r * a / 255;
colors[n] = b | (g << 8) | (r << 16) | (a << 24);
}
}
bool osd_conv_idx_to_rgba(struct osd_conv_cache *c, struct sub_bitmaps *imgs)
{
struct sub_bitmaps src = *imgs;
if (src.format != SUBBITMAP_INDEXED)
return false;
imgs->format = SUBBITMAP_RGBA;
talloc_free(c->parts);
imgs->parts = c->parts = talloc_array(c, struct sub_bitmap, src.num_parts);
for (int n = 0; n < src.num_parts; n++) {
struct sub_bitmap *d = &imgs->parts[n];
struct sub_bitmap *s = &src.parts[n];
struct osd_bmp_indexed sb = *(struct osd_bmp_indexed *)s->bitmap;
rgba_to_premultiplied_rgba(sb.palette, 256);
*d = *s;
struct mp_image *image = alloc_mpi(s->w, s->h, IMGFMT_BGRA);
talloc_steal(c->parts, image);
d->stride = image->stride[0];
d->bitmap = image->planes[0];
for (int y = 0; y < s->h; y++) {
uint8_t *inbmp = sb.bitmap + y * s->stride;
uint32_t *outbmp = (uint32_t*)((uint8_t*)d->bitmap + y * d->stride);
for (int x = 0; x < s->w; x++)
*outbmp++ = sb.palette[*inbmp++];
}
}
return true;
}

15
sub/img_convert.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef MPLAYER_SUB_IMG_CONVERT_H
#define MPLAYER_SUB_IMG_CONVERT_H
#include <stdbool.h>
struct osd_conv_cache;
struct sub_bitmaps;
struct osd_conv_cache *osd_conv_cache_new(void);
// These functions convert from one OSD format to another. On success, they copy
// the converted image data into c, and change imgs to point to the data.
bool osd_conv_idx_to_rgba(struct osd_conv_cache *c, struct sub_bitmaps *imgs);
#endif

View File

@ -6,18 +6,6 @@
#include "talloc.h"
#include "sub.h"
void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t *obj)
{
}
void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t *obj)
{
}
void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t *obj)
{
}
void osd_init_backend(struct osd_state *osd)
{
}
@ -26,14 +14,12 @@ void osd_destroy_backend(struct osd_state *osd)
{
}
void osd_font_invalidate(void)
{
}
void osd_font_load(struct osd_state *osd)
{
}
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function)
{
}
void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj,
struct sub_bitmaps *out_imgs)
{
*out_imgs = (struct sub_bitmaps) {0};
}

View File

@ -35,9 +35,6 @@ static const char osd_font_pfb[] =
#include "mp_core.h"
// Map OSD symbols (e.g. OSD_PLAY) to the glyphs in osd_font_pfb[].
#define OSD_CODEPOINTS 0xE000
// NOTE: \fs-5 to reduce the size of the symbols in relation to normal text.
// Done because libass doesn't center characters that are too high.
#define ASS_USE_OSD_FONT "{\\fnOSD\\fs-5}"
@ -55,133 +52,20 @@ void osd_init_backend(struct osd_state *osd)
void osd_destroy_backend(struct osd_state *osd)
{
if (osd) {
if (osd->osd_render)
ass_renderer_done(osd->osd_render);
osd->osd_render = NULL;
ass_library_done(osd->osd_ass_library);
osd->osd_ass_library = NULL;
}
if (osd->osd_render)
ass_renderer_done(osd->osd_render);
osd->osd_render = NULL;
ass_library_done(osd->osd_ass_library);
osd->osd_ass_library = NULL;
}
static void eosd_draw_alpha_a8i8(unsigned char *src,
int src_w, int src_h,
int src_stride,
unsigned char *dst_a,
unsigned char *dst_i,
size_t dst_stride,
int dst_x, int dst_y,
uint32_t color)
static void update_font_style(ASS_Track *track, ASS_Style *style, double factor)
{
const unsigned int r = (color >> 24) & 0xff;
const unsigned int g = (color >> 16) & 0xff;
const unsigned int b = (color >> 8) & 0xff;
const unsigned int a = 0xff - (color & 0xff);
// Set to neutral base direction, as opposed to VSFilter LTR default
style->Encoding = -1;
int gray = (r + g + b) / 3; // not correct
dst_a += dst_y * dst_stride + dst_x;
dst_i += dst_y * dst_stride + dst_x;
int src_skip = src_stride - src_w;
int dst_skip = dst_stride - src_w;
for (int y = 0; y < src_h; y++) {
for (int x = 0; x < src_w; x++) {
unsigned char as = (*src * a) >> 8;
unsigned char bs = (gray * as) >> 8;
// to mplayer scale
as = -as;
unsigned char *a = dst_a;
unsigned char *b = dst_i;
// NOTE: many special cases, because alpha=0 means transparency,
// while alpha=1..255 is opaque..transparent
if (as) {
*b = ((*b * as) >> 8) + bs;
if (*a) {
*a = (*a * as) >> 8;
if (*a < 1)
*a = 1;
} else {
*a = as;
}
}
dst_a++;
dst_i++;
src++;
}
dst_a += dst_skip;
dst_i += dst_skip;
src += src_skip;
}
}
static void eosd_render_a8i8(unsigned char *a, unsigned char *i, size_t stride,
int x, int y, ASS_Image *imgs)
{
for (ASS_Image *p = imgs; p; p = p->next) {
eosd_draw_alpha_a8i8(p->bitmap, p->w, p->h, p->stride, a, i, stride,
x + p->dst_x, y + p->dst_y, p->color);
}
}
static bool ass_bb(ASS_Image *imgs, int *x1, int *y1, int *x2, int *y2)
{
*x1 = *y1 = INT_MAX;
*x2 = *y2 = INT_MIN;
for (ASS_Image *p = imgs; p; p = p->next) {
*x1 = FFMIN(*x1, p->dst_x);
*y1 = FFMIN(*y1, p->dst_y);
*x2 = FFMAX(*x2, p->dst_x + p->w);
*y2 = FFMAX(*y2, p->dst_y + p->h);
}
return *x1 < *x2 && *y1 < *y2;
}
static void draw_ass_osd(struct osd_state *osd, mp_osd_obj_t *obj)
{
ass_set_frame_size(osd->osd_render, osd->w, osd->h);
ASS_Image *imgs = ass_render_frame(osd->osd_render, obj->osd_track, 0,
NULL);
int x1, y1, x2, y2;
if (!ass_bb(imgs, &x1, &y1, &x2, &y2)) {
obj->flags &= ~OSDFLAG_VISIBLE;
return;
}
obj->bbox.x1 = x1;
obj->bbox.y1 = y1;
obj->bbox.x2 = x2;
obj->bbox.y2 = y2;
obj->flags |= OSDFLAG_BBOX;
osd_alloc_buf(obj);
eosd_render_a8i8(obj->alpha_buffer, obj->bitmap_buffer, obj->stride,
-x1, -y1, imgs);
}
static void update_font_scale(ASS_Track *track, ASS_Style *style, double factor)
{
// duplicated from ass_mp.c
double fs = track->PlayResY * factor / 100.;
/* The font size is always proportional to video height only;
* real -subfont-autoscale behavior is not implemented.
* Apply a correction that corresponds to about 4:3 aspect ratio
* video to get a size somewhat closer to what non-libass rendering
* would produce with the same text_font_scale_factor
* and subtitle_autoscale.
*/
if (subtitle_autoscale == 2)
fs *= 1.3;
else if (subtitle_autoscale == 3)
fs *= 1.7;
style->FontSize = fs;
style->FontSize = track->PlayResY * factor / 100.;
style->Outline = style->FontSize / 16;
}
@ -193,7 +77,7 @@ static ASS_Track *create_osd_ass_track(struct osd_state *osd)
track->PlayResX = track->PlayResY * 1.33333;
update_font_scale(track, style, text_font_scale_factor);
update_font_style(track, style, text_font_scale_factor);
style->Alignment = 5;
@ -211,9 +95,16 @@ static ASS_Event *get_osd_ass_event(ASS_Track *track)
event->Start = 0;
event->Duration = 100;
event->Style = track->default_style;
assert(event->Text == NULL);
return event;
}
static void clear_obj(struct osd_object *obj)
{
if (obj->osd_track)
ass_flush_events(obj->osd_track);
}
static char *append_utf8_buffer(char *buffer, uint32_t codepoint)
{
char data[8];
@ -252,25 +143,27 @@ static char *mangle_ass(const char *in)
return res;
}
void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t* obj)
static void update_osd(struct osd_state *osd, struct osd_object *obj)
{
if (!osd->osd_text[0]) {
clear_obj(obj);
return;
}
if (!obj->osd_track)
obj->osd_track = create_osd_ass_track(osd);
ASS_Event *event = get_osd_ass_event(obj->osd_track);
event->Text = mangle_ass(osd->osd_text);
draw_ass_osd(osd, obj);
talloc_free(event->Text);
event->Text = NULL;
char *text = mangle_ass(osd->osd_text);
event->Text = strdup(text);
talloc_free(text);
}
#define OSDBAR_ELEMS 46
void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t* obj)
static void update_progbar(struct osd_state *osd, struct osd_object *obj)
{
obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE;
if (vo_osd_progbar_type < 0) {
obj->flags &= ~OSDFLAG_VISIBLE;
if (osd->progbar_type < 0) {
clear_obj(obj);
return;
}
@ -286,22 +179,22 @@ void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t* obj)
// Assume the OSD bar takes 2/3 of the OSD width at PlayResY=288 and
// FontSize=22 with an OSD aspect ratio of 16:9. Rescale as needed.
// xxx can fail when unknown fonts are involved
double asp = (double)osd->w / osd->h;
double asp = (double)obj->vo_res.w / obj->vo_res.h;
double scale = (asp / 1.77777) * (obj->osd_track->PlayResY / 288.0);
style->ScaleX = style->ScaleY = scale;
style->FontSize = 22.0;
style->Outline = style->FontSize / 16 * scale;
int active = (vo_osd_progbar_value * OSDBAR_ELEMS + 255) / 256;
int active = (osd->progbar_value * OSDBAR_ELEMS + 255) / 256;
active = FFMIN(OSDBAR_ELEMS, FFMAX(active, 0));
char *text = talloc_strdup(NULL, "{\\q2}");
if (vo_osd_progbar_type >= 32) {
text = append_utf8_buffer(text, vo_osd_progbar_type);
} else if (vo_osd_progbar_type > 0) {
if (osd->progbar_type >= 32) {
text = append_utf8_buffer(text, osd->progbar_type);
} else if (osd->progbar_type > 0) {
text = talloc_strdup_append_buffer(text, ASS_USE_OSD_FONT);
text = append_utf8_buffer(text, OSD_CODEPOINTS + vo_osd_progbar_type);
text = append_utf8_buffer(text, OSD_CODEPOINTS + osd->progbar_type);
text = talloc_strdup_append_buffer(text, "{\\r}");
}
@ -317,21 +210,16 @@ void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t* obj)
text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_END);
ASS_Event *event = get_osd_ass_event(obj->osd_track);
event->Text = text;
draw_ass_osd(osd, obj);
event->Text = NULL;
event->Text = strdup(text);
talloc_free(text);
}
void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj)
static void update_sub(struct osd_state *osd, struct osd_object *obj)
{
struct MPOpts *opts = osd->opts;
obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE;
if (!vo_sub || !opts->sub_visibility) {
obj->flags &= ~OSDFLAG_VISIBLE;
if (!(vo_sub && opts->sub_visibility)) {
clear_obj(obj);
return;
}
@ -340,7 +228,7 @@ void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj)
ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
update_font_scale(obj->osd_track, style, text_font_scale_factor);
update_font_style(obj->osd_track, style, text_font_scale_factor);
#if LIBASS_VERSION >= 0x01010000
ass_set_line_position(osd->osd_render, 100 - sub_pos);
#endif
@ -351,14 +239,40 @@ void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj)
text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]);
ASS_Event *event = get_osd_ass_event(obj->osd_track);
event->Text = mangle_ass(text);
draw_ass_osd(osd, obj);
talloc_free(event->Text);
event->Text = NULL;
char *escaped_text = mangle_ass(text);
event->Text = strdup(escaped_text);
talloc_free(escaped_text);
talloc_free(text);
}
// unneeded
void osd_font_invalidate(void) {}
void osd_font_load(struct osd_state *osd) {}
static void update_object(struct osd_state *osd, struct osd_object *obj)
{
switch (obj->type) {
case OSDTYPE_OSD:
update_osd(osd, obj);
break;
case OSDTYPE_SUBTITLE:
update_sub(osd, obj);
break;
case OSDTYPE_PROGBAR:
update_progbar(osd, obj);
break;
}
}
void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj,
struct sub_bitmaps *out_imgs)
{
if (obj->force_redraw)
update_object(osd, obj);
*out_imgs = (struct sub_bitmaps) {0};
if (!obj->osd_track)
return;
ass_set_frame_size(osd->osd_render, obj->vo_res.w, obj->vo_res.h);
ass_set_aspect_ratio(osd->osd_render, obj->vo_res.display_par, 1.0);
mp_ass_render_frame(osd->osd_render, obj->osd_track, 0,
&obj->parts_cache, out_imgs);
talloc_steal(obj, obj->parts_cache);
}

View File

@ -1,15 +1,14 @@
#ifndef MPLAYER_SD_H
#define MPLAYER_SD_H
struct osd_state;
struct sh_sub;
struct sub_bitmaps;
#include "dec_sub.h"
struct sd_functions {
int (*init)(struct sh_sub *sh, struct osd_state *osd);
void (*decode)(struct sh_sub *sh, struct osd_state *osd,
void *data, int data_len, double pts, double duration);
void (*get_bitmaps)(struct sh_sub *sh, struct osd_state *osd,
struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
void (*reset)(struct sh_sub *sh, struct osd_state *osd);
void (*switch_off)(struct sh_sub *sh, struct osd_state *osd);

View File

@ -28,6 +28,7 @@
#include "mp_msg.h"
#include "libmpdemux/stheader.h"
#include "sub.h"
#include "dec_sub.h"
#include "ass_mp.h"
#include "sd.h"
#include "subassconvert.h"
@ -36,6 +37,7 @@ struct sd_ass_priv {
struct ass_track *ass_track;
bool vsfilter_aspect;
bool incomplete_event;
struct sub_bitmap *parts;
};
static void free_last_event(ASS_Track *track)
@ -126,30 +128,26 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
}
static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
struct sd_ass_priv *ctx = sh->context;
struct MPOpts *opts = osd->opts;
if (osd->sub_pts == MP_NOPTS_VALUE)
if (pts == MP_NOPTS_VALUE)
return;
double scale = osd->normal_scale;
double scale = dim.display_par;
bool use_vs_aspect = opts->ass_style_override
? opts->ass_vsfilter_aspect_compat : 1;
if (ctx->vsfilter_aspect && use_vs_aspect)
scale = osd->vsfilter_scale;
scale = scale * dim.video_par;
ASS_Renderer *renderer = osd->ass_renderer;
mp_ass_configure(renderer, opts, &osd->dim, osd->unscaled);
mp_ass_configure(renderer, opts, &dim);
ass_set_aspect_ratio(renderer, scale, 1);
int changed;
res->imgs = ass_render_frame(renderer, ctx->ass_track,
osd->sub_pts * 1000 + .5, &changed);
if (changed == 2)
res->bitmap_id = ++res->bitmap_pos_id;
else if (changed)
res->bitmap_pos_id++;
res->type = SUBBITMAP_LIBASS;
mp_ass_render_frame(renderer, ctx->ass_track, pts * 1000 + .5,
&ctx->parts, res);
talloc_steal(ctx, ctx->parts);
}
static void reset(struct sh_sub *sh, struct osd_state *osd)

View File

@ -16,6 +16,7 @@
*/
#include <stdlib.h>
#include <assert.h>
#include <libavcodec/avcodec.h>
@ -23,58 +24,39 @@
#include "mp_msg.h"
#include "libmpdemux/stheader.h"
#include "sd.h"
#include "spudec.h"
// Current code still pushes subs directly to global spudec
#include "dec_sub.h"
#include "sub.h"
struct sd_lavc_priv {
AVCodecContext *avctx;
AVSubtitle sub;
bool have_sub;
int count;
struct sub_bitmap *inbitmaps;
struct sub_bitmap *outbitmaps;
struct osd_bmp_indexed *imgs;
bool bitmaps_changed;
double endpts;
};
static void old_avsub_to_spudec(AVSubtitleRect **rects, int num_rects,
double pts, double endpts)
static void guess_resolution(char type, int *w, int *h)
{
int i, xmin = INT_MAX, ymin = INT_MAX, xmax = INT_MIN, ymax = INT_MIN;
struct spu_packet_t *packet;
if (num_rects == 1) {
spudec_set_paletted(vo_spudec,
rects[0]->pict.data[0],
rects[0]->pict.linesize[0],
rects[0]->pict.data[1],
rects[0]->x,
rects[0]->y,
rects[0]->w,
rects[0]->h,
pts,
endpts);
return;
if (type == 'v') {
/* XXX Although the video frame is some size, the SPU frame is
always maximum size i.e. 720 wide and 576 or 480 high */
// For HD files in MKV the VobSub resolution can be higher though,
// see largeres_vobsub.mkv
if (*w <= 720 && *h <= 576) {
*w = 720;
*h = (*h == 480 || *h == 240) ? 480 : 576;
}
} else {
// Hope that PGS subs set these and 720/576 works for dvb subs
if (!*w)
*w = 720;
if (!*h)
*h = 576;
}
for (i = 0; i < num_rects; i++) {
xmin = FFMIN(xmin, rects[i]->x);
ymin = FFMIN(ymin, rects[i]->y);
xmax = FFMAX(xmax, rects[i]->x + rects[i]->w);
ymax = FFMAX(ymax, rects[i]->y + rects[i]->h);
}
packet = spudec_packet_create(xmin, ymin, xmax - xmin, ymax - ymin);
if (!packet)
return;
spudec_packet_clear(packet);
for (i = 0; i < num_rects; i++)
spudec_packet_fill(packet,
rects[i]->pict.data[0],
rects[i]->pict.linesize[0],
rects[i]->pict.data[1],
rects[i]->x - xmin,
rects[i]->y - ymin,
rects[i]->w,
rects[i]->h);
spudec_packet_send(vo_spudec, packet, pts, endpts);
}
static int init(struct sh_sub *sh, struct osd_state *osd)
@ -122,8 +104,13 @@ static void clear(struct sd_lavc_priv *priv)
talloc_free(priv->inbitmaps);
talloc_free(priv->outbitmaps);
priv->inbitmaps = priv->outbitmaps = NULL;
talloc_free(priv->imgs);
priv->imgs = NULL;
priv->bitmaps_changed = true;
priv->endpts = MP_NOPTS_VALUE;
if (priv->have_sub)
avsubtitle_free(&priv->sub);
priv->have_sub = false;
}
static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
@ -145,6 +132,8 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt);
if (res < 0 || !got_sub)
return;
priv->sub = sub;
priv->have_sub = true;
if (pts != MP_NOPTS_VALUE) {
if (sub.end_display_time > sub.start_display_time)
duration = (sub.end_display_time - sub.start_display_time) / 1000.0;
@ -153,39 +142,27 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
double endpts = MP_NOPTS_VALUE;
if (pts != MP_NOPTS_VALUE && duration >= 0)
endpts = pts + duration;
if (vo_spudec && sub.num_rects == 0)
spudec_set_paletted(vo_spudec, NULL, 0, NULL, 0, 0, 0, 0, pts, endpts);
if (sub.num_rects > 0) {
switch (sub.rects[0]->type) {
case SUBTITLE_BITMAP:
// Assume resolution heuristics only work for PGS and DVB
if (!osd->support_rgba || sh->type != 'p' && sh->type != 'b') {
if (!vo_spudec)
vo_spudec = spudec_new_scaled(NULL, ctx->width, ctx->height,
NULL, 0);
old_avsub_to_spudec(sub.rects, sub.num_rects, pts, endpts);
vo_osd_changed(OSDTYPE_SPU);
break;
}
priv->inbitmaps = talloc_array(priv, struct sub_bitmap,
sub.num_rects);
priv->imgs = talloc_array(priv, struct osd_bmp_indexed,
sub.num_rects);
for (int i = 0; i < sub.num_rects; i++) {
struct AVSubtitleRect *r = sub.rects[i];
struct sub_bitmap *b = &priv->inbitmaps[i];
uint32_t *outbmp = talloc_size(priv->inbitmaps,
r->w * r->h * 4);
b->bitmap = outbmp;
struct osd_bmp_indexed *img = &priv->imgs[i];
img->bitmap = r->pict.data[0];
assert(r->nb_colors > 0);
assert(r->nb_colors * 4 <= sizeof(img->palette));
memcpy(img->palette, r->pict.data[1], r->nb_colors * 4);
b->bitmap = img;
b->stride = r->pict.linesize[0];
b->w = r->w;
b->h = r->h;
b->x = r->x;
b->y = r->y;
uint8_t *inbmp = r->pict.data[0];
uint32_t *palette = (uint32_t *) r->pict.data[1];
for (int y = 0; y < r->h; y++) {
for (int x = 0; x < r->w; x++)
*outbmp++ = palette[*inbmp++];
inbmp += r->pict.linesize[0] - r->w;
};
}
priv->count = sub.num_rects;
priv->endpts = endpts;
@ -196,50 +173,39 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
break;
}
}
avsubtitle_free(&sub);
}
static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
struct mp_osd_res d, double pts,
struct sub_bitmaps *res)
{
struct sd_lavc_priv *priv = sh->context;
if (priv->endpts != MP_NOPTS_VALUE && (osd->sub_pts >= priv->endpts ||
osd->sub_pts < priv->endpts - 300))
if (priv->endpts != MP_NOPTS_VALUE && (pts >= priv->endpts ||
pts < priv->endpts - 300))
clear(priv);
if (!osd->support_rgba)
return;
if (priv->bitmaps_changed && priv->count > 0)
priv->outbitmaps = talloc_memdup(priv, priv->inbitmaps,
talloc_get_size(priv->inbitmaps));
bool pos_changed = false;
// Hope that PGS subs set these and 720/576 works for dvb subs
int inw = priv->avctx->width;
if (!inw)
inw = 720;
int inh = priv->avctx->height;
if (!inh)
inh = 576;
struct mp_eosd_res *d = &osd->dim;
double xscale = (double) (d->w - d->ml - d->mr) / inw;
double yscale = (double) (d->h - d->mt - d->mb) / inh;
guess_resolution(sh->type, &inw, &inh);
double xscale = (double) (d.w - d.ml - d.mr) / inw;
double yscale = (double) (d.h - d.mt - d.mb) / inh;
for (int i = 0; i < priv->count; i++) {
struct sub_bitmap *bi = &priv->inbitmaps[i];
struct sub_bitmap *bo = &priv->outbitmaps[i];
#define SET(var, val) pos_changed |= var != (int)(val); var = (val)
SET(bo->x, bi->x * xscale + d->ml);
SET(bo->y, bi->y * yscale + d->mt);
SET(bo->dw, bi->w * xscale);
SET(bo->dh, bi->h * yscale);
bo->x = bi->x * xscale + d.ml;
bo->y = bi->y * yscale + d.mt;
bo->dw = bi->w * xscale;
bo->dh = bi->h * yscale;
}
res->parts = priv->outbitmaps;
res->part_count = priv->count;
res->num_parts = priv->count;
if (priv->bitmaps_changed)
res->bitmap_id = ++res->bitmap_pos_id;
else if (pos_changed)
res->bitmap_pos_id++;
priv->bitmaps_changed = false;
res->type = SUBBITMAP_RGBA;
res->format = SUBBITMAP_INDEXED;
res->scaled = xscale != 1 || yscale != 1;
}
@ -256,6 +222,7 @@ static void uninit(struct sh_sub *sh)
{
struct sd_lavc_priv *priv = sh->context;
clear(priv);
avcodec_close(priv->avctx);
av_free(priv->avctx);
talloc_free(priv);

View File

@ -34,30 +34,19 @@
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <libavutil/common.h>
#include <libavutil/intreadwrite.h>
#include <libswscale/swscale.h>
#include "config.h"
#include "mp_msg.h"
#include "spudec.h"
#include "vobsub.h"
#include "sub.h"
#include "mpcommon.h"
/* Valid values for spu_aamode:
0: none (fastest, most ugly)
1: approximate
2: full (slowest)
3: bilinear (similiar to vobsub, fast and not too bad)
4: uses swscaler gaussian (this is the only one that looks good)
*/
int spu_aamode = 3;
int spu_alignment = -1;
float spu_gaussvar = 1.0;
extern int sub_pos;
#include "libvo/csputils.h"
typedef struct spu_packet_t packet_t;
struct spu_packet_t {
@ -78,13 +67,6 @@ struct spu_packet_t {
packet_t *next;
};
struct palette_crop_cache {
int valid;
uint32_t palette;
int sx, sy, ex, ey;
int result;
};
typedef struct {
packet_t *queue_head;
packet_t *queue_tail;
@ -106,23 +88,17 @@ typedef struct {
unsigned int width, height, stride;
size_t image_size; /* Size of the image buffer */
unsigned char *image; /* Grayscale value */
unsigned char *aimage; /* Alpha value */
unsigned int pal_start_col, pal_start_row;
unsigned int pal_width, pal_height;
unsigned char *pal_image; /* palette entry value */
unsigned int scaled_frame_width, scaled_frame_height;
unsigned int scaled_start_col, scaled_start_row;
unsigned int scaled_width, scaled_height, scaled_stride;
size_t scaled_image_size;
unsigned char *scaled_image;
unsigned char *scaled_aimage;
int auto_palette; /* 1 if we lack a palette and must use an heuristic. */
int font_start_level; /* Darkest value used for the computed font */
int spu_changed;
unsigned int forced_subs_only; /* flag: 0=display all subtitle, !0 display only forced subtitles */
unsigned int is_forced_sub; /* true if current subtitle is a forced subtitle */
struct palette_crop_cache palette_crop_cache;
struct sub_bitmap sub_part, borrowed_sub_part;
struct osd_bmp_indexed borrowed_bmp;
} spudec_handle_t;
static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet)
@ -185,40 +161,6 @@ static inline unsigned char get_nibble(packet_t *packet)
return nib;
}
/* Cut the sub to visible part */
static inline void spudec_cut_image(spudec_handle_t *this)
{
unsigned int fy, ly;
unsigned int first_y, last_y;
if (this->stride == 0 || this->height == 0) {
return;
}
for (fy = 0; fy < this->image_size && !this->aimage[fy]; fy++);
for (ly = this->stride * this->height-1; ly && !this->aimage[ly]; ly--);
first_y = fy / this->stride;
last_y = ly / this->stride;
//printf("first_y: %d, last_y: %d\n", first_y, last_y);
this->start_row += first_y;
// Some subtitles trigger this condition
if (last_y + 1 > first_y ) {
this->height = last_y - first_y +1;
} else {
this->height = 0;
return;
}
// printf("new h %d new start %d (sz %d st %d)---\n\n", this->height, this->start_row, this->image_size, this->stride);
if (first_y > 0) {
memmove(this->image, this->image + this->stride * first_y, this->stride * this->height);
memmove(this->aimage, this->aimage + this->stride * first_y, this->stride * this->height);
}
}
static int spudec_alloc_image(spudec_handle_t *this, int stride, int height)
{
if (this->width > stride) // just a safeguard
@ -237,7 +179,6 @@ static int spudec_alloc_image(spudec_handle_t *this, int stride, int height)
this->image = malloc(2 * this->stride * this->height);
if (this->image) {
this->image_size = this->stride * this->height;
this->aimage = this->image + this->image_size;
// use stride here as well to simplify reallocation checks
this->pal_image = malloc(this->stride * this->height);
}
@ -245,100 +186,67 @@ static int spudec_alloc_image(spudec_handle_t *this, int stride, int height)
return this->image != NULL;
}
/**
* \param pal palette in MPlayer-style gray-alpha values, i.e.
* alpha == 0 means transparent, 1 fully opaque,
* gray value <= 256 - alpha.
*/
static void pal2gray_alpha(const uint16_t *pal,
const uint8_t *src, int src_stride,
uint8_t *dst, uint8_t *dsta,
int dst_stride, int w, int h)
static void setup_palette(spudec_handle_t *spu, uint32_t palette[256])
{
int x, y;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
uint16_t pixel = pal[src[x]];
*dst++ = pixel;
*dsta++ = pixel >> 8;
memset(palette, 0, sizeof(palette));
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
csp.int_bits_in = 8;
csp.int_bits_out = 8;
float cmatrix[3][4];
mp_get_yuv2rgb_coeffs(&csp, cmatrix);
for (int i = 0; i < 4; ++i) {
int alpha = spu->alpha[i];
// extend 4 -> 8 bit
alpha |= alpha << 4;
if (spu->custom && (spu->cuspal[i] >> 31) != 0)
alpha = 0;
int color = spu->custom ? spu->cuspal[i] :
spu->global_palette[spu->palette[i]];
int c[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff};
mp_map_int_color(cmatrix, 8, c);
// R and G swapped, possibly due to vobsub_palette_to_yuv()
palette[i] = (alpha << 24u) | (c[2] << 16) | (c[1] << 8) | c[0];
}
for (; x < dst_stride; x++)
*dsta++ = *dst++ = 0;
src += src_stride;
}
}
static int apply_palette_crop(spudec_handle_t *this,
unsigned crop_x, unsigned crop_y,
unsigned crop_w, unsigned crop_h)
static void crop_image(struct sub_bitmap *part)
{
int i;
uint8_t *src;
uint16_t pal[4];
unsigned stride = (crop_w + 7) & ~7;
if (crop_x > this->pal_width || crop_y > this->pal_height ||
crop_w > this->pal_width - crop_x || crop_h > this->pal_width - crop_y ||
crop_w > 0x8000 || crop_h > 0x8000 ||
stride * crop_h > this->image_size) {
return 0;
}
for (i = 0; i < 4; ++i) {
int color;
int alpha = this->alpha[i];
// extend 4 -> 8 bit
alpha |= alpha << 4;
if (this->custom && (this->cuspal[i] >> 31) != 0)
alpha = 0;
color = this->custom ? this->cuspal[i] :
this->global_palette[this->palette[i]];
color = (color >> 16) & 0xff;
// convert to MPlayer-style gray/alpha palette
color = FFMIN(color, alpha);
pal[i] = (-alpha << 8) | color;
}
src = this->pal_image + crop_y * this->pal_width + crop_x;
pal2gray_alpha(pal, src, this->pal_width,
this->image, this->aimage, stride,
crop_w, crop_h);
this->width = crop_w;
this->height = crop_h;
this->stride = stride;
this->start_col = this->pal_start_col + crop_x;
this->start_row = this->pal_start_row + crop_y;
spudec_cut_image(this);
// reset scaled image
this->scaled_frame_width = 0;
this->scaled_frame_height = 0;
this->palette_crop_cache.valid = 0;
return 1;
}
int spudec_apply_palette_crop(void *this, uint32_t palette,
int sx, int sy, int ex, int ey)
{
spudec_handle_t *spu = this;
struct palette_crop_cache *c = &spu->palette_crop_cache;
if (c->valid && c->palette == palette &&
c->sx == sx && c->sy == sy && c->ex == ex && c->ey == ey)
return c->result;
spu->palette[0] = (palette >> 28) & 0xf;
spu->palette[1] = (palette >> 24) & 0xf;
spu->palette[2] = (palette >> 20) & 0xf;
spu->palette[3] = (palette >> 16) & 0xf;
spu->alpha[0] = (palette >> 12) & 0xf;
spu->alpha[1] = (palette >> 8) & 0xf;
spu->alpha[2] = (palette >> 4) & 0xf;
spu->alpha[3] = palette & 0xf;
spu->spu_changed = 1;
c->result = apply_palette_crop(spu,
sx - spu->pal_start_col, sy - spu->pal_start_row,
ex - sx, ey - sy);
c->palette = palette;
c->sx = sx; c->sy = sy;
c->ex = ex; c->ey = ey;
c->valid = 1;
return c->result;
if (part->w < 1 || part->h < 1)
return;
struct osd_bmp_indexed *bmp = part->bitmap;
bool invisible[256];
for (int n = 0; n < 256; n++)
invisible[n] = !(bmp->palette[n] >> 24);
int y0 = 0, y1 = part->h, x0 = part->w, x1 = 0;
bool y_all_invisible = true;
for (int y = 0; y < part->h; y++) {
uint8_t *pixels = bmp->bitmap + part->stride * y;
int cur = 0;
while (cur < part->w && invisible[pixels[cur]])
cur++;
int start_visible = cur;
int last_visible = -1;
while (cur < part->w) {
if (!invisible[pixels[cur]])
last_visible = cur;
cur++;
}
x0 = FFMIN(x0, start_visible);
x1 = FFMAX(x1, last_visible);
bool all_invisible = last_visible == -1;
if (all_invisible) {
if (y_all_invisible)
y0 = y;
} else {
y_all_invisible = false;
y1 = y + 1;
}
}
bmp->bitmap += x0 + y0 * part->stride;
part->w = FFMAX(x1 - x0, 0);
part->h = FFMAX(y1 - y0, 0);
part->x += x0;
part->y += y0;
}
static void spudec_process_data(spudec_handle_t *this, packet_t *packet)
@ -387,7 +295,18 @@ static void spudec_process_data(spudec_handle_t *this, packet_t *packet)
memset(dst, color, len);
dst += len;
}
apply_palette_crop(this, 0, 0, this->pal_width, this->pal_height);
struct sub_bitmap *sub_part = &this->sub_part;
struct osd_bmp_indexed *bmp = &this->borrowed_bmp;
bmp->bitmap = this->pal_image;
setup_palette(this, bmp->palette);
sub_part->bitmap = bmp;
sub_part->stride = this->pal_width;
sub_part->w = this->pal_width;
sub_part->h = this->pal_height;
sub_part->x = this->pal_start_col;
sub_part->y = this->pal_start_row;
crop_image(sub_part);
}
@ -413,8 +332,8 @@ static void compute_palette(spudec_handle_t *this, packet_t *packet)
start = 0x80;
step = 0;
} else {
start = this->font_start_level;
step = (0xF0-this->font_start_level)/(cused-1);
start = 72;
step = (0xF0-start)/(cused-1);
}
memset(used, 0, sizeof(used));
for (i=0; i<4; i++) {
@ -686,17 +605,12 @@ void spudec_heartbeat(void *this, unsigned int pts100)
free(spu->image);
spu->image_size = packet->data_len;
spu->image = packet->packet;
spu->aimage = packet->packet + packet->stride * packet->height;
packet->packet = NULL;
spu->width = packet->width;
spu->height = packet->height;
spu->stride = packet->stride;
spu->start_col = packet->start_col;
spu->start_row = packet->start_row;
// reset scaled image
spu->scaled_frame_width = 0;
spu->scaled_frame_height = 0;
} else {
if (spu->auto_palette)
compute_palette(spu, packet);
@ -724,486 +638,30 @@ void spudec_set_forced_subs_only(void * const this, const unsigned int flag)
}
}
void spudec_draw(void *this, void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx)
void spudec_get_indexed(void *this, struct mp_osd_res *dim,
struct sub_bitmaps *res)
{
spudec_handle_t *spu = this;
if (spudec_visible(spu))
{
draw_alpha(ctx, spu->start_col, spu->start_row, spu->width, spu->height,
spu->image, spu->aimage, spu->stride);
spu->spu_changed = 0;
*res = (struct sub_bitmaps) { .format = SUBBITMAP_INDEXED };
struct sub_bitmap *part = &spu->borrowed_sub_part;
res->parts = part;
*part = spu->sub_part;
// Empty subs do happen when cropping
bool empty = part->w < 1 || part->h < 1;
if (spudec_visible(spu) && !empty) {
double xscale = (double) (dim->w - dim->ml - dim->mr) / spu->orig_frame_width;
double yscale = (double) (dim->h - dim->mt - dim->mb) / spu->orig_frame_height;
part->x = part->x * xscale + dim->ml;
part->y = part->y * yscale + dim->mt;
part->dw = part->w * xscale;
part->dh = part->h * yscale;
res->num_parts = 1;
res->scaled = true;
}
}
/* calc the bbox for spudec subs */
void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox)
{
spudec_handle_t *spu = me;
if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
|| (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) {
// unscaled
bbox[0] = spu->start_col;
bbox[1] = spu->start_col + spu->width;
bbox[2] = spu->start_row;
bbox[3] = spu->start_row + spu->height;
}
else {
// scaled
unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
bbox[0] = spu->start_col * scalex / 0x100;
bbox[1] = spu->start_col * scalex / 0x100 + spu->width * scalex / 0x100;
switch (spu_alignment) {
case 0:
bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x100;
if (bbox[3] > dys) bbox[3] = dys;
bbox[2] = bbox[3] - spu->height * scaley / 0x100;
break;
case 1:
if (sub_pos < 50) {
bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x200;
bbox[3] = bbox[2] + spu->height;
} else {
bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x200;
if (bbox[3] > dys) bbox[3] = dys;
bbox[2] = bbox[3] - spu->height * scaley / 0x100;
}
break;
case 2:
bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x100;
bbox[3] = bbox[2] + spu->height;
break;
default: /* -1 */
bbox[2] = spu->start_row * scaley / 0x100;
bbox[3] = spu->start_row * scaley / 0x100 + spu->height * scaley / 0x100;
break;
if (spu->spu_changed) {
res->bitmap_id = res->bitmap_pos_id = 1;
spu->spu_changed = 0;
}
}
}
/* transform mplayer's alpha value into an opacity value that is linear */
static inline int canon_alpha(int alpha)
{
return (uint8_t)-alpha;
}
typedef struct {
unsigned position;
unsigned left_up;
unsigned right_down;
}scale_pixel;
static void scale_table(unsigned int start_src, unsigned int start_tar, unsigned int end_src, unsigned int end_tar, scale_pixel * table)
{
unsigned int t;
unsigned int delta_src = end_src - start_src;
unsigned int delta_tar = end_tar - start_tar;
int src = 0;
int src_step;
if (delta_src == 0 || delta_tar == 0) {
return;
}
src_step = (delta_src << 16) / delta_tar >>1;
for (t = 0; t<=delta_tar; src += (src_step << 1), t++){
table[t].position= FFMIN(src >> 16, end_src - 1);
table[t].right_down = src & 0xffff;
table[t].left_up = 0x10000 - table[t].right_down;
}
}
/* bilinear scale, similar to vobsub's code */
static void scale_image(int x, int y, scale_pixel* table_x, scale_pixel* table_y, spudec_handle_t * spu)
{
int alpha[4];
int color[4];
unsigned int scale[4];
int base = table_y[y].position * spu->stride + table_x[x].position;
int scaled = y * spu->scaled_stride + x;
alpha[0] = canon_alpha(spu->aimage[base]);
alpha[1] = canon_alpha(spu->aimage[base + 1]);
alpha[2] = canon_alpha(spu->aimage[base + spu->stride]);
alpha[3] = canon_alpha(spu->aimage[base + spu->stride + 1]);
color[0] = spu->image[base];
color[1] = spu->image[base + 1];
color[2] = spu->image[base + spu->stride];
color[3] = spu->image[base + spu->stride + 1];
scale[0] = (table_x[x].left_up * table_y[y].left_up >> 16) * alpha[0];
if (table_y[y].left_up == 0x10000) // necessary to avoid overflow-case
scale[0] = table_x[x].left_up * alpha[0];
scale[1] = (table_x[x].right_down * table_y[y].left_up >>16) * alpha[1];
scale[2] = (table_x[x].left_up * table_y[y].right_down >> 16) * alpha[2];
scale[3] = (table_x[x].right_down * table_y[y].right_down >> 16) * alpha[3];
spu->scaled_image[scaled] = (color[0] * scale[0] + color[1] * scale[1] + color[2] * scale[2] + color[3] * scale[3])>>24;
spu->scaled_aimage[scaled] = (scale[0] + scale[1] + scale[2] + scale[3]) >> 16;
if (spu->scaled_aimage[scaled]){
// ensure that MPlayer's simplified alpha-blending can not overflow
spu->scaled_image[scaled] = FFMIN(spu->scaled_image[scaled], spu->scaled_aimage[scaled]);
// convert to MPlayer-style alpha
spu->scaled_aimage[scaled] = -spu->scaled_aimage[scaled];
}
}
static void sws_spu_image(unsigned char *d1, unsigned char *d2, int dw, int dh,
int ds, const unsigned char* s1, unsigned char* s2,
int sw, int sh, int ss)
{
struct SwsContext *ctx;
static SwsFilter filter;
static int firsttime = 1;
static float oldvar;
int i;
if (!firsttime && oldvar != spu_gaussvar) sws_freeVec(filter.lumH);
if (firsttime) {
filter.lumH = filter.lumV =
filter.chrH = filter.chrV = sws_getGaussianVec(spu_gaussvar, 3.0);
sws_normalizeVec(filter.lumH, 1.0);
firsttime = 0;
oldvar = spu_gaussvar;
}
ctx=sws_getContext(sw, sh, PIX_FMT_GRAY8, dw, dh, PIX_FMT_GRAY8, SWS_GAUSS, &filter, NULL, NULL);
sws_scale(ctx,&s1,&ss,0,sh,&d1,&ds);
for (i=ss*sh-1; i>=0; i--) s2[i] = -s2[i];
sws_scale(ctx,(const uint8_t **)&s2,&ss,0,sh,&d2,&ds);
for (i=ds*dh-1; i>=0; i--) d2[i] = -d2[i];
sws_freeContext(ctx);
}
void spudec_draw_scaled(void *me, unsigned int dxs, unsigned int dys, void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx)
{
spudec_handle_t *spu = me;
scale_pixel *table_x;
scale_pixel *table_y;
if (spudec_visible(spu)) {
// check if only forced subtitles are requested
if( (spu->forced_subs_only) && !(spu->is_forced_sub) ){
return;
}
if (!(spu_aamode&16) && (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
|| (spu->orig_frame_width == dxs && spu->orig_frame_height == dys))) {
spudec_draw(spu, draw_alpha, ctx);
}
else {
if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) { /* Resizing is needed */
/* scaled_x = scalex * x / 0x100
scaled_y = scaley * y / 0x100
order of operations is important because of rounding. */
unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
spu->scaled_start_col = spu->start_col * scalex / 0x100;
spu->scaled_start_row = spu->start_row * scaley / 0x100;
spu->scaled_width = spu->width * scalex / 0x100;
spu->scaled_height = spu->height * scaley / 0x100;
/* Kludge: draw_alpha needs width multiple of 8 */
spu->scaled_stride = (spu->scaled_width + 7) & ~7;
if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) {
if (spu->scaled_image) {
free(spu->scaled_image);
spu->scaled_image_size = 0;
}
spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height);
if (spu->scaled_image) {
spu->scaled_image_size = spu->scaled_stride * spu->scaled_height;
spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size;
}
}
if (spu->scaled_image) {
unsigned int x, y;
// needs to be 0-initialized because draw_alpha draws always a
// multiple of 8 pixels. TODO: optimize
if (spu->scaled_width & 7)
memset(spu->scaled_image, 0, 2 * spu->scaled_image_size);
if (spu->scaled_width <= 1 || spu->scaled_height <= 1) {
goto nothing_to_do;
}
switch(spu_aamode&15) {
case 4:
sws_spu_image(spu->scaled_image, spu->scaled_aimage,
spu->scaled_width, spu->scaled_height, spu->scaled_stride,
spu->image, spu->aimage, spu->width, spu->height, spu->stride);
break;
case 3:
table_x = calloc(spu->scaled_width, sizeof(scale_pixel));
table_y = calloc(spu->scaled_height, sizeof(scale_pixel));
if (!table_x || !table_y) {
mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: spudec_draw_scaled: calloc failed\n");
}
scale_table(0, 0, spu->width - 1, spu->scaled_width - 1, table_x);
scale_table(0, 0, spu->height - 1, spu->scaled_height - 1, table_y);
for (y = 0; y < spu->scaled_height; y++)
for (x = 0; x < spu->scaled_width; x++)
scale_image(x, y, table_x, table_y, spu);
free(table_x);
free(table_y);
break;
case 0:
/* no antialiasing */
for (y = 0; y < spu->scaled_height; ++y) {
int unscaled_y = y * 0x100 / scaley;
int strides = spu->stride * unscaled_y;
int scaled_strides = spu->scaled_stride * y;
for (x = 0; x < spu->scaled_width; ++x) {
int unscaled_x = x * 0x100 / scalex;
spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x];
spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x];
}
}
break;
case 1:
{
/* Intermediate antialiasing. */
for (y = 0; y < spu->scaled_height; ++y) {
const unsigned int unscaled_top = y * spu->orig_frame_height / dys;
unsigned int unscaled_bottom = (y + 1) * spu->orig_frame_height / dys;
if (unscaled_bottom >= spu->height)
unscaled_bottom = spu->height - 1;
for (x = 0; x < spu->scaled_width; ++x) {
const unsigned int unscaled_left = x * spu->orig_frame_width / dxs;
unsigned int unscaled_right = (x + 1) * spu->orig_frame_width / dxs;
unsigned int color = 0;
unsigned int alpha = 0;
unsigned int walkx, walky;
unsigned int base, tmp;
if (unscaled_right >= spu->width)
unscaled_right = spu->width - 1;
for (walky = unscaled_top; walky <= unscaled_bottom; ++walky)
for (walkx = unscaled_left; walkx <= unscaled_right; ++walkx) {
base = walky * spu->stride + walkx;
tmp = canon_alpha(spu->aimage[base]);
alpha += tmp;
color += tmp * spu->image[base];
}
base = y * spu->scaled_stride + x;
spu->scaled_image[base] = alpha ? color / alpha : 0;
spu->scaled_aimage[base] =
alpha * (1 + unscaled_bottom - unscaled_top) * (1 + unscaled_right - unscaled_left);
/* spu->scaled_aimage[base] =
alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */
if (spu->scaled_aimage[base]) {
spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
}
}
}
}
break;
case 2:
{
/* Best antialiasing. Very slow. */
/* Any pixel (x, y) represents pixels from the original
rectangular region comprised between the columns
unscaled_y and unscaled_y + 0x100 / scaley and the rows
unscaled_x and unscaled_x + 0x100 / scalex
The original rectangular region that the scaled pixel
represents is cut in 9 rectangular areas like this:
+---+-----------------+---+
| 1 | 2 | 3 |
+---+-----------------+---+
| | | |
| 4 | 5 | 6 |
| | | |
+---+-----------------+---+
| 7 | 8 | 9 |
+---+-----------------+---+
The width of the left column is at most one pixel and
it is never null and its right column is at a pixel
boundary. The height of the top row is at most one
pixel it is never null and its bottom row is at a
pixel boundary. The width and height of region 5 are
integral values. The width of the right column is
what remains and is less than one pixel. The height
of the bottom row is what remains and is less than
one pixel.
The row above 1, 2, 3 is unscaled_y. The row between
1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4,
5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom.
The row beneath 7, 8, 9 is unscaled_y_bottom.
The column left of 1, 4, 7 is unscaled_x. The column
between 1, 4, 7 and 2, 5, 8 is left_right_column. The
column between 2, 5, 8 and 3, 6, 9 is (unsigned
int)unscaled_x_right. The column right of 3, 6, 9 is
unscaled_x_right. */
const double inv_scalex = (double) 0x100 / scalex;
const double inv_scaley = (double) 0x100 / scaley;
for (y = 0; y < spu->scaled_height; ++y) {
const double unscaled_y = y * inv_scaley;
const double unscaled_y_bottom = unscaled_y + inv_scaley;
const unsigned int top_low_row = FFMIN(unscaled_y_bottom, unscaled_y + 1.0);
const double top = top_low_row - unscaled_y;
const unsigned int height = unscaled_y_bottom > top_low_row
? (unsigned int) unscaled_y_bottom - top_low_row
: 0;
const double bottom = unscaled_y_bottom > top_low_row
? unscaled_y_bottom - floor(unscaled_y_bottom)
: 0.0;
for (x = 0; x < spu->scaled_width; ++x) {
const double unscaled_x = x * inv_scalex;
const double unscaled_x_right = unscaled_x + inv_scalex;
const unsigned int left_right_column = FFMIN(unscaled_x_right, unscaled_x + 1.0);
const double left = left_right_column - unscaled_x;
const unsigned int width = unscaled_x_right > left_right_column
? (unsigned int) unscaled_x_right - left_right_column
: 0;
const double right = unscaled_x_right > left_right_column
? unscaled_x_right - floor(unscaled_x_right)
: 0.0;
double color = 0.0;
double alpha = 0.0;
double tmp;
unsigned int base;
/* Now use these informations to compute a good alpha,
and lightness. The sum is on each of the 9
region's surface and alpha and lightness.
transformed alpha = sum(surface * alpha) / sum(surface)
transformed color = sum(surface * alpha * color) / sum(surface * alpha)
*/
/* 1: top left part */
base = spu->stride * (unsigned int) unscaled_y;
tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]);
alpha += tmp;
color += tmp * spu->image[base + (unsigned int) unscaled_x];
/* 2: top center part */
if (width > 0) {
unsigned int walkx;
for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
base = spu->stride * (unsigned int) unscaled_y + walkx;
tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]);
alpha += tmp;
color += tmp * spu->image[base];
}
}
/* 3: top right part */
if (right > 0.0) {
base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right;
tmp = right * top * canon_alpha(spu->aimage[base]);
alpha += tmp;
color += tmp * spu->image[base];
}
/* 4: center left part */
if (height > 0) {
unsigned int walky;
for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
base = spu->stride * walky + (unsigned int) unscaled_x;
tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]);
alpha += tmp;
color += tmp * spu->image[base];
}
}
/* 5: center part */
if (width > 0 && height > 0) {
unsigned int walky;
for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
unsigned int walkx;
base = spu->stride * walky;
for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]);
alpha += tmp;
color += tmp * spu->image[base + walkx];
}
}
}
/* 6: center right part */
if (right > 0.0 && height > 0) {
unsigned int walky;
for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
base = spu->stride * walky + (unsigned int) unscaled_x_right;
tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]);
alpha += tmp;
color += tmp * spu->image[base];
}
}
/* 7: bottom left part */
if (bottom > 0.0) {
base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x;
tmp = left * bottom * canon_alpha(spu->aimage[base]);
alpha += tmp;
color += tmp * spu->image[base];
}
/* 8: bottom center part */
if (width > 0 && bottom > 0.0) {
unsigned int walkx;
base = spu->stride * (unsigned int) unscaled_y_bottom;
for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]);
alpha += tmp;
color += tmp * spu->image[base + walkx];
}
}
/* 9: bottom right part */
if (right > 0.0 && bottom > 0.0) {
base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right;
tmp = right * bottom * canon_alpha(spu->aimage[base]);
alpha += tmp;
color += tmp * spu->image[base];
}
/* Finally mix these transparency and brightness information suitably */
base = spu->scaled_stride * y + x;
spu->scaled_image[base] = alpha > 0 ? color / alpha : 0;
spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000;
if (spu->scaled_aimage[base]) {
spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
}
}
}
}
}
nothing_to_do:
/* Kludge: draw_alpha needs width multiple of 8. */
if (spu->scaled_width < spu->scaled_stride)
for (y = 0; y < spu->scaled_height; ++y) {
memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0,
spu->scaled_stride - spu->scaled_width);
}
spu->scaled_frame_width = dxs;
spu->scaled_frame_height = dys;
}
}
if (spu->scaled_image){
switch (spu_alignment) {
case 0:
spu->scaled_start_row = dys*sub_pos/100;
if (spu->scaled_start_row + spu->scaled_height > dys)
spu->scaled_start_row = dys - spu->scaled_height;
break;
case 1:
spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height/2;
if (sub_pos >= 50 && spu->scaled_start_row + spu->scaled_height > dys)
spu->scaled_start_row = dys - spu->scaled_height;
break;
case 2:
spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height;
break;
}
draw_alpha(ctx, spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height,
spu->scaled_image, spu->scaled_aimage, spu->scaled_stride);
spu->spu_changed = 0;
}
}
}
else
{
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n",
spu->start_pts, spu->end_pts, spu->now_pts);
}
}
void spudec_set_font_factor(void * this, double factor)
{
spudec_handle_t *spu = this;
spu->font_start_level = (int)(0xF0-(0xE0*factor));
}
static void spudec_parse_extradata(spudec_handle_t *this,
@ -1302,11 +760,8 @@ void spudec_free(void *this)
spudec_free_packet(spudec_dequeue_packet(spu));
free(spu->packet);
spu->packet = NULL;
free(spu->scaled_image);
spu->scaled_image = NULL;
free(spu->image);
spu->image = NULL;
spu->aimage = NULL;
free(spu->pal_image);
spu->pal_image = NULL;
spu->image_size = 0;
@ -1314,87 +769,3 @@ void spudec_free(void *this)
free(spu);
}
}
#define MP_NOPTS_VALUE (-1LL<<63) //both int64_t and double should be able to represent this exactly
packet_t *spudec_packet_create(int x, int y, int w, int h)
{
packet_t *packet;
int stride = (w + 7) & ~7;
if ((unsigned)w >= 0x8000 || (unsigned)h > 0x4000)
return NULL;
packet = calloc(1, sizeof(packet_t));
packet->is_decoded = 1;
packet->width = w;
packet->height = h;
packet->stride = stride;
packet->start_col = x;
packet->start_row = y;
packet->data_len = 2 * stride * h;
if (packet->data_len) { // size 0 is a special "clear" packet
packet->packet = malloc(packet->data_len);
if (!packet->packet) {
free(packet);
packet = NULL;
}
}
return packet;
}
void spudec_packet_clear(packet_t *packet)
{
/* clear alpha and value, as value is premultiplied */
memset(packet->packet, 0, packet->data_len);
}
void spudec_packet_fill(packet_t *packet,
const uint8_t *pal_img, int pal_stride,
const void *palette,
int x, int y, int w, int h)
{
const uint32_t *pal = palette;
uint8_t *img = packet->packet + x + y * packet->stride;
uint8_t *aimg = img + packet->stride * packet->height;
int i;
uint16_t g8a8_pal[256];
for (i = 0; i < 256; i++) {
uint32_t pixel = pal[i];
int alpha = pixel >> 24;
int gray = (((pixel & 0x000000ff) >> 0) +
((pixel & 0x0000ff00) >> 7) +
((pixel & 0x00ff0000) >> 16)) >> 2;
gray = FFMIN(gray, alpha);
g8a8_pal[i] = (-alpha << 8) | gray;
}
pal2gray_alpha(g8a8_pal, pal_img, pal_stride,
img, aimg, packet->stride, w, h);
}
void spudec_packet_send(void *spu, packet_t *packet, double pts, double endpts)
{
packet->start_pts = 0;
packet->end_pts = 0x7fffffff;
if (pts != MP_NOPTS_VALUE)
packet->start_pts = pts * 90000;
if (endpts != MP_NOPTS_VALUE)
packet->end_pts = endpts * 90000;
spudec_queue_packet(spu, packet);
}
/**
* palette must contain at least 256 32-bit entries, otherwise crashes
* are possible
*/
void spudec_set_paletted(void *spu, const uint8_t *pal_img, int pal_stride,
const void *palette,
int x, int y, int w, int h,
double pts, double endpts)
{
packet_t *packet = spudec_packet_create(x, y, w, h);
if (!packet)
return;
if (packet->data_len) // size 0 is a special "clear" packet
spudec_packet_fill(packet, pal_img, pal_stride, palette, 0, 0, w, h);
spudec_packet_send(spu, packet, pts, endpts);
}

View File

@ -21,31 +21,18 @@
#include <stdint.h>
struct sub_bitmaps;
struct mp_osd_res;
void spudec_heartbeat(void *this, unsigned int pts100);
void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100);
void spudec_draw(void *this, void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx);
void spudec_draw_scaled(void *this, unsigned int dxs, unsigned int dys, void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx);
int spudec_apply_palette_crop(void *this, uint32_t palette, int sx, int ex, int sy, int ey);
void spudec_get_indexed(void *this, struct mp_osd_res *dim, struct sub_bitmaps *res);
void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len);
void *spudec_new(unsigned int *palette);
void spudec_free(void *this);
void spudec_reset(void *this); // called after seek
int spudec_visible(void *this); // check if spu is visible
void spudec_set_font_factor(void * this, double factor); // sets the equivalent to ffactor
int spudec_changed(void *this);
void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox);
void spudec_set_forced_subs_only(void * const this, const unsigned int flag);
void spudec_set_paletted(void *this, const uint8_t *pal_img, int stride,
const void *palette,
int x, int y, int w, int h,
double pts, double endpts);
struct spu_packet_t *spudec_packet_create(int x, int y, int w, int h);
void spudec_packet_fill(struct spu_packet_t *packet,
const uint8_t *pal_img, int pal_stride,
const void *palette,
int x, int y, int w, int h);
void spudec_packet_send(void *spu, struct spu_packet_t *packet,
double pts, double endpts);
void spudec_packet_clear(struct spu_packet_t *packet);
#endif /* MPLAYER_SPUDEC_H */

432
sub/sub.c
View File

@ -19,11 +19,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libavutil/mem.h>
#include <libavutil/common.h>
#include "config.h"
#include "mpcommon.h"
#include "stream/stream.h"
@ -35,8 +35,11 @@
#include "mp_msg.h"
#include "libvo/video_out.h"
#include "sub.h"
#include "sub/ass_mp.h"
#include "dec_sub.h"
#include "img_convert.h"
#include "draw_bmp.h"
#include "spudec.h"
#include "subreader.h"
char * const sub_osd_names[]={
@ -56,213 +59,69 @@ char * const sub_osd_names[]={
};
char * const sub_osd_names_short[] ={ "", "|>", "||", "[]", "<<" , ">>", "", "", "", "", "", "", "" };
int sub_unicode=0;
int sub_utf8=0;
int sub_pos=100;
int sub_width_p=100;
int sub_visibility=1;
int sub_bg_color=0; /* subtitles background color */
int sub_bg_alpha=0;
int sub_justify=0;
int vo_osd_progbar_type=-1;
int vo_osd_progbar_value=100; // 0..256
subtitle* vo_sub=NULL;
char *subtitle_font_encoding = NULL;
float text_font_scale_factor = 3.5;
float osd_font_scale_factor = 4.0;
float subtitle_font_radius = 2.0;
float subtitle_font_thickness = 2.0;
// 0 = no autoscale
// 1 = video height
// 2 = video width
// 3 = diagonal
int subtitle_autoscale = 3;
float text_font_scale_factor = 6;
char *font_name = NULL;
char *sub_font_name = NULL;
float font_factor = 0.75;
float sub_delay = 0;
float sub_fps = 0;
// allocates/enlarges the alpha/bitmap buffer
void osd_alloc_buf(mp_osd_obj_t* obj)
{
int len;
if (obj->bbox.x2 < obj->bbox.x1) obj->bbox.x2 = obj->bbox.x1;
if (obj->bbox.y2 < obj->bbox.y1) obj->bbox.y2 = obj->bbox.y1;
obj->stride = ((obj->bbox.x2-obj->bbox.x1)+7)&(~7);
len = obj->stride*(obj->bbox.y2-obj->bbox.y1);
if (obj->allocated<len) {
obj->allocated = len;
av_free(obj->bitmap_buffer);
av_free(obj->alpha_buffer);
obj->bitmap_buffer = av_malloc(len);
obj->alpha_buffer = av_malloc(len);
}
memset(obj->bitmap_buffer, sub_bg_color, len);
memset(obj->alpha_buffer, sub_bg_alpha, len);
}
// renders the buffer
void vo_draw_text_from_buffer(mp_osd_obj_t* obj,void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx)
{
if (obj->allocated > 0) {
draw_alpha(ctx,
obj->bbox.x1,obj->bbox.y1,
obj->bbox.x2-obj->bbox.x1,
obj->bbox.y2-obj->bbox.y1,
obj->bitmap_buffer,
obj->alpha_buffer,
obj->stride);
}
}
inline static void vo_update_spudec_sub(struct osd_state *osd, mp_osd_obj_t* obj)
{
unsigned int bbox[4];
spudec_calc_bbox(vo_spudec, osd->w, osd->h, bbox);
obj->bbox.x1 = bbox[0];
obj->bbox.x2 = bbox[1];
obj->bbox.y1 = bbox[2];
obj->bbox.y2 = bbox[3];
obj->flags |= OSDFLAG_BBOX;
}
inline static void vo_draw_spudec_sub(mp_osd_obj_t* obj, void (*draw_alpha)(void *ctx, int x0, int y0, int w, int h, unsigned char* src, unsigned char* srca, int stride), void *ctx)
{
spudec_draw_scaled(vo_spudec, obj->dxs, obj->dys, draw_alpha, ctx);
}
void *vo_spudec=NULL;
void *vo_vobsub=NULL;
mp_osd_obj_t* vo_osd_list=NULL;
static struct osd_state *global_osd;
static mp_osd_obj_t* new_osd_obj(int type){
mp_osd_obj_t* osd=malloc(sizeof(mp_osd_obj_t));
memset(osd,0,sizeof(mp_osd_obj_t));
osd->next=vo_osd_list;
vo_osd_list=osd;
osd->type=type;
osd->alpha_buffer = NULL;
osd->bitmap_buffer = NULL;
osd->allocated = -1;
return osd;
}
void osd_free(struct osd_state *osd)
static bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
{
osd_destroy_backend(osd);
mp_osd_obj_t* obj=vo_osd_list;
while(obj){
mp_osd_obj_t* next=obj->next;
av_free(obj->alpha_buffer);
av_free(obj->bitmap_buffer);
free(obj);
obj=next;
}
vo_osd_list=NULL;
talloc_free(osd);
}
static int osd_update_ext(struct osd_state *osd, int dxs, int dys,
int left_border, int top_border, int right_border,
int bottom_border, int orig_w, int orig_h)
{
struct MPOpts *opts = osd->opts;
mp_osd_obj_t* obj=vo_osd_list;
int chg=0;
osd->w = dxs;
osd->h = dys;
osd_font_load(osd);
while(obj){
if(dxs!=obj->dxs || dys!=obj->dys || obj->flags&OSDFLAG_FORCE_UPDATE){
int vis=obj->flags&OSDFLAG_VISIBLE;
obj->flags&=~OSDFLAG_BBOX;
switch(obj->type){
case OSDTYPE_SUBTITLE:
vo_update_text_sub(osd, obj);
break;
case OSDTYPE_PROGBAR:
vo_update_text_progbar(osd, obj);
break;
case OSDTYPE_SPU:
if (opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec)){
vo_update_spudec_sub(osd, obj);
obj->flags|=OSDFLAG_VISIBLE|OSDFLAG_CHANGED;
}
else
obj->flags&=~OSDFLAG_VISIBLE;
break;
case OSDTYPE_OSD:
if(osd->osd_text[0]){
vo_update_text_osd(osd, obj);
obj->flags|=OSDFLAG_VISIBLE|OSDFLAG_CHANGED;
} else
obj->flags&=~OSDFLAG_VISIBLE;
break;
}
// check bbox:
if(!(obj->flags&OSDFLAG_BBOX)){
// we don't know, so assume the whole screen changed :(
obj->bbox.x1=obj->bbox.y1=0;
obj->bbox.x2=dxs;
obj->bbox.y2=dys;
obj->flags|=OSDFLAG_BBOX;
} else {
// check bbox, reduce it if it's out of bounds (corners):
if(obj->bbox.x1<0) obj->bbox.x1=0;
if(obj->bbox.y1<0) obj->bbox.y1=0;
if(obj->bbox.x2>dxs) obj->bbox.x2=dxs;
if(obj->bbox.y2>dys) obj->bbox.y2=dys;
if(obj->flags&OSDFLAG_VISIBLE)
// debug:
mp_msg(MSGT_OSD,MSGL_DBG2,"OSD update: %d;%d %dx%d \n",
obj->bbox.x1,obj->bbox.y1,obj->bbox.x2-obj->bbox.x1,
obj->bbox.y2-obj->bbox.y1);
}
// check if visibility changed:
if(vis != (obj->flags&OSDFLAG_VISIBLE) ) obj->flags|=OSDFLAG_CHANGED;
// remove the cause of automatic update:
obj->dxs=dxs; obj->dys=dys;
obj->flags&=~OSDFLAG_FORCE_UPDATE;
}
if(obj->flags&OSDFLAG_CHANGED){
chg|=1<<obj->type;
mp_msg(MSGT_OSD,MSGL_DBG2,"OSD chg: %d V: %s pb:%d \n",obj->type,(obj->flags&OSDFLAG_VISIBLE)?"yes":"no",vo_osd_progbar_type);
}
obj=obj->next;
}
return chg;
}
int osd_update(struct osd_state *osd, int dxs, int dys)
{
return osd_update_ext(osd, dxs, dys, 0, 0, 0, 0, dxs, dys);
return a.w == b.w && a.h == b.h && a.ml == b.ml && a.mt == b.mt
&& a.mr == b.mr && a.mb == b.mb
&& a.display_par == b.display_par
&& a.video_par == b.video_par;
}
struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib)
{
struct osd_state *osd = talloc_zero(NULL, struct osd_state);
*osd = (struct osd_state){
*osd = (struct osd_state) {
.opts = opts,
.ass_library = asslib,
.osd_text = talloc_strdup(osd, ""),
.progbar_type = -1,
};
// temp hack, should be moved to mplayer later
new_osd_obj(OSDTYPE_OSD);
new_osd_obj(OSDTYPE_SUBTITLE);
new_osd_obj(OSDTYPE_PROGBAR);
new_osd_obj(OSDTYPE_SPU);
osd_font_invalidate();
osd->osd_text = talloc_strdup(osd, "");
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct osd_object *obj = talloc_struct(osd, struct osd_object, {
.type = n,
});
for (int i = 0; i < OSD_CONV_CACHE_MAX; i++)
obj->cache[i] = talloc_steal(obj, osd_conv_cache_new());
osd->objs[n] = obj;
}
osd->objs[OSDTYPE_SPU]->is_sub = true; // spudec.c
osd->objs[OSDTYPE_SUB]->is_sub = true; // dec_sub.c
osd->objs[OSDTYPE_SUBTITLE]->is_sub = true; // osd_libass.c
osd_init_backend(osd);
global_osd = osd;
return osd;
}
void osd_free(struct osd_state *osd)
{
if (!osd)
return;
osd_destroy_backend(osd);
talloc_free(osd);
global_osd = NULL;
}
void osd_set_text(struct osd_state *osd, const char *text)
{
if (!text)
@ -274,98 +133,155 @@ void osd_set_text(struct osd_state *osd, const char *text)
vo_osd_changed(OSDTYPE_OSD);
}
void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys,
int left_border, int top_border, int right_border,
int bottom_border, int orig_w, int orig_h,
void (*draw_alpha)(void *ctx, int x0, int y0, int w,
int h, unsigned char* src,
unsigned char *srca,
int stride),
void *ctx)
static bool spu_visible(struct osd_state *osd, struct osd_object *obj)
{
mp_osd_obj_t* obj=vo_osd_list;
osd_update_ext(osd, dxs, dys, left_border, top_border, right_border,
bottom_border, orig_w, orig_h);
while(obj){
if(obj->flags&OSDFLAG_VISIBLE){
switch(obj->type){
case OSDTYPE_SPU:
if (vo_spudec)
vo_draw_spudec_sub(obj, draw_alpha, ctx); // FIXME
break;
case OSDTYPE_OSD:
case OSDTYPE_SUBTITLE:
case OSDTYPE_PROGBAR:
vo_draw_text_from_buffer(obj, draw_alpha, ctx);
break;
}
obj->old_bbox=obj->bbox;
obj->flags|=OSDFLAG_OLD_BBOX;
}
obj->flags&=~OSDFLAG_CHANGED;
obj=obj->next;
struct MPOpts *opts = osd->opts;
return opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec);
}
static void render_object(struct osd_state *osd, struct osd_object *obj,
struct mp_osd_res res, double video_pts,
const bool formats[SUBBITMAP_COUNT],
struct sub_bitmaps *out_imgs)
{
*out_imgs = (struct sub_bitmaps) {0};
if (!osd_res_equals(res, obj->vo_res))
obj->force_redraw = true;
obj->vo_res = res;
if (obj->type == OSDTYPE_SPU) {
if (spu_visible(osd, obj))
spudec_get_indexed(vo_spudec, &obj->vo_res, out_imgs);
} else if (obj->type == OSDTYPE_SUB) {
double sub_pts = video_pts;
if (sub_pts != MP_NOPTS_VALUE)
sub_pts += sub_delay - osd->sub_offset;
sub_get_bitmaps(osd, obj->vo_res, sub_pts, out_imgs);
} else {
osd_object_get_bitmaps(osd, obj, out_imgs);
}
if (obj->force_redraw) {
out_imgs->bitmap_id++;
out_imgs->bitmap_pos_id++;
}
obj->force_redraw = false;
obj->vo_bitmap_id += out_imgs->bitmap_id;
obj->vo_bitmap_pos_id += out_imgs->bitmap_pos_id;
if (out_imgs->num_parts == 0)
return;
if (obj->cached.bitmap_id == obj->vo_bitmap_id
&& obj->cached.bitmap_pos_id == obj->vo_bitmap_pos_id
&& formats[obj->cached.format])
{
*out_imgs = obj->cached;
return;
}
out_imgs->render_index = obj->type;
out_imgs->bitmap_id = obj->vo_bitmap_id;
out_imgs->bitmap_pos_id = obj->vo_bitmap_pos_id;
if (formats[out_imgs->format])
return;
bool cached = false; // do we have a copy of all the image data?
if (formats[SUBBITMAP_RGBA] && out_imgs->format == SUBBITMAP_INDEXED)
cached |= osd_conv_idx_to_rgba(obj->cache[0], out_imgs);
if (cached)
obj->cached = *out_imgs;
}
// draw_flags is a bit field of OSD_DRAW_* constants
void osd_draw(struct osd_state *osd, struct mp_osd_res res,
double video_pts, int draw_flags,
const bool formats[SUBBITMAP_COUNT],
void (*cb)(void *ctx, struct sub_bitmaps *imgs), void *cb_ctx)
{
if (draw_flags & OSD_DRAW_SUB_FILTER)
draw_flags |= OSD_DRAW_SUB_ONLY;
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct osd_object *obj = osd->objs[n];
// Object is drawn into the video frame itself; don't draw twice
if (osd->render_subs_in_filter && obj->is_sub &&
!(draw_flags & OSD_DRAW_SUB_FILTER))
continue;
if ((draw_flags & OSD_DRAW_SUB_ONLY) && !obj->is_sub)
continue;
struct sub_bitmaps imgs;
render_object(osd, obj, res, video_pts, formats, &imgs);
if (imgs.num_parts > 0) {
if (formats[imgs.format]) {
cb(cb_ctx, &imgs);
} else {
mp_msg(MSGT_OSD, MSGL_ERR,
"Can't render OSD part %d (format %d).\n",
obj->type, imgs.format);
}
}
}
}
void osd_draw_text(struct osd_state *osd, int dxs, int dys,
void (*draw_alpha)(void *ctx, int x0, int y0, int w, int h,
unsigned char* src, unsigned char *srca,
int stride),
void *ctx)
struct draw_on_image_closure {
struct osd_state *osd;
struct mp_image *dest;
bool changed;
};
static void draw_on_image(void *ctx, struct sub_bitmaps *imgs)
{
osd_draw_text_ext(osd, dxs, dys, 0, 0, 0, 0, dxs, dys, draw_alpha, ctx);
struct draw_on_image_closure *closure = ctx;
struct osd_state *osd = closure->osd;
mp_draw_sub_bitmaps(&osd->draw_cache, closure->dest, imgs);
talloc_steal(osd, osd->draw_cache);
closure->changed = true;
}
// Returns whether anything was drawn.
bool osd_draw_on_image(struct osd_state *osd, struct mp_osd_res res,
double video_pts, int draw_flags, struct mp_image *dest)
{
struct draw_on_image_closure closure = {osd, dest};
osd_draw(osd, res, video_pts, draw_flags, mp_draw_sub_formats,
&draw_on_image, &closure);
return closure.changed;
}
void vo_osd_changed(int new_value)
{
mp_osd_obj_t* obj=vo_osd_list;
while(obj){
if(obj->type==new_value) obj->flags|=OSDFLAG_FORCE_UPDATE;
obj=obj->next;
struct osd_state *osd = global_osd;
for (int n = 0; n < MAX_OSD_PARTS; n++) {
if (osd->objs[n]->type == new_value)
osd->objs[n]->force_redraw = true;
}
osd->want_redraw = true;
}
void vo_osd_reset_changed(void)
bool sub_bitmaps_bb(struct sub_bitmaps *imgs, struct mp_rect *out_bb)
{
mp_osd_obj_t* obj = vo_osd_list;
while (obj) {
obj->flags = obj->flags & ~OSDFLAG_FORCE_UPDATE;
obj = obj->next;
struct mp_rect bb = {INT_MAX, INT_MAX, INT_MIN, INT_MIN};
for (int n = 0; n < imgs->num_parts; n++) {
struct sub_bitmap *p = &imgs->parts[n];
bb.x0 = FFMIN(bb.x0, p->x);
bb.y0 = FFMIN(bb.y0, p->y);
bb.x1 = FFMAX(bb.x1, p->x + p->dw);
bb.y1 = FFMAX(bb.y1, p->y + p->dh);
}
}
bool vo_osd_has_changed(struct osd_state *osd)
{
mp_osd_obj_t* obj = vo_osd_list;
while (obj) {
if (obj->flags & OSDFLAG_FORCE_UPDATE)
return true;
obj = obj->next;
}
return false;
}
// avoid degenerate bounding box if empty
bb.x0 = FFMIN(bb.x0, bb.x1);
bb.y0 = FFMIN(bb.y0, bb.y1);
void vo_osd_resized()
{
// font needs to be adjusted
osd_font_invalidate();
// OSD needs to be drawn fresh for new size
vo_osd_changed(OSDTYPE_OSD);
vo_osd_changed(OSDTYPE_SUBTITLE);
}
*out_bb = bb;
// return TRUE if we have osd in the specified rectangular area:
int vo_osd_check_range_update(int x1,int y1,int x2,int y2){
mp_osd_obj_t* obj=vo_osd_list;
while(obj){
if(obj->flags&OSDFLAG_VISIBLE){
if( (obj->bbox.x1<=x2 && obj->bbox.x2>=x1) &&
(obj->bbox.y1<=y2 && obj->bbox.y2>=y1) &&
obj->bbox.y2 > obj->bbox.y1 && obj->bbox.x2 > obj->bbox.x1
) return 1;
}
obj=obj->next;
}
return 0;
return bb.x0 < bb.x1 && bb.y0 < bb.y1;
}

252
sub/sub.h
View File

@ -19,161 +19,203 @@
#ifndef MPLAYER_SUB_H
#define MPLAYER_SUB_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "subreader.h"
#include "dec_sub.h"
// NOTE: VOs must support at least SUBBITMAP_LIBASS and SUBBITMAP_RGBA.
enum sub_bitmap_format {
SUBBITMAP_EMPTY = 0,// no bitmaps; always has num_parts==0
SUBBITMAP_LIBASS, // A8, with a per-surface blend color (libass.color)
SUBBITMAP_RGBA, // B8G8R8A8 (MSB=A, LSB=B), scaled, premultiplied alpha
SUBBITMAP_INDEXED, // scaled, bitmap points to osd_bmp_indexed
typedef struct mp_osd_bbox_s {
int x1,y1,x2,y2;
} mp_osd_bbox_t;
SUBBITMAP_COUNT
};
#define OSDTYPE_OSD 1
#define OSDTYPE_SUBTITLE 2
#define OSDTYPE_PROGBAR 3
#define OSDTYPE_SPU 4
// For SUBBITMAP_INDEXED
struct osd_bmp_indexed {
uint8_t *bitmap;
// Each entry is like a pixel in SUBBITMAP_RGBA format, but using straight
// alpha.
uint32_t palette[256];
};
#define OSDFLAG_VISIBLE 1
#define OSDFLAG_CHANGED 2
#define OSDFLAG_BBOX 4
#define OSDFLAG_OLD_BBOX 8
#define OSDFLAG_FORCE_UPDATE 16
#define MAX_UCS 1600
#define MAX_UCSLINES 16
typedef struct mp_osd_obj_s {
struct mp_osd_obj_s* next;
unsigned char type;
unsigned short flags;
int x,y;
int dxs,dys;
mp_osd_bbox_t bbox; // bounding box
mp_osd_bbox_t old_bbox; // the renderer will save bbox here
struct sub_bitmap {
void *bitmap;
int stride;
// Note: not clipped, going outside the screen area is allowed
// (except for SUBBITMAP_LIBASS, which is always clipped)
int w, h;
int x, y;
int dw, dh;
int allocated;
unsigned char *alpha_buffer;
unsigned char *bitmap_buffer;
union {
struct {
uint32_t color;
} libass;
};
};
struct sub_bitmaps {
// For VO cache state (limited by MAX_OSD_PARTS)
int render_index;
enum sub_bitmap_format format;
// If false, dw==w && dh==h.
// SUBBITMAP_LIBASS is never scaled.
bool scaled;
struct sub_bitmap *parts;
int num_parts;
// Incremented on each change
int bitmap_id, bitmap_pos_id;
};
struct mp_osd_res {
int w, h; // screen dimensions, including black borders
int mt, mb, ml, mr; // borders (top, bottom, left, right)
double display_par;
double video_par; // PAR of the original video (for some sub decoders)
};
enum mp_osdtype {
OSDTYPE_SUB,
OSDTYPE_OSD,
OSDTYPE_SUBTITLE,
OSDTYPE_PROGBAR,
OSDTYPE_SPU,
MAX_OSD_PARTS
};
#define OSD_CONV_CACHE_MAX 3
struct osd_object {
int type; // OSDTYPE_*
bool is_sub;
bool force_redraw;
// caches for OSD conversion (internal to render_object())
struct osd_conv_cache *cache[OSD_CONV_CACHE_MAX];
struct sub_bitmaps cached;
// VO cache state
int vo_bitmap_id;
int vo_bitmap_pos_id;
struct mp_osd_res vo_res;
// Internally used by osd_libass.c
struct ass_track *osd_track;
} mp_osd_obj_t;
struct sub_bitmap *parts_cache;
};
struct osd_state {
struct osd_object *objs[MAX_OSD_PARTS];
struct ass_library *ass_library;
struct ass_renderer *ass_renderer;
struct sh_sub *sh_sub;
int bitmap_id;
int bitmap_pos_id;
double sub_pts;
double sub_offset;
struct mp_eosd_res dim;
double normal_scale;
double vsfilter_scale;
bool unscaled;
bool support_rgba;
double vo_pts;
struct ass_renderer *osd_render;
struct ass_library *osd_ass_library;
char *osd_text;
int w, h;
bool render_subs_in_filter;
bool want_redraw;
char *osd_text; // OSDTYPE_OSD
int progbar_type, progbar_value; // OSDTYPE_PROGBAR
int switch_sub_id;
struct MPOpts *opts;
// Internal to sub.c
struct mp_draw_sub_cache *draw_cache;
// Internally used by osd_libass.c
struct ass_renderer *osd_render;
struct ass_library *osd_ass_library;
};
extern subtitle* vo_sub;
extern int vo_osd_progbar_type;
extern int vo_osd_progbar_value; // 0..255
extern struct subtitle* vo_sub;
extern void* vo_spudec;
extern void* vo_vobsub;
#define OSD_PLAY 0x01
#define OSD_PAUSE 0x02
#define OSD_STOP 0x03
#define OSD_REW 0x04
#define OSD_FFW 0x05
#define OSD_CLOCK 0x06
#define OSD_CONTRAST 0x07
#define OSD_SATURATION 0x08
#define OSD_VOLUME 0x09
#define OSD_BRIGHTNESS 0x0A
#define OSD_HUE 0x0B
#define OSD_BALANCE 0x0C
#define OSD_PANSCAN 0x50
// Start of OSD symbols in osd_font.pfb
#define OSD_CODEPOINTS 0xE000
#define OSD_PB_START 0x10
#define OSD_PB_0 0x11
#define OSD_PB_END 0x12
#define OSD_PB_1 0x13
// OSD symbols. osd_font.pfb has them starting from codepoint OSD_CODEPOINTS.
// Symbols with a value >= 32 are normal unicode codepoints.
enum mp_osd_font_codepoints {
OSD_PLAY = 0x01,
OSD_PAUSE = 0x02,
OSD_STOP = 0x03,
OSD_REW = 0x04,
OSD_FFW = 0x05,
OSD_CLOCK = 0x06,
OSD_CONTRAST = 0x07,
OSD_SATURATION = 0x08,
OSD_VOLUME = 0x09,
OSD_BRIGHTNESS = 0x0A,
OSD_HUE = 0x0B,
OSD_BALANCE = 0x0C,
OSD_PANSCAN = 0x50,
OSD_PB_START = 0x10,
OSD_PB_0 = 0x11,
OSD_PB_END = 0x12,
OSD_PB_1 = 0x13,
};
/* now in textform */
extern char * const sub_osd_names[];
extern char * const sub_osd_names_short[];
extern int sub_unicode;
extern int sub_utf8;
extern char *sub_cp;
extern int sub_pos;
extern int sub_width_p;
extern int sub_bg_color; /* subtitles background color */
extern int sub_bg_alpha;
extern int spu_alignment;
extern int spu_aamode;
extern float spu_gaussvar;
extern char *subtitle_font_encoding;
extern float text_font_scale_factor;
extern float osd_font_scale_factor;
extern float subtitle_font_radius;
extern float subtitle_font_thickness;
extern int subtitle_autoscale;
extern char *font_name;
extern char *sub_font_name;
extern float font_factor;
extern float sub_delay;
extern float sub_fps;
extern int sub_justify;
void osd_draw_text(struct osd_state *osd, int dxs, int dys,
void (*draw_alpha)(void *ctx, int x0, int y0, int w, int h,
unsigned char* src, unsigned char *srca,
int stride),
void *ctx);
void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys,
int left_border, int top_border, int right_border,
int bottom_border, int orig_w, int orig_h,
void (*draw_alpha)(void *ctx, int x0, int y0, int w,
int h, unsigned char* src,
unsigned char *srca,
int stride),
void *ctx);
struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib);
void osd_set_text(struct osd_state *osd, const char *text);
int osd_update(struct osd_state *osd, int dxs, int dys);
void vo_osd_changed(int new_value);
void vo_osd_reset_changed(void);
bool vo_osd_has_changed(struct osd_state *osd);
void vo_osd_resized(void);
int vo_osd_check_range_update(int,int,int,int);
void osd_free(struct osd_state *osd);
// used only by osd_ft.c or osd_libass.c
void osd_alloc_buf(mp_osd_obj_t* obj);
void vo_draw_text_from_buffer(mp_osd_obj_t* obj,void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx);
enum mp_osd_draw_flags {
OSD_DRAW_SUB_FILTER = (1 << 0),
OSD_DRAW_SUB_ONLY = (1 << 1),
};
// defined in osd_ft.c or osd_libass.c
void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t *obj);
void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t *obj);
void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t *obj);
void osd_draw(struct osd_state *osd, struct mp_osd_res res,
double video_pts, int draw_flags,
const bool formats[SUBBITMAP_COUNT],
void (*cb)(void *ctx, struct sub_bitmaps *imgs), void *cb_ctx);
struct mp_image;
bool osd_draw_on_image(struct osd_state *osd, struct mp_osd_res res,
double video_pts, int draw_flags, struct mp_image *dest);
struct mp_rect;
bool sub_bitmaps_bb(struct sub_bitmaps *imgs, struct mp_rect *out_bb);
// defined in osd_libass.c and osd_dummy.c
void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj,
struct sub_bitmaps *out_imgs);
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function);
void osd_font_invalidate(void);
void osd_font_load(struct osd_state *osd);
void osd_init_backend(struct osd_state *osd);
void osd_destroy_backend(struct osd_state *osd);