mirror of
https://github.com/mpv-player/mpv
synced 2025-04-01 23:00:41 +00:00
Merge branch 'osd_changes' into master
Conflicts: DOCS/man/en/options.rst
This commit is contained in:
commit
84829a4ea1
DOCS
Makefilecfg-mplayer.hcommand.cetc
image_writer.cimage_writer.hinput
libmpcodecs
mp_image.cmp_image.hsws_utils.csws_utils.hvd_ffmpeg.cvf.cvf.hvf_scale.cvf_scale.hvf_screenshot.cvf_sub.cvf_vo.cvfcap.h
libmpdemux
libvo
aspect.caspect.hbitmap_packer.cbitmap_packer.hcsputils.ccsputils.heosd_packer.ceosd_packer.hfastmemcpy.hgl_common.cgl_common.hgl_osd.cgl_osd.hosd.cosd.hosd_template.cvideo_out.cvideo_out.hvo_caca.cvo_corevideo.mvo_direct3d.cvo_image.cvo_lavc.cvo_opengl.cvo_opengl_old.cvo_opengl_shaders.glslvo_vdpau.cvo_x11.cvo_xv.c
mpcommon.hmplayer.cscreenshot.cscreenshot.hsub
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
=========================== = ==================================================
|
||||
|
@ -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
|
||||
=======
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
12
Makefile
12
Makefile
@ -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
|
||||
|
@ -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},
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
199
libmpcodecs/sws_utils.c
Normal 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
33
libmpcodecs/sws_utils.h
Normal 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
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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*
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
@ -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, ¶m) == 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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
@ -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 */
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
324
libvo/gl_osd.c
Normal 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
43
libvo/gl_osd.h
Normal 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
|
224
libvo/osd.c
224
libvo/osd.c
@ -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;
|
||||
}
|
34
libvo/osd.h
34
libvo/osd.h
@ -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 */
|
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
||||
|
135
libvo/vo_lavc.c
135
libvo/vo_lavc.c
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
425
libvo/vo_vdpau.c
425
libvo/vo_vdpau.c
@ -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))
|
||||
|
@ -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)
|
||||
|
218
libvo/vo_xv.c
218
libvo/vo_xv.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
132
mplayer.c
@ -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) {
|
||||
|
71
screenshot.c
71
screenshot.c
@ -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);
|
||||
}
|
||||
|
10
screenshot.h
10
screenshot.h
@ -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);
|
||||
|
53
sub/ass_mp.c
53
sub/ass_mp.c
@ -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,
|
||||
|
11
sub/ass_mp.h
11
sub/ass_mp.h
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
530
sub/draw_bmp.c
Normal 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
17
sub/draw_bmp.h
Normal 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
|
@ -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
89
sub/img_convert.c
Normal 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
15
sub/img_convert.h
Normal 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
|
@ -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};
|
||||
}
|
||||
|
234
sub/osd_libass.c
234
sub/osd_libass.c
@ -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);
|
||||
}
|
||||
|
5
sub/sd.h
5
sub/sd.h
@ -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);
|
||||
|
22
sub/sd_ass.c
22
sub/sd_ass.c
@ -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)
|
||||
|
133
sub/sd_lavc.c
133
sub/sd_lavc.c
@ -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);
|
||||
|
819
sub/spudec.c
819
sub/spudec.c
@ -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);
|
||||
}
|
||||
|
21
sub/spudec.h
21
sub/spudec.h
@ -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
432
sub/sub.c
@ -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
252
sub/sub.h
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user