|
View:
New views
4 Messages
—
Rating Filter:
Alert me
|
|
|
VAAPI support for xineSince a while i work on VAAPI support for xine.
The repository moved from Sourceforge to github : On the decoder side the ffmpeg VAAPI support is used.
On the output side i wrote a new plugin which is using VAAPI, The plugin is based on ideas found in mplayer and vdpau output plugin. cu Edgar Hucek ------------------------------------------------------------------------------ All the data continuously generated in your IT infrastructure contains a definitive record of customers, application performance, security threats, fraudulent activity and more. Splunk takes this data and makes sense of it. Business sense. IT sense. Common sense. http://p.sf.net/sfu/splunk-d2dcopy1 _______________________________________________ xine-devel mailing list xine-devel@... https://lists.sourceforge.net/lists/listinfo/xine-devel |
|
|
Re: VAAPI support for xineI demand that ebsi4711 may or may not have written...
> Since a while i work on VAAPI support for xine. > The repository moved from Sourceforge to github : > https://github.com/huceke/xine-lib-vaapi/commits/vaapi > and > https://github.com/huceke/xine-lib-vaapi/commits/vaapi-testing Would prefer this as a mercurial-format repository. However, there's mercurial-git, which should be sufficient to make this a non-problem. > On the decoder side the ffmpeg VAAPI support is used. > On the output side i wrote a new plugin which is using VAAPI, The plugin > is based on ideas found in mplayer and vdpau output plugin. Interesting. Any ABI or API changes (which are not merely extensions)? If there are, that makes this 1.2-only: such changes intended for 1.1 will be rejected outright. -- | Darren Salt | linux or ds at | nr. Ashington, | _ ASCII ribbon | using Debian | youmustbejoking | Northumberland | ( ) campaign against | GNU/Linux | ,demon,co,uk | | X HTML e-mail / \ www.asciiribbon.org No sentence fragments. ------------------------------------------------------------------------------ All of the data generated in your IT infrastructure is seriously valuable. Why? It contains a definitive record of application performance, security threats, fraudulent activity, and more. Splunk takes this data and makes sense of it. IT sense. And common sense. http://p.sf.net/sfu/splunk-d2dcopy2 _______________________________________________ xine-devel mailing list xine-devel@... https://lists.sourceforge.net/lists/listinfo/xine-devel |
|
|
Re: VAAPI support for xineThe VAAPI support is clear for 1.2. For the OSD it uses similiar functionality like VDPAU. ( ARGB Overlays. )
What i can say, there are no ABI changes from my side. Attached you a patch where you can see my changes
and additions. What's not done so far is the autoconf side, to check if libva is installed. There i would need help. Libva is a must for the support in the ffmpeg decoder. To make the support optinonal would end in an ifdef mess.
cu Edgar (gimli) Hucek
2011/9/26 Darren Salt <linux@...> I demand that ebsi4711 may or may not have written... [vaapi.patch] diff --git a/README.vaapi b/README.vaapi new file mode 100644 index 0000000..8ba7edc --- /dev/null +++ b/README.vaapi @@ -0,0 +1,97 @@ +Make sure you have the following in your ~/.xine/config. + +General ffmpeg settings : + +# Priorität für Dekoder ffmpeg-wmv8 +# numeric, default: 0 +engine.decoder_priorities.ffmpeg-wmv8:0 + +# Priorität für Dekoder ffmpeg-wmv9 +# numeric, default: 0 +engine.decoder_priorities.ffmpeg-wmv9:0 + +# Priorität für Dekoder ffmpegvideo +# numeric, default: 0 +engine.decoder_priorities.ffmpegvideo:1 + +# Priorität für Dekoder mpeg2 +# numeric, default: 0 +#engine.decoder_priorities.mpeg2:0 + +Video out settings : + +#vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob ). +# numeric, default: 0 +#video.output.vaapi_deinterlace:0 + +# vaapi: VDR osd height workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_height:0 + +# vaapi: VDR osd width workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_width:0 + +# VAAPI Mpeg2 softdecoding +# bool, default: 0 +#video.processing.vaapi_mpeg_softdec:0 + +# VAAPI Mpeg2 softdecoding deinterlace +# bool, default: 0 +#video.processing.vaapi_mpeg_softdec_deinterlace:0 + +# vaapi: opengl output rendering +# bool, default: 0 +#video.output.vaapi_opengl_render:0 + +# vaapi: opengl rendering tfp +# bool, default: 0 +#video.output.vaapi_opengl_use_tfp:0 + +# vaapi: set vaapi_guarded_render to 0 ( yes ) 1 ( no ) +# numeric, default: 0 +#video.output.vaapi_guarded_render:0 + +If you see crashes set guarded render mode. + +# vaapi: swap UV planes. +# bool, default: 0 +#video.output.vaapi_swap_uv_planes:0 + +Swap UV is a workaround for IronLake chipsets where the driver is bugy. + + +Notes on use with VDR. The OSD will have some delay. This is technical and can only be overcome +writting a complete new decoder which use VAAPI direct and not ffmpeg. + +For using unsclaed OSD there you can overwrite the OSD size in the config file. + +For xineliboutput use the defaults. + +For vdr-xine set in VDR the OSD size to 1920x1080 and use the following in ~/.xine/config : + +video.output.vaapi_vdr_osd_height:0 +video.output.vaapi_vdr_osd_width:0 + +The workaround is needed, because vdr-xine does not report the real unscaled OSD size. + + +When you get GPU hangs on Mpeg2 material set : + +video.processing.vaapi_mpeg_softdec:1 + +This disables VAAPI fpr Mpeg2. + + +You can use example.config.vaapi as an example config for xine. + +Call xine like : xine -V vaapi video.mkv + +xvba-vaapi hints: + +export LIBVA_DRIVER_NAME=xvba +export LIBVA_DRIVERS_PATH=/usr/lib/dri/ +video.output.vaapi_opengl_render:1 + + +Postprocessing does not work. Do not use "--post vdr_video --post vdr-audio --post vdr" in your xine call. diff --git a/example.config.vaapi b/example.config.vaapi new file mode 100644 index 0000000..901860b --- /dev/null +++ b/example.config.vaapi @@ -0,0 +1,749 @@ +# +# xine config file +# +.version:2 + +# Entries which are still set to their default values are commented out. +# Remove the '#' at the beginning of the line, if you want to change them. + +# Deinterlacing automatisch aktivieren +# bool, default: 0 +#gui.deinterlace_by_default:0 + +# Erfahrenheit einstellen +# { Beginner Advanced Expert Master of the known universe }, default: 0 +#gui.experience_level:Beginner + +# OSD-Unterstützung aktivieren +# bool, default: 1 +#gui.osd_enabled:1 + +# OSD Anzeigezeit [s] +# numeric, default: 3 +#gui.osd_timeout:3 + +# Benutzer fragen bei Wiedergabe mit nichtunterstütztem coder +# bool, default: 0 +#gui.play_anyway:0 + +# Automatische alte Playliste wiederherstellen +# bool, default: 0 +#gui.playlist_auto_reload:0 + +# Audio-Visualisierung +# { oscope fftscope fftgraph goom }, default: 0 +#gui.post_audio_plugin:oscope + +# gui skin Thema +# { xinetic }, default: 0 +#gui.skin:xinetic + +# xine-Verhalten für unerfahrene Benutzer anpassen +# bool, default: 1 +#gui.smart_mode:1 + +# Schnappschußverzeichnis +# string, default: /home/gimli +#gui.snapshotdir:/home/gimli + +# Startbildschirm anzeigen +# bool, default: 1 +#gui.splash:1 + +# Untertitel automatisch laden +# bool, default: 1 +#gui.subtitle_autoload:1 + +# Stil der Videoanimation +# { None Post Plugin Stream Animation }, default: 1 +#gui.visual_anim:Post Plugin + +# Fensterüberlagerung (mehr) +# bool, default: 0 +#gui.always_layer_above:0 + +# Audiomischpultmethode +# { Sound card Software }, default: 0 +#gui.audio_mixer_method:Sound card + +# Anzeigeverhalten von Bedienfeld +# bool, default: 0 +#gui.auto_panel_visibility:0 + +# Anzeigeverhalten des Ausgabefensters +# bool, default: 0 +#gui.auto_video_output_visibility:0 + +# Deinterlace-Plugin. +# string, default: tvtime:method=LinearBlend,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1 +#gui.deinterlace_plugin:tvtime:method=LinearBlend,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1 + +# Verhalten von Ereignissender +# bool, default: 1 +#gui.eventer_sticky:1 + +# Fensterüberlagerung +# bool, default: 0 +#gui.layer_above:0 + +# Unvergrößertes OSD benutzen +# bool, default: 1 +#gui.osd_use_unscaled:1 + +# Bildschirmschoner Resetintervall [s] +# numeric, default: 10 +#gui.screensaver_timeout:10 + +# Menu Tastenkürzelstil +# { Windows style Emacs style }, default: 0 +#gui.shortcut_style:Windows style + +# Datenstrominformationen +# bool, default: 0 +#gui.sinfo_auto_update:0 + +# Skin-Server URL +# string, default: http://xine.sourceforge.net/skins/skins.slx +#gui.skin_server_url:http://xine.sourceforge.net/skins/skins.slx + +# Kapitelspringen +# bool, default: 1 +#gui.skip_by_chapter:1 + +# Neue Datemstromgröße verändert Ausgabefenstergröße +# bool, default: 1 +#gui.stream_resize_window:1 + +# Hinweiszeit (ms) +# numeric, default: 5000 +#gui.tips_timeout:5000 + +# gui Hinweise sichtbar +# bool, default: 1 +#gui.tips_visible:1 + +# Name des Video-Bildschirms +# string, default: +#gui.video_display: + +# Synchrones X-Protokoll (Fehlersuche) +# bool, default: 0 +#gui.xsynchronize:0 + +# Doppelte Größe für kleine Datemströme (Erfordert stream_resize_window) +# bool, default: 0 +#gui.zoom_small_stream:0 + +# Logo MRL +# string, default: /usr/share/xine/skins/xine-ui_logo.png +#gui.logo_mrl:/usr/share/xine/skins/xine-ui_logo.png + +# Benutze XVidModeExtension beim Umschalten auf Vollbild +# bool, default: 0 +#gui.use_xvidext:0 + +# Höhe für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_height:-8192 + +# Breite für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_width:-8192 + +# X-Koordinate für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_x:-8192 + +# Y-Koordinate für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_y:-8192 + +# Zu nutzende Bildschirme im Xinerama Vollbildmodus (z.B. 0 2 3) +# string, default: 0 1 +#gui.xinerama_use_screens:0 1 + +# Verstärkungslevel +# [0..200], default: 100 +#gui.amp_level:100 + +# gui Fenster sichtbar +# bool, default: 1 +gui.panel_visible:0 + +# numeric, default: 200 +#gui.panel_x:200 + +# numeric, default: 100 +#gui.panel_y:100 + +gui.setup_x:81 + +gui.setup_y:104 + +# color specification yuv-opacity +# string, default: 8080c0-f +#gui.osdmenu.color_focused_button:8080c0-f + +# color specification yuv-opacity +# string, default: 808080-f +#gui.osdmenu.color_focused_slider:808080-f + +# color specification yuv-opacity +# string, default: ff8080-f +#gui.osdmenu.color_focused_slider_knob:ff8080-f + +# color specification yuv-opacity +# string, default: 808080-f +#gui.osdmenu.color_focused_text_border:808080-f + +# color specification yuv-opacity +# string, default: ff8080-f +#gui.osdmenu.color_focused_text_foreground:ff8080-f + +# color specification yuv-opacity +# string, default: 0080c0-f +#gui.osdmenu.color_label_border:0080c0-f + +# color specification yuv-opacity +# string, default: c08080-f +#gui.osdmenu.color_label_foreground:c08080-f + +# color specification yuv-opacity +# string, default: 0080c0-f +#gui.osdmenu.color_label_window:0080c0-f + +# color specification yuv-opacity +# string, default: 008000-f +#gui.osdmenu.color_slider:008000-f + +# color specification yuv-opacity +# string, default: ffff00-f +#gui.osdmenu.color_slider_knob:ffff00-f + +# color specification yuv-opacity +# string, default: 008000-f +#gui.osdmenu.color_text_border:008000-f + +# color specification yuv-opacity +# string, default: ffff00-f +#gui.osdmenu.color_text_foreground:ffff00-f + +# color specification yuv-opacity +# string, default: 0080c0-f +#gui.osdmenu.color_text_window:0080c0-f + +# directory a media in dvd device will be mounted +# string, default: /dvd +#gui.osdmenu.dvd_mountpoint:/dvd + +# Farbpalette (Vordergrund-Rand-Hintergrund) für Untertitel und OSD +# { white-black-transparent white-none-transparent white-none-translucid yellow-black-transparent }, default: 0 +#ui.osd.text_palette:white-black-transparent + +# Änderungen an Hardwaremixer melden +# bool, default: 1 +#audio.alsa_hw_mixer:1 + +# Zu benutzender Audiotreiber +# { auto null alsa oss file none }, default: 0 +#audio.driver:auto + +# Benutze dynamische A/52 Bereichskomprimierung +# bool, default: 0 +#audio.a52.dynamic_range:0 + +# Heruntermischen zu Zweikanal Stereo Raumklang +# bool, default: 0 +#audio.a52.surround_downmix:0 + +# A/52 Lautstärke +# [0..200], default: 100 +#audio.a52.level:100 + +# Gerät für Monoausgabe +# string, default: default +#audio.device.alsa_default_device:default + +# Gerät für Stereoausgabe +# string, default: plug:front:default +audio.device.alsa_front_device:default + +# ALSA Mixergerät +# string, default: PCM +#audio.device.alsa_mixer_name:PCM + +# Soundkarte unterstützt mmap +# bool, default: 0 +#audio.device.alsa_mmap_enable:0 + +# Gerät für 5.1-Kanalausgabe +# string, default: iec958:AES0=0x6,AES1=0x82,AES2=0x0,AES3=0x2 +#audio.device.alsa_passthrough_device:iec958:AES0=0x6,AES1=0x82,AES2=0x0,AES3=0x2 + +# Gerät für 4-Kanalausgabe +# string, default: plug:surround40:0 +#audio.device.alsa_surround40_device:plug:surround40:0 + +# Gerät für 5.1-Kanalausgabe +# string, default: plug:surround51:0 +#audio.device.alsa_surround51_device:plug:surround51:0 + +# Lautsprecherplazierung +# { Mono 1.0 Stereo 2.0 Headphones 2.0 Stereo 2.1 Surround 3.0 Surround 4.0 Surround 4.1 Surround 5.0 Surround 5.1 Surround 6.0 Surround 6.1 Surround 7.1 Pass Through }, default: 1 +#audio.output.speaker_arrangement:Stereo 2.0 + +# Versatz für digitales Passthrough +# numeric, default: 0 +#audio.synchronization.passthrough_offset:0 + +# Audiowiedergabe während langsamer/schneller Geschwindigkeit +# bool, default: 0 +#audio.synchronization.slow_fast_audio:0 + +# Methode für Audio/Videosynchronisation +# { metronom feedback resample }, default: 0 +#audio.synchronization.av_sync_method:metronom feedback + +# Wenn !=0, immer auf diese Rate anpassen +# numeric, default: 0 +#audio.synchronization.force_rate:0 + +# Resampling benutzen +# { auto off on }, default: 0 +#audio.synchronization.resample_mode:auto + +# Startlautstärke +# [0..100], default: 50 +#audio.volume.mixer_volume:50 + +# Lautstärke beim Starten wiederherstellen +# bool, default: 0 +#audio.volume.remember_volume:0 + +# Zu benutzender Videotreiber +# { auto vdpau xv vaapi opengl raw xshm none fb }, default: 0 +video.driver:vaapi + +# Alle Videoskalierungen deaktivieren +# bool, default: 0 +#video.output.disable_scaling:0 + +# Horizontale Bildposition im Ausgabefenster +# [0..100], default: 50 +#video.output.horizontal_position:50 + +# vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob ). +# numeric, default: 0 +#video.output.vaapi_deinterlace:0 + +# vaapi: indirect output rendering +# bool, default: 0 +#video.output.vaapi_indirect_render:0 + +# vaapi: VDR osd height workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_height:0 + +# vaapi: VDR osd width workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_width:0 + +# Vertikale Bildposition im Ausgabefenster +# [0..100], default: 50 +#video.output.vertical_position:50 + +# Choose speed over specification compliance +# bool, default: 0 +#video.processing.ffmpeg_choose_speed_over_accuracy:0 + +# Enable usage of VAAPI +# bool, default: 1 +#video.processing.ffmpeg_enable_vaapi:1 + +# Qualität der MPEG-4 Nachbearbeitungsstufe +# [0..6], default: 3 +#video.processing.ffmpeg_pp_quality:3 + +# Skip loop filter +# { default none nonref bidir nonkey all }, default: 0 +#video.processing.ffmpeg_skip_loop_filter:default + +# FFmpeg video decoding thread count +# numeric, default: 1 +#video.processing.ffmpeg_thread_count:1 + +# VAAPI Mpeg2 softdecoding +# bool, default: 0 +#video.processing.vaapi_mpeg_sofdec:0 + +# VAAPI Mpeg2 softdecoding deinterlace +# bool, default: 0 +#video.processing.vaapi_mpeg_sofdec_deinterlace:0 + +# Gerät für CD-Audio +# string, default: /dev/cdrom +#media.audio_cd.device:/dev/cdrom + +# Laufwerk auf diesen Faktor verlangsamen +# numeric, default: 4 +#media.audio_cd.drive_slowdown:4 + +# CDDB abfragen +# bool, default: 1 +#media.audio_cd.use_cddb:1 + +# CDDB Serverport +# numeric, default: 8880 +#media.audio_cd.cddb_port:8880 + +# CDDB Servername +# string, default: freedb.freedb.org +#media.audio_cd.cddb_server:freedb.freedb.org + +# BluRay player country code +# string, default: en +#media.bluray.country:en + +# device used for BluRay playback +# string, default: /dev/dvd +#media.bluray.device:/dev/dvd + +# default language for BluRay playback +# string, default: eng +#media.bluray.language:eng + +# BluRay mount point +# string, default: /mnt/bluray +#media.bluray.mountpoint:/mnt/bluray + +# parental control age limit (1-99) +# numeric, default: 99 +#media.bluray.parental:99 + +# BluRay player region code (1=A, 2=B, 4=C) +# numeric, default: 7 +#media.bluray.region:7 + +# Pfad zum Sichen von Datenströmen +# string, default: +#media.capture.save_dir: + +# Nummer der zu benutzenden DVB-Karte. +# numeric, default: 0 +#media.dvb.adapter:0 + +# Zuletzt gesehenen DVB-Kanal vermerken +# bool, default: 1 +#media.dvb.remember_channel:1 + +# Number of seconds until tuning times out. +# numeric, default: 0 +#media.dvb.tuning_timeout:0 + +# Enable the DVB GUI +# bool, default: 1 +#media.dvb.gui_enabled:1 + +# Zuletzt gesehener DVB-Kanal +# numeric, default: -1 +#media.dvb.last_channel:-1 + +# Standardsprache für die DVD-Wiedergabe +# string, default: en +#media.dvd.language:en + +# Region (1-8), aus der der DVD Player zu kommen scheint +# numeric, default: 1 +#media.dvd.region:1 + +# Gerät für DVD Wiedergabe +# string, default: /dev/dvd +#media.dvd.device:/dev/dvd + +# Pfad zum RAW-Device des DVD-Laufwerks +# string, default: /dev/rdvd +#media.dvd.raw_device:/dev/rdvd + +# Vorauseilendes Caching benutzen +# bool, default: 1 +#media.dvd.readahead:1 + +# CSS Entschlüsselungsmethode +# { key disc title }, default: 0 +#media.dvd.css_decryption_method:key + +# Wiedergabemodus falls Titel/Kapitel angegeben +# { entire dvd one chapter }, default: 0 +#media.dvd.play_single_chapter:entire dvd + +# Einheit beim Suchen +# { seek in program chain seek in program }, default: 0 +#media.dvd.seek_behaviour:seek in program chain + +# Einheit für die Überspringen-Aktion +# { skip program skip part skip title }, default: 0 +#media.dvd.skip_behaviour:skip program + +# Startverzeichnis für Dateisuche +# string, default: /home/gimli +#media.files.origin_path:/home/gimli + +# Versteckte Dateien anzeigen +# bool, default: 0 +#media.files.show_hidden_files:0 + +# Netzwerkbandbreite +# { 14.4 Kbps (Modem) 19.2 Kbps (Modem) 28.8 Kbps (Modem) 33.6 Kbps (Modem) 34.4 Kbps (Modem) 57.6 Kbps (Modem) 115.2 Kbps (ISDN) 262.2 Kbps (Cable/DSL) 393.2 Kbps (Cable/DSL) 524.3 Kbps (Cable/DSL) }, default: 10 + +# Zeitüberschreitung für Netzwerkdatenströme (in Sekunden) +# numeric, default: 30 +#media.network.timeout:30 + +# Domains, die den HTTP Proxy umgehen +# string, default: +#media.network.http_no_proxy: + +# HTTP Proxy Rechnername +# string, default: +#media.network.http_proxy_host: + +# HTTP Proxy Passwort +# string, default: +#media.network.http_proxy_password: + +# HTTP Proxy Portnummer +# numeric, default: 80 +#media.network.http_proxy_port:80 + +# HTTP Proxy Benutzername +# string, default: +#media.network.http_proxy_user: + +# MMS-Protokoll +# { auto TCP HTTP }, default: 0 +#media.network.mms_protocol:auto + +# default VDR host +# string, default: xvdr://127.0.0.1#nocache;demux:mpeg_block +#media.xvdr.default_mrl:xvdr://127.0.0.1#nocache;demux:mpeg_block + +# Fast (low-quality) OSD scaling +# bool, default: 0 +#media.xvdr.fast_osd_scaling:0 + +# number of buffers for HD content +# numeric, default: 2500 +#media.xvdr.num_buffers_hd:2500 + +# SCR-Treshold for HD-Playback (%) +# numeric, default: 40 +#media.xvdr.scr_treshold_hd:40 + +# SCR-Treshold for SD-Playback (%) +# numeric, default: 50 +#media.xvdr.scr_treshold_sd:50 + +# SRC tuning step +# numeric, default: 5000 +#media.xvdr.scr_tuning_step:5000 + +# Smoother SRC tuning +# bool, default: 0 +#media.xvdr.smooth_scr_tuning:0 + +# opacity for the black parts of bitmapped subtitles +# [0..100], default: 67 +#subtitles.bitmap.black_opacity:67 + +# opacity for the colour parts of bitmapped subtitles +# [0..100], default: 100 +#subtitles.bitmap.colour_opacity:100 + +# Untertitelgröße +# { tiny small normal large very large huge }, default: 1 +#subtitles.separate.subtitle_size:small + +# Vertikaler Versatz für Untertitel +# numeric, default: 0 +#subtitles.separate.vertical_offset:0 + +# Zeichensatz für Untertitel +# string, default: sans +#subtitles.separate.font:sans + +# Zeichenkodierung für Untertitel +# string, default: iso-8859-1 +#subtitles.separate.src_encoding:iso-8859-1 + +# Benutze unskaliertes OSD falls möglich +# bool, default: 1 +#subtitles.separate.use_unscaled_osd:1 + +# Zu generierende Bilder/Sekunde +# numeric, default: 14 +#effects.goom.fps:14 + +# Goom Bildhöhe +# numeric, default: 240 +#effects.goom.height:240 + +# Goom Bildbreite +# numeric, default: 320 +#effects.goom.width:320 + +# Farbraumkonvertierungsmethode +# { Fast but not photorealistic Slow but looks better }, default: 0 +#effects.goom.csc_method:Fast but not photorealistic + +# Anzahl der Audiopuffer +# numeric, default: 230 +#engine.buffers.audio_num_buffers:230 + +# Anzahl der Videopuffer +# numeric, default: 500 +#engine.buffers.video_num_buffers:500 + +# Standardanzahl von Videobildern +# numeric, default: 21 +engine.buffers.video_num_frames:22 + +# disable decoder flush at discontinuity +# bool, default: 0 +#engine.decoder.disable_flush_at_discontinuity:0 + +# disable decoder flush from video out +# bool, default: 0 +#engine.decoder.disable_flush_from_video_out:0 + +# Priorität für Dekoder a/52 +# numeric, default: 0 +#engine.decoder_priorities.a/52:0 + +# Priorität für Dekoder bitplane +# numeric, default: 0 +#engine.decoder_priorities.bitplane:0 + +# Priorität für Dekoder dts +# numeric, default: 0 +#engine.decoder_priorities.dts:0 + +# Priorität für Dekoder dvaudio +# numeric, default: 0 +#engine.decoder_priorities.dvaudio:0 + +# Priorität für Dekoder faad +# numeric, default: 0 +#engine.decoder_priorities.faad:0 + +# Priorität für Dekoder ffmpeg-wmv8 +# numeric, default: 0 +#engine.decoder_priorities.ffmpeg-wmv8:0 + +# Priorität für Dekoder ffmpeg-wmv9 +# numeric, default: 0 +#engine.decoder_priorities.ffmpeg-wmv9:0 + +# Priorität für Dekoder ffmpegaudio +# numeric, default: 0 +#engine.decoder_priorities.ffmpegaudio:0 + +# Priorität für Dekoder ffmpegvideo +# numeric, default: 0 +engine.decoder_priorities.ffmpegvideo:1 + +# Priorität für Dekoder flacdec +# numeric, default: 0 +#engine.decoder_priorities.flacdec:0 + +# Priorität für Dekoder gsm610 +# numeric, default: 0 +#engine.decoder_priorities.gsm610:0 + +# Priorität für Dekoder mad +# numeric, default: 0 +#engine.decoder_priorities.mad:0 + +# Priorität für Dekoder mpeg2 +# numeric, default: 0 +#engine.decoder_priorities.mpeg2:0 + +# Priorität für Dekoder pcm +# numeric, default: 0 +#engine.decoder_priorities.pcm:0 + +# Priorität für Dekoder rgb +# numeric, default: 0 +#engine.decoder_priorities.rgb:0 + +# Priorität für Dekoder spucc +# numeric, default: 0 +#engine.decoder_priorities.spucc:0 + +# Priorität für Dekoder spucmml +# numeric, default: 0 +#engine.decoder_priorities.spucmml:0 + +# Priorität für Dekoder spudec +# numeric, default: 0 +#engine.decoder_priorities.spudec:0 + +# Priorität für Dekoder spudvb +# numeric, default: 0 +#engine.decoder_priorities.spudvb:0 + +# Priorität für Dekoder spuhdmv +# numeric, default: 0 +#engine.decoder_priorities.spuhdmv:0 + +# Priorität für Dekoder sputext +# numeric, default: 0 +#engine.decoder_priorities.sputext:0 + +# Priorität für Dekoder theora +# numeric, default: 0 +#engine.decoder_priorities.theora:0 + +# Priorität für Dekoder vdpau_h264 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_h264:0 + +# Priorität für Dekoder vdpau_mpeg12 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_mpeg12:0 + +# Priorität für Dekoder vdpau_mpeg4 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_mpeg4:0 + +# Priorität für Dekoder vdpau_vc1 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_vc1:0 + +# Priorität für Dekoder vorbis +# numeric, default: 0 +#engine.decoder_priorities.vorbis:0 + +# Priorität für Dekoder yuv +# numeric, default: 0 +#engine.decoder_priorities.yuv:0 + +# Medienformaterkennungsstrategie +# { default reverse content extension }, default: 0 +#engine.demux.strategy:default + +# xines Methode zum Kopieren von Speicher +# { probe libc kernel mmx mmxext sse }, default: 0 +engine.performance.memcpy_method:libc + +# Erlaubter Prozentsatz für verworfene Frames +# numeric, default: 10 +#engine.performance.warn_discarded_threshold:10 + +# Erlaubter Prozentsatz für übersprungene Frames +# numeric, default: 10 +#engine.performance.warn_skipped_threshold:10 + +# Erlaube implizierte Änderungen an Konfiguration (z.B. durch MRL) +# bool, default: 0 +#misc.implicit_config:0 + diff --git a/include/xine.h b/include/xine.h index d6f54b7..e7a2ee3 100644 --- a/include/xine.h +++ b/include/xine.h @@ -452,6 +452,7 @@ int xine_get_current_frame_data (xine_stream_t *stream, #define XINE_IMGFMT_XVMC (('C'<<24)|('M'<<16)|('v'<<8)|'X') #define XINE_IMGFMT_XXMC (('C'<<24)|('M'<<16)|('x'<<8)|'X') #define XINE_IMGFMT_VDPAU (('A'<<24)|('P'<<16)|('D'<<8)|'V') +#define XINE_IMGFMT_VAAPI (('P'<<24)|('A'<<16)|('A'<<8)|'V') /* get current xine's virtual presentation timestamp (1/90000 sec) * note: this is mostly internal data. diff --git a/include/xine/video_out.h b/include/xine/video_out.h index 5a04011..d648f5d 100644 --- a/include/xine/video_out.h +++ b/include/xine/video_out.h @@ -303,6 +303,7 @@ struct xine_video_port_s { #define VO_CAP_VDPAU_MPEG12 0x00000100 /* driver can use VDPAU for mpeg1/2 */ #define VO_CAP_VDPAU_VC1 0x00000200 /* driver can use VDPAU for VC1 */ #define VO_CAP_VDPAU_MPEG4 0x00000400 /* driver can use VDPAU for mpeg4-part2 */ +#define VO_CAP_VAAPI 0x00000600 /* driver can use VAAPI */ #define VO_CAP_HUE 0x00010000 #define VO_CAP_SATURATION 0x00020000 #define VO_CAP_CONTRAST 0x00040000 diff --git a/src/combined/ffmpeg/Makefile.am b/src/combined/ffmpeg/Makefile.am index c6509ed..dc2622b 100644 --- a/src/combined/ffmpeg/Makefile.am +++ b/src/combined/ffmpeg/Makefile.am @@ -18,12 +18,12 @@ DISTCLEANFILES = $(ff_generated) EXTRA_DIST = xine_video.list xine_audio.list mkcodeclist.pl xineplug_decode_ff_la_SOURCES = ffmpeg_decoder.c ff_audio_decoder.c ff_video_decoder.c \ - ff_mpeg_parser.c ffmpeg_decoder.h ff_mpeg_parser.h + ff_mpeg_parser.c ffmpeg_decoder.h ff_mpeg_parser.h nodist_xineplug_decode_ff_la_SOURCES = ffmpeg_config.h xineplug_decode_ff_la_CFLAGS = $(AM_CFLAGS) $(FFMPEG_CFLAGS) $(FFMPEG_POSTPROC_CFLAGS) -xineplug_decode_ff_la_LIBADD = $(XINE_LIB) $(MLIB_LIBS) -lm $(ZLIB_LIBS) \ +xineplug_decode_ff_la_LIBADD = $(XINE_LIB) $(MLIB_LIBS) $(ZLIB_LIBS) \ $(FFMPEG_LIBS) $(AVUTIL_LIBS) $(FFMPEG_POSTPROC_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) xineplug_decode_ff_la_LDFLAGS = $(AM_LDFLAGS) $(IMPURE_TEXT_LDFLAGS) diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c index 5e4967f..20af8d1 100644 --- a/src/combined/ffmpeg/ff_video_decoder.c +++ b/src/combined/ffmpeg/ff_video_decoder.c @@ -50,6 +50,9 @@ # include <libpostproc/postprocess.h> #endif +#include <libavcodec/vaapi.h> +#include "accel_vaapi.h" + #define VIDEOBUFSIZE (128*1024) #define SLICE_BUFFER_SIZE (1194*1024) @@ -65,6 +68,10 @@ # define pp_mode pp_mode_t #endif +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 112) +# define DEPRECATED_AVCODEC_THREAD_INIT 1 +#endif + typedef struct ff_video_decoder_s ff_video_decoder_t; typedef struct ff_video_class_s { @@ -74,6 +81,9 @@ typedef struct ff_video_class_s { int thread_count; int8_t skip_loop_filter_enum; int8_t choose_speed_over_accuracy; + int enable_vaapi; + int vaapi_mpeg_softdec; + int vaapi_mpeg_softdec_deinterlace; xine_t *xine; } ff_video_class_t; @@ -137,8 +147,12 @@ struct ff_video_decoder_s { #ifdef LOG enum PixelFormat debug_fmt; #endif -}; + struct vaapi_context vaapi_context; + vaapi_accel_t *accel; + vo_frame_t *accel_img; + uint8_t set_stream_info; +}; static void set_stream_info(ff_video_decoder_t *this) { _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->bih.biWidth); @@ -162,13 +176,72 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ this->aspect_ratio = (double)width / (double)height; this->aspect_ratio_prio = 1; lprintf("default aspect ratio: %f\n", this->aspect_ratio); - set_stream_info(this); + this->set_stream_info = 1; } } avcodec_align_dimensions(context, &width, &height); - if( this->context->pix_fmt != PIX_FMT_YUV420P && this->context->pix_fmt != PIX_FMT_YUVJ420P ) { + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + + av_frame->opaque = NULL; + av_frame->data[0] = NULL; + av_frame->data[1] = NULL; + av_frame->data[2] = NULL; + av_frame->data[3] = NULL; + av_frame->type = FF_BUFFER_TYPE_USER; + av_frame->age = 1; + av_frame->reordered_opaque = context->reordered_opaque; + + if(!this->accel->guarded_render(this->accel_img)) { + img = this->stream->video_out->get_frame (this->stream->video_out, + width, + height, + this->aspect_ratio, + this->output_format, + VO_BOTH_FIELDS|this->frame_flags); + + av_frame->opaque = img; + xine_list_push_back(this->dr1_frames, av_frame); + + vaapi_accel_t *accel = (vaapi_accel_t*)img->accel_data; + ff_vaapi_surface_t *va_surface = accel->get_vaapi_surface(img); + + if(va_surface) { + av_frame->data[0] = (void *)va_surface;//(void *)(uintptr_t)va_surface->va_surface_id; + av_frame->data[3] = (void *)(uintptr_t)va_surface->va_surface_id; + } + } else { + ff_vaapi_surface_t *va_surface = this->accel->get_vaapi_surface(this->accel_img); + + if(va_surface) { + av_frame->data[0] = (void *)va_surface;//(void *)(uintptr_t)va_surface->va_surface_id; + av_frame->data[3] = (void *)(uintptr_t)va_surface->va_surface_id; + } + } + + lprintf("1: 0x%08x\n", av_frame->data[3]); + + av_frame->linesize[0] = 0; + av_frame->linesize[1] = 0; + av_frame->linesize[2] = 0; + av_frame->linesize[3] = 0; + + this->is_direct_rendering_disabled = 1; + + return 0; + } + + /* on vaapi out do not use direct rendeing */ + if(this->class->enable_vaapi) { + this->output_format = XINE_IMGFMT_YV12; + } + + int guarded_render = 0; + if(this->accel) + guarded_render = this->accel->guarded_render(this->accel_img); + + if( (this->context->pix_fmt != PIX_FMT_YUV420P && this->context->pix_fmt != PIX_FMT_YUVJ420P) || guarded_render) { if (!this->is_direct_rendering_disabled) { xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: unsupported frame format, DR1 disabled.\n")); @@ -235,6 +308,18 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ static void release_buffer(struct AVCodecContext *context, AVFrame *av_frame){ ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque; + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD ) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)av_frame->data[0]; + if(va_surface != NULL) { + this->accel->release_vaapi_surface(this->accel_img, va_surface); + lprintf("release_buffer: va_surface_id 0x%08x\n", (unsigned int)av_frame->data[3]); + } + } + } + + lprintf("3: 0x%08x\n", av_frame->data[3]); + if (av_frame->type == FF_BUFFER_TYPE_USER) { if ( av_frame->opaque ) { vo_frame_t *img = (vo_frame_t *)av_frame->opaque; @@ -245,7 +330,7 @@ static void release_buffer(struct AVCodecContext *context, AVFrame *av_frame){ xine_list_iterator_t it; it = xine_list_find(this->dr1_frames, av_frame); - assert(it); + //assert(it); if( it != NULL ) xine_list_remove(this->dr1_frames, it); } else { @@ -280,6 +365,51 @@ static const int skip_loop_filter_enum_values[] = { AVDISCARD_ALL }; +static enum PixelFormat get_format(struct AVCodecContext *context, const enum PixelFormat *fmt) +{ + int i, profile; + ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque; + + if(!this->class->enable_vaapi || !this->accel_img) + return PIX_FMT_YUV420P; + + vaapi_accel_t *accel = (vaapi_accel_t*)this->accel_img->accel_data; + + for (i = 0; fmt[i] != PIX_FMT_NONE; i++) { + if (fmt[i] != PIX_FMT_VAAPI_VLD) + continue; + + profile = accel->profile_from_imgfmt(this->accel_img, fmt[i], context->codec_id, this->class->vaapi_mpeg_softdec); + + if (profile >= 0) { + VAStatus status; + + status = accel->vaapi_init(this->accel_img, profile, context->width, context->height, 0); + + if( status == VA_STATUS_SUCCESS ) { + ff_vaapi_context_t *va_context = accel->get_context(this->accel_img); + + if(!va_context) + return PIX_FMT_YUV420P; + + context->draw_horiz_band = NULL; + context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; + context->dsp_mask = 0; + + this->vaapi_context.config_id = va_context->va_config_id; + this->vaapi_context.context_id = va_context->va_context_id; + this->vaapi_context.display = va_context->va_display; + + context->hwaccel_context = &this->vaapi_context; + this->pts = 0; + + return fmt[i]; + } + } + } + return PIX_FMT_YUV420P; +} + static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) { size_t i; @@ -291,6 +421,8 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) pthread_mutex_lock(&ffmpeg_lock); this->codec = avcodec_find_decoder(ff_video_lookup[i].id); pthread_mutex_unlock(&ffmpeg_lock); + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, "ffmpeg_video_dec: video codec %s\n", + ff_video_lookup[i].name); _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, ff_video_lookup[i].name); break; @@ -325,6 +457,47 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) if (this->class->choose_speed_over_accuracy) this->context->flags2 |= CODEC_FLAG2_FAST; + if(this->class->enable_vaapi) + { + this->class->thread_count = this->context->thread_count = 1; + + this->context->skip_loop_filter = AVDISCARD_DEFAULT; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: force AVDISCARD_DEFAULT for VAAPI\n")); + } else { + this->context->skip_loop_filter = skip_loop_filter_enum_values[this->class->skip_loop_filter_enum]; + } + + if (this->class->thread_count > 1) { + if (this->codec->id != CODEC_ID_SVQ3 +#ifndef DEPRECATED_AVCODEC_THREAD_INIT + && avcodec_thread_init(this->context, this->class->thread_count) != -1 +#endif + ) + this->context->thread_count = this->class->thread_count; + } + + /* enable direct rendering by default */ + this->output_format = XINE_IMGFMT_YV12; +#ifdef ENABLE_DIRECT_RENDERING + if( this->codec->capabilities & CODEC_CAP_DR1 && this->codec->id != CODEC_ID_H264 ) { + this->context->get_buffer = get_buffer; + this->context->release_buffer = release_buffer; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: direct rendering enabled\n")); + } + + if( this->class->enable_vaapi ) { + this->output_format = XINE_IMGFMT_VAAPI; + this->context->get_buffer = get_buffer; + this->context->reget_buffer = get_buffer; + this->context->release_buffer = release_buffer; + this->context->get_format = get_format; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: direct rendering enabled\n")); + } +#endif + pthread_mutex_lock(&ffmpeg_lock); if (avcodec_open (this->context, this->codec) < 0) { pthread_mutex_unlock(&ffmpeg_lock); @@ -351,15 +524,6 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) return; } } - - if (this->class->thread_count > 1) { - if (this->codec->id != CODEC_ID_SVQ3 - && avcodec_thread_init(this->context, this->class->thread_count) != -1) - this->context->thread_count = this->class->thread_count; - } - - this->context->skip_loop_filter = skip_loop_filter_enum_values[this->class->skip_loop_filter_enum]; - pthread_mutex_unlock(&ffmpeg_lock); lprintf("lavc decoder opened\n"); @@ -382,41 +546,50 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) this->skipframes = 0; - /* enable direct rendering by default */ - this->output_format = XINE_IMGFMT_YV12; -#ifdef ENABLE_DIRECT_RENDERING - if( this->codec->capabilities & CODEC_CAP_DR1 && this->codec->id != CODEC_ID_H264 ) { - this->context->get_buffer = get_buffer; - this->context->release_buffer = release_buffer; - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - _("ffmpeg_video_dec: direct rendering enabled\n")); - } -#endif - /* flag for interlaced streams */ this->frame_flags = 0; /* FIXME: which codecs can be interlaced? FIXME: check interlaced DCT and other codec specific info. */ - switch( codec_type ) { - case BUF_VIDEO_DV: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_MPEG: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_MJPEG: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_HUFFYUV: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_H264: - this->frame_flags |= VO_INTERLACED_FLAG; - break; + if(!this->class->enable_vaapi) { + switch( codec_type ) { + case BUF_VIDEO_DV: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_MPEG: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_MJPEG: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_HUFFYUV: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_H264: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + } } } +static void vaapi_enable_vaapi(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->enable_vaapi = entry->num_value; +} + +static void vaapi_mpeg_softdec_func(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->vaapi_mpeg_softdec = entry->num_value; +} + +static void vaapi_mpeg_softdec_deinterlace(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->vaapi_mpeg_softdec_deinterlace = entry->num_value; +} + static void choose_speed_over_accuracy_cb(void *user_data, xine_cfg_entry_t *entry) { ff_video_class_t *class = (ff_video_class_t *) user_data; @@ -543,7 +716,7 @@ static int ff_handle_mpeg_sequence(ff_video_decoder_t *this, mpeg_parser_t *pars return 1; } -static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { +static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img, AVFrame *av_frame) { int y; uint8_t *dy, *du, *dv, *sy, *su, *sv; @@ -555,9 +728,9 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { dy = img->base[0]; du = img->base[1]; dv = img->base[2]; - sy = this->av_frame->data[0]; - su = this->av_frame->data[1]; - sv = this->av_frame->data[2]; + sy = av_frame->data[0]; + su = av_frame->data[1]; + sv = av_frame->data[2]; /* Some segfaults & heap corruption have been observed with img->height, * so we use this->bih.biHeight instead (which is the displayed height) @@ -567,18 +740,18 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { yuv9_to_yv12( /* Y */ - this->av_frame->data[0], - this->av_frame->linesize[0], + av_frame->data[0], + av_frame->linesize[0], img->base[0], img->pitches[0], /* U */ - this->av_frame->data[1], - this->av_frame->linesize[1], + av_frame->data[1], + av_frame->linesize[1], img->base[1], img->pitches[1], /* V */ - this->av_frame->data[2], - this->av_frame->linesize[2], + av_frame->data[2], + av_frame->linesize[2], img->base[2], img->pitches[2], /* width x height */ @@ -589,18 +762,18 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { yuv411_to_yv12( /* Y */ - this->av_frame->data[0], - this->av_frame->linesize[0], + av_frame->data[0], + av_frame->linesize[0], img->base[0], img->pitches[0], /* U */ - this->av_frame->data[1], - this->av_frame->linesize[1], + av_frame->data[1], + av_frame->linesize[1], img->base[1], img->pitches[1], /* V */ - this->av_frame->data[2], - this->av_frame->linesize[2], + av_frame->data[2], + av_frame->linesize[2], img->base[2], img->pitches[2], /* width x height */ @@ -630,7 +803,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -659,7 +832,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -688,7 +861,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -712,7 +885,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -736,7 +909,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -775,7 +948,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = v_palette[pixel]; plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -787,7 +960,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { dy += img->pitches[0]; - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } for (y = 0; y < this->bih.biHeight / 2; y++) { @@ -824,11 +997,11 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { dv += img->pitches[2]; if (this->context->pix_fmt != PIX_FMT_YUV420P) { - su += 2*this->av_frame->linesize[1]; - sv += 2*this->av_frame->linesize[2]; + su += 2*av_frame->linesize[1]; + sv += 2*av_frame->linesize[2]; } else { - su += this->av_frame->linesize[1]; - sv += this->av_frame->linesize[2]; + su += av_frame->linesize[1]; + sv += av_frame->linesize[2]; } } } @@ -1013,6 +1186,52 @@ static void ff_handle_special_buffer (ff_video_decoder_t *this, buf_element_t *b } } +static uint64_t ff_tag_pts(ff_video_decoder_t *this, uint64_t pts) +{ + return pts | this->pts_tag; +} + +static uint64_t ff_untag_pts(ff_video_decoder_t *this, uint64_t pts) +{ + if (this->pts_tag_mask == 0) + return pts; /* pts tagging inactive */ + + if (this->pts_tag != 0 && (pts & this->pts_tag_mask) != this->pts_tag) + return 0; /* reset pts if outdated while waiting for first pass (see below) */ + + return pts & ~this->pts_tag_mask; +} + +static void ff_check_pts_tagging(ff_video_decoder_t *this, uint64_t pts) +{ + if (this->pts_tag_mask == 0) + return; /* pts tagging inactive */ + if ((pts & this->pts_tag_mask) != this->pts_tag) { + this->pts_tag_stable_counter = 0; + return; /* pts still outdated */ + } + + /* the tag should be stable for 100 frames */ + this->pts_tag_stable_counter++; + + if (this->pts_tag != 0) { + if (this->pts_tag_stable_counter >= 100) { + /* first pass: reset pts_tag */ + this->pts_tag = 0; + this->pts_tag_stable_counter = 0; + } + } else if (pts == 0) + return; /* cannot detect second pass */ + else { + if (this->pts_tag_stable_counter >= 100) { + /* second pass: reset pts_tag_mask and pts_tag_counter */ + this->pts_tag_mask = 0; + this->pts_tag_counter = 0; + this->pts_tag_stable_counter = 0; + } + } +} + static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *buf) { vo_frame_t *img; @@ -1021,9 +1240,22 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu int offset = 0; int flush = 0; int size = buf->size; + uint8_t *buf_deint = 0; + AVFrame *av_framedisp = this->av_frame; + AVFrame *av_framedeint = NULL; + int bDeint = 0; lprintf("handle_mpeg12_buffer\n"); + /* + if(this->mpeg_parser->buffer_size == 0) { + this->av_frame->reordered_opaque = ff_tag_pts(this, this->pts); + if (this->context) + this->context->reordered_opaque = ff_tag_pts(this, this->pts); + this->pts = 0; + } + */ + while ((size > 0) || (flush == 1)) { uint8_t *current; @@ -1070,12 +1302,23 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu avpkt.data = (uint8_t *)this->mpeg_parser->chunk_buffer; avpkt.size = this->mpeg_parser->buffer_size; avpkt.flags = AV_PKT_FLAG_KEY; - len = avcodec_decode_video2 (this->context, this->av_frame, + if(this->accel) { + len = this->accel->avcodec_decode_video2 ( this->accel_img, this->context, av_framedisp, + &got_picture, &avpkt); + } else { + len = avcodec_decode_video2 ( this->context, av_framedisp, &got_picture, &avpkt); + } #else - len = avcodec_decode_video (this->context, this->av_frame, + if(this->accel) { + len = this->accel->avcodec_decode_video ( this->accel_img, this->context, av_framedisp, &got_picture, this->mpeg_parser->chunk_buffer, this->mpeg_parser->buffer_size); + } else { + len = avcodec_decode_video (this->context, av_framedisp, + &got_picture, this->mpeg_parser->chunk_buffer, + this->mpeg_parser->buffer_size); + } #endif lprintf("avcodec_decode_video: decoded_size=%d, got_picture=%d\n", len, got_picture); @@ -1091,11 +1334,31 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu } else { size -= len; offset += len; + /* + this->av_frame->reordered_opaque = ff_tag_pts(this, this->pts); + this->context->reordered_opaque = ff_tag_pts(this, this->pts); + this->pts = 0; + */ + } + + if(got_picture && this->class->enable_vaapi) { + int width, height; + width = this->context->width; + height = this->context->height; + if((this->bih.biWidth != width) || (this->bih.biHeight != height)) { + this->bih.biWidth = width; + this->bih.biHeight = height; + } } - if (got_picture && this->av_frame->data[0]) { + if( this->set_stream_info) { + set_stream_info(this); + this->set_stream_info = 0; + } + + if (got_picture && av_framedisp->data[0]) { /* got a picture, draw it */ - if(!this->av_frame->opaque) { + if(!av_framedisp->opaque) { /* indirect rendering */ img = this->stream->video_out->get_frame (this->stream->video_out, this->bih.biWidth, @@ -1106,21 +1369,74 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu free_img = 1; } else { /* DR1 */ - img = (vo_frame_t*) this->av_frame->opaque; + img = (vo_frame_t*) av_framedisp->opaque; free_img = 0; } - img->pts = this->pts; - this->pts = 0; + if( this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { + if(av_framedisp->interlaced_frame && this->class->vaapi_mpeg_softdec_deinterlace) { + int size; + int ret; + + av_framedeint = avcodec_alloc_frame(); + + size = avpicture_get_size(this->context->pix_fmt, this->context->width, this->context->height); + buf_deint = av_malloc(size); + + if(av_framedeint) { + avpicture_fill((AVPicture*)av_framedeint, buf_deint, this->context->pix_fmt, this->context->width, this->context->height); - if (this->av_frame->repeat_pict) + ret = avpicture_deinterlace((AVPicture*)av_framedeint, (AVPicture*) av_framedisp, + this->context->pix_fmt, this->context->width, this->context->height); + + if(ret) { + av_free( buf_deint ); + av_free( av_framedeint ); + } else { + bDeint = 1; + av_framedisp = av_framedeint; + } + } else { + av_free( buf_deint ); + } + } + + if(bDeint) { + ff_convert_frame(this, img, av_framedeint); + } else { + ff_convert_frame(this, img, av_framedisp); + } + } + + img->progressive_frame = !this->av_frame->interlaced_frame; + img->top_field_first = this->av_frame->top_field_first; + img->bad_frame = 0; + + if (av_framedisp->repeat_pict) img->duration = this->video_step * 3 / 2; else img->duration = this->video_step; + img->pts = this->pts; + this->pts = 0; + + /* + img->pts = ff_untag_pts(this, this->av_frame->reordered_opaque); + ff_check_pts_tagging(this, this->av_frame->reordered_opaque); + this->av_frame->reordered_opaque = 0; + */ + img->crop_right = this->crop_right; img->crop_bottom = this->crop_bottom; + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)av_framedisp->data[0]; + this->accel->render_vaapi_surface(img, va_surface); + lprintf("handle_mpeg12_buffer: render_vaapi_surface va_surface_id 0x%08x\n", av_framedisp->data[0]); + } + } + this->skipframes = img->draw(img, this->stream); if(free_img) @@ -1142,6 +1458,10 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu this->aspect_ratio, this->output_format, VO_BOTH_FIELDS|this->frame_flags); + /* + img->pts = ff_untag_pts(this, this->av_frame->reordered_opaque); + this->av_frame->reordered_opaque = 0; + */ img->pts = 0; img->duration = this->video_step; img->bad_frame = 1; @@ -1149,51 +1469,11 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu img->free(img); } } - } -} - -static uint64_t ff_tag_pts(ff_video_decoder_t *this, uint64_t pts) -{ - return pts | this->pts_tag; -} - -static uint64_t ff_untag_pts(ff_video_decoder_t *this, uint64_t pts) -{ - if (this->pts_tag_mask == 0) - return pts; /* pts tagging inactive */ - - if (this->pts_tag != 0 && (pts & this->pts_tag_mask) != this->pts_tag) - return 0; /* reset pts if outdated while waiting for first pass (see below) */ - - return pts & ~this->pts_tag_mask; -} - -static void ff_check_pts_tagging(ff_video_decoder_t *this, uint64_t pts) -{ - if (this->pts_tag_mask == 0) - return; /* pts tagging inactive */ - if ((pts & this->pts_tag_mask) != this->pts_tag) { - this->pts_tag_stable_counter = 0; - return; /* pts still outdated */ - } - /* the tag should be stable for 100 frames */ - this->pts_tag_stable_counter++; - - if (this->pts_tag != 0) { - if (this->pts_tag_stable_counter >= 100) { - /* first pass: reset pts_tag */ - this->pts_tag = 0; - this->pts_tag_stable_counter = 0; - } - } else if (pts == 0) - return; /* cannot detect second pass */ - else { - if (this->pts_tag_stable_counter >= 100) { - /* second pass: reset pts_tag_mask and pts_tag_counter */ - this->pts_tag_mask = 0; - this->pts_tag_counter = 0; - this->pts_tag_stable_counter = 0; + /* free deinterlace picture */ + if(bDeint) { + av_free( buf_deint ); + av_free( av_framedeint ); } } } @@ -1334,12 +1614,23 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { avpkt.data = (uint8_t *)&chunk_buf[offset]; avpkt.size = this->size; avpkt.flags = AV_PKT_FLAG_KEY; - len = avcodec_decode_video2 (this->context, this->av_frame, + if(this->accel) { + len = this->accel->avcodec_decode_video2 ( this->accel_img, this->context, this->av_frame, &got_picture, &avpkt); + } else { + len = avcodec_decode_video2 (this->context, this->av_frame, + &got_picture, &avpkt); + } #else + if(this->accel) { + len = this->accel->avcodec_decode_video ( this->accel_img, this->context, this->av_frame, + &got_picture, &chunk_buf[offset], + this->size); + } else { len = avcodec_decode_video (this->context, this->av_frame, &got_picture, &chunk_buf[offset], this->size); + } #endif /* reset consumed pts value */ this->context->reordered_opaque = ff_tag_pts(this, 0); @@ -1391,6 +1682,11 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { set_stream_info(this); } + if( this->set_stream_info) { + set_stream_info(this); + this->set_stream_info = 0; + } + if (got_picture && this->av_frame->data[0]) { /* got a picture, draw it */ got_one_picture = 1; @@ -1398,7 +1694,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { /* indirect rendering */ /* initialize the colorspace converter */ - if (!this->cs_convert_init) { + if (!this->cs_convert_init && !this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { if ((this->context->pix_fmt == PIX_FMT_RGB32) || (this->context->pix_fmt == PIX_FMT_RGB565) || (this->context->pix_fmt == PIX_FMT_RGB555) || @@ -1434,10 +1730,10 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { } /* post processing */ - if(this->pp_quality != this->class->pp_quality) + if(this->pp_quality != this->class->pp_quality && this->context->pix_fmt != PIX_FMT_VAAPI_VLD) pp_change_quality(this); - if(this->pp_available && this->pp_quality) { + if(this->pp_available && this->pp_quality && this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { if(this->av_frame->opaque) { /* DR1 */ @@ -1450,7 +1746,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { free_img = 1; } - pp_postprocess(this->av_frame->data, this->av_frame->linesize, + pp_postprocess((const uint8_t **)this->av_frame->data, this->av_frame->linesize, img->base, img->pitches, img->width, img->height, this->av_frame->qscale_table, this->av_frame->qstride, @@ -1459,7 +1755,8 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { } else if (!this->av_frame->opaque) { /* colorspace conversion or copy */ - ff_convert_frame(this, img); + if( this->context->pix_fmt != PIX_FMT_VAAPI_VLD) + ff_convert_frame(this, img, this->av_frame); } img->pts = ff_untag_pts(this, this->av_frame->reordered_opaque); @@ -1488,6 +1785,15 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { img->progressive_frame = !this->av_frame->interlaced_frame; img->top_field_first = this->av_frame->top_field_first; + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)this->av_frame->data[0]; + this->accel->render_vaapi_surface(img, va_surface); + if(va_surface) + lprintf("handle_buffer: render_vaapi_surface va_surface_id 0x%08x\n", this->av_frame->data[0]); + } + } + this->skipframes = img->draw(img, this->stream); if(free_img) @@ -1696,6 +2002,9 @@ static void ff_dispose (video_decoder_t *this_gen) { xine_list_delete(this->dr1_frames); + if(this->accel_img) + this->accel_img->free(this->accel_img); + free (this_gen); } @@ -1737,11 +2046,37 @@ static video_decoder_t *ff_video_open_plugin (video_decoder_class_t *class_gen, this->mpeg_parser = NULL; this->dr1_frames = xine_list_new(); + this->set_stream_info = 0; #ifdef LOG this->debug_fmt = -1; #endif + memset(&this->vaapi_context, 0x0 ,sizeof(struct vaapi_context)); + + this->dr1_frames = xine_list_new(); + + this->accel = NULL; + this->accel_img = NULL; + + + if(this->class->enable_vaapi) { + this->accel_img = stream->video_out->get_frame( stream->video_out, 1920, 1080, 1, XINE_IMGFMT_VAAPI, VO_BOTH_FIELDS ); + + if( this->accel_img && (stream->video_driver->get_capabilities(stream->video_driver) & VO_CAP_VAAPI) ) { + this->accel = (vaapi_accel_t*)this->accel_img->accel_data; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled in config.\n")); + } else { + this->class->enable_vaapi = 0; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled disabled by driver.\n")); + } + } + + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: vaapi_mpeg_softdec %d\n"), + this->class->vaapi_mpeg_softdec ); + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: vaapi_mpeg_softdec_deinterlace %d\n"), + this->class->vaapi_mpeg_softdec_deinterlace ); + return &this->video_decoder; } @@ -1801,6 +2136,21 @@ void *init_video_plugin (xine_t *xine, void *data) { "A change of this setting will take effect with playing the next stream."), 10, choose_speed_over_accuracy_cb, this); + this->vaapi_mpeg_softdec = xine->config->register_bool(config, "video.processing.vaapi_mpeg_softdec", 0, + _("VAAPI Mpeg2 softdecoding"), + _("If the machine freezes on mpeg2 decoding use mpeg2 software decoding."), + 10, vaapi_mpeg_softdec_func, this); + + this->vaapi_mpeg_softdec_deinterlace = xine->config->register_bool(config, "video.processing.vaapi_mpeg_softdec_deinterlace", 0, + _("VAAPI Mpeg2 softdecoding deinterlace"), + _("FFMPEGS simple deinterlacer with Mpeg2 software decoding."), + 10, vaapi_mpeg_softdec_deinterlace, this); + + this->enable_vaapi = xine->config->register_bool(config, "video.processing.ffmpeg_enable_vaapi", 1, + _("Enable VAAPI"), + _("Enable or disable usage of vaapi"), + 10, vaapi_enable_vaapi, this); + return this; } diff --git a/src/combined/ffmpeg/ffmpeg_decoder.c b/src/combined/ffmpeg/ffmpeg_decoder.c index 4f9a0f7..a0314c8 100644 --- a/src/combined/ffmpeg/ffmpeg_decoder.c +++ b/src/combined/ffmpeg/ffmpeg_decoder.c @@ -39,6 +39,9 @@ void init_once_routine(void) { pthread_mutex_init(&ffmpeg_lock, NULL); avcodec_init(); avcodec_register_all(); + + av_log_set_level(AV_LOG_QUIET); + } /* diff --git a/src/combined/ffmpeg/xine_video.list b/src/combined/ffmpeg/xine_video.list index 009f4da..6364c45 100644 --- a/src/combined/ffmpeg/xine_video.list +++ b/src/combined/ffmpeg/xine_video.list @@ -70,7 +70,7 @@ WNV1 WNV1 Winnow Video XL VIXL Miro/Pinnacle VideoXL RT21 INDEO2 Indeo/RealTime 2 FPS1 FRAPS Fraps -MPEG MPEG1VIDEO MPEG 1/2 +MPEG MPEG2VIDEO MPEG 1/2 CSCD CSCD CamStudio AVS AVS AVS ALGMM MMVIDEO American Laser Games MM diff --git a/src/post/deinterlace/xine_plugin.c b/src/post/deinterlace/xine_plugin.c index 5615e4b..d96aafb 100644 --- a/src/post/deinterlace/xine_plugin.c +++ b/src/post/deinterlace/xine_plugin.c @@ -437,6 +437,8 @@ static void deinterlace_dispose(post_plugin_t *this_gen) if (_x_post_dispose(this_gen)) { _flush_frames(this); pthread_mutex_destroy(&this->lock); + if(this->tvtime) + free(this->tvtime ); free(this); } } diff --git a/src/video_out/Makefile.am b/src/video_out/Makefile.am index 45a6f03..76708e2 100644 --- a/src/video_out/Makefile.am +++ b/src/video_out/Makefile.am @@ -43,6 +43,8 @@ if ENABLE_VDPAU vdpau_module = xineplug_vo_out_vdpau.la endif +vaapi_module = xineplug_vo_out_vaapi.la + if ENABLE_XCB XCBOSD = xcbosd.c if ENABLE_XCBSHM @@ -117,6 +119,7 @@ xineplug_LTLIBRARIES = $(xshm_module) $(xv_module) $(xvmc_module) \ $(xcbshm_module) \ $(xcbxv_module) \ $(vdpau_module) \ + $(vaapi_module) \ xineplug_vo_out_raw.la \ xineplug_vo_out_none.la @@ -124,6 +127,10 @@ xineplug_vo_out_vdpau_la_SOURCES = video_out_vdpau.c xineplug_vo_out_vdpau_la_LIBADD = $(YUV_LIBS) $(PTHREAD_LIBS) $(X_LIBS) $(LTLIBINTL) $(VDPAU_LIBS) -lm xineplug_vo_out_vdpau_la_CFLAGS = $(VISIBILITY_FLAG) $(MLIB_CFLAGS) $(X_CFLAGS) $(VDPAU_CFLAGS) +xineplug_vo_out_vaapi_la_SOURCES = $(X11OSD) video_out_vaapi.c +xineplug_vo_out_vaapi_la_LIBADD = $(YUV_LIBS) $(XINE_LIB) $(OPENGL_LIBS) $(FFMPEG_LIBS) $(AVUTIL_LIBS) $(X_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) -ldl -lGLU -lva-glx -lva-x11 -lva +xineplug_vo_out_vaapi_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) $(XV_CFLAGS) -fno-strict-aliasing + xineplug_vo_out_xcbshm_la_SOURCES = video_out_xcbshm.c $(XCBOSD) xineplug_vo_out_xcbshm_la_LIBADD = $(YUV_LIBS) $(PTHREAD_LIBS) $(XCB_LIBS) $(XCBSHM_LIBS) $(LTLIBINTL) xineplug_vo_out_xcbshm_la_CFLAGS = $(AM_CFLAGS) $(XCB_CFLAGS) $(XCBSHM_CFLAGS) $(AVUTIL_CFLAGS) diff --git a/src/video_out/video_out_vaapi.c b/src/video_out/video_out_vaapi.c new file mode 100644 index 0000000..42a6360 --- /dev/null +++ b/src/video_out/video_out_vaapi.c @@ -0,0 +1,3951 @@ +/* + * Copyright (C) 2000-2004, 2008 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * video_out_vaapi.c, VAAPI video extension interface for xine + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> + +#include <sys/types.h> +#if defined(__FreeBSD__) +#include <machine/param.h> +#endif +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/time.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/cursorfont.h> +#include <time.h> +#include <unistd.h> +#include "yuv2rgb.h" + +#define LOG_MODULE "video_out_vaapi" +#define LOG_VERBOSE +/* +#define LOG +*/ +/* +#define DEBUG_SURFACE +*/ +#include "xine.h" +#include <xine/video_out.h> +#include <xine/xine_internal.h> +#include <xine/xineutils.h> +#include <xine/vo_scale.h> + +#include <GL/glu.h> +#include <GL/glx.h> +#include <GL/glext.h> +#include <GL/gl.h> +#include <dlfcn.h> + +#include <va/va_x11.h> +#include <va/va_glx.h> + +#include "accel_vaapi.h" + +#define RENDER_SURFACES 50 +#define SOFT_SURFACES 3 +#define SW_WIDTH 1920 +#define SW_HEIGHT 1080 +#define STABLE_FRAME_COUNTER 4 +#define SW_CONTEXT_INIT_FORMAT -1 //VAProfileH264Main + +#if defined VA_SRC_BT601 && defined VA_SRC_BT709 +# define USE_VAAPI_COLORSPACE 1 +#else +# define USE_VAAPI_COLORSPACE 0 +#endif + +#define IMGFMT_VAAPI 0x56410000 /* 'VA'00 */ +#define IMGFMT_VAAPI_MASK 0xFFFF0000 +#define IMGFMT_IS_VAAPI(fmt) (((fmt) & IMGFMT_VAAPI_MASK) == IMGFMT_VAAPI) +#define IMGFMT_VAAPI_CODEC_MASK 0x000000F0 +#define IMGFMT_VAAPI_CODEC(fmt) ((fmt) & IMGFMT_VAAPI_CODEC_MASK) +#define IMGFMT_VAAPI_CODEC_MPEG2 (0x10) +#define IMGFMT_VAAPI_CODEC_MPEG4 (0x20) +#define IMGFMT_VAAPI_CODEC_H264 (0x30) +#define IMGFMT_VAAPI_CODEC_VC1 (0x40) +#define IMGFMT_VAAPI_MPEG2 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2) +#define IMGFMT_VAAPI_MPEG2_IDCT (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|1) +#define IMGFMT_VAAPI_MPEG2_MOCO (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|2) +#define IMGFMT_VAAPI_MPEG4 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4) +#define IMGFMT_VAAPI_H263 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4|1) +#define IMGFMT_VAAPI_H264 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_H264) +#define IMGFMT_VAAPI_VC1 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1) +#define IMGFMT_VAAPI_WMV3 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1|1) + +#define FOVY 60.0f +#define ASPECT 1.0f +#define Z_NEAR 0.1f +#define Z_FAR 100.0f +#define Z_CAMERA 0.869f + +#ifndef GLAPIENTRY +#ifdef APIENTRY +#define GLAPIENTRY APIENTRY +#else +#define GLAPIENTRY +#endif +#endif + +#if defined(__linux__) +// Linux select() changes its timeout parameter upon return to contain +// the remaining time. Most other unixen leave it unchanged or undefined. +#define SELECT_SETS_REMAINING +#elif defined(__FreeBSD__) || defined(__sun__) || (defined(__MACH__) && defined(__APPLE__)) +#define USE_NANOSLEEP +#elif defined(HAVE_PTHREADS) && defined(sgi) +// SGI pthreads has a bug when using pthreads+signals+nanosleep, +// so instead of using nanosleep, wait on a CV which is never signalled. +#include <pthread.h> +#define USE_COND_TIMEDWAIT +#endif + +#define RECT_IS_EQ(a, b) ((a).x1 == (b).x1 && (a).y1 == (b).y1 && (a).x2 == (b).x2 && (a).y2 == (b).y2) + +static const char *const scaling_level_enum_names[] = { + "default", /* VA_FILTER_SCALING_DEFAULT */ + "fast", /* VA_FILTER_SCALING_FAST */ + "hq", /* VA_FILTER_SCALING_HQ */ + "nla", /* VA_FILTER_SCALING_NL_ANAMORPHIC */ + NULL +}; + +static const int scaling_level_enum_values[] = { + VA_FILTER_SCALING_DEFAULT, + VA_FILTER_SCALING_FAST, + VA_FILTER_SCALING_HQ, + VA_FILTER_SCALING_NL_ANAMORPHIC +}; + +typedef struct vaapi_driver_s vaapi_driver_t; + +typedef struct { + int x0, y0; + int x1, y1, x2, y2; +} vaapi_rect_t; + +typedef struct { + vo_frame_t vo_frame; + + int width, height, format, flags; + double ratio; + + vaapi_accel_t vaapi_accel_data; +} vaapi_frame_t; + +typedef struct { + VADisplayAttribType type; + int value; + int min; + int max; + int atom; + + cfg_entry_t *entry; + + vaapi_driver_t *this; + +} va_property_t; + +struct vaapi_driver_s { + + vo_driver_t vo_driver; + + config_values_t *config; + + /* X11 related stuff */ + Display *display; + int screen; + Drawable drawable; + XColor black; + Window window; + + uint32_t capabilities; + + int ovl_changed; + vo_overlay_t *overlays[XINE_VORAW_MAX_OVL]; + uint32_t *overlay_bitmap; + int overlay_bitmap_size; + uint32_t overlay_bitmap_width; + uint32_t overlay_bitmap_height; + vaapi_rect_t overlay_bitmap_src; + vaapi_rect_t overlay_bitmap_dst; + + uint32_t vdr_osd_width; + uint32_t vdr_osd_height; + + uint32_t overlay_output_width; + uint32_t overlay_output_height; + vaapi_rect_t overlay_dirty_rect; + int has_overlay; + + uint32_t overlay_unscaled_width; + uint32_t overlay_unscaled_height; + vaapi_rect_t overlay_unscaled_dirty_rect; + + yuv2rgb_factory_t *yuv2rgb_factory; + yuv2rgb_t *ovl_yuv2rgb; + + /* all scaling information goes here */ + vo_scale_t sc; + + xine_t *xine; + + unsigned int deinterlace; + + int valid_opengl_context; + int opengl_render; + int opengl_use_tfp; + int query_va_status; + + GLuint gl_texture; + GLXContext gl_context; + XVisualInfo *gl_vinfo; + Pixmap gl_pixmap; + Pixmap gl_image_pixmap; + + ff_vaapi_context_t *va_context; + + int num_frame_buffers; + vaapi_frame_t *frames[RENDER_SURFACES]; + + pthread_mutex_t vaapi_lock; + + unsigned int init_opengl_render; + unsigned int guarded_render; + unsigned int scaling_level_enum; + unsigned int scaling_level; + va_property_t props[VO_NUM_PROPERTIES]; + unsigned int swap_uv_planes; +}; + +ff_vaapi_surface_t *va_render_surfaces = NULL; +VASurfaceID *va_surface_ids = NULL; +VASurfaceID *va_soft_surface_ids = NULL; +VAImage *va_soft_images = NULL; + +static void vaapi_destroy_subpicture(vo_driver_t *this_gen); +static void vaapi_destroy_image(vo_driver_t *this_gen, VAImage *va_image); +static int vaapi_ovl_associate(vo_driver_t *this_gen, int format, int bShow); +static VAStatus vaapi_destroy_soft_surfaces(vo_driver_t *this_gen); +static VAStatus vaapi_destroy_render_surfaces(vo_driver_t *this_gen); +static const char *vaapi_profile_to_string(VAProfile profile); +static int vaapi_set_property (vo_driver_t *this_gen, int property, int value); +static void vaapi_show_display_props(vo_driver_t *this_gen); + +static void nv12_to_yv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *uv_src, int uv_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *u_dst, int u_dst_pitch, + uint8_t *v_dst, int v_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int src_data_size); + +static void yv12_to_nv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *u_src, int u_src_pitch, + const uint8_t *v_src, int v_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *uv_dst, int uv_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int dst_data_size); + +void (GLAPIENTRY *mpglGenTextures)(GLsizei, GLuint *); +void (GLAPIENTRY *mpglBindTexture)(GLenum, GLuint); +void (GLAPIENTRY *mpglXBindTexImage)(Display *, GLXDrawable, int, const int *); +void (GLAPIENTRY *mpglXReleaseTexImage)(Display *, GLXDrawable, int); +GLXPixmap (GLAPIENTRY *mpglXCreatePixmap)(Display *, GLXFBConfig, Pixmap, const int *); +void (GLAPIENTRY *mpglXDestroyPixmap)(Display *, GLXPixmap); +const GLubyte *(GLAPIENTRY *mpglGetString)(GLenum); +void (GLAPIENTRY *mpglGenPrograms)(GLsizei, GLuint *); + +static const char *string_of_VAImageFormat(VAImageFormat *imgfmt) +{ + static char str[5]; + str[0] = imgfmt->fourcc; + str[1] = imgfmt->fourcc >> 8; + str[2] = imgfmt->fourcc >> 16; + str[3] = imgfmt->fourcc >> 24; + str[4] = '\0'; + return str; +} + +static int vaapi_check_status(vo_driver_t *this_gen, VAStatus vaStatus, const char *msg) +{ + + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if (vaStatus != VA_STATUS_SUCCESS) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " Error : %s: %s\n", msg, vaErrorStr(vaStatus)); + return 0; + } + return 1; +} + +/* Wrapper for ffmpeg avcodec_decode_video2 */ +#if AVVIDEO > 1 +static int guarded_avcodec_decode_video2(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, AVPacket *avpkt) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + int len = 0; + + + if(this->guarded_render) { + lprintf("guarded_avcodec_decode_video2 enter\n"); + pthread_mutex_lock(&this->vaapi_lock); + //XLockDisplay( this->display ); + } + + len = avcodec_decode_video2 (avctx, picture, got_picture_ptr, avpkt); + + if(this->guarded_render) { + //XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + lprintf("guarded_avcodec_decode_video2 exit\n"); + } + + + return len; +} +#else +static int guarded_avcodec_decode_video(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, uint8_t *buf, int buf_size) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + int len = 0; + + + if(this->guarded_render) { + lprintf("guarded_avcodec_decode_video enter\n"); + pthread_mutex_lock(&this->vaapi_lock); + //XLockDisplay( this->display ); + } + + len = avcodec_decode_video (avctx, picture, got_picture_ptr, buf, buf_size); + + if(this->guarded_render) { + //XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + lprintf("guarded_avcodec_decode_video exit\n"); + } + + + return len; +} +#endif + +static int guarded_render(vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + return this->guarded_render; +} + +static ff_vaapi_surface_t *get_vaapi_surface(vo_frame_t *frame_gen) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + ff_vaapi_surface_t *va_surface = NULL; + VAStatus vaStatus; + + lprintf("get_vaapi_surface\n"); + + if(!va_render_surfaces) + return NULL; + + if(this->guarded_render) { + /* Get next VAAPI surface marked as SURFACE_FREE */ + for(;;) { + int old_head = va_context->va_head; + va_context->va_head = (va_context->va_head + 1) % ((RENDER_SURFACES)); + + va_surface = &va_render_surfaces[old_head]; + + if( va_surface->status == SURFACE_FREE ) { + + VASurfaceStatus surf_status = 0; + + if(this->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface->va_surface_id, &surf_status); + vaapi_check_status(va_context->driver, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status == VASurfaceReady) { + + va_surface->status = SURFACE_ALOC; + +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface 0x%08x\n", va_surface->va_surface_id); +#endif + + return &va_render_surfaces[old_head]; + } else { +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface busy\n"); +#endif + } + } +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface miss\n"); +#endif + } + } else { + va_surface = &va_render_surfaces[frame->vaapi_accel_data.index]; + } + + return va_surface; +} + +/* Set VAAPI surface status to render */ +static void render_vaapi_surface(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_accel_t *accel = (vaapi_accel_t*)frame_gen->accel_data; + + lprintf("render_vaapi_surface\n"); + + if(!this->guarded_render || !accel || !va_surface) + return; + + pthread_mutex_lock(&this->vaapi_lock); + //XLockDisplay( this->display ); + + accel->index = va_surface->index; + + va_surface->status = SURFACE_RENDER; +#ifdef DEBUG_SURFACE + printf("render_vaapi_surface 0x%08x\n", va_surface->va_surface_id); +#endif + + //XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); +} + +/* Set VAAPI surface status to free */ +static void release_vaapi_surface(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + lprintf("release_vaapi_surface\n"); + + if(va_surface == NULL || !this->guarded_render) { + return; + } + + if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RENDER_RELEASE; + } else if (va_surface->status != SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface 0x%08x\n", va_surface->va_surface_id); +#endif + } +} + +static VADisplay vaapi_get_display(Display *display, int opengl_render) +{ + VADisplay ret; + + if(opengl_render) { + ret = vaGetDisplayGLX(display); + } else { + ret = vaGetDisplay(display); + } + + if(vaDisplayIsValid(ret)) + return ret; + else + return 0; +} + +typedef struct { + void *funcptr; + const char *extstr; + const char *funcnames[7]; + void *fallback; +} extfunc_desc_t; + +#define DEF_FUNC_DESC(name) {&mpgl##name, NULL, {"gl"#name, NULL}, gl ##name} +static const extfunc_desc_t extfuncs[] = { + DEF_FUNC_DESC(GenTextures), + + {&mpglBindTexture, NULL, {"glBindTexture", "glBindTextureARB", "glBindTextureEXT", NULL}}, + {&mpglXBindTexImage, "GLX_EXT_texture_from_pixmap", {"glXBindTexImageEXT", NULL}}, + {&mpglXReleaseTexImage, "GLX_EXT_texture_from_pixmap", {"glXReleaseTexImageEXT", NULL}}, + {&mpglXCreatePixmap, "GLX_EXT_texture_from_pixmap", {"glXCreatePixmap", NULL}}, + {&mpglXDestroyPixmap, "GLX_EXT_texture_from_pixmap", {"glXDestroyPixmap", NULL}}, + {&mpglGenPrograms, "_program", {"glGenProgramsARB", NULL}}, + {NULL} +}; + +typedef struct { + video_driver_class_t driver_class; + + config_values_t *config; + xine_t *xine; +} vaapi_class_t; + +static int gl_visual_attr[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GL_NONE +}; + +static void delay_usec(unsigned int usec) +{ + int was_error; + +#if defined(USE_NANOSLEEP) + struct timespec elapsed, tv; +#elif defined(USE_COND_TIMEDWAIT) + // Use a local mutex and cv, so threads remain independent + pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER; + struct timespec elapsed; + uint64_t future; +#else + struct timeval tv; +#ifndef SELECT_SETS_REMAINING + uint64_t then, now, elapsed; +#endif +#endif + + // Set the timeout interval - Linux only needs to do this once +#if defined(SELECT_SETS_REMAINING) + tv.tv_sec = 0; + tv.tv_usec = usec; +#elif defined(USE_NANOSLEEP) + elapsed.tv_sec = 0; + elapsed.tv_nsec = usec * 1000; +#elif defined(USE_COND_TIMEDWAIT) + future = get_ticks_usec() + usec; + elapsed.tv_sec = future / 1000000; + elapsed.tv_nsec = (future % 1000000) * 1000; +#else + then = get_ticks_usec(); +#endif + + do { + errno = 0; +#if defined(USE_NANOSLEEP) + tv.tv_sec = elapsed.tv_sec; + tv.tv_nsec = elapsed.tv_nsec; + was_error = nanosleep(&tv, &elapsed); +#elif defined(USE_COND_TIMEDWAIT) + was_error = pthread_mutex_lock(&delay_mutex); + was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed); + was_error = pthread_mutex_unlock(&delay_mutex); +#else +#ifndef SELECT_SETS_REMAINING + // Calculate the time interval left (in case of interrupt) + now = get_ticks_usec(); + elapsed = now - then; + then = now; + if (elapsed >= usec) + break; + usec -= elapsed; + tv.tv_sec = 0; + tv.tv_usec = usec; +#endif + was_error = select(0, NULL, NULL, NULL, &tv); +#endif + } while (was_error && (errno == EINTR)); +} + +static void vaapi_x11_wait_event(Display *dpy, Window w, int type) +{ + XEvent e; + while (!XCheckTypedWindowEvent(dpy, w, type, &e)) + delay_usec(10); +} + +/* X11 Error handler and error functions */ +static int vaapi_x11_error_code = 0; +static int (*vaapi_x11_old_error_handler)(Display *, XErrorEvent *); + +static int vaapi_x11_error_handler(Display *dpy, XErrorEvent *error) +{ + vaapi_x11_error_code = error->error_code; + return 0; +} + +static void vaapi_x11_trap_errors(void) +{ + vaapi_x11_error_code = 0; + vaapi_x11_old_error_handler = XSetErrorHandler(vaapi_x11_error_handler); +} + +static int vaapi_x11_untrap_errors(void) +{ + XSetErrorHandler(vaapi_x11_old_error_handler); + return vaapi_x11_error_code; +} + +static void vaapi_appendstr(char **dst, const char *str) +{ + int newsize; + char *newstr; + if (!str) + return; + newsize = strlen(*dst) + 1 + strlen(str) + 1; + newstr = realloc(*dst, newsize); + if (!newstr) + return; + *dst = newstr; + strcat(*dst, " "); + strcat(*dst, str); +} + +/* Return the address of a linked function */ +static void *vaapi_getdladdr (const char *s) { + void *ret = NULL; + void *handle = dlopen(NULL, RTLD_LAZY); + if (!handle) + return NULL; + ret = dlsym(handle, s); + dlclose(handle); + + return ret; +} + +/* Resolve opengl functions. */ +static void vaapi_get_functions(vo_driver_t *this_gen, void *(*getProcAddress)(const GLubyte *), + const char *ext2) { + const extfunc_desc_t *dsc; + const char *extensions; + char *allexts; + + if (!getProcAddress) + getProcAddress = (void *)vaapi_getdladdr; + + /* special case, we need glGetString before starting to find the other functions */ + mpglGetString = getProcAddress("glGetString"); + if (!mpglGetString) + mpglGetString = glGetString; + + extensions = (const char *)mpglGetString(GL_EXTENSIONS); + if (!extensions) extensions = ""; + if (!ext2) ext2 = ""; + allexts = malloc(strlen(extensions) + strlen(ext2) + 2); + strcpy(allexts, extensions); + strcat(allexts, " "); + strcat(allexts, ext2); + lprintf("vaapi_get_functions: OpenGL extensions string:\n%s\n", allexts); + for (dsc = extfuncs; dsc->funcptr; dsc++) { + void *ptr = NULL; + int i; + if (!dsc->extstr || strstr(allexts, dsc->extstr)) { + for (i = 0; !ptr && dsc->funcnames[i]; i++) + ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]); + } + if (!ptr) + ptr = dsc->fallback; + *(void **)dsc->funcptr = ptr; + } + lprintf("\n"); + free(allexts); +} + +/* Check if opengl indirect/software rendering is used */ +static int vaapi_opengl_verify_direct (x11_visual_t *vis) { + Window root, win; + XVisualInfo *visinfo; + GLXContext ctx; + XSetWindowAttributes xattr; + int ret = 0; + + if (!vis || !vis->display || ! (root = RootWindow (vis->display, vis->screen))) { + lprintf ("vaapi_opengl_verify_direct: Don't have a root window to verify\n"); + return 0; + } + + if (! (visinfo = glXChooseVisual (vis->display, vis->screen, gl_visual_attr))) + return 0; + + if (! (ctx = glXCreateContext (vis->display, visinfo, NULL, 1))) + return 0; + + memset (&xattr, 0, sizeof (xattr)); + xattr.colormap = XCreateColormap(vis->display, root, visinfo->visual, AllocNone); + xattr.event_mask = StructureNotifyMask | ExposureMask; + + if ( (win = XCreateWindow (vis->display, root, 0, 0, 1, 1, 0, visinfo->depth, + InputOutput, visinfo->visual, + CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, + &xattr))) { + if (glXMakeCurrent (vis->display, win, ctx)) { + const char *renderer = (const char *) glGetString(GL_RENDERER); + if (glXIsDirect (vis->display, ctx) && + ! strstr (renderer, "Software") && + ! strstr (renderer, "Indirect")) + ret = 1; + glXMakeCurrent (vis->display, None, NULL); + } + XDestroyWindow (vis->display, win); + } + glXDestroyContext (vis->display, ctx); + XFreeColormap (vis->display, xattr.colormap); + + return ret; +} + +static int vaapi_glx_bind_texture(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + glEnable(GL_TEXTURE_2D); + mpglBindTexture(GL_TEXTURE_2D, this->gl_texture); + + if (this->opengl_use_tfp) { + vaapi_x11_trap_errors(); + mpglXBindTexImage(this->display, this->gl_pixmap, GLX_FRONT_LEFT_EXT, NULL); + XSync(this->display, False); + if (vaapi_x11_untrap_errors()) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_bind_texture : Update bind_tex_image failed\n"); + } + + return 0; +} + +static int vaapi_glx_unbind_texture(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if (this->opengl_use_tfp) { + vaapi_x11_trap_errors(); + mpglXReleaseTexImage(this->display, this->gl_pixmap, GLX_FRONT_LEFT_EXT); + if (vaapi_x11_untrap_errors()) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_unbind_texture : Failed to release?\n"); + } + + mpglBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + return 0; +} + +static void vaapi_glx_render_frame(vo_frame_t *frame_gen, int left, int top, int right, int bottom) +{ + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + int x1, x2, y1, y2; + float tx, ty; + + if (vaapi_glx_bind_texture(frame_gen->driver) < 0) + return; + + /* Calc texture/rectangle coords */ + x1 = this->sc.output_xoffset; + y1 = this->sc.output_yoffset; + x2 = x1 + this->sc.output_width; + y2 = y1 + this->sc.output_height; + tx = (float) frame->width / va_context->width; + ty = (float) frame->height / va_context->height; + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + /* Draw quad */ + glBegin (GL_QUADS); + + glTexCoord2f (tx, ty); glVertex2i (x2, y2); + glTexCoord2f (0, ty); glVertex2i (x1, y2); + glTexCoord2f (0, 0); glVertex2i (x1, y1); + glTexCoord2f (tx, 0); glVertex2i (x2, y1); + lprintf("render_frame left %d top %d right %d bottom %d\n", x1, y1, x2, y2); + + glEnd (); + + if (vaapi_glx_unbind_texture(frame_gen->driver) < 0) + return; +} + +static void vaapi_glx_flip_page(vo_frame_t *frame_gen, int left, int top, int right, int bottom) +{ + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + glClear(GL_COLOR_BUFFER_BIT); + + vaapi_glx_render_frame(frame_gen, left, top, right, bottom); + + //if (gl_finish) + // glFinish(); + + glXSwapBuffers(this->display, this->window); + +} + +static void destroy_glx(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if(!this->opengl_render || !va_context->valid_context) + return; + + //if (gl_finish) + // glFinish(); + + if(va_context->gl_surface) { + VAStatus vaStatus = vaDestroySurfaceGLX(va_context->va_display, va_context->gl_surface); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaceGLX()"); + va_context->gl_surface = NULL; + } + + if(this->gl_context) + glXMakeCurrent(this->display, None, NULL); + + if(this->gl_pixmap) { + vaapi_x11_trap_errors(); + mpglXDestroyPixmap(this->display, this->gl_pixmap); + XSync(this->display, False); + vaapi_x11_untrap_errors(); + this->gl_pixmap = None; + } + + if(this->gl_image_pixmap) { + XFreePixmap(this->display, this->gl_image_pixmap); + this->gl_image_pixmap = None; + } + + if(this->gl_texture) { + glDeleteTextures(1, &this->gl_texture); + this->gl_texture = GL_NONE; + } + + if(this->gl_context) { + glXDestroyContext(this->display, this->gl_context); + this->gl_context = 0; + } + + if(this->gl_vinfo) { + XFree(this->gl_vinfo); + this->gl_vinfo = NULL; + } + + this->valid_opengl_context = 0; +} + +static GLXFBConfig *get_fbconfig_for_depth(vo_driver_t *this_gen, int depth) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + GLXFBConfig *fbconfigs, *ret = NULL; + int n_elements, i, found; + int db, stencil, alpha, rgba, value; + + static GLXFBConfig *cached_config = NULL; + static int have_cached_config = 0; + + if (have_cached_config) + return cached_config; + + fbconfigs = glXGetFBConfigs(this->display, this->screen, &n_elements); + + db = SHRT_MAX; + stencil = SHRT_MAX; + rgba = 0; + + found = n_elements; + + for (i = 0; i < n_elements; i++) { + XVisualInfo *vi; + int visual_depth; + + vi = glXGetVisualFromFBConfig(this->display, fbconfigs[i]); + if (!vi) + continue; + + visual_depth = vi->depth; + XFree(vi); + + if (visual_depth != depth) + continue; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_ALPHA_SIZE, &alpha); + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_BUFFER_SIZE, &value); + if (value != depth && (value - alpha) != depth) + continue; + + value = 0; + if (depth == 32) { + glXGetFBConfigAttrib(this->display, fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); + if (value) + rgba = 1; + } + + if (!value) { + if (rgba) + continue; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGB_EXT, &value); + if (!value) + continue; + } + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_DOUBLEBUFFER, &value); + if (value > db) + continue; + db = value; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_STENCIL_SIZE, &value); + if (value > stencil) + continue; + stencil = value; + + found = i; + } + + if (found != n_elements) { + ret = malloc(sizeof(*ret)); + *ret = fbconfigs[found]; + } + + if (n_elements) + XFree(fbconfigs); + + have_cached_config = 1; + cached_config = ret; + return ret; +} + +static int vaapi_glx_config_tfp(vo_driver_t *this_gen, unsigned int width, unsigned int height) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + GLXFBConfig *fbconfig; + int attribs[7], i = 0; + const int depth = 24; + + if (!mpglXBindTexImage || !mpglXReleaseTexImage) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : No GLX texture-from-pixmap extension available\n"); + return 0; + } + + if (depth != 24 && depth != 32) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : color depth wrong.\n"); + return 0; + } + + this->gl_image_pixmap = XCreatePixmap(this->display, this->window, width, height, depth); + if (!this->gl_image_pixmap) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not create X11 pixmap\n"); + return 0; + } + + fbconfig = get_fbconfig_for_depth(this_gen, depth); + if (!fbconfig) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not find an FBConfig for 32-bit pixmap\n"); + return 0; + } + + attribs[i++] = GLX_TEXTURE_TARGET_EXT; + attribs[i++] = GLX_TEXTURE_2D_EXT; + attribs[i++] = GLX_TEXTURE_FORMAT_EXT; + if (depth == 24) + attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; + else if (depth == 32) + attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; + attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; + attribs[i++] = GL_FALSE; + attribs[i++] = None; + + vaapi_x11_trap_errors(); + this->gl_pixmap = mpglXCreatePixmap(this->display, *fbconfig, this->gl_image_pixmap, attribs); + XSync(this->display, False); + if (vaapi_x11_untrap_errors()) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not create GLX pixmap\n"); + return 0; + } + + return 1; +} + +static int vaapi_glx_config_glx(vo_driver_t *this_gen, unsigned int width, unsigned int height) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + this->gl_vinfo = glXChooseVisual(this->display, this->screen, gl_visual_attr); + if(!this->gl_vinfo) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXChooseVisual\n"); + this->opengl_render = 0; + } + + glXMakeCurrent(this->display, None, NULL); + this->gl_context = glXCreateContext (this->display, this->gl_vinfo, NULL, True); + if (this->gl_context) { + if(!glXMakeCurrent (this->display, this->window, this->gl_context)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXMakeCurrent\n"); + goto error; + } + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXCreateContext\n"); + goto error; + } + + void *(*getProcAddress)(const GLubyte *); + const char *(*glXExtStr)(Display *, int); + char *glxstr = strdup(" "); + + getProcAddress = vaapi_getdladdr("glXGetProcAddress"); + if (!getProcAddress) + getProcAddress = vaapi_getdladdr("glXGetProcAddressARB"); + glXExtStr = vaapi_getdladdr("glXQueryExtensionsString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, this->screen)); + glXExtStr = vaapi_getdladdr("glXGetClientString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, GLX_EXTENSIONS)); + glXExtStr = vaapi_getdladdr("glXGetServerString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, GLX_EXTENSIONS)); + + vaapi_get_functions(this_gen, getProcAddress, glxstr); + if (!mpglGenPrograms && mpglGetString && + getProcAddress && + strstr(mpglGetString(GL_EXTENSIONS), "GL_ARB_vertex_program")) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : Broken glXGetProcAddress detected, trying workaround\n"); + vaapi_get_functions(this_gen, NULL, glxstr); + } + free(glxstr); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glDrawBuffer(GL_BACK); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* Create TFP resources */ + if(this->opengl_use_tfp && vaapi_glx_config_tfp(this_gen, width, height)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : Using GLX texture-from-pixmap extension\n"); + } else { + this->opengl_use_tfp = 0; + } + + /* Create OpenGL texture */ + /* XXX: assume GL_ARB_texture_non_power_of_two is available */ + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &this->gl_texture); + mpglBindTexture(GL_TEXTURE_2D, this->gl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (!this->opengl_use_tfp) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + mpglBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + if(!this->gl_texture) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : gl_texture NULL\n"); + goto error; + } + + if(!this->opengl_use_tfp) { + VAStatus vaStatus = vaCreateSurfaceGLX(va_context->va_display, GL_TEXTURE_2D, this->gl_texture, &va_context->gl_surface); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaceGLX()")) { + va_context->gl_surface = NULL; + goto error; + } + } else { + va_context->gl_surface = NULL; + } + + lprintf("vaapi_glx_config_glx : GL setup done\n"); + + this->valid_opengl_context = 1; + return 1; + +error: + destroy_glx(this_gen); + this->valid_opengl_context = 0; + return 0; +} + +static uint32_t vaapi_get_capabilities (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + return this->capabilities; +} + +static const struct { + int fmt; + enum PixelFormat pix_fmt; + enum CodecID codec_id; +} conversion_map[] = { + {IMGFMT_VAAPI_MPEG2, PIX_FMT_VAAPI_VLD, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG2_IDCT,PIX_FMT_VAAPI_IDCT, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG2_MOCO,PIX_FMT_VAAPI_MOCO, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG4, PIX_FMT_VAAPI_VLD, CODEC_ID_MPEG4}, + {IMGFMT_VAAPI_H263, PIX_FMT_VAAPI_VLD, CODEC_ID_H263}, + {IMGFMT_VAAPI_H264, PIX_FMT_VAAPI_VLD, CODEC_ID_H264}, + {IMGFMT_VAAPI_WMV3, PIX_FMT_VAAPI_VLD, CODEC_ID_WMV3}, + {IMGFMT_VAAPI_VC1, PIX_FMT_VAAPI_VLD, CODEC_ID_VC1}, + {0, PIX_FMT_NONE} +}; + +static int vaapi_pixfmt2imgfmt(enum PixelFormat pix_fmt, int codec_id) +{ + int i; + int fmt; + for (i = 0; conversion_map[i].pix_fmt != PIX_FMT_NONE; i++) { + if (conversion_map[i].pix_fmt == pix_fmt && + (conversion_map[i].codec_id == 0 || + conversion_map[i].codec_id == codec_id)) { + break; + } + } + fmt = conversion_map[i].fmt; + return fmt; +} + +static int vaapi_has_profile(VAProfile *va_profiles, int va_num_profiles, VAProfile profile) +{ + if (va_profiles && va_num_profiles > 0) { + int i; + for (i = 0; i < va_num_profiles; i++) { + if (va_profiles[i] == profile) + return 1; + } + } + return 0; +} + +static int profile_from_imgfmt(vo_frame_t *frame_gen, enum PixelFormat pix_fmt, int codec_id, int vaapi_mpeg_sofdec) +{ + vo_driver_t *this_gen = (vo_driver_t *) frame_gen->driver; + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + int profile = -1; + int maj, min; + int i; + int va_num_profiles; + int max_profiles; + VAProfile *va_profiles = NULL; + int inited = 0; + + if(va_context->va_display == NULL) { + lprintf("profile_from_imgfmt vaInitialize\n"); + inited = 1; + va_context->va_display = vaapi_get_display(this->display, this->opengl_render); + if(!va_context->va_display) + goto out; + + vaStatus = vaInitialize(va_context->va_display, &maj, &min); + if(!vaapi_check_status(this_gen, vaStatus, "vaInitialize()")) + goto out; + + } + + max_profiles = vaMaxNumProfiles(va_context->va_display); + va_profiles = calloc(max_profiles, sizeof(*va_profiles)); + if (!va_profiles) + goto out; + + vaStatus = vaQueryConfigProfiles(va_context->va_display, va_profiles, &va_num_profiles); + if(!vaapi_check_status(this_gen, vaStatus, "vaQueryConfigProfiles()")) + goto out; + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " VAAPI Supported Profiles : "); + for (i = 0; i < va_num_profiles; i++) { + printf("%s ", vaapi_profile_to_string(va_profiles[i])); + } + printf("\n"); + + uint32_t format = vaapi_pixfmt2imgfmt(pix_fmt, codec_id); + + static const int mpeg2_profiles[] = { VAProfileMPEG2Main, VAProfileMPEG2Simple, -1 }; + static const int mpeg4_profiles[] = { VAProfileMPEG4Main, VAProfileMPEG4AdvancedSimple, VAProfileMPEG4Simple, -1 }; + static const int h264_profiles[] = { VAProfileH264High, VAProfileH264Main, VAProfileH264Baseline, -1 }; + static const int wmv3_profiles[] = { VAProfileVC1Main, VAProfileVC1Simple, -1 }; + static const int vc1_profiles[] = { VAProfileVC1Advanced, -1 }; + + const int *profiles = NULL; + switch (IMGFMT_VAAPI_CODEC(format)) + { + case IMGFMT_VAAPI_CODEC_MPEG2: + if(!vaapi_mpeg_sofdec) { + profiles = mpeg2_profiles; + } + break; + case IMGFMT_VAAPI_CODEC_MPEG4: + profiles = mpeg4_profiles; + break; + case IMGFMT_VAAPI_CODEC_H264: + profiles = h264_profiles; + break; + case IMGFMT_VAAPI_CODEC_VC1: + switch (format) { + case IMGFMT_VAAPI_WMV3: + profiles = wmv3_profiles; + break; + case IMGFMT_VAAPI_VC1: + profiles = vc1_profiles; + break; + } + break; + } + + if (profiles) { + int i; + for (i = 0; profiles[i] != -1; i++) { + if (vaapi_has_profile(va_profiles, va_num_profiles, profiles[i])) { + profile = profiles[i]; + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " VAAPI Profile %s supported by your hardware\n", vaapi_profile_to_string(profiles[i])); + break; + } + } + } + +out: + if(va_profiles) + free(va_profiles); + if(inited) { + vaStatus = vaTerminate(va_context->va_display); + vaapi_check_status(this_gen, vaStatus, "vaTerminate()"); + } + return profile; +} + + +static const char *vaapi_profile_to_string(VAProfile profile) +{ + switch(profile) { +#define PROFILE(profile) \ + case VAProfile##profile: return "VAProfile" #profile + PROFILE(MPEG2Simple); + PROFILE(MPEG2Main); + PROFILE(MPEG4Simple); + PROFILE(MPEG4AdvancedSimple); + PROFILE(MPEG4Main); + PROFILE(H264Baseline); + PROFILE(H264Main); + PROFILE(H264High); + PROFILE(VC1Simple); + PROFILE(VC1Main); + PROFILE(VC1Advanced); +#undef PROFILE + default: break; + } + return "<unknown>"; +} + +static const char *vaapi_entrypoint_to_string(VAEntrypoint entrypoint) +{ + switch(entrypoint) + { +#define ENTRYPOINT(entrypoint) \ + case VAEntrypoint##entrypoint: return "VAEntrypoint" #entrypoint + ENTRYPOINT(VLD); + ENTRYPOINT(IZZ); + ENTRYPOINT(IDCT); + ENTRYPOINT(MoComp); + ENTRYPOINT(Deblocking); +#undef ENTRYPOINT + default: break; + } + return "<unknown>"; +} + +/* Init subpicture */ +static void vaapi_init_subpicture(vaapi_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + va_context->va_subpic_width = 0; + va_context->va_subpic_height = 0; + va_context->va_subpic_id = VA_INVALID_ID; + va_context->va_subpic_image.image_id = VA_INVALID_ID; + + this->overlay_output_width = this->overlay_output_height = 0; + this->overlay_unscaled_width = this->overlay_unscaled_height = 0; + this->ovl_changed = 0; + this->has_overlay = 0; + this->overlay_bitmap = NULL; + this->overlay_bitmap_size = 0; + +} + +/* Init vaapi context */ +static void vaapi_init_va_context(vaapi_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + + va_context->va_config_id = VA_INVALID_ID; + va_context->va_context_id = VA_INVALID_ID; + va_context->va_profile = 0; + va_context->va_colorspace = 1; + va_context->is_bound = 0; + va_context->gl_surface = NULL; + va_context->soft_head = 0; + va_context->valid_context = 0; + va_context->va_head = 0; + va_context->va_soft_head = 0; + + for(i = 0; i < RENDER_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = VA_INVALID_SURFACE; + + va_surface_ids[i] = VA_INVALID_SURFACE; + } + + for(i = 0; i < SOFT_SURFACES; i++) { + va_soft_surface_ids[i] = VA_INVALID_SURFACE; + va_soft_images[i].image_id = VA_INVALID_ID; + } + + va_context->va_image_formats = NULL; + va_context->va_num_image_formats = 0; + + va_context->va_subpic_formats = NULL; + va_context->va_num_subpic_formats = 0; +} + +/* Close vaapi */ +static void vaapi_close(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context || !va_context->va_display || !va_context->valid_context) + return; + + vaapi_ovl_associate(this_gen, 0, 0); + destroy_glx((vo_driver_t *)this); + + vaapi_destroy_subpicture(this_gen); + + vaapi_destroy_soft_surfaces(this_gen); + + vaapi_destroy_render_surfaces(this_gen); + + if(va_context->va_config_id != VA_INVALID_ID) { + vaStatus = vaDestroyConfig(va_context->va_display, va_context->va_config_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyConfig()"); + va_context->va_config_id = VA_INVALID_ID; + } + + if(va_context->va_context_id != VA_INVALID_ID) { + vaStatus = vaDestroyContext(va_context->va_display, va_context->va_context_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyContext()"); + va_context->va_context_id = VA_INVALID_ID; + } + + vaStatus = vaTerminate(va_context->va_display); + vaapi_check_status(this_gen, vaStatus, "vaTerminate()"); + va_context->va_display = NULL; + + if(va_context->va_image_formats) { + free(va_context->va_image_formats); + va_context->va_image_formats = NULL; + va_context->va_num_image_formats = 0; + } + if(va_context->va_subpic_formats) { + free(va_context->va_subpic_formats); + va_context->va_subpic_formats = NULL; + va_context->va_num_subpic_formats = 0; + } + + va_context->valid_context = 0; +} + +/* Returns internal VAAPI context */ +static ff_vaapi_context_t *get_context(vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + return this->va_context; +} + +/* Free allocated VAAPI image */ +static void vaapi_destroy_image(vo_driver_t *this_gen, VAImage *va_image) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(va_image->image_id != VA_INVALID_ID) { + lprintf("vaapi_destroy_image 0x%08x\n", va_image->image_id); + vaStatus = vaDestroyImage(va_context->va_display, va_image->image_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyImage()"); + } + va_image->image_id = VA_INVALID_ID; + va_image->width = 0; + va_image->height = 0; +} + +/* Allocated VAAPI image */ +static VAStatus vaapi_create_image(vo_driver_t *this_gen, VASurfaceID va_surface_id, VAImage *va_image, int width, int height, int clear) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int i = 0; + VAStatus vaStatus; + + if(!va_context->valid_context || va_context->va_image_formats == NULL || va_context->va_num_image_formats == 0) + return VA_STATUS_ERROR_UNKNOWN; + + va_context->is_bound = 0; + + vaStatus = vaDeriveImage(va_context->va_display, va_surface_id, va_image); + if(vaStatus == VA_STATUS_SUCCESS) { + if (va_image->image_id != VA_INVALID_ID && va_image->buf != VA_INVALID_ID) { + va_context->is_bound = 1; + } + } + + if(!va_context->is_bound) { + for (i = 0; i < va_context->va_num_image_formats; i++) { + if (va_context->va_image_formats[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_context->va_image_formats[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) /*|| + va_context->va_image_formats[i].fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) */) { + vaStatus = vaCreateImage( va_context->va_display, &va_context->va_image_formats[i], width, height, va_image ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateImage()")) + goto error; + break; + } + } + } + + void *p_base = NULL; + + vaStatus = vaMapBuffer( va_context->va_display, va_image->buf, &p_base ); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + goto error; + + if(clear) { + if(va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' )) { + memset((uint8_t*)p_base + va_image->offsets[0], 0, va_image->pitches[0] * va_image->height); + memset((uint8_t*)p_base + va_image->offsets[1], 128, va_image->pitches[1] * (va_image->height/2)); + memset((uint8_t*)p_base + va_image->offsets[2], 128, va_image->pitches[2] * (va_image->height/2)); + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) ) { + memset((uint8_t*)p_base + va_image->offsets[0], 0, va_image->pitches[0] * va_image->height); + memset((uint8_t*)p_base + va_image->offsets[1], 128, va_image->pitches[1] * (va_image->height/2)); + } + } + + vaStatus = vaUnmapBuffer( va_context->va_display, va_image->buf ); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + lprintf("vaapi_create_image 0x%08x width %d height %d format %s\n", va_image->image_id, va_image->width, va_image->height, + string_of_VAImageFormat(&va_image->format)); + + return VA_STATUS_SUCCESS; + +error: + /* house keeping */ + vaapi_destroy_image(this_gen, va_image); + return VA_STATUS_ERROR_UNKNOWN; +} + +/* Deassociate and free subpicture */ +static void vaapi_destroy_subpicture(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + lprintf("destroy sub 0x%08x 0x%08x 0x%08x\n", va_context->va_subpic_id, + va_context->va_subpic_image.image_id, va_context->va_subpic_image.buf); + + if(va_context->va_subpic_id != VA_INVALID_ID) { + vaStatus = vaDestroySubpicture(va_context->va_display, va_context->va_subpic_id); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } + va_context->va_subpic_id = VA_INVALID_ID; + + vaapi_destroy_image(this_gen, &va_context->va_subpic_image); + +} + +/* Create VAAPI subpicture */ +static VAStatus vaapi_create_subpicture(vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + int i = 0; + + if(!va_context->valid_context || !va_context->va_subpic_formats || va_context->va_num_subpic_formats == 0) + return VA_STATUS_ERROR_UNKNOWN; + + for (i = 0; i < va_context->va_num_subpic_formats; i++) { + if ( va_context->va_subpic_formats[i].fourcc == VA_FOURCC('B','G','R','A')) { + + vaStatus = vaCreateImage( va_context->va_display, &va_context->va_subpic_formats[i], width, height, &va_context->va_subpic_image ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateImage()")) + goto error; + + vaStatus = vaCreateSubpicture(va_context->va_display, va_context->va_subpic_image.image_id, &va_context->va_subpic_id ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSubpicture()")) + goto error; + } + } + + if(va_context->va_subpic_image.image_id == VA_INVALID_ID || va_context->va_subpic_id == VA_INVALID_ID) + goto error; + + void *p_base = NULL; + + lprintf("create sub 0x%08x 0x%08x 0x%08x\n", va_context->va_subpic_id, + va_context->va_subpic_image.image_id, va_context->va_subpic_image.buf); + + vaStatus = vaMapBuffer(va_context->va_display, va_context->va_subpic_image.buf, &p_base); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + goto error; + + memset((uint32_t *)p_base, 0x0, va_context->va_subpic_image.data_size); + vaStatus = vaUnmapBuffer(va_context->va_display, va_context->va_subpic_image.buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + this->overlay_output_width = width; + this->overlay_output_height = height; + + lprintf("vaapi_create_subpicture 0x%08x format %s\n", va_context->va_subpic_image.image_id, + string_of_VAImageFormat(&va_context->va_subpic_image.format)); + + return VA_STATUS_SUCCESS; + +error: + /* house keeping */ + if(va_context->va_subpic_id != VA_INVALID_ID) + vaapi_destroy_subpicture(this_gen); + va_context->va_subpic_id = VA_INVALID_ID; + + vaapi_destroy_image(this_gen, &va_context->va_subpic_image); + + this->overlay_output_width = 0; + this->overlay_output_height = 0; + + return VA_STATUS_ERROR_UNKNOWN; +} + +static inline int vaapi_get_colorspace_flags(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if(!va_context) + return 0; + + int colorspace = 0; +#if USE_VAAPI_COLORSPACE + switch (va_context->va_colorspace) { + case 0: + colorspace = ((va_context->sw_width >= 1280 || va_context->sw_height > 576) ? + VA_SRC_BT709 : VA_SRC_BT601); + break; + case 1: + colorspace = VA_SRC_BT601; + break; + case 2: + colorspace = VA_SRC_BT709; + break; + case 3: + colorspace = VA_SRC_SMPTE_240; + break; + default: + colorspace = VA_SRC_BT601; + break; + } +#endif + return colorspace; +} + +static void vaapi_property_callback (void *property_gen, xine_cfg_entry_t *entry) { + va_property_t *property = (va_property_t *) property_gen; + vaapi_driver_t *this = property->this; + ff_vaapi_context_t *va_context = this->va_context; + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + VADisplayAttribute attr; + + attr.type = property->type; + attr.value = entry->num_value; + + lprintf("vaapi_property_callback property=%d, value=%d\n", property->type, entry->num_value ); + + VAStatus vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + vaapi_check_status((vo_driver_t *)this, vaStatus, "vaSetDisplayAttributes()"); + + vaapi_show_display_props((vo_driver_t*)this); + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); +} + +/* called xlocked */ +static void vaapi_check_capability (vaapi_driver_t *this, + int property, VADisplayAttribute attr, + const char *config_name, + const char *config_desc, + const char *config_help) { + int int_default = 0; + cfg_entry_t *entry; + + this->props[property].type = attr.type; + this->props[property].min = attr.min_value; + this->props[property].max = attr.max_value; + int_default = attr.value; + this->props[property].atom = 1; + + if (config_name) { + /* is this a boolean property ? */ + if ((attr.min_value == 0) && (attr.max_value == 1)) { + this->config->register_bool (this->config, config_name, int_default, + config_desc, + config_help, 20, vaapi_property_callback, &this->props[property]); + + } else { + this->config->register_range (this->config, config_name, int_default, + this->props[property].min, this->props[property].max, + config_desc, + config_help, 20, vaapi_property_callback, &this->props[property]); + } + + entry = this->config->lookup_entry (this->config, config_name); + if((entry->num_value < this->props[property].min) || + (entry->num_value > this->props[property].max)) { + + this->config->update_num(this->config, config_name, + ((this->props[property].min + this->props[property].max) >> 1)); + + entry = this->config->lookup_entry (this->config, config_name); + } + + this->props[property].entry = entry; + + vaapi_set_property(&this->vo_driver, property, entry->num_value); + } else { + this->props[property].value = int_default; + } +} + +static void vaapi_show_display_props(vo_driver_t *this_gen) { + /* + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if(this->capabilities & VO_CAP_BRIGHTNESS) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : brightness : %d\n", this->props[VO_PROP_BRIGHTNESS].value); + if(this->capabilities & VO_CAP_CONTRAST) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : contrast : %d\n", this->props[VO_PROP_CONTRAST].value); + if(this->capabilities & VO_CAP_HUE) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : hue : %d\n", this->props[VO_PROP_HUE].value); + if(this->capabilities & VO_CAP_SATURATION) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : saturation : %d\n", this->props[VO_PROP_SATURATION].value); + */ +} + +/* VAAPI display attributes. */ +static void vaapi_display_attribs(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int num_display_attrs, max_display_attrs; + VAStatus vaStatus; + VADisplayAttribute *display_attrs; + int i; + + max_display_attrs = vaMaxNumDisplayAttributes(va_context->va_display); + display_attrs = calloc(max_display_attrs, sizeof(*display_attrs)); + + if (display_attrs) { + num_display_attrs = 0; + vaStatus = vaQueryDisplayAttributes(va_context->va_display, + display_attrs, &num_display_attrs); + if(vaapi_check_status(this_gen, vaStatus, "vaQueryDisplayAttributes()")) { + for (i = 0; i < num_display_attrs; i++) { + switch (display_attrs[i].type) { + case VADisplayAttribBrightness: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_BRIGHTNESS; + vaapi_check_capability(this, VO_PROP_BRIGHTNESS, display_attrs[i], "video.output.vaapi_brightness", "Brightness setting", "Brightness setting"); + } + break; + case VADisplayAttribContrast: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_CONTRAST; + vaapi_check_capability(this, VO_PROP_CONTRAST, display_attrs[i], "video.output.vaapi_contrast", "Contrast setting", "Contrast setting"); + } + break; + case VADisplayAttribHue: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_HUE; + vaapi_check_capability(this, VO_PROP_HUE, display_attrs[i], "video.output.vaapi_hue", "Hue setting", "Hue setting"); + } + break; + case VADisplayAttribSaturation: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_SATURATION; + vaapi_check_capability(this, VO_PROP_SATURATION, display_attrs[i], "video.output.vaapi_saturation", "Saturation setting", "Saturation setting"); + } + break; + default: + break; + } + } + } + free(display_attrs); + } + vaapi_show_display_props(this_gen); +} + +static void vaapi_set_background_color(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context->valid_context) + return; + + VADisplayAttribute attr; + memset( &attr, 0, sizeof(attr) ); + + attr.type = VADisplayAttribBackgroundColor; + attr.value = 0x000000; + + vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + vaapi_check_status(this_gen, vaStatus, "vaSetDisplayAttributes()"); +} + +static VAStatus vaapi_destroy_render_surfaces(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + VAStatus vaStatus; + + for(i = 0; i < RENDER_SURFACES; i++) { + if(va_surface_ids[i] != VA_INVALID_SURFACE) { + vaStatus = vaSyncSurface(va_context->va_display, va_surface_ids[i]); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + vaStatus = vaDestroySurfaces(va_context->va_display, &va_surface_ids[i], 1); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaces()"); + va_surface_ids[i] = VA_INVALID_SURFACE; + + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = va_surface_ids[i]; + } + } + + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_destroy_soft_surfaces(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + VAStatus vaStatus; + + + for(i = 0; i < SOFT_SURFACES; i++) { + if(va_soft_images[i].image_id != VA_INVALID_ID) + vaapi_destroy_image((vo_driver_t *)this, &va_soft_images[i]); + va_soft_images[i].image_id = VA_INVALID_ID; + + if(va_soft_surface_ids[i] != VA_INVALID_SURFACE) { +#ifdef DEBUG_SURFACE + printf("vaapi_close destroy render surface 0x%08x\n", va_soft_surface_ids[i]); +#endif + vaStatus = vaSyncSurface(va_context->va_display, va_soft_surface_ids[i]); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + vaStatus = vaDestroySurfaces(va_context->va_display, &va_soft_surface_ids[i], 1); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaces()"); + va_soft_surface_ids[i] = VA_INVALID_SURFACE; + } + } + + va_context->sw_width = 0; + va_context->sw_height = 0; + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_init_soft_surfaces(vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + int i; + + vaapi_destroy_soft_surfaces(this_gen); + + vaStatus = vaCreateSurfaces(va_context->va_display, width, height, VA_RT_FORMAT_YUV420, SOFT_SURFACES, va_soft_surface_ids); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaces()")) + goto error; + + /* allocate software surfaces */ + for(i = 0; i < SOFT_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + + vaStatus = vaapi_create_image((vo_driver_t *)this, va_soft_surface_ids[i], &va_soft_images[i], width, height, 1); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_create_image()")) { + va_soft_images[i].image_id = VA_INVALID_ID; + goto error; + } + + va_surface->index = i; + + vaStatus = vaPutImage(va_context->va_display, va_soft_surface_ids[i], va_soft_images[i].image_id, + 0, 0, va_soft_images[i].width, va_soft_images[i].height, + 0, 0, va_soft_images[i].width, va_soft_images[i].height); + vaapi_check_status(this_gen, vaStatus, "vaPutImage()"); +#ifdef DEBUG_SURFACE + printf("vaapi_init_soft_surfaces 0x%08x\n", va_soft_surface_ids[i]); +#endif + } + + va_context->sw_width = width; + va_context->sw_height = height; + return VA_STATUS_SUCCESS; + +error: + va_context->sw_width = 0; + va_context->sw_height = 0; + vaapi_destroy_soft_surfaces(this_gen); + return VA_STATUS_ERROR_UNKNOWN; +} + +static VAStatus vaapi_init_internal(vo_driver_t *this_gen, int va_profile, int width, int height, int softrender) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAConfigAttrib va_attrib; + int maj, min, i; + VAStatus vaStatus; + + vaapi_close(this_gen); + vaapi_init_va_context(this); + + this->va_context->va_display = vaapi_get_display(this->display, this->opengl_render); + + if(!this->va_context->va_display) + goto error; + + vaStatus = vaInitialize(this->va_context->va_display, &maj, &min); + if(!vaapi_check_status((vo_driver_t *)this, vaStatus, "vaInitialize()")) + goto error; + + lprintf("libva: %d.%d\n", maj, min); + + va_context->valid_context = 1; + + int fmt_count = 0; + fmt_count = vaMaxNumImageFormats( va_context->va_display ); + va_context->va_image_formats = calloc( fmt_count, sizeof(*va_context->va_image_formats) ); + + vaStatus = vaQueryImageFormats(va_context->va_display, va_context->va_image_formats, &va_context->va_num_image_formats); + if(!vaapi_check_status(this_gen, vaStatus, "vaQueryImageFormats()")) + goto error; + + fmt_count = vaMaxNumSubpictureFormats( va_context->va_display ); + va_context->va_subpic_formats = calloc( fmt_count, sizeof(*va_context->va_subpic_formats) ); + + vaStatus = vaQuerySubpictureFormats( va_context->va_display , va_context->va_subpic_formats, 0, &va_context->va_num_subpic_formats ); + if(!vaapi_check_status(this_gen, vaStatus, "vaQuerySubpictureFormats()")) + goto error; + + const char *vendor = vaQueryVendorString(va_context->va_display); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Vendor : %s\n", vendor); + + this->query_va_status = 1; + char *p = (char *)vendor; + for(i = 0; i < strlen(vendor); i++, p++) { + if(strncmp(p, "VDPAU", strlen("VDPAU")) == 0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Enable Splitted-Desktop Systems VDPAU-VIDEO workarounds.\n"); + this->query_va_status = 0; + this->opengl_use_tfp = 0; + break; + } + } + + vaapi_set_background_color(this_gen); + vaapi_display_attribs((vo_driver_t *)this); + + va_context->width = width; + va_context->height = height; + va_context->va_profile = va_profile; + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : Context width %d height %d\n", va_context->width, va_context->height); + + /* allocate decoding surfaces */ + vaStatus = vaCreateSurfaces(va_context->va_display, va_context->width, va_context->height, VA_RT_FORMAT_YUV420, RENDER_SURFACES, va_surface_ids); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaces()")) + goto error; + + /* hardware decoding needs more setup */ + if(!softrender && va_profile >= 0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : Profile: %d (%s) Entrypoint %d (%s) Surfaces %d\n", va_context->va_profile, vaapi_profile_to_string(va_context->va_profile), VAEntrypointVLD, vaapi_entrypoint_to_string(VAEntrypointVLD), RENDER_SURFACES); + + memset( &va_attrib, 0, sizeof(va_attrib) ); + va_attrib.type = VAConfigAttribRTFormat; + + vaStatus = vaGetConfigAttributes(va_context->va_display, va_context->va_profile, VAEntrypointVLD, &va_attrib, 1); + if(!vaapi_check_status(this_gen, vaStatus, "vaGetConfigAttributes()")) + goto error; + + if( (va_attrib.value & VA_RT_FORMAT_YUV420) == 0 ) + goto error; + + vaStatus = vaCreateConfig(va_context->va_display, va_context->va_profile, VAEntrypointVLD, &va_attrib, 1, &va_context->va_config_id); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateConfig()")) { + va_context->va_config_id = VA_INVALID_ID; + goto error; + } + + vaStatus = vaCreateContext(va_context->va_display, va_context->va_config_id, va_context->width, va_context->height, + VA_PROGRESSIVE, va_surface_ids, RENDER_SURFACES, &va_context->va_context_id); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateContext()")) { + va_context->va_context_id = VA_INVALID_ID; + goto error; + } + } + + /* xine was told to allocate RENDER_SURFACES frames. assign the frames the rendering surfaces. */ + for(i = 0; i < RENDER_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = va_surface_ids[i]; + + if(this->frames[i]) { + vaapi_frame_t *frame = this->frames[i]; + frame->vaapi_accel_data.index = i; + + VAImage va_image; + vaStatus = vaapi_create_image(va_context->driver, va_surface_ids[i], &va_image, width, height, 1); + if(vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + vaStatus = vaPutImage(va_context->va_display, va_surface_ids[i], va_image.image_id, + 0, 0, va_image.width, va_image.height, + 0, 0, va_image.width, va_image.height); + vaapi_destroy_image(va_context->driver, &va_image); + } + } +#ifdef DEBUG_SURFACE + printf("vaapi_init_internal 0x%08x\n", va_surface_ids[i]); +#endif + } + + vaStatus = vaapi_init_soft_surfaces(this_gen, width, height); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_init_soft_surfaces()")) { + vaapi_destroy_soft_surfaces(this_gen); + goto error; + } + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : guarded render : %d\n", this->guarded_render); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : glxrender : %d\n", this->opengl_render); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : glxrender tfp : %d\n", this->opengl_use_tfp); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : is_bound : %d\n", va_context->is_bound); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : scaling level : name %s value 0x%08x\n", scaling_level_enum_names[this->scaling_level_enum], this->scaling_level); + + this->init_opengl_render = 1; + + return VA_STATUS_SUCCESS; + +error: + vaapi_close(this_gen); + vaapi_init_va_context(this); + va_context->valid_context = 0; + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : error init vaapi\n"); + + return VA_STATUS_ERROR_UNKNOWN; +} + +/* + * Init VAAPI. This function is called from the decoder side. + * When the decoder uses software decoding vaapi_init is not called. + * Therefore we do it in vaapi_display_frame to get a valid VAAPI context + */ +static VAStatus vaapi_init(vo_frame_t *frame_gen, int va_profile, int width, int height, int softrender) { + if(!frame_gen) + return VA_STATUS_ERROR_UNKNOWN; + + vo_driver_t *this_gen = (vo_driver_t *) frame_gen->driver; + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + VAStatus vaStatus; + + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + if(!this->guarded_render) { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + } + + vaStatus = vaapi_init_internal(this_gen, va_profile, width, height, softrender); + + if(!this->guarded_render) { + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + } + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + return vaStatus; +} + +static void vaapi_frame_proc_slice (vo_frame_t *vo_img, uint8_t **src) +{ + vo_img->proc_called = 1; +} + +static void vaapi_frame_field (vo_frame_t *vo_img, int which_field) +{ +} + +static void vaapi_frame_dispose (vo_frame_t *vo_img) { + vaapi_driver_t *this = (vaapi_driver_t *) vo_img->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) vo_img ; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + + lprintf("vaapi_frame_dispose\n"); + + av_free (frame->vo_frame.base[0]); + av_free (frame->vo_frame.base[1]); + av_free (frame->vo_frame.base[2]); + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + va_surface->status = SURFACE_FREE; + } + + free (frame); +} + +static vo_frame_t *vaapi_alloc_frame (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame; + + frame = (vaapi_frame_t *) calloc(1, sizeof(vaapi_frame_t)); + + if (!frame) + return NULL; + + this->frames[this->num_frame_buffers++] = frame; + + frame->vo_frame.base[0] = frame->vo_frame.base[1] = frame->vo_frame.base[2] = NULL; + frame->width = frame->height = frame->format = frame->flags = 0; + + frame->vo_frame.accel_data = &frame->vaapi_accel_data; + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + + /* + * supply required functions + */ + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + frame->vo_frame.proc_slice = vaapi_frame_proc_slice; + frame->vo_frame.proc_frame = NULL; + frame->vo_frame.field = vaapi_frame_field; + frame->vo_frame.dispose = vaapi_frame_dispose; + frame->vo_frame.driver = this_gen; + + frame->vaapi_accel_data.vo_frame = &frame->vo_frame; + frame->vaapi_accel_data.vaapi_init = &vaapi_init; + frame->vaapi_accel_data.profile_from_imgfmt = &profile_from_imgfmt; + frame->vaapi_accel_data.get_context = &get_context; + +#if AVVIDEO > 1 + frame->vaapi_accel_data.avcodec_decode_video2 = &guarded_avcodec_decode_video2; +#else + frame->vaapi_accel_data.avcodec_decode_video = &guarded_avcodec_decode_video; +#endif + + frame->vaapi_accel_data.get_vaapi_surface = &get_vaapi_surface; + frame->vaapi_accel_data.render_vaapi_surface = &render_vaapi_surface; + frame->vaapi_accel_data.release_vaapi_surface = &release_vaapi_surface; + frame->vaapi_accel_data.guarded_render = &guarded_render; + + lprintf("alloc frame\n"); + + return (vo_frame_t *) frame; +} + + +/* Display OSD */ +static int vaapi_ovl_associate(vo_driver_t *this_gen, int format, int bShow) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context->valid_context) + return 0; + + if(va_context->last_sub_image_fmt && !bShow) { + if(va_context->va_subpic_id != VA_INVALID_ID) { + if(va_context->last_sub_image_fmt == XINE_IMGFMT_VAAPI) { + vaStatus = vaDeassociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_surface_ids, RENDER_SURFACES); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } else if(va_context->last_sub_image_fmt == XINE_IMGFMT_YV12 || + va_context->last_sub_image_fmt == XINE_IMGFMT_YUY2) { + vaStatus = vaDeassociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_soft_surface_ids, SOFT_SURFACES); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } + } + va_context->last_sub_image_fmt = 0; + return 1; + } + + if(!va_context->last_sub_image_fmt && bShow) { + unsigned int flags = 0; + unsigned int output_width = va_context->width; + unsigned int output_height = va_context->height; + void *p_base = NULL; + + VAStatus vaStatus; + + vaapi_destroy_subpicture(this_gen); + vaStatus = vaapi_create_subpicture(this_gen, this->overlay_bitmap_width, this->overlay_bitmap_height); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_create_subpicture()")) + return 0; + + vaStatus = vaMapBuffer(va_context->va_display, va_context->va_subpic_image.buf, &p_base); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + return 0; + + xine_fast_memcpy((uint32_t *)p_base, this->overlay_bitmap, this->overlay_bitmap_width * this->overlay_bitmap_height * sizeof(uint32_t)); + + vaStatus = vaUnmapBuffer(va_context->va_display, va_context->va_subpic_image.buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + lprintf( "vaapi_ovl_associate: overlay_width=%d overlay_height=%d unscaled %d va_subpic_id 0x%08x ovl_changed %d has_overlay %d bShow %d overlay_bitmap_width %d overlay_bitmap_height %d va_context->width %d va_context->height %d\n", + this->overlay_output_width, this->overlay_output_height, this->has_overlay, + va_context->va_subpic_id, this->ovl_changed, this->has_overlay, bShow, + this->overlay_bitmap_width, this->overlay_bitmap_height, + va_context->width, va_context->height); + + if(format == XINE_IMGFMT_VAAPI) { + lprintf("vaapi_ovl_associate hw\n"); + vaStatus = vaAssociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_surface_ids, RENDER_SURFACES, + 0, 0, va_context->va_subpic_image.width, va_context->va_subpic_image.height, + 0, 0, output_width, output_height, flags); + } else { + lprintf("vaapi_ovl_associate sw\n"); + vaStatus = vaAssociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_soft_surface_ids, SOFT_SURFACES, + 0, 0, va_context->va_subpic_image.width, va_context->va_subpic_image.height, + 0, 0, va_soft_images[0].width, va_soft_images[0].height, flags); + } + + if(vaapi_check_status(this_gen, vaStatus, "vaAssociateSubpicture()")) { + va_context->last_sub_image_fmt = format; + return 1; + } + } + return 0; +} + +static void vaapi_overlay_clut_yuv2rgb(vaapi_driver_t *this, vo_overlay_t *overlay, vaapi_frame_t *frame) +{ + int i; + clut_t* clut = (clut_t*) overlay->color; + + if (!overlay->rgb_clut) { + for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++ ) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->rgb_clut++; + } + if (!overlay->hili_rgb_clut) { + clut = (clut_t*) overlay->hili_color; + for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->hili_rgb_clut++; + } +} + +static void vaapi_overlay_begin (vo_driver_t *this_gen, + vo_frame_t *frame_gen, int changed) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if ( !changed ) + return; + + this->has_overlay = 0; + ++this->ovl_changed; + + /* Apply OSD layer. */ + if(va_context->valid_context) { + lprintf("vaapi_overlay_begin chaned %d\n", changed); + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + } +} + +static void vaapi_overlay_blend (vo_driver_t *this_gen, + vo_frame_t *frame_gen, vo_overlay_t *overlay) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + int i = this->ovl_changed; + + if (!i) + return; + + if (--i >= XINE_VORAW_MAX_OVL) + return; + + if (overlay->width <= 0 || overlay->height <= 0 || (!overlay->rle && (!overlay->argb_layer || !overlay->argb_layer->buffer))) + return; + + if (overlay->rle) + lprintf("overlay[%d] rle %s%s %dx%d@%d,%d hili rect %d,%d-%d,%d\n", i, + overlay->unscaled ? " unscaled ": " scaled ", + (overlay->rgb_clut > 0 || overlay->hili_rgb_clut > 0) ? " rgb ": " ycbcr ", + overlay->width, overlay->height, overlay->x, overlay->y, + overlay->hili_left, overlay->hili_top, + overlay->hili_right, overlay->hili_bottom); + if (overlay->argb_layer && overlay->argb_layer->buffer) + lprintf("overlay[%d] argb %s %dx%d@%d,%d dirty rect %d,%d-%d,%d\n", i, + overlay->unscaled ? " unscaled ": " scaled ", + overlay->width, overlay->height, overlay->x, overlay->y, + overlay->argb_layer->x1, overlay->argb_layer->y1, + overlay->argb_layer->x2, overlay->argb_layer->y2); + + + this->overlays[i] = overlay; + + ++this->ovl_changed; +} + +static void vaapi_overlay_end (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int novls = this->ovl_changed; + if (novls < 2) { + this->ovl_changed = 0; + return; + } + --novls; + + uint32_t output_width = frame->width; + uint32_t output_height = frame->height; + uint32_t unscaled_width = 0, unscaled_height = 0; + vo_overlay_t *first_scaled = NULL, *first_unscaled = NULL; + vaapi_rect_t dirty_rect, unscaled_dirty_rect; + int has_rle = 0; + + int i; + for (i = 0; i < novls; ++i) { + vo_overlay_t *ovl = this->overlays[i]; + + if (ovl->rle) + has_rle = 1; + + if (ovl->unscaled) { + if (first_unscaled) { + if (ovl->x < unscaled_dirty_rect.x1) + unscaled_dirty_rect.x1 = ovl->x; + if (ovl->y < unscaled_dirty_rect.y1) + unscaled_dirty_rect.y1 = ovl->y; + if ((ovl->x + ovl->width) > unscaled_dirty_rect.x2) + unscaled_dirty_rect.x2 = ovl->x + ovl->width; + if ((ovl->y + ovl->height) > unscaled_dirty_rect.y2) + unscaled_dirty_rect.y2 = ovl->y + ovl->height; + } else { + first_unscaled = ovl; + unscaled_dirty_rect.x1 = ovl->x; + unscaled_dirty_rect.y1 = ovl->y; + unscaled_dirty_rect.x2 = ovl->x + ovl->width; + unscaled_dirty_rect.y2 = ovl->y + ovl->height; + } + + unscaled_width = unscaled_dirty_rect.x2; + unscaled_height = unscaled_dirty_rect.y2; + } else { + if (first_scaled) { + if (ovl->x < dirty_rect.x1) + dirty_rect.x1 = ovl->x; + if (ovl->y < dirty_rect.y1) + dirty_rect.y1 = ovl->y; + if ((ovl->x + ovl->width) > dirty_rect.x2) + dirty_rect.x2 = ovl->x + ovl->width; + if ((ovl->y + ovl->height) > dirty_rect.y2) + dirty_rect.y2 = ovl->y + ovl->height; + } else { + first_scaled = ovl; + dirty_rect.x1 = ovl->x; + dirty_rect.y1 = ovl->y; + dirty_rect.x2 = ovl->x + ovl->width; + dirty_rect.y2 = ovl->y + ovl->height; + } + + if (dirty_rect.x2 > output_width) + output_width = dirty_rect.x2; + if (dirty_rect.y2 > output_height) + output_height = dirty_rect.y2; + + } + } + + int need_init = 0; + + lprintf("dirty_rect.x0 %d dirty_rect.y0 %d dirty_rect.x2 %d dirty_rect.y2 %d output_width %d output_height %d\n", + dirty_rect.x0, dirty_rect.y0, dirty_rect.x2, dirty_rect.y2, output_width, output_height); + + if (first_scaled) { + vaapi_rect_t dest; + dest.x1 = first_scaled->x; + dest.y1 = first_scaled->y; + dest.x2 = first_scaled->x + first_scaled->width; + dest.y2 = first_scaled->y + first_scaled->height; + if (!RECT_IS_EQ(dest, dirty_rect)) + need_init = 1; + } + + int need_unscaled_init = (first_unscaled && + (first_unscaled->x != unscaled_dirty_rect.x1 || + first_unscaled->y != unscaled_dirty_rect.y1 || + (first_unscaled->x + first_unscaled->width) != unscaled_dirty_rect.x2 || + (first_unscaled->y + first_unscaled->height) != unscaled_dirty_rect.y2)); + + if (first_scaled) { + this->overlay_output_width = output_width; + this->overlay_output_height = output_height; + + need_init = 1; + + this->overlay_dirty_rect = dirty_rect; + } + + if (first_unscaled) { + this->overlay_unscaled_width = unscaled_width; + this->overlay_unscaled_height = unscaled_height; + + need_unscaled_init = 1; + this->overlay_unscaled_dirty_rect = unscaled_dirty_rect; + } + + if (has_rle || need_init || need_unscaled_init) { + lprintf("has_rle %d need_init %d need_unscaled_init %d unscaled_width %d unscaled_height %d output_width %d output_height %d\n", + has_rle, need_init, need_unscaled_init, unscaled_width, unscaled_height, output_width, output_height); + if (need_init) { + this->overlay_bitmap_width = output_width; + this->overlay_bitmap_height = output_height; + } + if (need_unscaled_init) { + + if(this->vdr_osd_width) + this->overlay_bitmap_width = (this->vdr_osd_width > this->sc.gui_width) ? this->vdr_osd_width : this->sc.gui_width; + else + this->overlay_bitmap_width = (unscaled_width > this->sc.gui_width) ? unscaled_width : this->sc.gui_width; + + if(this->vdr_osd_height) + this->overlay_bitmap_height = (this->vdr_osd_height > this->sc.gui_height) ? this->vdr_osd_height : this->sc.gui_height; + else + this->overlay_bitmap_height = (unscaled_height > this->sc.gui_height) ? unscaled_height : this->sc.gui_height; + + } else if (need_init) { + + if(this->vdr_osd_width) + this->overlay_bitmap_width = (this->vdr_osd_width > this->sc.gui_width) ? this->vdr_osd_width : this->sc.gui_width; + else + this->overlay_bitmap_width = (output_width > this->sc.gui_width) ? output_width : this->sc.gui_width; + + if(this->vdr_osd_height) + this->overlay_bitmap_height = (this->vdr_osd_height > this->sc.gui_height) ? this->vdr_osd_height : this->sc.gui_height; + else + this->overlay_bitmap_height = (output_height > this->sc.gui_height) ? output_height : this->sc.gui_height; + + } + } + + if ((this->overlay_bitmap_width * this->overlay_bitmap_height) > this->overlay_bitmap_size) { + this->overlay_bitmap_size = this->overlay_bitmap_width * this->overlay_bitmap_height; + free(this->overlay_bitmap); + this->overlay_bitmap = calloc( this->overlay_bitmap_size, sizeof(uint32_t)); + } else { + memset(this->overlay_bitmap, 0x0, this->overlay_bitmap_size * sizeof(uint32_t)); + } + + for (i = 0; i < novls; ++i) { + vo_overlay_t *ovl = this->overlays[i]; + uint32_t *bitmap = NULL; + uint32_t *rgba = NULL; + + if (ovl->rle) { + if(ovl->width<=0 || ovl->height<=0) + continue; + + if (!ovl->rgb_clut || !ovl->hili_rgb_clut) + vaapi_overlay_clut_yuv2rgb (this, ovl, frame); + + bitmap = rgba = calloc(ovl->width * ovl->height * 4, sizeof(uint32_t)); + + int num_rle = ovl->num_rle; + rle_elem_t *rle = ovl->rle; + uint32_t red, green, blue, alpha; + clut_t *low_colors = (clut_t*)ovl->color; + clut_t *hili_colors = (clut_t*)ovl->hili_color; + uint8_t *low_trans = ovl->trans; + uint8_t *hili_trans = ovl->hili_trans; + clut_t *colors; + uint8_t *trans; + int rlelen = 0; + uint8_t clr = 0; + int i, pos=0, x, y; + + while (num_rle > 0) { + x = pos % ovl->width; + y = pos / ovl->width; + + if ( (x>=ovl->hili_left && x<=ovl->hili_right) && (y>=ovl->hili_top && y<=ovl->hili_bottom) ) { + colors = hili_colors; + trans = hili_trans; + } + else { + colors = low_colors; + trans = low_trans; + } + rlelen = rle->len; + clr = rle->color; + for ( i=0; i<rlelen; ++i ) { + if ( trans[clr] == 0 ) { + alpha = red = green = blue = 0; + } + else { + red = colors[clr].y; // red + green = colors[clr].cr; // green + blue = colors[clr].cb; // blue + alpha = trans[clr]*255/15; + } + *rgba = (alpha<<24) | (red<<16) | (green<<8) | blue; + rgba++; + ++pos; + } + ++rle; + --num_rle; + } + lprintf("width %d height %d pos %d %d\n", ovl->width, ovl->height, pos, ovl->width * ovl->height); + } else { + pthread_mutex_lock(&ovl->argb_layer->mutex); + bitmap = ovl->argb_layer->buffer; + } + + /* Blit overlay to destination */ + uint32_t pitch = ovl->width * sizeof(uint32_t); + uint32_t *copy_dst = this->overlay_bitmap; + uint32_t *copy_src = NULL; + uint32_t height = 0; + + copy_src = bitmap; + + copy_dst += ovl->y * this->overlay_bitmap_width; + + lprintf("overlay_bitmap_width %d overlay_bitmap_height %d ovl->x %d ovl->y %d ovl->width %d ovl->height %d width %d height %d\n", + this->overlay_bitmap_width, this->overlay_bitmap_height, ovl->x, ovl->y, ovl->width, ovl->height, this->overlay_bitmap_width, this->overlay_bitmap_height); + + for(height = 0; height < ovl->height; height++) { + if((height + ovl->y) >= this->overlay_bitmap_height) + break; + + xine_fast_memcpy(copy_dst + ovl->x, copy_src, pitch); + copy_dst += this->overlay_bitmap_width; + copy_src += ovl->width; + } + + if (ovl->rle) { + if(bitmap) { + free(bitmap); + bitmap = NULL; + } + } + + if (!ovl->rle) + pthread_mutex_unlock(&ovl->argb_layer->mutex); + + } + + this->ovl_changed = 0; + this->has_overlay = (first_scaled != NULL) | (first_unscaled != NULL); + + lprintf("this->has_overlay %d\n", this->has_overlay); + /* Apply OSD layer. */ + if(va_context->valid_context) { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + } +} + +static void vaapi_resize_glx_window (vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if(this->valid_opengl_context) { + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(FOVY, ASPECT, Z_NEAR, Z_FAR); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(-0.5f, -0.5f, -Z_CAMERA); + glScalef(1.0f / (GLfloat)width, + -1.0f / (GLfloat)height, + 1.0f / (GLfloat)width); + glTranslatef(0.0f, -1.0f * (GLfloat)height, 0.0f); + } +} + +static int vaapi_redraw_needed (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + int ret = 0; + + _x_vo_scale_compute_ideal_size( &this->sc ); + + if ( _x_vo_scale_redraw_needed( &this->sc ) ) { + _x_vo_scale_compute_output_size( &this->sc ); + + XMoveResizeWindow(this->display, this->window, + 0, 0, this->sc.gui_width, this->sc.gui_height); + + ret = 1; + } + + return ret; +} + +static void vaapi_provide_standard_frame_data (vo_frame_t *orig, xine_current_frame_data_t *data) +{ + vaapi_driver_t *driver = (vaapi_driver_t *) orig->driver; + ff_vaapi_context_t *va_context = driver->va_context; + + vaapi_accel_t *accel = (vaapi_accel_t *) orig->accel_data; + vo_frame_t *this = accel->vo_frame; + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + uint32_t pitches[3]; + uint8_t *base[3]; + + if(driver == NULL) { + return; + } + + if (this->format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_provide_standard_frame_data: unexpected frame format 0x%08x!\n", this->format); + return; + } + + if( !accel || va_surface->va_surface_id == VA_INVALID_SURFACE ) + return; + + lprintf("vaapi_provide_standard_frame_data %s 0x%08x width %d height %d\n", + (this->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((this->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + va_surface->va_surface_id, this->width, this->height); + + pthread_mutex_lock(&driver->vaapi_lock); + XLockDisplay(driver->display); + + int width = va_context->width; + int height = va_context->height; + + data->format = XINE_IMGFMT_YV12; + data->img_size = width * height + + ((width + 1) / 2) * ((height + 1) / 2) + + ((width + 1) / 2) * ((height + 1) / 2); + if (data->img) { + pitches[0] = width; + pitches[2] = width / 2; + pitches[1] = width / 2; + base[0] = data->img; + base[2] = data->img + width * height; + base[1] = data->img + width * height + width * this->height / 4; + + VAImage va_image; + VAStatus vaStatus; + void *p_base; + + vaStatus = vaSyncSurface(va_context->va_display, va_surface->va_surface_id); + vaapi_check_status(va_context->driver, vaStatus, "vaSyncSurface()"); + + VASurfaceStatus surf_status = 0; + + if(driver->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface->va_surface_id, &surf_status); + vaapi_check_status(va_context->driver, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status != VASurfaceReady) + goto error; + + vaStatus = vaapi_create_image(va_context->driver, va_surface->va_surface_id, &va_image, width, height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) + goto error; + + lprintf("vaapi_provide_standard_frame_data accel->va_surface_id 0x%08x va_image.image_id 0x%08x va_context->width %d va_context->height %d va_image.width %d va_image.height %d width %d height %d size1 %d size2 %d %d %d %d status %d num_planes %d\n", + va_surface->va_surface_id, va_image.image_id, va_context->width, va_context->height, va_image.width, va_image.height, width, height, va_image.data_size, data->img_size, + va_image.pitches[0], va_image.pitches[1], va_image.pitches[2], surf_status, va_image.num_planes); + + if(va_image.image_id == VA_INVALID_ID) + goto error; + + if(!va_context->is_bound) { + vaStatus = vaGetImage(va_context->va_display, va_surface->va_surface_id, 0, 0, + va_image.width, va_image.height, va_image.image_id); + } else { + vaStatus = VA_STATUS_SUCCESS; + } + + if(vaapi_check_status(va_context->driver, vaStatus, "vaGetImage()")) { + vaStatus = vaMapBuffer( va_context->va_display, va_image.buf, &p_base ) ; + if(vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) { + + /* + uint8_t *src[3] = { NULL, }; + src[0] = (uint8_t *)p_base + va_image.offsets[0]; + src[1] = (uint8_t *)p_base + va_image.offsets[1]; + src[2] = (uint8_t *)p_base + va_image.offsets[2]; + */ + + if( va_image.format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image.format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("VAAPI YV12 image\n"); + + yv12_to_yv12( + (uint8_t*)p_base + va_image.offsets[0], va_image.pitches[0], + base[0], pitches[0], + (uint8_t*)p_base + va_image.offsets[1], va_image.pitches[1], + base[1], pitches[1], + (uint8_t*)p_base + va_image.offsets[2], va_image.pitches[2], + base[2], pitches[2], + va_image.width, va_image.height); + + } else if( va_image.format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) ) { + lprintf("VAAPI NV12 image\n"); + + lprintf("va_image.offsets[0] %d va_image.offsets[1] %d va_image.offsets[2] %d size %d size %d size %d width %d height %d width %d height %d\n", + va_image.offsets[0], va_image.offsets[1], va_image.offsets[2], va_image.data_size, va_image.width * va_image.height, + data->img_size, width, height, va_image.width, va_image.height); + + base[0] = data->img; + base[1] = data->img + width * height; + base[2] = data->img + width * height + width * height / 4; + + nv12_to_yv12((uint8_t *)p_base + va_image.offsets[0], va_image.pitches[0], + (uint8_t *)p_base + va_image.offsets[1], va_image.pitches[1], + base[0], pitches[0], + base[1], pitches[1], + base[2], pitches[2], + va_image.width, va_image.height, + width, height, + va_image.data_size); + + } else { + printf("vaapi_provide_standard_frame_data unsupported image format\n"); + } + + vaStatus = vaUnmapBuffer(va_context->va_display, va_image.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + vaapi_destroy_image(va_context->driver, &va_image); + } + } + } + +error: + XUnlockDisplay(driver->display); + pthread_mutex_unlock(&driver->vaapi_lock); +} + +static void vaapi_duplicate_frame_data (vo_frame_t *this_gen, vo_frame_t *original) +{ + vaapi_driver_t *driver = (vaapi_driver_t *) original->driver; + ff_vaapi_context_t *va_context = driver->va_context; + + vaapi_frame_t *this = (vaapi_frame_t *)this_gen; + vaapi_frame_t *orig = (vaapi_frame_t *)original; + + vaapi_accel_t *accel_this = &this->vaapi_accel_data; + vaapi_accel_t *accel_orig = &orig->vaapi_accel_data; + + ff_vaapi_surface_t *va_surface_this = &va_render_surfaces[accel_this->index]; + ff_vaapi_surface_t *va_surface_orig = &va_render_surfaces[accel_orig->index]; + + lprintf("vaapi_duplicate_frame_data %s %s 0x%08x 0x%08x\n", + (this_gen->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((this_gen->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + (original->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((original->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + va_surface_this->va_surface_id, va_surface_orig->va_surface_id); + + if (orig->vo_frame.format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_duplicate_frame_data: unexpected frame format 0x%08x!\n", orig->format); + return; + } + + if (this->vo_frame.format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_duplicate_frame_data: unexpected frame format 0x%08x!\n", this->format); + return; + } + + pthread_mutex_lock(&driver->vaapi_lock); + XLockDisplay(driver->display); + + VAImage va_image_orig; + VAImage va_image_this; + VAStatus vaStatus; + void *p_base_orig = NULL; + void *p_base_this = NULL; + + vaStatus = vaSyncSurface(va_context->va_display, va_surface_orig->va_surface_id); + vaapi_check_status(va_context->driver, vaStatus, "vaSyncSurface()"); + + int this_width = va_context->width; + int this_height = va_context->height; + int orig_width = va_context->width; + int orig_height = va_context->height; + + vaStatus = vaapi_create_image(va_context->driver, va_surface_orig->va_surface_id, &va_image_orig, orig_width, orig_height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + va_image_orig.image_id = VA_INVALID_ID; + goto error; + } + + vaStatus = vaapi_create_image(va_context->driver, va_surface_this->va_surface_id, &va_image_this, this_width, this_height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + va_image_this.image_id = VA_INVALID_ID; + goto error; + } + + if(va_image_orig.image_id == VA_INVALID_ID || va_image_this.image_id == VA_INVALID_ID) { + printf("vaapi_duplicate_frame_data invalid image\n"); + goto error; + } + + lprintf("vaapi_duplicate_frame_data va_image_orig.image_id 0x%08x va_image_orig.width %d va_image_orig.height %d width %d height %d size %d %d %d %d\n", + va_image_orig.image_id, va_image_orig.width, va_image_orig.height, this->width, this->height, va_image_orig.data_size, + va_image_orig.pitches[0], va_image_orig.pitches[1], va_image_orig.pitches[2]); + + if(!va_context->is_bound) { + vaStatus = vaGetImage(va_context->va_display, va_surface_orig->va_surface_id, 0, 0, + va_image_orig.width, va_image_orig.height, va_image_orig.image_id); + } else { + vaStatus = VA_STATUS_SUCCESS; + } + + if(vaapi_check_status(va_context->driver, vaStatus, "vaGetImage()")) { + + if(!va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_surface_this->va_surface_id, va_image_orig.image_id, + 0, 0, va_image_orig.width, va_image_orig.height, + 0, 0, va_image_this.width, va_image_this.height); + vaapi_check_status(va_context->driver, vaStatus, "vaPutImage()"); + } else { + vaStatus = vaMapBuffer( va_context->va_display, va_image_orig.buf, &p_base_orig ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + goto error; + + vaStatus = vaMapBuffer( va_context->va_display, va_image_this.buf, &p_base_this ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + goto error; + + int size = (va_image_orig.data_size > va_image_this.data_size) ? va_image_this.data_size : va_image_orig.data_size; + xine_fast_memcpy((uint8_t *) p_base_this, (uint8_t *) p_base_orig, size); + + } + } + +error: + if(p_base_orig) { + vaStatus = vaUnmapBuffer(va_context->va_display, va_image_orig.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + } + if(p_base_this) { + vaStatus = vaUnmapBuffer(va_context->va_display, va_image_this.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + } + + vaapi_destroy_image(va_context->driver, &va_image_orig); + vaapi_destroy_image(va_context->driver, &va_image_this); + + XUnlockDisplay(driver->display); + pthread_mutex_unlock(&driver->vaapi_lock); +} + +static void vaapi_update_frame_format (vo_driver_t *this_gen, + vo_frame_t *frame_gen, + uint32_t width, uint32_t height, + double ratio, int format, int flags) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t*)frame_gen; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + + lprintf("vaapi_update_frame_format\n"); + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + lprintf("vaapi_update_frame_format %s %s width %d height %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + (format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + width, height); + + frame->vo_frame.width = width; + frame->vo_frame.height = height; + + if ((frame->width != width) + || (frame->height != height) + || (frame->format != format)) { + + // (re-) allocate render space + av_freep (&frame->vo_frame.base[0]); + av_freep (&frame->vo_frame.base[1]); + av_freep (&frame->vo_frame.base[2]); + + /* set init_vaapi on frame formats XINE_IMGFMT_YV12/XINE_IMGFMT_YUY2 only. + * for XINE_IMGFMT_VAAPI the init was already done. + */ + if (format == XINE_IMGFMT_YV12) { + frame->vo_frame.pitches[0] = 8*((width + 7) / 8); + frame->vo_frame.pitches[1] = 8*((width + 15) / 16); + frame->vo_frame.pitches[2] = 8*((width + 15) / 16); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.base[1] = av_mallocz (frame->vo_frame.pitches[1] * ((height+1)/2) + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.base[2] = av_mallocz (frame->vo_frame.pitches[2] * ((height+1)/2) + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + lprintf("XINE_IMGFMT_YV12 width %d height %d\n", width, height); + } else if (format == XINE_IMGFMT_YUY2){ + frame->vo_frame.pitches[0] = 8*((width + 3) / 4); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + lprintf("XINE_IMGFMT_YUY2 width %d height %d\n", width, height); + } else if (format == XINE_IMGFMT_VAAPI) { + frame->vo_frame.proc_duplicate_frame_data = vaapi_duplicate_frame_data; + frame->vo_frame.proc_provide_standard_frame_data = vaapi_provide_standard_frame_data; + lprintf("XINE_IMGFMT_VAAPI width %d height %d\n", width, height); + } + + frame->width = width; + frame->height = height; + frame->format = format; + frame->flags = flags; + vaapi_frame_field ((vo_frame_t *)frame, flags); + } + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + if(va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_update_frame_format 0x%08x\n", va_surface->va_surface_id); +#endif + } else if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RELEASE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_update_frame_format 0x%08x\n", va_surface->va_surface_id); +#endif + } + } + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + + frame->ratio = ratio; + frame->vo_frame.future_frame = NULL; +} + +static inline uint8_t clip_uint8_vlc( int32_t a ) +{ + if( a&(~255) ) return (-a)>>31; + else return a; +} + + +static void nv12_to_yv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *uv_src, int uv_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *u_dst, int u_dst_pitch, + uint8_t *v_dst, int v_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int src_data_size) { + + int y_src_size = src_height * y_src_pitch; + int y, x; + + int uv_src_size = src_height * uv_src_pitch / 2; + if((y_src_size + uv_src_size) != (src_data_size)) + printf("nv12_to_yv12 strange %d\n", (y_src_size + uv_src_size) - (src_data_size)); + + int height = (src_height > dst_height) ? dst_height : src_height; + int width = (src_width > dst_width) ? dst_width : src_width; + + for(y = 0; y < height; y++) { + xine_fast_memcpy(y_dst, y_src, width); + y_src += y_src_pitch; + y_dst += y_dst_pitch; + } + + for(y = 0; y < height; y++) { + const uint8_t *uv_src_tmp = uv_src; + for(x = 0; x < u_dst_pitch; x++) { + if(((y * uv_src_pitch) + x) < uv_src_size) { + *(u_dst + x) = *(uv_src_tmp ); + *(v_dst + x) = *(uv_src_tmp + 1); + } + uv_src_tmp += 2; + } + uv_src += uv_src_pitch; + u_dst += u_dst_pitch; + v_dst += v_dst_pitch; + } +} + +static void yv12_to_nv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *u_src, int u_src_pitch, + const uint8_t *v_src, int v_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *uv_dst, int uv_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int dst_data_size) { + + int y_dst_size = dst_height * y_dst_pitch; + int y, x; + + lprintf("yv12_to_nv12 converter\n"); + + int uv_dst_size = dst_height * uv_dst_pitch / 2; + if((y_dst_size + uv_dst_size) != (dst_data_size)) + printf("yv12_to_nv12 strange %d\n", (y_dst_size + uv_dst_size) - (dst_data_size)); + + int height = (src_height > dst_height) ? dst_height : src_height; + int width = (src_width > dst_width) ? dst_width : src_width; + + for(y = 0; y < height; y++) { + xine_fast_memcpy(y_dst, y_src, width); + y_src += y_src_pitch; + y_dst += y_dst_pitch; + } + + for(y = 0; y < height; y++) { + uint8_t *uv_dst_tmp = uv_dst; + for(x = 0; x < u_src_pitch; x++) { + if(((y * uv_dst_pitch) + x) < uv_dst_size) { + *(uv_dst_tmp ) = *(u_src + x); + *(uv_dst_tmp + 1) = *(v_src + x); + } + uv_dst_tmp += 2; + } + uv_dst += uv_dst_pitch; + u_src += u_src_pitch; + v_src += v_src_pitch; + } +} + +static VAStatus vaapi_software_render_frame(vo_driver_t *this_gen, vo_frame_t *frame_gen, + VAImage *va_image, VASurfaceID va_surface_id) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + void *p_base = NULL; + VAStatus vaStatus; + + if(va_image == NULL || va_image->image_id == VA_INVALID_ID || + va_surface_id == VA_INVALID_SURFACE || !va_context->valid_context) + return VA_STATUS_ERROR_UNKNOWN; + + lprintf("vaapi_software_render_frame : va_surface_id 0x%08x va_image.image_id 0x%08x width %d height %d f_width %d f_height %d sw_width %d sw_height %d\n", + va_surface_id, va_image->image_id, va_image->width, va_image->height, frame->width, frame->height, + va_context->sw_width, va_context->sw_height); + + if(frame->width != va_image->width || frame->height != va_image->height) + return VA_STATUS_SUCCESS; + + vaStatus = vaMapBuffer( va_context->va_display, va_image->buf, &p_base ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + return vaStatus; + + + uint8_t *dst[3] = { NULL, }; + uint32_t pitches[3]; + + if(this->swap_uv_planes) { + dst[0] = (uint8_t *)p_base + va_image->offsets[0]; pitches[0] = va_image->pitches[0]; + dst[1] = (uint8_t *)p_base + va_image->offsets[1]; pitches[1] = va_image->pitches[1]; + dst[2] = (uint8_t *)p_base + va_image->offsets[2]; pitches[2] = va_image->pitches[2]; + } else { + dst[0] = (uint8_t *)p_base + va_image->offsets[0]; pitches[0] = va_image->pitches[0]; + dst[1] = (uint8_t *)p_base + va_image->offsets[2]; pitches[1] = va_image->pitches[1]; + dst[2] = (uint8_t *)p_base + va_image->offsets[1]; pitches[2] = va_image->pitches[2]; + } + + /* Copy xine frames into VAAPI images */ + if(frame->format == XINE_IMGFMT_YV12) { + + if (va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("vaapi_software_render_frame yv12 -> yv12 convert\n"); + + yv12_to_yv12( + /* Y */ + frame_gen->base[0], frame_gen->pitches[0], + dst[0], pitches[0], + /* U */ + frame_gen->base[1], frame_gen->pitches[1], + dst[1], pitches[1], + /* V */ + frame_gen->base[2], frame_gen->pitches[2], + dst[2], pitches[2], + /* width x height */ + frame_gen->width, frame_gen->height); + + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' )) { + lprintf("vaapi_software_render_frame yv12 -> nv12 convert\n"); + + yv12_to_nv12(frame_gen->base[0], frame_gen->pitches[0], + frame_gen->base[1], frame_gen->pitches[1], + frame_gen->base[2], frame_gen->pitches[2], + (uint8_t *)p_base + va_image->offsets[0], va_image->pitches[0], + (uint8_t *)p_base + va_image->offsets[1], va_image->pitches[1], + frame_gen->width, frame_gen->height, + va_image->width, va_image->height, + va_image->data_size); + + } + } else if (frame->format == XINE_IMGFMT_YUY2) { + + if (va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("vaapi_software_render_frame yuy2 -> yv12 convert\n"); + + yuy2_to_yv12(frame_gen->base[0], frame_gen->pitches[0], + dst[0], pitches[0], + dst[1], pitches[1], + dst[2], pitches[2], + frame_gen->width, frame_gen->height); + + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' )) { + lprintf("vaapi_software_render_frame yuy2 -> nv12 convert\n"); + printf("yuy2 -> nv12 convert missing\n"); + } + + } + + vaStatus = vaUnmapBuffer(va_context->va_display, va_image->buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + if(!va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_surface_id, va_image->image_id, + 0, 0, va_image->width, va_image->height, + 0, 0, va_image->width, va_image->height); + } + + if(!vaapi_check_status(va_context->driver, vaStatus, "vaPutImage()")) + return vaStatus; + + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_hardware_render_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen, + VASurfaceID va_surface_id) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus = VA_STATUS_ERROR_UNKNOWN; + int i = 0; + int interlaced_frame = !frame->vo_frame.progressive_frame; + int top_field_first = frame->vo_frame.top_field_first; + int width, height; + + if(frame->format == XINE_IMGFMT_VAAPI) { + width = va_context->width; + height = va_context->height; + } else { + width = (frame->width > va_context->sw_width) ? va_context->sw_width : frame->width; + height = (frame->height > va_context->sw_height) ? va_context->sw_height : frame->height; + } + + if(!va_context->valid_context || va_surface_id == VA_INVALID_SURFACE) + return VA_STATUS_ERROR_UNKNOWN; + + if(this->opengl_render && !this->valid_opengl_context) + return VA_STATUS_ERROR_UNKNOWN; + + /* Final VAAPI rendering. The deinterlacing can be controled by xine config.*/ + unsigned int deint = this->deinterlace; + for(i = 0; i <= !!((deint > 1) && interlaced_frame); i++) { + unsigned int flags = (deint && (interlaced_frame) ? (((!!(top_field_first)) ^ i) == 0 ? VA_BOTTOM_FIELD : VA_TOP_FIELD) : VA_FRAME_PICTURE); + + //flags |= vaapi_get_colorspace_flags(this_gen); + + flags |= VA_CLEAR_DRAWABLE; + flags |= this->scaling_level; + + lprintf("Putsrfc srfc 0x%08X flags 0x%08x %dx%d -> %dx%d interlaced %d top_field_first %d\n", + va_surface_id, flags, width, height, + this->sc.output_width, this->sc.output_height, + interlaced_frame, top_field_first); + + if(this->opengl_render) { + + vaapi_x11_trap_errors(); + + if(this->opengl_use_tfp) { + lprintf("opengl render tfp\n"); + vaStatus = vaPutSurface(va_context->va_display, va_surface_id, this->gl_image_pixmap, + 0, 0, width, height, 0, 0, width, height, NULL, 0, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaPutSurface()")) + return vaStatus; + } else { + lprintf("opengl render\n"); + vaStatus = vaCopySurfaceGLX(va_context->va_display, va_context->gl_surface, va_surface_id, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaCopySurfaceGLX()")) + return vaStatus; + } + if(vaapi_x11_untrap_errors()) + return VA_STATUS_ERROR_UNKNOWN; + + vaapi_glx_flip_page(frame_gen, 0, 0, va_context->width, va_context->height); + + } else { + + vaStatus = vaPutSurface(va_context->va_display, va_surface_id, this->window, + 0, 0, width, height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height, + NULL, 0, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaPutSurface()")) + return vaStatus; + } + } + return VA_STATUS_SUCCESS; +} + +/* Used in vaapi_display_frame to determine how long displaying a frame takes + - if slower than 60fps, print a message +*/ +/* +static double timeOfDay() +{ + struct timeval t; + gettimeofday( &t, NULL ); + return ((double)t.tv_sec) + (((double)t.tv_usec)/1000000.0); +} +*/ + +static void vaapi_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + ff_vaapi_context_t *va_context = this->va_context; + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + VASurfaceID va_surface_id = VA_INVALID_SURFACE; + VAImage *va_image = NULL; + VAStatus vaStatus; + + lprintf("vaapi_display_frame\n"); + + /* + if((frame->height < 17 || frame->width < 17) && ((frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2))) { + frame->vo_frame.free( frame_gen ); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " frame size to small width %d height %d\n", frame->height, frame->width); + return; + } + */ + + /* + * let's see if this frame is different in size / aspect + * ratio from the previous one + */ + + if ( (frame->width != this->sc.delivered_width) + || (frame->height != this->sc.delivered_height) + || (frame->ratio != this->sc.delivered_ratio) + || (frame->vo_frame.crop_left != this->sc.crop_left) + || (frame->vo_frame.crop_right != this->sc.crop_right) + || (frame->vo_frame.crop_top != this->sc.crop_top) + || (frame->vo_frame.crop_bottom != this->sc.crop_bottom) ) { + lprintf("frame format changed\n"); + this->sc.force_redraw = 1; + } + + /* + * tell gui that we are about to display a frame, + * ask for offset and output size + */ + this->sc.delivered_height = frame->height; + this->sc.delivered_width = frame->width; + this->sc.delivered_ratio = frame->ratio; + this->sc.crop_left = frame->vo_frame.crop_left; + this->sc.crop_right = frame->vo_frame.crop_right; + this->sc.crop_top = frame->vo_frame.crop_top; + this->sc.crop_bottom = frame->vo_frame.crop_bottom; + + /* + * tell gui that we are about to display a frame, + * ask for offset and output size + */ + this->sc.delivered_height = frame->height; + this->sc.delivered_width = frame->width; + this->sc.delivered_ratio = frame->ratio; + + this->sc.crop_left = frame->vo_frame.crop_left; + this->sc.crop_right = frame->vo_frame.crop_right; + this->sc.crop_top = frame->vo_frame.crop_top; + this->sc.crop_bottom = frame->vo_frame.crop_bottom; + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + lprintf("vaapi_display_frame %s frame->width %d frame->height %d va_context->sw_width %d va_context->sw_height %d valid_context %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height, va_context->sw_width, va_context->sw_height, va_context->valid_context); + + if( ((frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2)) + && ((frame->width != va_context->sw_width) ||(frame->height != va_context->sw_height )) ) { + + lprintf("vaapi_display_frame %s frame->width %d frame->height %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height); + + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + if(!va_context->valid_context) { + lprintf("vaapi_display_frame init full context\n"); + vaapi_init_internal(frame_gen->driver, SW_CONTEXT_INIT_FORMAT, frame->width, frame->height, 0); + } else { + lprintf("vaapi_display_frame init soft surfaces\n"); + vaapi_init_soft_surfaces(frame_gen->driver, frame->width, frame->height); + } + + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + } + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + + vaapi_redraw_needed (this_gen); + + /* posible race could happen while the lock is opened */ + if(!this->va_context || !this->va_context->valid_context) + return; + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + /* initialize opengl rendering */ + if(this->opengl_render && this->init_opengl_render && va_context->valid_context) { + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + destroy_glx(this_gen); + + vaapi_glx_config_glx(frame_gen->driver, va_context->width, va_context->height); + + vaapi_resize_glx_window(frame_gen->driver, this->sc.gui_width, this->sc.gui_height); + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + this->sc.force_redraw = 1; + this->init_opengl_render = 0; + } + + /* + double start_time; + double end_time; + double elapse_time; + int factor; + + start_time = timeOfDay(); + */ + + if(va_context->valid_context && ( (frame->format == XINE_IMGFMT_VAAPI) || (frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2) )) { + + if((frame->format == XINE_IMGFMT_YUY2) || (frame->format == XINE_IMGFMT_YV12)) { + va_surface_id = va_soft_surface_ids[va_context->va_soft_head]; + va_image = &va_soft_images[va_context->va_soft_head]; + va_context->va_soft_head = (va_context->va_soft_head + 1) % (SOFT_SURFACES); + } + + if(this->guarded_render) { + if(frame->format == XINE_IMGFMT_VAAPI) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + if(va_surface->status == SURFACE_RENDER || va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface_id = va_surface->va_surface_id; + } + va_image = NULL; + } +#ifdef DEBUG_SURFACE + printf("vaapi_display_frame va_surface 0x%08x status %d index %d\n", va_surface_id, va_surface->status, accel->index); +#endif + } else { + if(frame->format == XINE_IMGFMT_VAAPI) { + va_surface_id = va_surface->va_surface_id; + va_image = NULL; + } + } + + lprintf("2: 0x%08x\n", va_surface_id); + + VASurfaceStatus surf_status = 0; + if(va_surface_id != VA_INVALID_SURFACE) { + + if(this->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface_id, &surf_status); + vaapi_check_status(this_gen, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status != VASurfaceReady) { + va_surface_id = VA_INVALID_SURFACE; + va_image = NULL; +#ifdef DEBUG_SURFACE + printf("Surface srfc 0x%08X not ready for render\n", va_surface_id); +#endif + } + } else { +#ifdef DEBUG_SURFACE + printf("Invalid srfc 0x%08X\n", va_surface_id); +#endif + } + + if(va_surface_id != VA_INVALID_SURFACE) { + + lprintf("vaapi_display_frame: 0x%08x %d %d\n", va_surface_id, va_context->width, va_context->height); + + vaStatus = vaSyncSurface(va_context->va_display, va_surface_id); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + + /* transfer image data to a VAAPI surface */ + if((frame->format == XINE_IMGFMT_YUY2 || frame->format == XINE_IMGFMT_YV12)) + vaapi_software_render_frame(this_gen, frame_gen, va_image, va_surface_id); + + vaapi_hardware_render_frame(this_gen, frame_gen, va_surface_id); + + } + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " unsupported image format %s width %d height %d valid_context %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height, va_context->valid_context); + } + + XSync(this->display, False); + + //end_time = timeOfDay(); + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + if(va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_display_frame 0x%08x\n", va_surface->va_surface_id); +#endif + } else if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RELEASE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_display_frame 0x%08x\n", va_surface->va_surface_id); +#endif + } + } + + XUnlockDisplay(this->display); + + frame->vo_frame.free( frame_gen ); + + pthread_mutex_unlock(&this->vaapi_lock); + + /* + elapse_time = end_time - start_time; + factor = (int)(elapse_time/(1.0/60.0)); + + if( factor > 1 ) + { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " PutImage %dX interval (%fs)\n", factor, elapse_time ); + } + */ +} + +static int vaapi_get_property (vo_driver_t *this_gen, int property) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + switch (property) { + case VO_PROP_WINDOW_WIDTH: + this->props[property].value = this->sc.gui_width; + break; + case VO_PROP_WINDOW_HEIGHT: + this->props[property].value = this->sc.gui_height; + break; + case VO_PROP_OUTPUT_WIDTH: + this->props[property].value = this->sc.output_width; + break; + case VO_PROP_OUTPUT_HEIGHT: + this->props[property].value = this->sc.output_height; + break; + case VO_PROP_OUTPUT_XOFFSET: + this->props[property].value = this->sc.output_xoffset; + break; + case VO_PROP_OUTPUT_YOFFSET: + this->props[property].value = this->sc.output_yoffset; + break; + case VO_PROP_MAX_NUM_FRAMES: + if(!this->guarded_render) + this->props[property].value = RENDER_SURFACES; + else + this->props[property].value = 2; + break; + } + + lprintf("vaapi_get_property property=%d, value=%d\n", property, this->props[property].value ); + + return this->props[property].value; +} + +static int vaapi_set_property (vo_driver_t *this_gen, int property, int value) { + + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + lprintf("vaapi_set_property property=%d, value=%d\n", property, value ); + + if(this->props[property].atom) { + VADisplayAttribute attr; + + if((value < this->props[property].min) || (value > this->props[property].max)) + value = (this->props[property].min + this->props[property].max) >> 1; + + this->props[property].value = value; + attr.type = this->props[property].type; + attr.value = value; + + if(va_context && va_context->valid_context) { + VAStatus vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + vaapi_check_status((vo_driver_t *)this, vaStatus, "vaSetDisplayAttributes()"); + } + + if (this->props[property].entry) + this->props[property].entry->num_value = this->props[property].value; + + vaapi_show_display_props((vo_driver_t*)this); + + return this->props[property].value; + } else { + switch (property) { + + case VO_PROP_ASPECT_RATIO: + if (value>=XINE_VO_ASPECT_NUM_RATIOS) + value = XINE_VO_ASPECT_AUTO; + this->props[property].value = value; + this->sc.user_ratio = value; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + break; + + case VO_PROP_ZOOM_X: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + this->sc.zoom_factor_x = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + } + break; + + case VO_PROP_ZOOM_Y: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + this->sc.zoom_factor_y = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + } + break; + } + } + return value; +} + +static void vaapi_get_property_min_max (vo_driver_t *this_gen, + int property, int *min, int *max) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + *min = this->props[property].min; + *max = this->props[property].max; +} + +static int vaapi_gui_data_exchange (vo_driver_t *this_gen, + int data_type, void *data) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + lprintf("vaapi_gui_data_exchange %d\n", data_type); + + switch (data_type) { +#ifndef XINE_DISABLE_DEPRECATED_FEATURES + case XINE_GUI_SEND_COMPLETION_EVENT: + break; +#endif + + case XINE_GUI_SEND_EXPOSE_EVENT: { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay( this->display ); + lprintf("XINE_GUI_SEND_EXPOSE_EVENT:\n"); + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + } + break; + + case XINE_GUI_SEND_WILL_DESTROY_DRAWABLE: { + printf("XINE_GUI_SEND_WILL_DESTROY_DRAWABLE\n"); + } + break; + + case XINE_GUI_SEND_DRAWABLE_CHANGED: { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay( this->display ); + lprintf("XINE_GUI_SEND_DRAWABLE_CHANGED\n"); + + this->drawable = (Drawable) data; + + XReparentWindow(this->display, this->window, this->drawable, 0, 0); + + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + + XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + } + break; + + case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO: { + int x1, y1, x2, y2; + x11_rectangle_t *rect = data; + + _x_vo_scale_translate_gui2video(&this->sc, rect->x, rect->y, &x1, &y1); + _x_vo_scale_translate_gui2video(&this->sc, rect->x + rect->w, rect->y + rect->h, &x2, &y2); + rect->x = x1; + rect->y = y1; + rect->w = x2-x1; + rect->h = y2-y1; + } + break; + + default: + return -1; + } + + return 0; +} + +static void vaapi_dispose (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + lprintf("vaapi_dispose\n"); + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + this->ovl_yuv2rgb->dispose(this->ovl_yuv2rgb); + this->yuv2rgb_factory->dispose (this->yuv2rgb_factory); + + vaapi_close(this_gen); + free(va_context); + + if(this->overlay_bitmap) + free(this->overlay_bitmap); + + if(va_surface_ids) + free(va_surface_ids); + if(va_soft_surface_ids) + free(va_soft_surface_ids); + if(va_render_surfaces) + free(va_render_surfaces); + if(va_soft_images) + free(va_soft_images); + + XDestroyWindow(this->display, this->window); + XUnlockDisplay(this->display); + + pthread_mutex_unlock(&this->vaapi_lock); + pthread_mutex_destroy(&this->vaapi_lock); + + free (this); +} + +static void vaapi_vdr_osd_width_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->vdr_osd_width = entry->num_value; +} + +static void vaapi_vdr_osd_height_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->vdr_osd_height = entry->num_value; +} + +static void vaapi_deinterlace_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->deinterlace = entry->num_value; + if(this->deinterlace > 2) + this->deinterlace = 2; +} + +static void vaapi_opengl_render( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->opengl_render = entry->num_value; +} + +static void vaapi_opengl_use_tfp( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->opengl_use_tfp = entry->num_value; +} + +static void vaapi_guarded_render( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->guarded_render = entry->num_value; +} + +static void vaapi_scaling_level( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->scaling_level = entry->num_value; +} + +static void vaapi_swap_uv_planes(void *this_gen, xine_cfg_entry_t *entry) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->swap_uv_planes = entry->num_value; +} + +static vo_driver_t *vaapi_open_plugin (video_driver_class_t *class_gen, const void *visual_gen) { + + vaapi_class_t *class = (vaapi_class_t *) class_gen; + x11_visual_t *visual = (x11_visual_t *) visual_gen; + vaapi_driver_t *this; + config_values_t *config = class->config; + XSetWindowAttributes xswa; + unsigned long xswa_mask; + XWindowAttributes wattr; + unsigned long black_pixel; + XVisualInfo visualInfo; + XVisualInfo *vi; + int depth; + const int x11_event_mask = ExposureMask | + StructureNotifyMask; + + this = (vaapi_driver_t *) calloc(1, sizeof(vaapi_driver_t)); + if (!this) + return NULL; + + this->config = config; + this->xine = class->xine; + + this->display = visual->display; + this->screen = visual->screen; + this->drawable = visual->d; + + this->va_context = calloc(1, sizeof(ff_vaapi_context_t)); + + /* number of video frames from config - register it with the default value. */ + int frame_num = config->register_num (config, "engine.buffers.video_num_frames", RENDER_SURFACES, /* default */ + _("default number of video frames"), + _("The default number of video frames to request " + "from xine video out driver. Some drivers will " + "override this setting with their own values."), + 20, NULL, this); + + /* now make sure we have at least 22 frames, to prevent + * locks with vdpau_h264 */ + if(frame_num != RENDER_SURFACES) + config->update_num(config,"engine.buffers.video_num_frames", RENDER_SURFACES); + + this->opengl_render = config->register_bool( config, "video.output.vaapi_opengl_render", 0, + _("vaapi: opengl output rendering"), + _("vaapi: opengl output rendering"), + 20, vaapi_opengl_render, this ); + + this->init_opengl_render = 1; + + this->opengl_use_tfp = config->register_bool( config, "video.output.vaapi_opengl_use_tfp", 0, + _("vaapi: opengl rendering tfp"), + _("vaapi: opengl rendering tfp"), + 20, vaapi_opengl_use_tfp, this ); + + if(this->opengl_render) { + this->opengl_render = vaapi_opengl_verify_direct ((x11_visual_t *)visual_gen); + if(!this->opengl_render) + xprintf (this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Opengl indirect/software rendering does not work. Fallback to plain VAAPI output !!!!\n"); + } + + this->valid_opengl_context = 0; + this->gl_vinfo = NULL; + this->gl_pixmap = None; + this->gl_image_pixmap = None; + this->gl_texture = GL_NONE; + + this->num_frame_buffers = 0; + + va_render_surfaces = calloc(RENDER_SURFACES + 1, sizeof(ff_vaapi_surface_t)); + va_surface_ids = calloc(RENDER_SURFACES + 1, sizeof(VASurfaceID)); + va_soft_surface_ids = calloc(SOFT_SURFACES + 1, sizeof(VASurfaceID)); + va_soft_images = calloc(SOFT_SURFACES + 1, sizeof(VAImage)); + + vaapi_init_va_context(this); + vaapi_init_subpicture(this); + + _x_vo_scale_init (&this->sc, 1, 0, config ); + + this->sc.frame_output_cb = visual->frame_output_cb; + this->sc.dest_size_cb = visual->dest_size_cb; + this->sc.user_data = visual->user_data; + this->sc.user_ratio = XINE_VO_ASPECT_AUTO; + + black_pixel = BlackPixel(this->display, this->screen); + + XGetWindowAttributes(this->display, this->drawable, &wattr); + + depth = wattr.depth; + if (depth != 15 && depth != 16 && depth != 24 && depth != 32) + depth = 24; + + vi = &visualInfo; + XMatchVisualInfo(this->display, this->screen, depth, TrueColor, vi); + + xswa_mask = CWBorderPixel | CWBackPixel | CWColormap; + xswa.border_pixel = black_pixel; + xswa.background_pixel = black_pixel; + xswa.colormap = CopyFromParent; + + this->window = XCreateWindow(this->display, this->drawable, + 0, 0, 1, 1, 0, depth, + InputOutput, vi->visual, xswa_mask, &xswa); + + if(this->window == None) + return NULL; + + XSelectInput(this->display, this->window, x11_event_mask); + + XMapWindow(this->display, this->window); + vaapi_x11_wait_event(this->display, this->window, MapNotify); + + if(vi != &visualInfo) + XFree(vi); + + this->capabilities = VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_CROP | VO_CAP_UNSCALED_OVERLAY | VO_CAP_ARGB_LAYER_OVERLAY | VO_CAP_VAAPI | VO_CAP_CUSTOM_EXTENT_OVERLAY; + + /* overlay converter */ + this->yuv2rgb_factory = yuv2rgb_factory_init (MODE_24_BGR, 0, NULL); + this->ovl_yuv2rgb = this->yuv2rgb_factory->create_converter( this->yuv2rgb_factory ); + + this->vo_driver.get_capabilities = vaapi_get_capabilities; + this->vo_driver.alloc_frame = vaapi_alloc_frame; + this->vo_driver.update_frame_format = vaapi_update_frame_format; + this->vo_driver.overlay_begin = vaapi_overlay_begin; + this->vo_driver.overlay_blend = vaapi_overlay_blend; + this->vo_driver.overlay_end = vaapi_overlay_end; + this->vo_driver.display_frame = vaapi_display_frame; + this->vo_driver.get_property = vaapi_get_property; + this->vo_driver.set_property = vaapi_set_property; + this->vo_driver.get_property_min_max = vaapi_get_property_min_max; + this->vo_driver.gui_data_exchange = vaapi_gui_data_exchange; + this->vo_driver.dispose = vaapi_dispose; + this->vo_driver.redraw_needed = vaapi_redraw_needed; + + this->deinterlace = 0; + this->vdr_osd_width = 0; + this->vdr_osd_height = 0; + + this->vdr_osd_width = config->register_num( config, "video.output.vaapi_vdr_osd_width", 0, + _("vaapi: VDR osd width workaround."), + _("vaapi: VDR osd width workaround."), + 10, vaapi_vdr_osd_width_flag, this ); + + this->vdr_osd_height = config->register_num( config, "video.output.vaapi_vdr_osd_height", 0, + _("vaapi: VDR osd height workaround."), + _("vaapi: VDR osd height workaround."), + 10, vaapi_vdr_osd_height_flag, this ); + + this->deinterlace = config->register_num( config, "video.output.vaapi_deinterlace", 0, + _("vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob )."), + _("vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob )."), + 10, vaapi_deinterlace_flag, this ); + + this->guarded_render = config->register_num( config, "video.output.vaapi_guarded_render", 1, + _("vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )"), + _("vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )"), + 10, vaapi_guarded_render, this ); + + this->scaling_level_enum = config->register_enum(config, "video.output.vaapi_scaling_level", 0, + (char **)scaling_level_enum_names, + _("vaapi: set scaling level to : default (default) fast (fast) hq (HQ) nla (anamorphic)"), + _("vaapi: set scaling level to : default (default) fast (fast) hq (HQ) nla (anamorphic)"), + 10, vaapi_scaling_level, this); + + this->scaling_level = scaling_level_enum_values[this->scaling_level_enum]; + + this->swap_uv_planes = config->register_bool( config, "video.output.vaapi_swap_uv_planes", 0, + _("vaapi: swap UV planes."), + _("vaapi: this is a workaround for buggy drivers ( intel IronLake ).\n" + "There the UV planes are swapped.\n"), + 10, vaapi_swap_uv_planes, this); + + + pthread_mutex_init(&this->vaapi_lock, NULL); + + pthread_mutex_lock(&this->vaapi_lock); + + int i; + for (i = 0; i < VO_NUM_PROPERTIES; i++) { + this->props[i].value = 0; + this->props[i].min = 0; + this->props[i].max = 0; + this->props[i].atom = 0; + this->props[i].entry = NULL; + this->props[i].this = this; + } + + this->sc.user_ratio = + this->props[VO_PROP_ASPECT_RATIO].value = XINE_VO_ASPECT_AUTO; + this->props[VO_PROP_ZOOM_X].value = 100; + this->props[VO_PROP_ZOOM_Y].value = 100; + + this->va_context->last_sub_surface_id = VA_INVALID_SURFACE; + this->va_context->last_sub_image_fmt = 0; + + if(vaapi_init_internal((vo_driver_t *)this, SW_CONTEXT_INIT_FORMAT, SW_WIDTH, SW_HEIGHT, 0) != VA_STATUS_SUCCESS) { + vaapi_dispose((vo_driver_t *)this); + return NULL; + } + vaapi_close((vo_driver_t *)this); + this->va_context->valid_context = 0; + this->va_context->driver = (vo_driver_t *)this; + + pthread_mutex_unlock(&this->vaapi_lock); + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Deinterlace : %d\n", this->deinterlace); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Render surfaces : %d\n", RENDER_SURFACES); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Opengl render : %d\n", this->opengl_render); + + return &this->vo_driver; +} + +/* + * class functions + */ +static void *vaapi_init_class (xine_t *xine, void *visual_gen) { + vaapi_class_t *this = (vaapi_class_t *) calloc(1, sizeof(vaapi_class_t)); + + this->driver_class.open_plugin = vaapi_open_plugin; + this->driver_class.identifier = "vaapi"; + this->driver_class.description = N_("xine video output plugin using the MIT X video extension"); + this->driver_class.dispose = default_video_driver_class_dispose; + this->config = xine->config; + this->xine = xine; + + return this; +} + +static const vo_info_t vo_info_vaapi = { + 9, /* priority */ + XINE_VISUAL_TYPE_X11 /* visual type */ +}; + +/* + * exported plugin catalog entry + */ + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_OUT, 22, "vaapi", XINE_VERSION_CODE, &vo_info_vaapi, vaapi_init_class }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/xine-engine/Makefile.am b/src/xine-engine/Makefile.am index a0131a5..baed5b7 100644 --- a/src/xine-engine/Makefile.am +++ b/src/xine-engine/Makefile.am @@ -11,7 +11,7 @@ XINEUTILS_LIB = $(top_builddir)/src/xine-utils/libxineutils.la YUV_LIB = $(top_builddir)/src/video_out/libyuv2rgb.la # FIXME: these are currently unused: -EXTRA_DIST = lrb.c lrb.h accel_vdpau.h accel_xvmc.h +EXTRA_DIST = lrb.c lrb.h accel_vdpau.h accel_vaapi.h accel_xvmc.h if WIN32 DEF_FILE = libxine-$(XINE_MAJOR).def diff --git a/src/xine-engine/accel_vaapi.h b/src/xine-engine/accel_vaapi.h new file mode 100644 index 0000000..666b23f --- /dev/null +++ b/src/xine-engine/accel_vaapi.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2008 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + * Common acceleration definitions for vdpau + * + * + */ + +#ifndef HAVE_XINE_ACCEL_VAAPI_H +#define HAVE_XINE_ACCEL_VAAPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <va/va_x11.h> +#include <pthread.h> +#ifdef HAVE_FFMPEG_AVUTIL_H +# include <avcodec.h> +#else +# include <libavcodec/avcodec.h> +#endif + +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 32) +# define AVVIDEO 2 +#else +# define AVVIDEO 1 +# define pp_context pp_context_t +# define pp_mode pp_mode_t +#endif + +#define NUM_OUTPUT_SURFACES 22 + +#define SURFACE_FREE 0 +#define SURFACE_ALOC 1 +#define SURFACE_RELEASE 2 +#define SURFACE_RENDER 3 +#define SURFACE_RENDER_RELEASE 5 + +struct vaapi_equalizer { + VADisplayAttribute brightness; + VADisplayAttribute contrast; + VADisplayAttribute hue; + VADisplayAttribute saturation; +}; + +typedef struct ff_vaapi_context_s ff_vaapi_context_t; + +struct ff_vaapi_context_s { + VADisplay va_display; + VAContextID va_context_id; + VAConfigID va_config_id; + int width; + int height; + int sw_width; + int sw_height; + int va_profile; + unsigned int va_colorspace; + VAImage va_subpic_image; + VASubpictureID va_subpic_id; + int va_subpic_width; + int va_subpic_height; + int is_bound; + void *gl_surface; + unsigned int soft_head; + unsigned int valid_context; + unsigned int va_head; + unsigned int va_soft_head; + vo_driver_t *driver; + unsigned int last_sub_image_fmt; + VASurfaceID last_sub_surface_id; + struct vaapi_equalizer va_equalizer; + VAImageFormat *va_image_formats; + int va_num_image_formats; + VAImageFormat *va_subpic_formats; + int va_num_subpic_formats; +}; + +typedef struct ff_vaapi_surface_s ff_vaapi_surface_t; +typedef struct vaapi_accel_s vaapi_accel_t; + +struct ff_vaapi_surface_s { + unsigned int index; + vaapi_accel_t *accel; + VASurfaceID va_surface_id; + unsigned int status; +}; + +struct vaapi_accel_s { + unsigned int index; + vo_frame_t *vo_frame; + +#if AVVIDEO > 1 + int (*avcodec_decode_video2)(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, AVPacket *avpkt); +#else + int (*avcodec_decode_video)(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, uint8_t *buf, int buf_size); +#endif + VAStatus (*vaapi_init)(vo_frame_t *frame_gen, int va_profile, int width, int height, int softrender); + int (*profile_from_imgfmt)(vo_frame_t *frame_gen, enum PixelFormat pix_fmt, int codec_id, int vaapi_mpeg_sofdec); + ff_vaapi_context_t *(*get_context)(vo_frame_t *frame_gen); + int (*guarded_render)(vo_frame_t *frame_gen); + ff_vaapi_surface_t *(*get_vaapi_surface)(vo_frame_t *frame_gen); + void (*render_vaapi_surface)(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface); + void (*release_vaapi_surface)(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface); +}; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/xine-engine/video_out.c b/src/xine-engine/video_out.c index cc42813..819e423 100644 --- a/src/xine-engine/video_out.c +++ b/src/xine-engine/video_out.c @@ -737,6 +737,13 @@ static vo_frame_t *vo_get_frame (xine_video_port_t *this_gen, return img; } +static double tt() +{ + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0; +} + static int vo_frame_draw (vo_frame_t *img, xine_stream_t *stream) { vos_t *this = (vos_t *) img->port; @@ -746,6 +753,30 @@ static int vo_frame_draw (vo_frame_t *img, xine_stream_t *stream) { int frames_to_skip; int duration; +/* +if (1) +{ + double t1 = tt(); + static double t0 = -1; + if (t0 == -1) t0 = t1; + double dt = t1 - t0; + t0 = t1; + static int64_t pts = 0; + fprintf(stderr, "img->pts: %12" PRId64 ", pts: %12" PRId64 ", img->duration: %4d, time since last vo_frame_draw: %7.3lf ms", img->pts, pts, img->duration, dt); + if (img->pts) + { + if (pts != img->pts) + fprintf(stderr, " ERROR: %12" PRId64 "", img->pts - pts); + pts = img->pts + img->duration; + } + else + { + pts += img->duration; + } + fprintf(stderr, "\n"); +} +*/ + /* handle anonymous streams like NULL for easy checking */ if (stream == XINE_ANON_STREAM) stream = NULL; ------------------------------------------------------------------------------ All the data continuously generated in your IT infrastructure contains a definitive record of customers, application performance, security threats, fraudulent activity and more. Splunk takes this data and makes sense of it. Business sense. IT sense. Common sense. http://p.sf.net/sfu/splunk-d2d-oct _______________________________________________ xine-devel mailing list xine-devel@... https://lists.sourceforge.net/lists/listinfo/xine-devel |
|
|
Re: VAAPI support for xineUpdated the patch to the last changes in xine-lib-1.2 hg and also fixed the broken mpeg12 decoding.
The fix for mpeg12 you can see here standalone : cu Edgar (gimli) Hucek
2011/9/26 ebsi4711 <ebsi4711@...> The VAAPI support is clear for 1.2. For the OSD it uses similiar functionality like VDPAU. ( ARGB Overlays. ) [vaapi.patch] diff --git a/README.vaapi b/README.vaapi new file mode 100644 index 0000000..8ba7edc --- /dev/null +++ b/README.vaapi @@ -0,0 +1,97 @@ +Make sure you have the following in your ~/.xine/config. + +General ffmpeg settings : + +# Priorität für Dekoder ffmpeg-wmv8 +# numeric, default: 0 +engine.decoder_priorities.ffmpeg-wmv8:0 + +# Priorität für Dekoder ffmpeg-wmv9 +# numeric, default: 0 +engine.decoder_priorities.ffmpeg-wmv9:0 + +# Priorität für Dekoder ffmpegvideo +# numeric, default: 0 +engine.decoder_priorities.ffmpegvideo:1 + +# Priorität für Dekoder mpeg2 +# numeric, default: 0 +#engine.decoder_priorities.mpeg2:0 + +Video out settings : + +#vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob ). +# numeric, default: 0 +#video.output.vaapi_deinterlace:0 + +# vaapi: VDR osd height workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_height:0 + +# vaapi: VDR osd width workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_width:0 + +# VAAPI Mpeg2 softdecoding +# bool, default: 0 +#video.processing.vaapi_mpeg_softdec:0 + +# VAAPI Mpeg2 softdecoding deinterlace +# bool, default: 0 +#video.processing.vaapi_mpeg_softdec_deinterlace:0 + +# vaapi: opengl output rendering +# bool, default: 0 +#video.output.vaapi_opengl_render:0 + +# vaapi: opengl rendering tfp +# bool, default: 0 +#video.output.vaapi_opengl_use_tfp:0 + +# vaapi: set vaapi_guarded_render to 0 ( yes ) 1 ( no ) +# numeric, default: 0 +#video.output.vaapi_guarded_render:0 + +If you see crashes set guarded render mode. + +# vaapi: swap UV planes. +# bool, default: 0 +#video.output.vaapi_swap_uv_planes:0 + +Swap UV is a workaround for IronLake chipsets where the driver is bugy. + + +Notes on use with VDR. The OSD will have some delay. This is technical and can only be overcome +writting a complete new decoder which use VAAPI direct and not ffmpeg. + +For using unsclaed OSD there you can overwrite the OSD size in the config file. + +For xineliboutput use the defaults. + +For vdr-xine set in VDR the OSD size to 1920x1080 and use the following in ~/.xine/config : + +video.output.vaapi_vdr_osd_height:0 +video.output.vaapi_vdr_osd_width:0 + +The workaround is needed, because vdr-xine does not report the real unscaled OSD size. + + +When you get GPU hangs on Mpeg2 material set : + +video.processing.vaapi_mpeg_softdec:1 + +This disables VAAPI fpr Mpeg2. + + +You can use example.config.vaapi as an example config for xine. + +Call xine like : xine -V vaapi video.mkv + +xvba-vaapi hints: + +export LIBVA_DRIVER_NAME=xvba +export LIBVA_DRIVERS_PATH=/usr/lib/dri/ +video.output.vaapi_opengl_render:1 + + +Postprocessing does not work. Do not use "--post vdr_video --post vdr-audio --post vdr" in your xine call. diff --git a/example.config.vaapi b/example.config.vaapi new file mode 100644 index 0000000..901860b --- /dev/null +++ b/example.config.vaapi @@ -0,0 +1,749 @@ +# +# xine config file +# +.version:2 + +# Entries which are still set to their default values are commented out. +# Remove the '#' at the beginning of the line, if you want to change them. + +# Deinterlacing automatisch aktivieren +# bool, default: 0 +#gui.deinterlace_by_default:0 + +# Erfahrenheit einstellen +# { Beginner Advanced Expert Master of the known universe }, default: 0 +#gui.experience_level:Beginner + +# OSD-Unterstützung aktivieren +# bool, default: 1 +#gui.osd_enabled:1 + +# OSD Anzeigezeit [s] +# numeric, default: 3 +#gui.osd_timeout:3 + +# Benutzer fragen bei Wiedergabe mit nichtunterstütztem coder +# bool, default: 0 +#gui.play_anyway:0 + +# Automatische alte Playliste wiederherstellen +# bool, default: 0 +#gui.playlist_auto_reload:0 + +# Audio-Visualisierung +# { oscope fftscope fftgraph goom }, default: 0 +#gui.post_audio_plugin:oscope + +# gui skin Thema +# { xinetic }, default: 0 +#gui.skin:xinetic + +# xine-Verhalten für unerfahrene Benutzer anpassen +# bool, default: 1 +#gui.smart_mode:1 + +# Schnappschußverzeichnis +# string, default: /home/gimli +#gui.snapshotdir:/home/gimli + +# Startbildschirm anzeigen +# bool, default: 1 +#gui.splash:1 + +# Untertitel automatisch laden +# bool, default: 1 +#gui.subtitle_autoload:1 + +# Stil der Videoanimation +# { None Post Plugin Stream Animation }, default: 1 +#gui.visual_anim:Post Plugin + +# Fensterüberlagerung (mehr) +# bool, default: 0 +#gui.always_layer_above:0 + +# Audiomischpultmethode +# { Sound card Software }, default: 0 +#gui.audio_mixer_method:Sound card + +# Anzeigeverhalten von Bedienfeld +# bool, default: 0 +#gui.auto_panel_visibility:0 + +# Anzeigeverhalten des Ausgabefensters +# bool, default: 0 +#gui.auto_video_output_visibility:0 + +# Deinterlace-Plugin. +# string, default: tvtime:method=LinearBlend,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1 +#gui.deinterlace_plugin:tvtime:method=LinearBlend,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1 + +# Verhalten von Ereignissender +# bool, default: 1 +#gui.eventer_sticky:1 + +# Fensterüberlagerung +# bool, default: 0 +#gui.layer_above:0 + +# Unvergrößertes OSD benutzen +# bool, default: 1 +#gui.osd_use_unscaled:1 + +# Bildschirmschoner Resetintervall [s] +# numeric, default: 10 +#gui.screensaver_timeout:10 + +# Menu Tastenkürzelstil +# { Windows style Emacs style }, default: 0 +#gui.shortcut_style:Windows style + +# Datenstrominformationen +# bool, default: 0 +#gui.sinfo_auto_update:0 + +# Skin-Server URL +# string, default: http://xine.sourceforge.net/skins/skins.slx +#gui.skin_server_url:http://xine.sourceforge.net/skins/skins.slx + +# Kapitelspringen +# bool, default: 1 +#gui.skip_by_chapter:1 + +# Neue Datemstromgröße verändert Ausgabefenstergröße +# bool, default: 1 +#gui.stream_resize_window:1 + +# Hinweiszeit (ms) +# numeric, default: 5000 +#gui.tips_timeout:5000 + +# gui Hinweise sichtbar +# bool, default: 1 +#gui.tips_visible:1 + +# Name des Video-Bildschirms +# string, default: +#gui.video_display: + +# Synchrones X-Protokoll (Fehlersuche) +# bool, default: 0 +#gui.xsynchronize:0 + +# Doppelte Größe für kleine Datemströme (Erfordert stream_resize_window) +# bool, default: 0 +#gui.zoom_small_stream:0 + +# Logo MRL +# string, default: /usr/share/xine/skins/xine-ui_logo.png +#gui.logo_mrl:/usr/share/xine/skins/xine-ui_logo.png + +# Benutze XVidModeExtension beim Umschalten auf Vollbild +# bool, default: 0 +#gui.use_xvidext:0 + +# Höhe für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_height:-8192 + +# Breite für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_width:-8192 + +# X-Koordinate für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_x:-8192 + +# Y-Koordinate für Xinerama-Vollbildmodus (-8192 = automatisch) +# numeric, default: -8192 +#gui.xinerama_fullscreen_y:-8192 + +# Zu nutzende Bildschirme im Xinerama Vollbildmodus (z.B. 0 2 3) +# string, default: 0 1 +#gui.xinerama_use_screens:0 1 + +# Verstärkungslevel +# [0..200], default: 100 +#gui.amp_level:100 + +# gui Fenster sichtbar +# bool, default: 1 +gui.panel_visible:0 + +# numeric, default: 200 +#gui.panel_x:200 + +# numeric, default: 100 +#gui.panel_y:100 + +gui.setup_x:81 + +gui.setup_y:104 + +# color specification yuv-opacity +# string, default: 8080c0-f +#gui.osdmenu.color_focused_button:8080c0-f + +# color specification yuv-opacity +# string, default: 808080-f +#gui.osdmenu.color_focused_slider:808080-f + +# color specification yuv-opacity +# string, default: ff8080-f +#gui.osdmenu.color_focused_slider_knob:ff8080-f + +# color specification yuv-opacity +# string, default: 808080-f +#gui.osdmenu.color_focused_text_border:808080-f + +# color specification yuv-opacity +# string, default: ff8080-f +#gui.osdmenu.color_focused_text_foreground:ff8080-f + +# color specification yuv-opacity +# string, default: 0080c0-f +#gui.osdmenu.color_label_border:0080c0-f + +# color specification yuv-opacity +# string, default: c08080-f +#gui.osdmenu.color_label_foreground:c08080-f + +# color specification yuv-opacity +# string, default: 0080c0-f +#gui.osdmenu.color_label_window:0080c0-f + +# color specification yuv-opacity +# string, default: 008000-f +#gui.osdmenu.color_slider:008000-f + +# color specification yuv-opacity +# string, default: ffff00-f +#gui.osdmenu.color_slider_knob:ffff00-f + +# color specification yuv-opacity +# string, default: 008000-f +#gui.osdmenu.color_text_border:008000-f + +# color specification yuv-opacity +# string, default: ffff00-f +#gui.osdmenu.color_text_foreground:ffff00-f + +# color specification yuv-opacity +# string, default: 0080c0-f +#gui.osdmenu.color_text_window:0080c0-f + +# directory a media in dvd device will be mounted +# string, default: /dvd +#gui.osdmenu.dvd_mountpoint:/dvd + +# Farbpalette (Vordergrund-Rand-Hintergrund) für Untertitel und OSD +# { white-black-transparent white-none-transparent white-none-translucid yellow-black-transparent }, default: 0 +#ui.osd.text_palette:white-black-transparent + +# Änderungen an Hardwaremixer melden +# bool, default: 1 +#audio.alsa_hw_mixer:1 + +# Zu benutzender Audiotreiber +# { auto null alsa oss file none }, default: 0 +#audio.driver:auto + +# Benutze dynamische A/52 Bereichskomprimierung +# bool, default: 0 +#audio.a52.dynamic_range:0 + +# Heruntermischen zu Zweikanal Stereo Raumklang +# bool, default: 0 +#audio.a52.surround_downmix:0 + +# A/52 Lautstärke +# [0..200], default: 100 +#audio.a52.level:100 + +# Gerät für Monoausgabe +# string, default: default +#audio.device.alsa_default_device:default + +# Gerät für Stereoausgabe +# string, default: plug:front:default +audio.device.alsa_front_device:default + +# ALSA Mixergerät +# string, default: PCM +#audio.device.alsa_mixer_name:PCM + +# Soundkarte unterstützt mmap +# bool, default: 0 +#audio.device.alsa_mmap_enable:0 + +# Gerät für 5.1-Kanalausgabe +# string, default: iec958:AES0=0x6,AES1=0x82,AES2=0x0,AES3=0x2 +#audio.device.alsa_passthrough_device:iec958:AES0=0x6,AES1=0x82,AES2=0x0,AES3=0x2 + +# Gerät für 4-Kanalausgabe +# string, default: plug:surround40:0 +#audio.device.alsa_surround40_device:plug:surround40:0 + +# Gerät für 5.1-Kanalausgabe +# string, default: plug:surround51:0 +#audio.device.alsa_surround51_device:plug:surround51:0 + +# Lautsprecherplazierung +# { Mono 1.0 Stereo 2.0 Headphones 2.0 Stereo 2.1 Surround 3.0 Surround 4.0 Surround 4.1 Surround 5.0 Surround 5.1 Surround 6.0 Surround 6.1 Surround 7.1 Pass Through }, default: 1 +#audio.output.speaker_arrangement:Stereo 2.0 + +# Versatz für digitales Passthrough +# numeric, default: 0 +#audio.synchronization.passthrough_offset:0 + +# Audiowiedergabe während langsamer/schneller Geschwindigkeit +# bool, default: 0 +#audio.synchronization.slow_fast_audio:0 + +# Methode für Audio/Videosynchronisation +# { metronom feedback resample }, default: 0 +#audio.synchronization.av_sync_method:metronom feedback + +# Wenn !=0, immer auf diese Rate anpassen +# numeric, default: 0 +#audio.synchronization.force_rate:0 + +# Resampling benutzen +# { auto off on }, default: 0 +#audio.synchronization.resample_mode:auto + +# Startlautstärke +# [0..100], default: 50 +#audio.volume.mixer_volume:50 + +# Lautstärke beim Starten wiederherstellen +# bool, default: 0 +#audio.volume.remember_volume:0 + +# Zu benutzender Videotreiber +# { auto vdpau xv vaapi opengl raw xshm none fb }, default: 0 +video.driver:vaapi + +# Alle Videoskalierungen deaktivieren +# bool, default: 0 +#video.output.disable_scaling:0 + +# Horizontale Bildposition im Ausgabefenster +# [0..100], default: 50 +#video.output.horizontal_position:50 + +# vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob ). +# numeric, default: 0 +#video.output.vaapi_deinterlace:0 + +# vaapi: indirect output rendering +# bool, default: 0 +#video.output.vaapi_indirect_render:0 + +# vaapi: VDR osd height workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_height:0 + +# vaapi: VDR osd width workaround. +# numeric, default: 0 +#video.output.vaapi_vdr_osd_width:0 + +# Vertikale Bildposition im Ausgabefenster +# [0..100], default: 50 +#video.output.vertical_position:50 + +# Choose speed over specification compliance +# bool, default: 0 +#video.processing.ffmpeg_choose_speed_over_accuracy:0 + +# Enable usage of VAAPI +# bool, default: 1 +#video.processing.ffmpeg_enable_vaapi:1 + +# Qualität der MPEG-4 Nachbearbeitungsstufe +# [0..6], default: 3 +#video.processing.ffmpeg_pp_quality:3 + +# Skip loop filter +# { default none nonref bidir nonkey all }, default: 0 +#video.processing.ffmpeg_skip_loop_filter:default + +# FFmpeg video decoding thread count +# numeric, default: 1 +#video.processing.ffmpeg_thread_count:1 + +# VAAPI Mpeg2 softdecoding +# bool, default: 0 +#video.processing.vaapi_mpeg_sofdec:0 + +# VAAPI Mpeg2 softdecoding deinterlace +# bool, default: 0 +#video.processing.vaapi_mpeg_sofdec_deinterlace:0 + +# Gerät für CD-Audio +# string, default: /dev/cdrom +#media.audio_cd.device:/dev/cdrom + +# Laufwerk auf diesen Faktor verlangsamen +# numeric, default: 4 +#media.audio_cd.drive_slowdown:4 + +# CDDB abfragen +# bool, default: 1 +#media.audio_cd.use_cddb:1 + +# CDDB Serverport +# numeric, default: 8880 +#media.audio_cd.cddb_port:8880 + +# CDDB Servername +# string, default: freedb.freedb.org +#media.audio_cd.cddb_server:freedb.freedb.org + +# BluRay player country code +# string, default: en +#media.bluray.country:en + +# device used for BluRay playback +# string, default: /dev/dvd +#media.bluray.device:/dev/dvd + +# default language for BluRay playback +# string, default: eng +#media.bluray.language:eng + +# BluRay mount point +# string, default: /mnt/bluray +#media.bluray.mountpoint:/mnt/bluray + +# parental control age limit (1-99) +# numeric, default: 99 +#media.bluray.parental:99 + +# BluRay player region code (1=A, 2=B, 4=C) +# numeric, default: 7 +#media.bluray.region:7 + +# Pfad zum Sichen von Datenströmen +# string, default: +#media.capture.save_dir: + +# Nummer der zu benutzenden DVB-Karte. +# numeric, default: 0 +#media.dvb.adapter:0 + +# Zuletzt gesehenen DVB-Kanal vermerken +# bool, default: 1 +#media.dvb.remember_channel:1 + +# Number of seconds until tuning times out. +# numeric, default: 0 +#media.dvb.tuning_timeout:0 + +# Enable the DVB GUI +# bool, default: 1 +#media.dvb.gui_enabled:1 + +# Zuletzt gesehener DVB-Kanal +# numeric, default: -1 +#media.dvb.last_channel:-1 + +# Standardsprache für die DVD-Wiedergabe +# string, default: en +#media.dvd.language:en + +# Region (1-8), aus der der DVD Player zu kommen scheint +# numeric, default: 1 +#media.dvd.region:1 + +# Gerät für DVD Wiedergabe +# string, default: /dev/dvd +#media.dvd.device:/dev/dvd + +# Pfad zum RAW-Device des DVD-Laufwerks +# string, default: /dev/rdvd +#media.dvd.raw_device:/dev/rdvd + +# Vorauseilendes Caching benutzen +# bool, default: 1 +#media.dvd.readahead:1 + +# CSS Entschlüsselungsmethode +# { key disc title }, default: 0 +#media.dvd.css_decryption_method:key + +# Wiedergabemodus falls Titel/Kapitel angegeben +# { entire dvd one chapter }, default: 0 +#media.dvd.play_single_chapter:entire dvd + +# Einheit beim Suchen +# { seek in program chain seek in program }, default: 0 +#media.dvd.seek_behaviour:seek in program chain + +# Einheit für die Überspringen-Aktion +# { skip program skip part skip title }, default: 0 +#media.dvd.skip_behaviour:skip program + +# Startverzeichnis für Dateisuche +# string, default: /home/gimli +#media.files.origin_path:/home/gimli + +# Versteckte Dateien anzeigen +# bool, default: 0 +#media.files.show_hidden_files:0 + +# Netzwerkbandbreite +# { 14.4 Kbps (Modem) 19.2 Kbps (Modem) 28.8 Kbps (Modem) 33.6 Kbps (Modem) 34.4 Kbps (Modem) 57.6 Kbps (Modem) 115.2 Kbps (ISDN) 262.2 Kbps (Cable/DSL) 393.2 Kbps (Cable/DSL) 524.3 Kbps (Cable/DSL) }, default: 10 + +# Zeitüberschreitung für Netzwerkdatenströme (in Sekunden) +# numeric, default: 30 +#media.network.timeout:30 + +# Domains, die den HTTP Proxy umgehen +# string, default: +#media.network.http_no_proxy: + +# HTTP Proxy Rechnername +# string, default: +#media.network.http_proxy_host: + +# HTTP Proxy Passwort +# string, default: +#media.network.http_proxy_password: + +# HTTP Proxy Portnummer +# numeric, default: 80 +#media.network.http_proxy_port:80 + +# HTTP Proxy Benutzername +# string, default: +#media.network.http_proxy_user: + +# MMS-Protokoll +# { auto TCP HTTP }, default: 0 +#media.network.mms_protocol:auto + +# default VDR host +# string, default: xvdr://127.0.0.1#nocache;demux:mpeg_block +#media.xvdr.default_mrl:xvdr://127.0.0.1#nocache;demux:mpeg_block + +# Fast (low-quality) OSD scaling +# bool, default: 0 +#media.xvdr.fast_osd_scaling:0 + +# number of buffers for HD content +# numeric, default: 2500 +#media.xvdr.num_buffers_hd:2500 + +# SCR-Treshold for HD-Playback (%) +# numeric, default: 40 +#media.xvdr.scr_treshold_hd:40 + +# SCR-Treshold for SD-Playback (%) +# numeric, default: 50 +#media.xvdr.scr_treshold_sd:50 + +# SRC tuning step +# numeric, default: 5000 +#media.xvdr.scr_tuning_step:5000 + +# Smoother SRC tuning +# bool, default: 0 +#media.xvdr.smooth_scr_tuning:0 + +# opacity for the black parts of bitmapped subtitles +# [0..100], default: 67 +#subtitles.bitmap.black_opacity:67 + +# opacity for the colour parts of bitmapped subtitles +# [0..100], default: 100 +#subtitles.bitmap.colour_opacity:100 + +# Untertitelgröße +# { tiny small normal large very large huge }, default: 1 +#subtitles.separate.subtitle_size:small + +# Vertikaler Versatz für Untertitel +# numeric, default: 0 +#subtitles.separate.vertical_offset:0 + +# Zeichensatz für Untertitel +# string, default: sans +#subtitles.separate.font:sans + +# Zeichenkodierung für Untertitel +# string, default: iso-8859-1 +#subtitles.separate.src_encoding:iso-8859-1 + +# Benutze unskaliertes OSD falls möglich +# bool, default: 1 +#subtitles.separate.use_unscaled_osd:1 + +# Zu generierende Bilder/Sekunde +# numeric, default: 14 +#effects.goom.fps:14 + +# Goom Bildhöhe +# numeric, default: 240 +#effects.goom.height:240 + +# Goom Bildbreite +# numeric, default: 320 +#effects.goom.width:320 + +# Farbraumkonvertierungsmethode +# { Fast but not photorealistic Slow but looks better }, default: 0 +#effects.goom.csc_method:Fast but not photorealistic + +# Anzahl der Audiopuffer +# numeric, default: 230 +#engine.buffers.audio_num_buffers:230 + +# Anzahl der Videopuffer +# numeric, default: 500 +#engine.buffers.video_num_buffers:500 + +# Standardanzahl von Videobildern +# numeric, default: 21 +engine.buffers.video_num_frames:22 + +# disable decoder flush at discontinuity +# bool, default: 0 +#engine.decoder.disable_flush_at_discontinuity:0 + +# disable decoder flush from video out +# bool, default: 0 +#engine.decoder.disable_flush_from_video_out:0 + +# Priorität für Dekoder a/52 +# numeric, default: 0 +#engine.decoder_priorities.a/52:0 + +# Priorität für Dekoder bitplane +# numeric, default: 0 +#engine.decoder_priorities.bitplane:0 + +# Priorität für Dekoder dts +# numeric, default: 0 +#engine.decoder_priorities.dts:0 + +# Priorität für Dekoder dvaudio +# numeric, default: 0 +#engine.decoder_priorities.dvaudio:0 + +# Priorität für Dekoder faad +# numeric, default: 0 +#engine.decoder_priorities.faad:0 + +# Priorität für Dekoder ffmpeg-wmv8 +# numeric, default: 0 +#engine.decoder_priorities.ffmpeg-wmv8:0 + +# Priorität für Dekoder ffmpeg-wmv9 +# numeric, default: 0 +#engine.decoder_priorities.ffmpeg-wmv9:0 + +# Priorität für Dekoder ffmpegaudio +# numeric, default: 0 +#engine.decoder_priorities.ffmpegaudio:0 + +# Priorität für Dekoder ffmpegvideo +# numeric, default: 0 +engine.decoder_priorities.ffmpegvideo:1 + +# Priorität für Dekoder flacdec +# numeric, default: 0 +#engine.decoder_priorities.flacdec:0 + +# Priorität für Dekoder gsm610 +# numeric, default: 0 +#engine.decoder_priorities.gsm610:0 + +# Priorität für Dekoder mad +# numeric, default: 0 +#engine.decoder_priorities.mad:0 + +# Priorität für Dekoder mpeg2 +# numeric, default: 0 +#engine.decoder_priorities.mpeg2:0 + +# Priorität für Dekoder pcm +# numeric, default: 0 +#engine.decoder_priorities.pcm:0 + +# Priorität für Dekoder rgb +# numeric, default: 0 +#engine.decoder_priorities.rgb:0 + +# Priorität für Dekoder spucc +# numeric, default: 0 +#engine.decoder_priorities.spucc:0 + +# Priorität für Dekoder spucmml +# numeric, default: 0 +#engine.decoder_priorities.spucmml:0 + +# Priorität für Dekoder spudec +# numeric, default: 0 +#engine.decoder_priorities.spudec:0 + +# Priorität für Dekoder spudvb +# numeric, default: 0 +#engine.decoder_priorities.spudvb:0 + +# Priorität für Dekoder spuhdmv +# numeric, default: 0 +#engine.decoder_priorities.spuhdmv:0 + +# Priorität für Dekoder sputext +# numeric, default: 0 +#engine.decoder_priorities.sputext:0 + +# Priorität für Dekoder theora +# numeric, default: 0 +#engine.decoder_priorities.theora:0 + +# Priorität für Dekoder vdpau_h264 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_h264:0 + +# Priorität für Dekoder vdpau_mpeg12 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_mpeg12:0 + +# Priorität für Dekoder vdpau_mpeg4 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_mpeg4:0 + +# Priorität für Dekoder vdpau_vc1 +# numeric, default: 0 +#engine.decoder_priorities.vdpau_vc1:0 + +# Priorität für Dekoder vorbis +# numeric, default: 0 +#engine.decoder_priorities.vorbis:0 + +# Priorität für Dekoder yuv +# numeric, default: 0 +#engine.decoder_priorities.yuv:0 + +# Medienformaterkennungsstrategie +# { default reverse content extension }, default: 0 +#engine.demux.strategy:default + +# xines Methode zum Kopieren von Speicher +# { probe libc kernel mmx mmxext sse }, default: 0 +engine.performance.memcpy_method:libc + +# Erlaubter Prozentsatz für verworfene Frames +# numeric, default: 10 +#engine.performance.warn_discarded_threshold:10 + +# Erlaubter Prozentsatz für übersprungene Frames +# numeric, default: 10 +#engine.performance.warn_skipped_threshold:10 + +# Erlaube implizierte Änderungen an Konfiguration (z.B. durch MRL) +# bool, default: 0 +#misc.implicit_config:0 + diff --git a/include/xine.h b/include/xine.h index d6f54b7..e7a2ee3 100644 --- a/include/xine.h +++ b/include/xine.h @@ -452,6 +452,7 @@ int xine_get_current_frame_data (xine_stream_t *stream, #define XINE_IMGFMT_XVMC (('C'<<24)|('M'<<16)|('v'<<8)|'X') #define XINE_IMGFMT_XXMC (('C'<<24)|('M'<<16)|('x'<<8)|'X') #define XINE_IMGFMT_VDPAU (('A'<<24)|('P'<<16)|('D'<<8)|'V') +#define XINE_IMGFMT_VAAPI (('P'<<24)|('A'<<16)|('A'<<8)|'V') /* get current xine's virtual presentation timestamp (1/90000 sec) * note: this is mostly internal data. diff --git a/include/xine/video_out.h b/include/xine/video_out.h index 5a04011..d648f5d 100644 --- a/include/xine/video_out.h +++ b/include/xine/video_out.h @@ -303,6 +303,7 @@ struct xine_video_port_s { #define VO_CAP_VDPAU_MPEG12 0x00000100 /* driver can use VDPAU for mpeg1/2 */ #define VO_CAP_VDPAU_VC1 0x00000200 /* driver can use VDPAU for VC1 */ #define VO_CAP_VDPAU_MPEG4 0x00000400 /* driver can use VDPAU for mpeg4-part2 */ +#define VO_CAP_VAAPI 0x00000600 /* driver can use VAAPI */ #define VO_CAP_HUE 0x00010000 #define VO_CAP_SATURATION 0x00020000 #define VO_CAP_CONTRAST 0x00040000 diff --git a/src/combined/ffmpeg/Makefile.am b/src/combined/ffmpeg/Makefile.am index c6509ed..dc2622b 100644 --- a/src/combined/ffmpeg/Makefile.am +++ b/src/combined/ffmpeg/Makefile.am @@ -18,12 +18,12 @@ DISTCLEANFILES = $(ff_generated) EXTRA_DIST = xine_video.list xine_audio.list mkcodeclist.pl xineplug_decode_ff_la_SOURCES = ffmpeg_decoder.c ff_audio_decoder.c ff_video_decoder.c \ - ff_mpeg_parser.c ffmpeg_decoder.h ff_mpeg_parser.h + ff_mpeg_parser.c ffmpeg_decoder.h ff_mpeg_parser.h nodist_xineplug_decode_ff_la_SOURCES = ffmpeg_config.h xineplug_decode_ff_la_CFLAGS = $(AM_CFLAGS) $(FFMPEG_CFLAGS) $(FFMPEG_POSTPROC_CFLAGS) -xineplug_decode_ff_la_LIBADD = $(XINE_LIB) $(MLIB_LIBS) -lm $(ZLIB_LIBS) \ +xineplug_decode_ff_la_LIBADD = $(XINE_LIB) $(MLIB_LIBS) $(ZLIB_LIBS) \ $(FFMPEG_LIBS) $(AVUTIL_LIBS) $(FFMPEG_POSTPROC_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) xineplug_decode_ff_la_LDFLAGS = $(AM_LDFLAGS) $(IMPURE_TEXT_LDFLAGS) diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c index 03a4b8c..66f486f 100644 --- a/src/combined/ffmpeg/ff_video_decoder.c +++ b/src/combined/ffmpeg/ff_video_decoder.c @@ -50,6 +50,9 @@ # include <libpostproc/postprocess.h> #endif +#include <libavcodec/vaapi.h> +#include "accel_vaapi.h" + #define VIDEOBUFSIZE (128*1024) #define SLICE_BUFFER_SIZE (1194*1024) @@ -78,6 +81,9 @@ typedef struct ff_video_class_s { int thread_count; int8_t skip_loop_filter_enum; int8_t choose_speed_over_accuracy; + int enable_vaapi; + int vaapi_mpeg_softdec; + int vaapi_mpeg_softdec_deinterlace; xine_t *xine; } ff_video_class_t; @@ -141,8 +147,12 @@ struct ff_video_decoder_s { #ifdef LOG enum PixelFormat debug_fmt; #endif -}; + struct vaapi_context vaapi_context; + vaapi_accel_t *accel; + vo_frame_t *accel_img; + uint8_t set_stream_info; +}; static void set_stream_info(ff_video_decoder_t *this) { _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->bih.biWidth); @@ -166,13 +176,72 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ this->aspect_ratio = (double)width / (double)height; this->aspect_ratio_prio = 1; lprintf("default aspect ratio: %f\n", this->aspect_ratio); - set_stream_info(this); + this->set_stream_info = 1; } } avcodec_align_dimensions(context, &width, &height); - if( this->context->pix_fmt != PIX_FMT_YUV420P && this->context->pix_fmt != PIX_FMT_YUVJ420P ) { + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + + av_frame->opaque = NULL; + av_frame->data[0] = NULL; + av_frame->data[1] = NULL; + av_frame->data[2] = NULL; + av_frame->data[3] = NULL; + av_frame->type = FF_BUFFER_TYPE_USER; + av_frame->age = 1; + av_frame->reordered_opaque = context->reordered_opaque; + + if(!this->accel->guarded_render(this->accel_img)) { + img = this->stream->video_out->get_frame (this->stream->video_out, + width, + height, + this->aspect_ratio, + this->output_format, + VO_BOTH_FIELDS|this->frame_flags); + + av_frame->opaque = img; + xine_list_push_back(this->dr1_frames, av_frame); + + vaapi_accel_t *accel = (vaapi_accel_t*)img->accel_data; + ff_vaapi_surface_t *va_surface = accel->get_vaapi_surface(img); + + if(va_surface) { + av_frame->data[0] = (void *)va_surface;//(void *)(uintptr_t)va_surface->va_surface_id; + av_frame->data[3] = (void *)(uintptr_t)va_surface->va_surface_id; + } + } else { + ff_vaapi_surface_t *va_surface = this->accel->get_vaapi_surface(this->accel_img); + + if(va_surface) { + av_frame->data[0] = (void *)va_surface;//(void *)(uintptr_t)va_surface->va_surface_id; + av_frame->data[3] = (void *)(uintptr_t)va_surface->va_surface_id; + } + } + + lprintf("1: 0x%08x\n", av_frame->data[3]); + + av_frame->linesize[0] = 0; + av_frame->linesize[1] = 0; + av_frame->linesize[2] = 0; + av_frame->linesize[3] = 0; + + this->is_direct_rendering_disabled = 1; + + return 0; + } + + /* on vaapi out do not use direct rendeing */ + if(this->class->enable_vaapi) { + this->output_format = XINE_IMGFMT_YV12; + } + + int guarded_render = 0; + if(this->accel) + guarded_render = this->accel->guarded_render(this->accel_img); + + if( (this->context->pix_fmt != PIX_FMT_YUV420P && this->context->pix_fmt != PIX_FMT_YUVJ420P) || guarded_render) { if (!this->is_direct_rendering_disabled) { xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: unsupported frame format, DR1 disabled.\n")); @@ -239,6 +308,18 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ static void release_buffer(struct AVCodecContext *context, AVFrame *av_frame){ ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque; + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD ) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)av_frame->data[0]; + if(va_surface != NULL) { + this->accel->release_vaapi_surface(this->accel_img, va_surface); + lprintf("release_buffer: va_surface_id 0x%08x\n", (unsigned int)av_frame->data[3]); + } + } + } + + lprintf("3: 0x%08x\n", av_frame->data[3]); + if (av_frame->type == FF_BUFFER_TYPE_USER) { if ( av_frame->opaque ) { vo_frame_t *img = (vo_frame_t *)av_frame->opaque; @@ -249,7 +330,7 @@ static void release_buffer(struct AVCodecContext *context, AVFrame *av_frame){ xine_list_iterator_t it; it = xine_list_find(this->dr1_frames, av_frame); - assert(it); + //assert(it); if( it != NULL ) xine_list_remove(this->dr1_frames, it); } else { @@ -284,6 +365,51 @@ static const int skip_loop_filter_enum_values[] = { AVDISCARD_ALL }; +static enum PixelFormat get_format(struct AVCodecContext *context, const enum PixelFormat *fmt) +{ + int i, profile; + ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque; + + if(!this->class->enable_vaapi || !this->accel_img) + return PIX_FMT_YUV420P; + + vaapi_accel_t *accel = (vaapi_accel_t*)this->accel_img->accel_data; + + for (i = 0; fmt[i] != PIX_FMT_NONE; i++) { + if (fmt[i] != PIX_FMT_VAAPI_VLD) + continue; + + profile = accel->profile_from_imgfmt(this->accel_img, fmt[i], context->codec_id, this->class->vaapi_mpeg_softdec); + + if (profile >= 0) { + VAStatus status; + + status = accel->vaapi_init(this->accel_img, profile, context->width, context->height, 0); + + if( status == VA_STATUS_SUCCESS ) { + ff_vaapi_context_t *va_context = accel->get_context(this->accel_img); + + if(!va_context) + return PIX_FMT_YUV420P; + + context->draw_horiz_band = NULL; + context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; + context->dsp_mask = 0; + + this->vaapi_context.config_id = va_context->va_config_id; + this->vaapi_context.context_id = va_context->va_context_id; + this->vaapi_context.display = va_context->va_display; + + context->hwaccel_context = &this->vaapi_context; + this->pts = 0; + + return fmt[i]; + } + } + } + return PIX_FMT_YUV420P; +} + static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) { size_t i; @@ -329,6 +455,47 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) if (this->class->choose_speed_over_accuracy) this->context->flags2 |= CODEC_FLAG2_FAST; + if(this->class->enable_vaapi) + { + this->class->thread_count = this->context->thread_count = 1; + + this->context->skip_loop_filter = AVDISCARD_DEFAULT; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: force AVDISCARD_DEFAULT for VAAPI\n")); + } else { + this->context->skip_loop_filter = skip_loop_filter_enum_values[this->class->skip_loop_filter_enum]; + } + + if (this->class->thread_count > 1) { + if (this->codec->id != CODEC_ID_SVQ3 +#ifndef DEPRECATED_AVCODEC_THREAD_INIT + && avcodec_thread_init(this->context, this->class->thread_count) != -1 +#endif + ) + this->context->thread_count = this->class->thread_count; + } + + /* enable direct rendering by default */ + this->output_format = XINE_IMGFMT_YV12; +#ifdef ENABLE_DIRECT_RENDERING + if( this->codec->capabilities & CODEC_CAP_DR1 && this->codec->id != CODEC_ID_H264 ) { + this->context->get_buffer = get_buffer; + this->context->release_buffer = release_buffer; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: direct rendering enabled\n")); + } + + if( this->class->enable_vaapi ) { + this->output_format = XINE_IMGFMT_VAAPI; + this->context->get_buffer = get_buffer; + this->context->reget_buffer = get_buffer; + this->context->release_buffer = release_buffer; + this->context->get_format = get_format; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: direct rendering enabled\n")); + } +#endif + pthread_mutex_lock(&ffmpeg_lock); if (avcodec_open (this->context, this->codec) < 0) { pthread_mutex_unlock(&ffmpeg_lock); @@ -356,17 +523,6 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) } } - if (this->class->thread_count > 1) { - if (this->codec->id != CODEC_ID_SVQ3 -#ifndef DEPRECATED_AVCODEC_THREAD_INIT - && avcodec_thread_init(this->context, this->class->thread_count) != -1 -#endif - ) - this->context->thread_count = this->class->thread_count; - } - - this->context->skip_loop_filter = skip_loop_filter_enum_values[this->class->skip_loop_filter_enum]; - pthread_mutex_unlock(&ffmpeg_lock); lprintf("lavc decoder opened\n"); @@ -389,41 +545,50 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) this->skipframes = 0; - /* enable direct rendering by default */ - this->output_format = XINE_IMGFMT_YV12; -#ifdef ENABLE_DIRECT_RENDERING - if( this->codec->capabilities & CODEC_CAP_DR1 && this->codec->id != CODEC_ID_H264 ) { - this->context->get_buffer = get_buffer; - this->context->release_buffer = release_buffer; - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - _("ffmpeg_video_dec: direct rendering enabled\n")); - } -#endif - /* flag for interlaced streams */ this->frame_flags = 0; /* FIXME: which codecs can be interlaced? FIXME: check interlaced DCT and other codec specific info. */ - switch( codec_type ) { - case BUF_VIDEO_DV: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_MPEG: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_MJPEG: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_HUFFYUV: - this->frame_flags |= VO_INTERLACED_FLAG; - break; - case BUF_VIDEO_H264: - this->frame_flags |= VO_INTERLACED_FLAG; - break; + if(!this->class->enable_vaapi) { + switch( codec_type ) { + case BUF_VIDEO_DV: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_MPEG: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_MJPEG: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_HUFFYUV: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + case BUF_VIDEO_H264: + this->frame_flags |= VO_INTERLACED_FLAG; + break; + } } } +static void vaapi_enable_vaapi(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->enable_vaapi = entry->num_value; +} + +static void vaapi_mpeg_softdec_func(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->vaapi_mpeg_softdec = entry->num_value; +} + +static void vaapi_mpeg_softdec_deinterlace(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->vaapi_mpeg_softdec_deinterlace = entry->num_value; +} + static void choose_speed_over_accuracy_cb(void *user_data, xine_cfg_entry_t *entry) { ff_video_class_t *class = (ff_video_class_t *) user_data; @@ -550,7 +715,7 @@ static int ff_handle_mpeg_sequence(ff_video_decoder_t *this, mpeg_parser_t *pars return 1; } -static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { +static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img, AVFrame *av_frame) { int y; uint8_t *dy, *du, *dv, *sy, *su, *sv; @@ -562,9 +727,9 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { dy = img->base[0]; du = img->base[1]; dv = img->base[2]; - sy = this->av_frame->data[0]; - su = this->av_frame->data[1]; - sv = this->av_frame->data[2]; + sy = av_frame->data[0]; + su = av_frame->data[1]; + sv = av_frame->data[2]; /* Some segfaults & heap corruption have been observed with img->height, * so we use this->bih.biHeight instead (which is the displayed height) @@ -574,18 +739,18 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { yuv9_to_yv12( /* Y */ - this->av_frame->data[0], - this->av_frame->linesize[0], + av_frame->data[0], + av_frame->linesize[0], img->base[0], img->pitches[0], /* U */ - this->av_frame->data[1], - this->av_frame->linesize[1], + av_frame->data[1], + av_frame->linesize[1], img->base[1], img->pitches[1], /* V */ - this->av_frame->data[2], - this->av_frame->linesize[2], + av_frame->data[2], + av_frame->linesize[2], img->base[2], img->pitches[2], /* width x height */ @@ -596,18 +761,18 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { yuv411_to_yv12( /* Y */ - this->av_frame->data[0], - this->av_frame->linesize[0], + av_frame->data[0], + av_frame->linesize[0], img->base[0], img->pitches[0], /* U */ - this->av_frame->data[1], - this->av_frame->linesize[1], + av_frame->data[1], + av_frame->linesize[1], img->base[1], img->pitches[1], /* V */ - this->av_frame->data[2], - this->av_frame->linesize[2], + av_frame->data[2], + av_frame->linesize[2], img->base[2], img->pitches[2], /* width x height */ @@ -637,7 +802,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -666,7 +831,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -695,7 +860,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -719,7 +884,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -743,7 +908,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = COMPUTE_V(r, g, b); plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -782,7 +947,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { this->yuv.v[plane_ptr] = v_palette[pixel]; plane_ptr++; } - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } yuv444_to_yuy2(&this->yuv, img->base[0], img->pitches[0]); @@ -794,7 +959,7 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { dy += img->pitches[0]; - sy += this->av_frame->linesize[0]; + sy += av_frame->linesize[0]; } for (y = 0; y < this->bih.biHeight / 2; y++) { @@ -831,11 +996,11 @@ static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { dv += img->pitches[2]; if (this->context->pix_fmt != PIX_FMT_YUV420P) { - su += 2*this->av_frame->linesize[1]; - sv += 2*this->av_frame->linesize[2]; + su += 2*av_frame->linesize[1]; + sv += 2*av_frame->linesize[2]; } else { - su += this->av_frame->linesize[1]; - sv += this->av_frame->linesize[2]; + su += av_frame->linesize[1]; + sv += av_frame->linesize[2]; } } } @@ -900,7 +1065,6 @@ static void ff_init_mpeg12_mode(ff_video_decoder_t *this) if ( this->mpeg_parser == NULL ) { this->mpeg_parser = calloc(1, sizeof(mpeg_parser_t)); mpeg_parser_init(this->mpeg_parser); - this->decoder_init_mode = 0; } } @@ -1073,6 +1237,52 @@ static void ff_handle_special_buffer (ff_video_decoder_t *this, buf_element_t *b } } +static uint64_t ff_tag_pts(ff_video_decoder_t *this, uint64_t pts) +{ + return pts | this->pts_tag; +} + +static uint64_t ff_untag_pts(ff_video_decoder_t *this, uint64_t pts) +{ + if (this->pts_tag_mask == 0) + return pts; /* pts tagging inactive */ + + if (this->pts_tag != 0 && (pts & this->pts_tag_mask) != this->pts_tag) + return 0; /* reset pts if outdated while waiting for first pass (see below) */ + + return pts & ~this->pts_tag_mask; +} + +static void ff_check_pts_tagging(ff_video_decoder_t *this, uint64_t pts) +{ + if (this->pts_tag_mask == 0) + return; /* pts tagging inactive */ + if ((pts & this->pts_tag_mask) != this->pts_tag) { + this->pts_tag_stable_counter = 0; + return; /* pts still outdated */ + } + + /* the tag should be stable for 100 frames */ + this->pts_tag_stable_counter++; + + if (this->pts_tag != 0) { + if (this->pts_tag_stable_counter >= 100) { + /* first pass: reset pts_tag */ + this->pts_tag = 0; + this->pts_tag_stable_counter = 0; + } + } else if (pts == 0) + return; /* cannot detect second pass */ + else { + if (this->pts_tag_stable_counter >= 100) { + /* second pass: reset pts_tag_mask and pts_tag_counter */ + this->pts_tag_mask = 0; + this->pts_tag_counter = 0; + this->pts_tag_stable_counter = 0; + } + } +} + static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *buf) { vo_frame_t *img; @@ -1081,6 +1291,10 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu int offset = 0; int flush = 0; int size = buf->size; + uint8_t *buf_deint = 0; + AVFrame *av_framedisp = this->av_frame; + AVFrame *av_framedeint = NULL; + int bDeint = 0; lprintf("handle_mpeg12_buffer\n"); @@ -1135,12 +1349,23 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu avpkt.data = (uint8_t *)this->mpeg_parser->chunk_buffer; avpkt.size = this->mpeg_parser->buffer_size; avpkt.flags = AV_PKT_FLAG_KEY; - len = avcodec_decode_video2 (this->context, this->av_frame, + if(this->accel) { + len = this->accel->avcodec_decode_video2 ( this->accel_img, this->context, av_framedisp, &got_picture, &avpkt); + } else { + len = avcodec_decode_video2 ( this->context, av_framedisp, + &got_picture, &avpkt); + } #else - len = avcodec_decode_video (this->context, this->av_frame, + if(this->accel) { + len = this->accel->avcodec_decode_video ( this->accel_img, this->context, av_framedisp, &got_picture, this->mpeg_parser->chunk_buffer, this->mpeg_parser->buffer_size); + } else { + len = avcodec_decode_video (this->context, av_framedisp, + &got_picture, this->mpeg_parser->chunk_buffer, + this->mpeg_parser->buffer_size); + } #endif lprintf("avcodec_decode_video: decoded_size=%d, got_picture=%d\n", len, got_picture); @@ -1158,9 +1383,24 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu offset += len; } - if (got_picture && this->av_frame->data[0]) { + if(got_picture && this->class->enable_vaapi) { + int width, height; + width = this->context->width; + height = this->context->height; + if((this->bih.biWidth != width) || (this->bih.biHeight != height)) { + this->bih.biWidth = width; + this->bih.biHeight = height; + } + } + + if( this->set_stream_info) { + set_stream_info(this); + this->set_stream_info = 0; + } + + if (got_picture && av_framedisp->data[0]) { /* got a picture, draw it */ - if(!this->av_frame->opaque) { + if(!av_framedisp->opaque) { /* indirect rendering */ img = this->stream->video_out->get_frame (this->stream->video_out, this->bih.biWidth, @@ -1171,14 +1411,53 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu free_img = 1; } else { /* DR1 */ - img = (vo_frame_t*) this->av_frame->opaque; + img = (vo_frame_t*) av_framedisp->opaque; free_img = 0; } + if( this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { + if(av_framedisp->interlaced_frame && this->class->vaapi_mpeg_softdec_deinterlace) { + int size; + int ret; + + av_framedeint = avcodec_alloc_frame(); + + size = avpicture_get_size(this->context->pix_fmt, this->context->width, this->context->height); + buf_deint = av_malloc(size); + + if(av_framedeint) { + avpicture_fill((AVPicture*)av_framedeint, buf_deint, this->context->pix_fmt, this->context->width, this->context->height); + + ret = avpicture_deinterlace((AVPicture*)av_framedeint, (AVPicture*) av_framedisp, + this->context->pix_fmt, this->context->width, this->context->height); + + if(ret) { + av_free( buf_deint ); + av_free( av_framedeint ); + } else { + bDeint = 1; + av_framedisp = av_framedeint; + } + } else { + av_free( buf_deint ); + } + } + + if(bDeint) { + ff_convert_frame(this, img, av_framedeint); + } else { + ff_convert_frame(this, img, av_framedisp); + } + } + + img->progressive_frame = !this->av_frame->interlaced_frame; + img->top_field_first = this->av_frame->top_field_first; + img->bad_frame = 0; + img->pts = this->pts; this->pts = 0; - if (this->av_frame->repeat_pict) + if (av_framedisp->repeat_pict) img->duration = this->video_step * 3 / 2; else img->duration = this->video_step; @@ -1186,6 +1465,14 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu img->crop_right = this->crop_right; img->crop_bottom = this->crop_bottom; + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)av_framedisp->data[0]; + this->accel->render_vaapi_surface(img, va_surface); + lprintf("handle_mpeg12_buffer: render_vaapi_surface va_surface_id 0x%08x\n", av_framedisp->data[0]); + } + } + this->skipframes = img->draw(img, this->stream); if(free_img) @@ -1214,51 +1501,11 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu img->free(img); } } - } -} - -static uint64_t ff_tag_pts(ff_video_decoder_t *this, uint64_t pts) -{ - return pts | this->pts_tag; -} - -static uint64_t ff_untag_pts(ff_video_decoder_t *this, uint64_t pts) -{ - if (this->pts_tag_mask == 0) - return pts; /* pts tagging inactive */ - - if (this->pts_tag != 0 && (pts & this->pts_tag_mask) != this->pts_tag) - return 0; /* reset pts if outdated while waiting for first pass (see below) */ - - return pts & ~this->pts_tag_mask; -} - -static void ff_check_pts_tagging(ff_video_decoder_t *this, uint64_t pts) -{ - if (this->pts_tag_mask == 0) - return; /* pts tagging inactive */ - if ((pts & this->pts_tag_mask) != this->pts_tag) { - this->pts_tag_stable_counter = 0; - return; /* pts still outdated */ - } - - /* the tag should be stable for 100 frames */ - this->pts_tag_stable_counter++; - if (this->pts_tag != 0) { - if (this->pts_tag_stable_counter >= 100) { - /* first pass: reset pts_tag */ - this->pts_tag = 0; - this->pts_tag_stable_counter = 0; - } - } else if (pts == 0) - return; /* cannot detect second pass */ - else { - if (this->pts_tag_stable_counter >= 100) { - /* second pass: reset pts_tag_mask and pts_tag_counter */ - this->pts_tag_mask = 0; - this->pts_tag_counter = 0; - this->pts_tag_stable_counter = 0; + /* free deinterlace picture */ + if(bDeint) { + av_free( buf_deint ); + av_free( av_framedeint ); } } } @@ -1356,12 +1603,23 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { avpkt.data = (uint8_t *)&chunk_buf[offset]; avpkt.size = this->size; avpkt.flags = AV_PKT_FLAG_KEY; - len = avcodec_decode_video2 (this->context, this->av_frame, + if(this->accel) { + len = this->accel->avcodec_decode_video2 ( this->accel_img, this->context, this->av_frame, + &got_picture, &avpkt); + } else { + len = avcodec_decode_video2 (this->context, this->av_frame, &got_picture, &avpkt); + } #else + if(this->accel) { + len = this->accel->avcodec_decode_video ( this->accel_img, this->context, this->av_frame, + &got_picture, &chunk_buf[offset], + this->size); + } else { len = avcodec_decode_video (this->context, this->av_frame, &got_picture, &chunk_buf[offset], this->size); + } #endif /* reset consumed pts value */ this->context->reordered_opaque = ff_tag_pts(this, 0); @@ -1413,6 +1671,11 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { set_stream_info(this); } + if( this->set_stream_info) { + set_stream_info(this); + this->set_stream_info = 0; + } + if (got_picture && this->av_frame->data[0]) { /* got a picture, draw it */ got_one_picture = 1; @@ -1420,7 +1683,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { /* indirect rendering */ /* initialize the colorspace converter */ - if (!this->cs_convert_init) { + if (!this->cs_convert_init && !this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { if ((this->context->pix_fmt == PIX_FMT_RGB32) || (this->context->pix_fmt == PIX_FMT_RGB565) || (this->context->pix_fmt == PIX_FMT_RGB555) || @@ -1456,10 +1719,10 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { } /* post processing */ - if(this->pp_quality != this->class->pp_quality) + if(this->pp_quality != this->class->pp_quality && this->context->pix_fmt != PIX_FMT_VAAPI_VLD) pp_change_quality(this); - if(this->pp_available && this->pp_quality) { + if(this->pp_available && this->pp_quality && this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { if(this->av_frame->opaque) { /* DR1 */ @@ -1472,7 +1735,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { free_img = 1; } - pp_postprocess(this->av_frame->data, this->av_frame->linesize, + pp_postprocess((const uint8_t **)this->av_frame->data, this->av_frame->linesize, img->base, img->pitches, img->width, img->height, this->av_frame->qscale_table, this->av_frame->qstride, @@ -1481,7 +1744,8 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { } else if (!this->av_frame->opaque) { /* colorspace conversion or copy */ - ff_convert_frame(this, img); + if( this->context->pix_fmt != PIX_FMT_VAAPI_VLD) + ff_convert_frame(this, img, this->av_frame); } img->pts = ff_untag_pts(this, this->av_frame->reordered_opaque); @@ -1510,6 +1774,15 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { img->progressive_frame = !this->av_frame->interlaced_frame; img->top_field_first = this->av_frame->top_field_first; + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)this->av_frame->data[0]; + this->accel->render_vaapi_surface(img, va_surface); + if(va_surface) + lprintf("handle_buffer: render_vaapi_surface va_surface_id 0x%08x\n", this->av_frame->data[0]); + } + } + this->skipframes = img->draw(img, this->stream); if(free_img) @@ -1718,6 +1991,9 @@ static void ff_dispose (video_decoder_t *this_gen) { xine_list_delete(this->dr1_frames); + if(this->accel_img) + this->accel_img->free(this->accel_img); + free (this_gen); } @@ -1759,11 +2035,42 @@ static video_decoder_t *ff_video_open_plugin (video_decoder_class_t *class_gen, this->mpeg_parser = NULL; this->dr1_frames = xine_list_new(); + this->set_stream_info = 0; #ifdef LOG this->debug_fmt = -1; #endif + memset(&this->vaapi_context, 0x0 ,sizeof(struct vaapi_context)); + + this->dr1_frames = xine_list_new(); + + this->accel = NULL; + this->accel_img = NULL; + + + if(this->class->enable_vaapi && (stream->video_driver->get_capabilities(stream->video_driver) & VO_CAP_VAAPI)) { + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: vaapi_mpeg_softdec %d\n"), + this->class->vaapi_mpeg_softdec ); + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: vaapi_mpeg_softdec_deinterlace %d\n"), + this->class->vaapi_mpeg_softdec_deinterlace ); + + this->accel_img = stream->video_out->get_frame( stream->video_out, 1920, 1080, 1, XINE_IMGFMT_VAAPI, VO_BOTH_FIELDS ); + + if( this->accel_img ) { + this->accel = (vaapi_accel_t*)this->accel_img->accel_data; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled in config.\n")); + } else { + this->class->enable_vaapi = 0; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled disabled by driver.\n")); + } + } else { + this->class->enable_vaapi = 0; + this->class->vaapi_mpeg_softdec = 0; + this->class->vaapi_mpeg_softdec_deinterlace = 0; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled disabled by driver.\n")); + } + return &this->video_decoder; } @@ -1823,6 +2130,21 @@ void *init_video_plugin (xine_t *xine, void *data) { "A change of this setting will take effect with playing the next stream."), 10, choose_speed_over_accuracy_cb, this); + this->vaapi_mpeg_softdec = xine->config->register_bool(config, "video.processing.vaapi_mpeg_softdec", 0, + _("VAAPI Mpeg2 softdecoding"), + _("If the machine freezes on mpeg2 decoding use mpeg2 software decoding."), + 10, vaapi_mpeg_softdec_func, this); + + this->vaapi_mpeg_softdec_deinterlace = xine->config->register_bool(config, "video.processing.vaapi_mpeg_softdec_deinterlace", 0, + _("VAAPI Mpeg2 softdecoding deinterlace"), + _("FFMPEGS simple deinterlacer with Mpeg2 software decoding."), + 10, vaapi_mpeg_softdec_deinterlace, this); + + this->enable_vaapi = xine->config->register_bool(config, "video.processing.ffmpeg_enable_vaapi", 1, + _("Enable VAAPI"), + _("Enable or disable usage of vaapi"), + 10, vaapi_enable_vaapi, this); + return this; } diff --git a/src/combined/ffmpeg/ffmpeg_decoder.c b/src/combined/ffmpeg/ffmpeg_decoder.c index 4f9a0f7..a0314c8 100644 --- a/src/combined/ffmpeg/ffmpeg_decoder.c +++ b/src/combined/ffmpeg/ffmpeg_decoder.c @@ -39,6 +39,9 @@ void init_once_routine(void) { pthread_mutex_init(&ffmpeg_lock, NULL); avcodec_init(); avcodec_register_all(); + + av_log_set_level(AV_LOG_QUIET); + } /* diff --git a/src/combined/ffmpeg/xine_video.list b/src/combined/ffmpeg/xine_video.list index 009f4da..6364c45 100644 --- a/src/combined/ffmpeg/xine_video.list +++ b/src/combined/ffmpeg/xine_video.list @@ -70,7 +70,7 @@ WNV1 WNV1 Winnow Video XL VIXL Miro/Pinnacle VideoXL RT21 INDEO2 Indeo/RealTime 2 FPS1 FRAPS Fraps -MPEG MPEG1VIDEO MPEG 1/2 +MPEG MPEG2VIDEO MPEG 1/2 CSCD CSCD CamStudio AVS AVS AVS ALGMM MMVIDEO American Laser Games MM diff --git a/src/post/deinterlace/xine_plugin.c b/src/post/deinterlace/xine_plugin.c index 5615e4b..d96aafb 100644 --- a/src/post/deinterlace/xine_plugin.c +++ b/src/post/deinterlace/xine_plugin.c @@ -437,6 +437,8 @@ static void deinterlace_dispose(post_plugin_t *this_gen) if (_x_post_dispose(this_gen)) { _flush_frames(this); pthread_mutex_destroy(&this->lock); + if(this->tvtime) + free(this->tvtime ); free(this); } } diff --git a/src/video_out/Makefile.am b/src/video_out/Makefile.am index 45a6f03..76708e2 100644 --- a/src/video_out/Makefile.am +++ b/src/video_out/Makefile.am @@ -43,6 +43,8 @@ if ENABLE_VDPAU vdpau_module = xineplug_vo_out_vdpau.la endif +vaapi_module = xineplug_vo_out_vaapi.la + if ENABLE_XCB XCBOSD = xcbosd.c if ENABLE_XCBSHM @@ -117,6 +119,7 @@ xineplug_LTLIBRARIES = $(xshm_module) $(xv_module) $(xvmc_module) \ $(xcbshm_module) \ $(xcbxv_module) \ $(vdpau_module) \ + $(vaapi_module) \ xineplug_vo_out_raw.la \ xineplug_vo_out_none.la @@ -124,6 +127,10 @@ xineplug_vo_out_vdpau_la_SOURCES = video_out_vdpau.c xineplug_vo_out_vdpau_la_LIBADD = $(YUV_LIBS) $(PTHREAD_LIBS) $(X_LIBS) $(LTLIBINTL) $(VDPAU_LIBS) -lm xineplug_vo_out_vdpau_la_CFLAGS = $(VISIBILITY_FLAG) $(MLIB_CFLAGS) $(X_CFLAGS) $(VDPAU_CFLAGS) +xineplug_vo_out_vaapi_la_SOURCES = $(X11OSD) video_out_vaapi.c +xineplug_vo_out_vaapi_la_LIBADD = $(YUV_LIBS) $(XINE_LIB) $(OPENGL_LIBS) $(FFMPEG_LIBS) $(AVUTIL_LIBS) $(X_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) -ldl -lGLU -lva-glx -lva-x11 -lva +xineplug_vo_out_vaapi_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) $(XV_CFLAGS) -fno-strict-aliasing + xineplug_vo_out_xcbshm_la_SOURCES = video_out_xcbshm.c $(XCBOSD) xineplug_vo_out_xcbshm_la_LIBADD = $(YUV_LIBS) $(PTHREAD_LIBS) $(XCB_LIBS) $(XCBSHM_LIBS) $(LTLIBINTL) xineplug_vo_out_xcbshm_la_CFLAGS = $(AM_CFLAGS) $(XCB_CFLAGS) $(XCBSHM_CFLAGS) $(AVUTIL_CFLAGS) diff --git a/src/video_out/video_out_vaapi.c b/src/video_out/video_out_vaapi.c new file mode 100644 index 0000000..42a6360 --- /dev/null +++ b/src/video_out/video_out_vaapi.c @@ -0,0 +1,3951 @@ +/* + * Copyright (C) 2000-2004, 2008 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * video_out_vaapi.c, VAAPI video extension interface for xine + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> + +#include <sys/types.h> +#if defined(__FreeBSD__) +#include <machine/param.h> +#endif +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/time.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/cursorfont.h> +#include <time.h> +#include <unistd.h> +#include "yuv2rgb.h" + +#define LOG_MODULE "video_out_vaapi" +#define LOG_VERBOSE +/* +#define LOG +*/ +/* +#define DEBUG_SURFACE +*/ +#include "xine.h" +#include <xine/video_out.h> +#include <xine/xine_internal.h> +#include <xine/xineutils.h> +#include <xine/vo_scale.h> + +#include <GL/glu.h> +#include <GL/glx.h> +#include <GL/glext.h> +#include <GL/gl.h> +#include <dlfcn.h> + +#include <va/va_x11.h> +#include <va/va_glx.h> + +#include "accel_vaapi.h" + +#define RENDER_SURFACES 50 +#define SOFT_SURFACES 3 +#define SW_WIDTH 1920 +#define SW_HEIGHT 1080 +#define STABLE_FRAME_COUNTER 4 +#define SW_CONTEXT_INIT_FORMAT -1 //VAProfileH264Main + +#if defined VA_SRC_BT601 && defined VA_SRC_BT709 +# define USE_VAAPI_COLORSPACE 1 +#else +# define USE_VAAPI_COLORSPACE 0 +#endif + +#define IMGFMT_VAAPI 0x56410000 /* 'VA'00 */ +#define IMGFMT_VAAPI_MASK 0xFFFF0000 +#define IMGFMT_IS_VAAPI(fmt) (((fmt) & IMGFMT_VAAPI_MASK) == IMGFMT_VAAPI) +#define IMGFMT_VAAPI_CODEC_MASK 0x000000F0 +#define IMGFMT_VAAPI_CODEC(fmt) ((fmt) & IMGFMT_VAAPI_CODEC_MASK) +#define IMGFMT_VAAPI_CODEC_MPEG2 (0x10) +#define IMGFMT_VAAPI_CODEC_MPEG4 (0x20) +#define IMGFMT_VAAPI_CODEC_H264 (0x30) +#define IMGFMT_VAAPI_CODEC_VC1 (0x40) +#define IMGFMT_VAAPI_MPEG2 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2) +#define IMGFMT_VAAPI_MPEG2_IDCT (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|1) +#define IMGFMT_VAAPI_MPEG2_MOCO (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|2) +#define IMGFMT_VAAPI_MPEG4 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4) +#define IMGFMT_VAAPI_H263 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4|1) +#define IMGFMT_VAAPI_H264 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_H264) +#define IMGFMT_VAAPI_VC1 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1) +#define IMGFMT_VAAPI_WMV3 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1|1) + +#define FOVY 60.0f +#define ASPECT 1.0f +#define Z_NEAR 0.1f +#define Z_FAR 100.0f +#define Z_CAMERA 0.869f + +#ifndef GLAPIENTRY +#ifdef APIENTRY +#define GLAPIENTRY APIENTRY +#else +#define GLAPIENTRY +#endif +#endif + +#if defined(__linux__) +// Linux select() changes its timeout parameter upon return to contain +// the remaining time. Most other unixen leave it unchanged or undefined. +#define SELECT_SETS_REMAINING +#elif defined(__FreeBSD__) || defined(__sun__) || (defined(__MACH__) && defined(__APPLE__)) +#define USE_NANOSLEEP +#elif defined(HAVE_PTHREADS) && defined(sgi) +// SGI pthreads has a bug when using pthreads+signals+nanosleep, +// so instead of using nanosleep, wait on a CV which is never signalled. +#include <pthread.h> +#define USE_COND_TIMEDWAIT +#endif + +#define RECT_IS_EQ(a, b) ((a).x1 == (b).x1 && (a).y1 == (b).y1 && (a).x2 == (b).x2 && (a).y2 == (b).y2) + +static const char *const scaling_level_enum_names[] = { + "default", /* VA_FILTER_SCALING_DEFAULT */ + "fast", /* VA_FILTER_SCALING_FAST */ + "hq", /* VA_FILTER_SCALING_HQ */ + "nla", /* VA_FILTER_SCALING_NL_ANAMORPHIC */ + NULL +}; + +static const int scaling_level_enum_values[] = { + VA_FILTER_SCALING_DEFAULT, + VA_FILTER_SCALING_FAST, + VA_FILTER_SCALING_HQ, + VA_FILTER_SCALING_NL_ANAMORPHIC +}; + +typedef struct vaapi_driver_s vaapi_driver_t; + +typedef struct { + int x0, y0; + int x1, y1, x2, y2; +} vaapi_rect_t; + +typedef struct { + vo_frame_t vo_frame; + + int width, height, format, flags; + double ratio; + + vaapi_accel_t vaapi_accel_data; +} vaapi_frame_t; + +typedef struct { + VADisplayAttribType type; + int value; + int min; + int max; + int atom; + + cfg_entry_t *entry; + + vaapi_driver_t *this; + +} va_property_t; + +struct vaapi_driver_s { + + vo_driver_t vo_driver; + + config_values_t *config; + + /* X11 related stuff */ + Display *display; + int screen; + Drawable drawable; + XColor black; + Window window; + + uint32_t capabilities; + + int ovl_changed; + vo_overlay_t *overlays[XINE_VORAW_MAX_OVL]; + uint32_t *overlay_bitmap; + int overlay_bitmap_size; + uint32_t overlay_bitmap_width; + uint32_t overlay_bitmap_height; + vaapi_rect_t overlay_bitmap_src; + vaapi_rect_t overlay_bitmap_dst; + + uint32_t vdr_osd_width; + uint32_t vdr_osd_height; + + uint32_t overlay_output_width; + uint32_t overlay_output_height; + vaapi_rect_t overlay_dirty_rect; + int has_overlay; + + uint32_t overlay_unscaled_width; + uint32_t overlay_unscaled_height; + vaapi_rect_t overlay_unscaled_dirty_rect; + + yuv2rgb_factory_t *yuv2rgb_factory; + yuv2rgb_t *ovl_yuv2rgb; + + /* all scaling information goes here */ + vo_scale_t sc; + + xine_t *xine; + + unsigned int deinterlace; + + int valid_opengl_context; + int opengl_render; + int opengl_use_tfp; + int query_va_status; + + GLuint gl_texture; + GLXContext gl_context; + XVisualInfo *gl_vinfo; + Pixmap gl_pixmap; + Pixmap gl_image_pixmap; + + ff_vaapi_context_t *va_context; + + int num_frame_buffers; + vaapi_frame_t *frames[RENDER_SURFACES]; + + pthread_mutex_t vaapi_lock; + + unsigned int init_opengl_render; + unsigned int guarded_render; + unsigned int scaling_level_enum; + unsigned int scaling_level; + va_property_t props[VO_NUM_PROPERTIES]; + unsigned int swap_uv_planes; +}; + +ff_vaapi_surface_t *va_render_surfaces = NULL; +VASurfaceID *va_surface_ids = NULL; +VASurfaceID *va_soft_surface_ids = NULL; +VAImage *va_soft_images = NULL; + +static void vaapi_destroy_subpicture(vo_driver_t *this_gen); +static void vaapi_destroy_image(vo_driver_t *this_gen, VAImage *va_image); +static int vaapi_ovl_associate(vo_driver_t *this_gen, int format, int bShow); +static VAStatus vaapi_destroy_soft_surfaces(vo_driver_t *this_gen); +static VAStatus vaapi_destroy_render_surfaces(vo_driver_t *this_gen); +static const char *vaapi_profile_to_string(VAProfile profile); +static int vaapi_set_property (vo_driver_t *this_gen, int property, int value); +static void vaapi_show_display_props(vo_driver_t *this_gen); + +static void nv12_to_yv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *uv_src, int uv_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *u_dst, int u_dst_pitch, + uint8_t *v_dst, int v_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int src_data_size); + +static void yv12_to_nv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *u_src, int u_src_pitch, + const uint8_t *v_src, int v_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *uv_dst, int uv_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int dst_data_size); + +void (GLAPIENTRY *mpglGenTextures)(GLsizei, GLuint *); +void (GLAPIENTRY *mpglBindTexture)(GLenum, GLuint); +void (GLAPIENTRY *mpglXBindTexImage)(Display *, GLXDrawable, int, const int *); +void (GLAPIENTRY *mpglXReleaseTexImage)(Display *, GLXDrawable, int); +GLXPixmap (GLAPIENTRY *mpglXCreatePixmap)(Display *, GLXFBConfig, Pixmap, const int *); +void (GLAPIENTRY *mpglXDestroyPixmap)(Display *, GLXPixmap); +const GLubyte *(GLAPIENTRY *mpglGetString)(GLenum); +void (GLAPIENTRY *mpglGenPrograms)(GLsizei, GLuint *); + +static const char *string_of_VAImageFormat(VAImageFormat *imgfmt) +{ + static char str[5]; + str[0] = imgfmt->fourcc; + str[1] = imgfmt->fourcc >> 8; + str[2] = imgfmt->fourcc >> 16; + str[3] = imgfmt->fourcc >> 24; + str[4] = '\0'; + return str; +} + +static int vaapi_check_status(vo_driver_t *this_gen, VAStatus vaStatus, const char *msg) +{ + + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if (vaStatus != VA_STATUS_SUCCESS) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " Error : %s: %s\n", msg, vaErrorStr(vaStatus)); + return 0; + } + return 1; +} + +/* Wrapper for ffmpeg avcodec_decode_video2 */ +#if AVVIDEO > 1 +static int guarded_avcodec_decode_video2(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, AVPacket *avpkt) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + int len = 0; + + + if(this->guarded_render) { + lprintf("guarded_avcodec_decode_video2 enter\n"); + pthread_mutex_lock(&this->vaapi_lock); + //XLockDisplay( this->display ); + } + + len = avcodec_decode_video2 (avctx, picture, got_picture_ptr, avpkt); + + if(this->guarded_render) { + //XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + lprintf("guarded_avcodec_decode_video2 exit\n"); + } + + + return len; +} +#else +static int guarded_avcodec_decode_video(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, uint8_t *buf, int buf_size) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + int len = 0; + + + if(this->guarded_render) { + lprintf("guarded_avcodec_decode_video enter\n"); + pthread_mutex_lock(&this->vaapi_lock); + //XLockDisplay( this->display ); + } + + len = avcodec_decode_video (avctx, picture, got_picture_ptr, buf, buf_size); + + if(this->guarded_render) { + //XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + lprintf("guarded_avcodec_decode_video exit\n"); + } + + + return len; +} +#endif + +static int guarded_render(vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + return this->guarded_render; +} + +static ff_vaapi_surface_t *get_vaapi_surface(vo_frame_t *frame_gen) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + ff_vaapi_surface_t *va_surface = NULL; + VAStatus vaStatus; + + lprintf("get_vaapi_surface\n"); + + if(!va_render_surfaces) + return NULL; + + if(this->guarded_render) { + /* Get next VAAPI surface marked as SURFACE_FREE */ + for(;;) { + int old_head = va_context->va_head; + va_context->va_head = (va_context->va_head + 1) % ((RENDER_SURFACES)); + + va_surface = &va_render_surfaces[old_head]; + + if( va_surface->status == SURFACE_FREE ) { + + VASurfaceStatus surf_status = 0; + + if(this->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface->va_surface_id, &surf_status); + vaapi_check_status(va_context->driver, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status == VASurfaceReady) { + + va_surface->status = SURFACE_ALOC; + +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface 0x%08x\n", va_surface->va_surface_id); +#endif + + return &va_render_surfaces[old_head]; + } else { +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface busy\n"); +#endif + } + } +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface miss\n"); +#endif + } + } else { + va_surface = &va_render_surfaces[frame->vaapi_accel_data.index]; + } + + return va_surface; +} + +/* Set VAAPI surface status to render */ +static void render_vaapi_surface(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_accel_t *accel = (vaapi_accel_t*)frame_gen->accel_data; + + lprintf("render_vaapi_surface\n"); + + if(!this->guarded_render || !accel || !va_surface) + return; + + pthread_mutex_lock(&this->vaapi_lock); + //XLockDisplay( this->display ); + + accel->index = va_surface->index; + + va_surface->status = SURFACE_RENDER; +#ifdef DEBUG_SURFACE + printf("render_vaapi_surface 0x%08x\n", va_surface->va_surface_id); +#endif + + //XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); +} + +/* Set VAAPI surface status to free */ +static void release_vaapi_surface(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + lprintf("release_vaapi_surface\n"); + + if(va_surface == NULL || !this->guarded_render) { + return; + } + + if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RENDER_RELEASE; + } else if (va_surface->status != SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface 0x%08x\n", va_surface->va_surface_id); +#endif + } +} + +static VADisplay vaapi_get_display(Display *display, int opengl_render) +{ + VADisplay ret; + + if(opengl_render) { + ret = vaGetDisplayGLX(display); + } else { + ret = vaGetDisplay(display); + } + + if(vaDisplayIsValid(ret)) + return ret; + else + return 0; +} + +typedef struct { + void *funcptr; + const char *extstr; + const char *funcnames[7]; + void *fallback; +} extfunc_desc_t; + +#define DEF_FUNC_DESC(name) {&mpgl##name, NULL, {"gl"#name, NULL}, gl ##name} +static const extfunc_desc_t extfuncs[] = { + DEF_FUNC_DESC(GenTextures), + + {&mpglBindTexture, NULL, {"glBindTexture", "glBindTextureARB", "glBindTextureEXT", NULL}}, + {&mpglXBindTexImage, "GLX_EXT_texture_from_pixmap", {"glXBindTexImageEXT", NULL}}, + {&mpglXReleaseTexImage, "GLX_EXT_texture_from_pixmap", {"glXReleaseTexImageEXT", NULL}}, + {&mpglXCreatePixmap, "GLX_EXT_texture_from_pixmap", {"glXCreatePixmap", NULL}}, + {&mpglXDestroyPixmap, "GLX_EXT_texture_from_pixmap", {"glXDestroyPixmap", NULL}}, + {&mpglGenPrograms, "_program", {"glGenProgramsARB", NULL}}, + {NULL} +}; + +typedef struct { + video_driver_class_t driver_class; + + config_values_t *config; + xine_t *xine; +} vaapi_class_t; + +static int gl_visual_attr[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GL_NONE +}; + +static void delay_usec(unsigned int usec) +{ + int was_error; + +#if defined(USE_NANOSLEEP) + struct timespec elapsed, tv; +#elif defined(USE_COND_TIMEDWAIT) + // Use a local mutex and cv, so threads remain independent + pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER; + struct timespec elapsed; + uint64_t future; +#else + struct timeval tv; +#ifndef SELECT_SETS_REMAINING + uint64_t then, now, elapsed; +#endif +#endif + + // Set the timeout interval - Linux only needs to do this once +#if defined(SELECT_SETS_REMAINING) + tv.tv_sec = 0; + tv.tv_usec = usec; +#elif defined(USE_NANOSLEEP) + elapsed.tv_sec = 0; + elapsed.tv_nsec = usec * 1000; +#elif defined(USE_COND_TIMEDWAIT) + future = get_ticks_usec() + usec; + elapsed.tv_sec = future / 1000000; + elapsed.tv_nsec = (future % 1000000) * 1000; +#else + then = get_ticks_usec(); +#endif + + do { + errno = 0; +#if defined(USE_NANOSLEEP) + tv.tv_sec = elapsed.tv_sec; + tv.tv_nsec = elapsed.tv_nsec; + was_error = nanosleep(&tv, &elapsed); +#elif defined(USE_COND_TIMEDWAIT) + was_error = pthread_mutex_lock(&delay_mutex); + was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed); + was_error = pthread_mutex_unlock(&delay_mutex); +#else +#ifndef SELECT_SETS_REMAINING + // Calculate the time interval left (in case of interrupt) + now = get_ticks_usec(); + elapsed = now - then; + then = now; + if (elapsed >= usec) + break; + usec -= elapsed; + tv.tv_sec = 0; + tv.tv_usec = usec; +#endif + was_error = select(0, NULL, NULL, NULL, &tv); +#endif + } while (was_error && (errno == EINTR)); +} + +static void vaapi_x11_wait_event(Display *dpy, Window w, int type) +{ + XEvent e; + while (!XCheckTypedWindowEvent(dpy, w, type, &e)) + delay_usec(10); +} + +/* X11 Error handler and error functions */ +static int vaapi_x11_error_code = 0; +static int (*vaapi_x11_old_error_handler)(Display *, XErrorEvent *); + +static int vaapi_x11_error_handler(Display *dpy, XErrorEvent *error) +{ + vaapi_x11_error_code = error->error_code; + return 0; +} + +static void vaapi_x11_trap_errors(void) +{ + vaapi_x11_error_code = 0; + vaapi_x11_old_error_handler = XSetErrorHandler(vaapi_x11_error_handler); +} + +static int vaapi_x11_untrap_errors(void) +{ + XSetErrorHandler(vaapi_x11_old_error_handler); + return vaapi_x11_error_code; +} + +static void vaapi_appendstr(char **dst, const char *str) +{ + int newsize; + char *newstr; + if (!str) + return; + newsize = strlen(*dst) + 1 + strlen(str) + 1; + newstr = realloc(*dst, newsize); + if (!newstr) + return; + *dst = newstr; + strcat(*dst, " "); + strcat(*dst, str); +} + +/* Return the address of a linked function */ +static void *vaapi_getdladdr (const char *s) { + void *ret = NULL; + void *handle = dlopen(NULL, RTLD_LAZY); + if (!handle) + return NULL; + ret = dlsym(handle, s); + dlclose(handle); + + return ret; +} + +/* Resolve opengl functions. */ +static void vaapi_get_functions(vo_driver_t *this_gen, void *(*getProcAddress)(const GLubyte *), + const char *ext2) { + const extfunc_desc_t *dsc; + const char *extensions; + char *allexts; + + if (!getProcAddress) + getProcAddress = (void *)vaapi_getdladdr; + + /* special case, we need glGetString before starting to find the other functions */ + mpglGetString = getProcAddress("glGetString"); + if (!mpglGetString) + mpglGetString = glGetString; + + extensions = (const char *)mpglGetString(GL_EXTENSIONS); + if (!extensions) extensions = ""; + if (!ext2) ext2 = ""; + allexts = malloc(strlen(extensions) + strlen(ext2) + 2); + strcpy(allexts, extensions); + strcat(allexts, " "); + strcat(allexts, ext2); + lprintf("vaapi_get_functions: OpenGL extensions string:\n%s\n", allexts); + for (dsc = extfuncs; dsc->funcptr; dsc++) { + void *ptr = NULL; + int i; + if (!dsc->extstr || strstr(allexts, dsc->extstr)) { + for (i = 0; !ptr && dsc->funcnames[i]; i++) + ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]); + } + if (!ptr) + ptr = dsc->fallback; + *(void **)dsc->funcptr = ptr; + } + lprintf("\n"); + free(allexts); +} + +/* Check if opengl indirect/software rendering is used */ +static int vaapi_opengl_verify_direct (x11_visual_t *vis) { + Window root, win; + XVisualInfo *visinfo; + GLXContext ctx; + XSetWindowAttributes xattr; + int ret = 0; + + if (!vis || !vis->display || ! (root = RootWindow (vis->display, vis->screen))) { + lprintf ("vaapi_opengl_verify_direct: Don't have a root window to verify\n"); + return 0; + } + + if (! (visinfo = glXChooseVisual (vis->display, vis->screen, gl_visual_attr))) + return 0; + + if (! (ctx = glXCreateContext (vis->display, visinfo, NULL, 1))) + return 0; + + memset (&xattr, 0, sizeof (xattr)); + xattr.colormap = XCreateColormap(vis->display, root, visinfo->visual, AllocNone); + xattr.event_mask = StructureNotifyMask | ExposureMask; + + if ( (win = XCreateWindow (vis->display, root, 0, 0, 1, 1, 0, visinfo->depth, + InputOutput, visinfo->visual, + CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, + &xattr))) { + if (glXMakeCurrent (vis->display, win, ctx)) { + const char *renderer = (const char *) glGetString(GL_RENDERER); + if (glXIsDirect (vis->display, ctx) && + ! strstr (renderer, "Software") && + ! strstr (renderer, "Indirect")) + ret = 1; + glXMakeCurrent (vis->display, None, NULL); + } + XDestroyWindow (vis->display, win); + } + glXDestroyContext (vis->display, ctx); + XFreeColormap (vis->display, xattr.colormap); + + return ret; +} + +static int vaapi_glx_bind_texture(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + glEnable(GL_TEXTURE_2D); + mpglBindTexture(GL_TEXTURE_2D, this->gl_texture); + + if (this->opengl_use_tfp) { + vaapi_x11_trap_errors(); + mpglXBindTexImage(this->display, this->gl_pixmap, GLX_FRONT_LEFT_EXT, NULL); + XSync(this->display, False); + if (vaapi_x11_untrap_errors()) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_bind_texture : Update bind_tex_image failed\n"); + } + + return 0; +} + +static int vaapi_glx_unbind_texture(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if (this->opengl_use_tfp) { + vaapi_x11_trap_errors(); + mpglXReleaseTexImage(this->display, this->gl_pixmap, GLX_FRONT_LEFT_EXT); + if (vaapi_x11_untrap_errors()) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_unbind_texture : Failed to release?\n"); + } + + mpglBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + return 0; +} + +static void vaapi_glx_render_frame(vo_frame_t *frame_gen, int left, int top, int right, int bottom) +{ + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + int x1, x2, y1, y2; + float tx, ty; + + if (vaapi_glx_bind_texture(frame_gen->driver) < 0) + return; + + /* Calc texture/rectangle coords */ + x1 = this->sc.output_xoffset; + y1 = this->sc.output_yoffset; + x2 = x1 + this->sc.output_width; + y2 = y1 + this->sc.output_height; + tx = (float) frame->width / va_context->width; + ty = (float) frame->height / va_context->height; + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + /* Draw quad */ + glBegin (GL_QUADS); + + glTexCoord2f (tx, ty); glVertex2i (x2, y2); + glTexCoord2f (0, ty); glVertex2i (x1, y2); + glTexCoord2f (0, 0); glVertex2i (x1, y1); + glTexCoord2f (tx, 0); glVertex2i (x2, y1); + lprintf("render_frame left %d top %d right %d bottom %d\n", x1, y1, x2, y2); + + glEnd (); + + if (vaapi_glx_unbind_texture(frame_gen->driver) < 0) + return; +} + +static void vaapi_glx_flip_page(vo_frame_t *frame_gen, int left, int top, int right, int bottom) +{ + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + glClear(GL_COLOR_BUFFER_BIT); + + vaapi_glx_render_frame(frame_gen, left, top, right, bottom); + + //if (gl_finish) + // glFinish(); + + glXSwapBuffers(this->display, this->window); + +} + +static void destroy_glx(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if(!this->opengl_render || !va_context->valid_context) + return; + + //if (gl_finish) + // glFinish(); + + if(va_context->gl_surface) { + VAStatus vaStatus = vaDestroySurfaceGLX(va_context->va_display, va_context->gl_surface); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaceGLX()"); + va_context->gl_surface = NULL; + } + + if(this->gl_context) + glXMakeCurrent(this->display, None, NULL); + + if(this->gl_pixmap) { + vaapi_x11_trap_errors(); + mpglXDestroyPixmap(this->display, this->gl_pixmap); + XSync(this->display, False); + vaapi_x11_untrap_errors(); + this->gl_pixmap = None; + } + + if(this->gl_image_pixmap) { + XFreePixmap(this->display, this->gl_image_pixmap); + this->gl_image_pixmap = None; + } + + if(this->gl_texture) { + glDeleteTextures(1, &this->gl_texture); + this->gl_texture = GL_NONE; + } + + if(this->gl_context) { + glXDestroyContext(this->display, this->gl_context); + this->gl_context = 0; + } + + if(this->gl_vinfo) { + XFree(this->gl_vinfo); + this->gl_vinfo = NULL; + } + + this->valid_opengl_context = 0; +} + +static GLXFBConfig *get_fbconfig_for_depth(vo_driver_t *this_gen, int depth) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + GLXFBConfig *fbconfigs, *ret = NULL; + int n_elements, i, found; + int db, stencil, alpha, rgba, value; + + static GLXFBConfig *cached_config = NULL; + static int have_cached_config = 0; + + if (have_cached_config) + return cached_config; + + fbconfigs = glXGetFBConfigs(this->display, this->screen, &n_elements); + + db = SHRT_MAX; + stencil = SHRT_MAX; + rgba = 0; + + found = n_elements; + + for (i = 0; i < n_elements; i++) { + XVisualInfo *vi; + int visual_depth; + + vi = glXGetVisualFromFBConfig(this->display, fbconfigs[i]); + if (!vi) + continue; + + visual_depth = vi->depth; + XFree(vi); + + if (visual_depth != depth) + continue; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_ALPHA_SIZE, &alpha); + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_BUFFER_SIZE, &value); + if (value != depth && (value - alpha) != depth) + continue; + + value = 0; + if (depth == 32) { + glXGetFBConfigAttrib(this->display, fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); + if (value) + rgba = 1; + } + + if (!value) { + if (rgba) + continue; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGB_EXT, &value); + if (!value) + continue; + } + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_DOUBLEBUFFER, &value); + if (value > db) + continue; + db = value; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_STENCIL_SIZE, &value); + if (value > stencil) + continue; + stencil = value; + + found = i; + } + + if (found != n_elements) { + ret = malloc(sizeof(*ret)); + *ret = fbconfigs[found]; + } + + if (n_elements) + XFree(fbconfigs); + + have_cached_config = 1; + cached_config = ret; + return ret; +} + +static int vaapi_glx_config_tfp(vo_driver_t *this_gen, unsigned int width, unsigned int height) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + GLXFBConfig *fbconfig; + int attribs[7], i = 0; + const int depth = 24; + + if (!mpglXBindTexImage || !mpglXReleaseTexImage) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : No GLX texture-from-pixmap extension available\n"); + return 0; + } + + if (depth != 24 && depth != 32) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : color depth wrong.\n"); + return 0; + } + + this->gl_image_pixmap = XCreatePixmap(this->display, this->window, width, height, depth); + if (!this->gl_image_pixmap) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not create X11 pixmap\n"); + return 0; + } + + fbconfig = get_fbconfig_for_depth(this_gen, depth); + if (!fbconfig) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not find an FBConfig for 32-bit pixmap\n"); + return 0; + } + + attribs[i++] = GLX_TEXTURE_TARGET_EXT; + attribs[i++] = GLX_TEXTURE_2D_EXT; + attribs[i++] = GLX_TEXTURE_FORMAT_EXT; + if (depth == 24) + attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; + else if (depth == 32) + attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; + attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; + attribs[i++] = GL_FALSE; + attribs[i++] = None; + + vaapi_x11_trap_errors(); + this->gl_pixmap = mpglXCreatePixmap(this->display, *fbconfig, this->gl_image_pixmap, attribs); + XSync(this->display, False); + if (vaapi_x11_untrap_errors()) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not create GLX pixmap\n"); + return 0; + } + + return 1; +} + +static int vaapi_glx_config_glx(vo_driver_t *this_gen, unsigned int width, unsigned int height) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + this->gl_vinfo = glXChooseVisual(this->display, this->screen, gl_visual_attr); + if(!this->gl_vinfo) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXChooseVisual\n"); + this->opengl_render = 0; + } + + glXMakeCurrent(this->display, None, NULL); + this->gl_context = glXCreateContext (this->display, this->gl_vinfo, NULL, True); + if (this->gl_context) { + if(!glXMakeCurrent (this->display, this->window, this->gl_context)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXMakeCurrent\n"); + goto error; + } + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXCreateContext\n"); + goto error; + } + + void *(*getProcAddress)(const GLubyte *); + const char *(*glXExtStr)(Display *, int); + char *glxstr = strdup(" "); + + getProcAddress = vaapi_getdladdr("glXGetProcAddress"); + if (!getProcAddress) + getProcAddress = vaapi_getdladdr("glXGetProcAddressARB"); + glXExtStr = vaapi_getdladdr("glXQueryExtensionsString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, this->screen)); + glXExtStr = vaapi_getdladdr("glXGetClientString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, GLX_EXTENSIONS)); + glXExtStr = vaapi_getdladdr("glXGetServerString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, GLX_EXTENSIONS)); + + vaapi_get_functions(this_gen, getProcAddress, glxstr); + if (!mpglGenPrograms && mpglGetString && + getProcAddress && + strstr(mpglGetString(GL_EXTENSIONS), "GL_ARB_vertex_program")) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : Broken glXGetProcAddress detected, trying workaround\n"); + vaapi_get_functions(this_gen, NULL, glxstr); + } + free(glxstr); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glDrawBuffer(GL_BACK); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* Create TFP resources */ + if(this->opengl_use_tfp && vaapi_glx_config_tfp(this_gen, width, height)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : Using GLX texture-from-pixmap extension\n"); + } else { + this->opengl_use_tfp = 0; + } + + /* Create OpenGL texture */ + /* XXX: assume GL_ARB_texture_non_power_of_two is available */ + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &this->gl_texture); + mpglBindTexture(GL_TEXTURE_2D, this->gl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (!this->opengl_use_tfp) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + mpglBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + if(!this->gl_texture) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : gl_texture NULL\n"); + goto error; + } + + if(!this->opengl_use_tfp) { + VAStatus vaStatus = vaCreateSurfaceGLX(va_context->va_display, GL_TEXTURE_2D, this->gl_texture, &va_context->gl_surface); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaceGLX()")) { + va_context->gl_surface = NULL; + goto error; + } + } else { + va_context->gl_surface = NULL; + } + + lprintf("vaapi_glx_config_glx : GL setup done\n"); + + this->valid_opengl_context = 1; + return 1; + +error: + destroy_glx(this_gen); + this->valid_opengl_context = 0; + return 0; +} + +static uint32_t vaapi_get_capabilities (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + return this->capabilities; +} + +static const struct { + int fmt; + enum PixelFormat pix_fmt; + enum CodecID codec_id; +} conversion_map[] = { + {IMGFMT_VAAPI_MPEG2, PIX_FMT_VAAPI_VLD, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG2_IDCT,PIX_FMT_VAAPI_IDCT, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG2_MOCO,PIX_FMT_VAAPI_MOCO, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG4, PIX_FMT_VAAPI_VLD, CODEC_ID_MPEG4}, + {IMGFMT_VAAPI_H263, PIX_FMT_VAAPI_VLD, CODEC_ID_H263}, + {IMGFMT_VAAPI_H264, PIX_FMT_VAAPI_VLD, CODEC_ID_H264}, + {IMGFMT_VAAPI_WMV3, PIX_FMT_VAAPI_VLD, CODEC_ID_WMV3}, + {IMGFMT_VAAPI_VC1, PIX_FMT_VAAPI_VLD, CODEC_ID_VC1}, + {0, PIX_FMT_NONE} +}; + +static int vaapi_pixfmt2imgfmt(enum PixelFormat pix_fmt, int codec_id) +{ + int i; + int fmt; + for (i = 0; conversion_map[i].pix_fmt != PIX_FMT_NONE; i++) { + if (conversion_map[i].pix_fmt == pix_fmt && + (conversion_map[i].codec_id == 0 || + conversion_map[i].codec_id == codec_id)) { + break; + } + } + fmt = conversion_map[i].fmt; + return fmt; +} + +static int vaapi_has_profile(VAProfile *va_profiles, int va_num_profiles, VAProfile profile) +{ + if (va_profiles && va_num_profiles > 0) { + int i; + for (i = 0; i < va_num_profiles; i++) { + if (va_profiles[i] == profile) + return 1; + } + } + return 0; +} + +static int profile_from_imgfmt(vo_frame_t *frame_gen, enum PixelFormat pix_fmt, int codec_id, int vaapi_mpeg_sofdec) +{ + vo_driver_t *this_gen = (vo_driver_t *) frame_gen->driver; + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + int profile = -1; + int maj, min; + int i; + int va_num_profiles; + int max_profiles; + VAProfile *va_profiles = NULL; + int inited = 0; + + if(va_context->va_display == NULL) { + lprintf("profile_from_imgfmt vaInitialize\n"); + inited = 1; + va_context->va_display = vaapi_get_display(this->display, this->opengl_render); + if(!va_context->va_display) + goto out; + + vaStatus = vaInitialize(va_context->va_display, &maj, &min); + if(!vaapi_check_status(this_gen, vaStatus, "vaInitialize()")) + goto out; + + } + + max_profiles = vaMaxNumProfiles(va_context->va_display); + va_profiles = calloc(max_profiles, sizeof(*va_profiles)); + if (!va_profiles) + goto out; + + vaStatus = vaQueryConfigProfiles(va_context->va_display, va_profiles, &va_num_profiles); + if(!vaapi_check_status(this_gen, vaStatus, "vaQueryConfigProfiles()")) + goto out; + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " VAAPI Supported Profiles : "); + for (i = 0; i < va_num_profiles; i++) { + printf("%s ", vaapi_profile_to_string(va_profiles[i])); + } + printf("\n"); + + uint32_t format = vaapi_pixfmt2imgfmt(pix_fmt, codec_id); + + static const int mpeg2_profiles[] = { VAProfileMPEG2Main, VAProfileMPEG2Simple, -1 }; + static const int mpeg4_profiles[] = { VAProfileMPEG4Main, VAProfileMPEG4AdvancedSimple, VAProfileMPEG4Simple, -1 }; + static const int h264_profiles[] = { VAProfileH264High, VAProfileH264Main, VAProfileH264Baseline, -1 }; + static const int wmv3_profiles[] = { VAProfileVC1Main, VAProfileVC1Simple, -1 }; + static const int vc1_profiles[] = { VAProfileVC1Advanced, -1 }; + + const int *profiles = NULL; + switch (IMGFMT_VAAPI_CODEC(format)) + { + case IMGFMT_VAAPI_CODEC_MPEG2: + if(!vaapi_mpeg_sofdec) { + profiles = mpeg2_profiles; + } + break; + case IMGFMT_VAAPI_CODEC_MPEG4: + profiles = mpeg4_profiles; + break; + case IMGFMT_VAAPI_CODEC_H264: + profiles = h264_profiles; + break; + case IMGFMT_VAAPI_CODEC_VC1: + switch (format) { + case IMGFMT_VAAPI_WMV3: + profiles = wmv3_profiles; + break; + case IMGFMT_VAAPI_VC1: + profiles = vc1_profiles; + break; + } + break; + } + + if (profiles) { + int i; + for (i = 0; profiles[i] != -1; i++) { + if (vaapi_has_profile(va_profiles, va_num_profiles, profiles[i])) { + profile = profiles[i]; + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " VAAPI Profile %s supported by your hardware\n", vaapi_profile_to_string(profiles[i])); + break; + } + } + } + +out: + if(va_profiles) + free(va_profiles); + if(inited) { + vaStatus = vaTerminate(va_context->va_display); + vaapi_check_status(this_gen, vaStatus, "vaTerminate()"); + } + return profile; +} + + +static const char *vaapi_profile_to_string(VAProfile profile) +{ + switch(profile) { +#define PROFILE(profile) \ + case VAProfile##profile: return "VAProfile" #profile + PROFILE(MPEG2Simple); + PROFILE(MPEG2Main); + PROFILE(MPEG4Simple); + PROFILE(MPEG4AdvancedSimple); + PROFILE(MPEG4Main); + PROFILE(H264Baseline); + PROFILE(H264Main); + PROFILE(H264High); + PROFILE(VC1Simple); + PROFILE(VC1Main); + PROFILE(VC1Advanced); +#undef PROFILE + default: break; + } + return "<unknown>"; +} + +static const char *vaapi_entrypoint_to_string(VAEntrypoint entrypoint) +{ + switch(entrypoint) + { +#define ENTRYPOINT(entrypoint) \ + case VAEntrypoint##entrypoint: return "VAEntrypoint" #entrypoint + ENTRYPOINT(VLD); + ENTRYPOINT(IZZ); + ENTRYPOINT(IDCT); + ENTRYPOINT(MoComp); + ENTRYPOINT(Deblocking); +#undef ENTRYPOINT + default: break; + } + return "<unknown>"; +} + +/* Init subpicture */ +static void vaapi_init_subpicture(vaapi_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + va_context->va_subpic_width = 0; + va_context->va_subpic_height = 0; + va_context->va_subpic_id = VA_INVALID_ID; + va_context->va_subpic_image.image_id = VA_INVALID_ID; + + this->overlay_output_width = this->overlay_output_height = 0; + this->overlay_unscaled_width = this->overlay_unscaled_height = 0; + this->ovl_changed = 0; + this->has_overlay = 0; + this->overlay_bitmap = NULL; + this->overlay_bitmap_size = 0; + +} + +/* Init vaapi context */ +static void vaapi_init_va_context(vaapi_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + + va_context->va_config_id = VA_INVALID_ID; + va_context->va_context_id = VA_INVALID_ID; + va_context->va_profile = 0; + va_context->va_colorspace = 1; + va_context->is_bound = 0; + va_context->gl_surface = NULL; + va_context->soft_head = 0; + va_context->valid_context = 0; + va_context->va_head = 0; + va_context->va_soft_head = 0; + + for(i = 0; i < RENDER_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = VA_INVALID_SURFACE; + + va_surface_ids[i] = VA_INVALID_SURFACE; + } + + for(i = 0; i < SOFT_SURFACES; i++) { + va_soft_surface_ids[i] = VA_INVALID_SURFACE; + va_soft_images[i].image_id = VA_INVALID_ID; + } + + va_context->va_image_formats = NULL; + va_context->va_num_image_formats = 0; + + va_context->va_subpic_formats = NULL; + va_context->va_num_subpic_formats = 0; +} + +/* Close vaapi */ +static void vaapi_close(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context || !va_context->va_display || !va_context->valid_context) + return; + + vaapi_ovl_associate(this_gen, 0, 0); + destroy_glx((vo_driver_t *)this); + + vaapi_destroy_subpicture(this_gen); + + vaapi_destroy_soft_surfaces(this_gen); + + vaapi_destroy_render_surfaces(this_gen); + + if(va_context->va_config_id != VA_INVALID_ID) { + vaStatus = vaDestroyConfig(va_context->va_display, va_context->va_config_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyConfig()"); + va_context->va_config_id = VA_INVALID_ID; + } + + if(va_context->va_context_id != VA_INVALID_ID) { + vaStatus = vaDestroyContext(va_context->va_display, va_context->va_context_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyContext()"); + va_context->va_context_id = VA_INVALID_ID; + } + + vaStatus = vaTerminate(va_context->va_display); + vaapi_check_status(this_gen, vaStatus, "vaTerminate()"); + va_context->va_display = NULL; + + if(va_context->va_image_formats) { + free(va_context->va_image_formats); + va_context->va_image_formats = NULL; + va_context->va_num_image_formats = 0; + } + if(va_context->va_subpic_formats) { + free(va_context->va_subpic_formats); + va_context->va_subpic_formats = NULL; + va_context->va_num_subpic_formats = 0; + } + + va_context->valid_context = 0; +} + +/* Returns internal VAAPI context */ +static ff_vaapi_context_t *get_context(vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + return this->va_context; +} + +/* Free allocated VAAPI image */ +static void vaapi_destroy_image(vo_driver_t *this_gen, VAImage *va_image) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(va_image->image_id != VA_INVALID_ID) { + lprintf("vaapi_destroy_image 0x%08x\n", va_image->image_id); + vaStatus = vaDestroyImage(va_context->va_display, va_image->image_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyImage()"); + } + va_image->image_id = VA_INVALID_ID; + va_image->width = 0; + va_image->height = 0; +} + +/* Allocated VAAPI image */ +static VAStatus vaapi_create_image(vo_driver_t *this_gen, VASurfaceID va_surface_id, VAImage *va_image, int width, int height, int clear) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int i = 0; + VAStatus vaStatus; + + if(!va_context->valid_context || va_context->va_image_formats == NULL || va_context->va_num_image_formats == 0) + return VA_STATUS_ERROR_UNKNOWN; + + va_context->is_bound = 0; + + vaStatus = vaDeriveImage(va_context->va_display, va_surface_id, va_image); + if(vaStatus == VA_STATUS_SUCCESS) { + if (va_image->image_id != VA_INVALID_ID && va_image->buf != VA_INVALID_ID) { + va_context->is_bound = 1; + } + } + + if(!va_context->is_bound) { + for (i = 0; i < va_context->va_num_image_formats; i++) { + if (va_context->va_image_formats[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_context->va_image_formats[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) /*|| + va_context->va_image_formats[i].fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) */) { + vaStatus = vaCreateImage( va_context->va_display, &va_context->va_image_formats[i], width, height, va_image ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateImage()")) + goto error; + break; + } + } + } + + void *p_base = NULL; + + vaStatus = vaMapBuffer( va_context->va_display, va_image->buf, &p_base ); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + goto error; + + if(clear) { + if(va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' )) { + memset((uint8_t*)p_base + va_image->offsets[0], 0, va_image->pitches[0] * va_image->height); + memset((uint8_t*)p_base + va_image->offsets[1], 128, va_image->pitches[1] * (va_image->height/2)); + memset((uint8_t*)p_base + va_image->offsets[2], 128, va_image->pitches[2] * (va_image->height/2)); + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) ) { + memset((uint8_t*)p_base + va_image->offsets[0], 0, va_image->pitches[0] * va_image->height); + memset((uint8_t*)p_base + va_image->offsets[1], 128, va_image->pitches[1] * (va_image->height/2)); + } + } + + vaStatus = vaUnmapBuffer( va_context->va_display, va_image->buf ); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + lprintf("vaapi_create_image 0x%08x width %d height %d format %s\n", va_image->image_id, va_image->width, va_image->height, + string_of_VAImageFormat(&va_image->format)); + + return VA_STATUS_SUCCESS; + +error: + /* house keeping */ + vaapi_destroy_image(this_gen, va_image); + return VA_STATUS_ERROR_UNKNOWN; +} + +/* Deassociate and free subpicture */ +static void vaapi_destroy_subpicture(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + lprintf("destroy sub 0x%08x 0x%08x 0x%08x\n", va_context->va_subpic_id, + va_context->va_subpic_image.image_id, va_context->va_subpic_image.buf); + + if(va_context->va_subpic_id != VA_INVALID_ID) { + vaStatus = vaDestroySubpicture(va_context->va_display, va_context->va_subpic_id); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } + va_context->va_subpic_id = VA_INVALID_ID; + + vaapi_destroy_image(this_gen, &va_context->va_subpic_image); + +} + +/* Create VAAPI subpicture */ +static VAStatus vaapi_create_subpicture(vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + int i = 0; + + if(!va_context->valid_context || !va_context->va_subpic_formats || va_context->va_num_subpic_formats == 0) + return VA_STATUS_ERROR_UNKNOWN; + + for (i = 0; i < va_context->va_num_subpic_formats; i++) { + if ( va_context->va_subpic_formats[i].fourcc == VA_FOURCC('B','G','R','A')) { + + vaStatus = vaCreateImage( va_context->va_display, &va_context->va_subpic_formats[i], width, height, &va_context->va_subpic_image ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateImage()")) + goto error; + + vaStatus = vaCreateSubpicture(va_context->va_display, va_context->va_subpic_image.image_id, &va_context->va_subpic_id ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSubpicture()")) + goto error; + } + } + + if(va_context->va_subpic_image.image_id == VA_INVALID_ID || va_context->va_subpic_id == VA_INVALID_ID) + goto error; + + void *p_base = NULL; + + lprintf("create sub 0x%08x 0x%08x 0x%08x\n", va_context->va_subpic_id, + va_context->va_subpic_image.image_id, va_context->va_subpic_image.buf); + + vaStatus = vaMapBuffer(va_context->va_display, va_context->va_subpic_image.buf, &p_base); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + goto error; + + memset((uint32_t *)p_base, 0x0, va_context->va_subpic_image.data_size); + vaStatus = vaUnmapBuffer(va_context->va_display, va_context->va_subpic_image.buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + this->overlay_output_width = width; + this->overlay_output_height = height; + + lprintf("vaapi_create_subpicture 0x%08x format %s\n", va_context->va_subpic_image.image_id, + string_of_VAImageFormat(&va_context->va_subpic_image.format)); + + return VA_STATUS_SUCCESS; + +error: + /* house keeping */ + if(va_context->va_subpic_id != VA_INVALID_ID) + vaapi_destroy_subpicture(this_gen); + va_context->va_subpic_id = VA_INVALID_ID; + + vaapi_destroy_image(this_gen, &va_context->va_subpic_image); + + this->overlay_output_width = 0; + this->overlay_output_height = 0; + + return VA_STATUS_ERROR_UNKNOWN; +} + +static inline int vaapi_get_colorspace_flags(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if(!va_context) + return 0; + + int colorspace = 0; +#if USE_VAAPI_COLORSPACE + switch (va_context->va_colorspace) { + case 0: + colorspace = ((va_context->sw_width >= 1280 || va_context->sw_height > 576) ? + VA_SRC_BT709 : VA_SRC_BT601); + break; + case 1: + colorspace = VA_SRC_BT601; + break; + case 2: + colorspace = VA_SRC_BT709; + break; + case 3: + colorspace = VA_SRC_SMPTE_240; + break; + default: + colorspace = VA_SRC_BT601; + break; + } +#endif + return colorspace; +} + +static void vaapi_property_callback (void *property_gen, xine_cfg_entry_t *entry) { + va_property_t *property = (va_property_t *) property_gen; + vaapi_driver_t *this = property->this; + ff_vaapi_context_t *va_context = this->va_context; + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + VADisplayAttribute attr; + + attr.type = property->type; + attr.value = entry->num_value; + + lprintf("vaapi_property_callback property=%d, value=%d\n", property->type, entry->num_value ); + + VAStatus vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + vaapi_check_status((vo_driver_t *)this, vaStatus, "vaSetDisplayAttributes()"); + + vaapi_show_display_props((vo_driver_t*)this); + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); +} + +/* called xlocked */ +static void vaapi_check_capability (vaapi_driver_t *this, + int property, VADisplayAttribute attr, + const char *config_name, + const char *config_desc, + const char *config_help) { + int int_default = 0; + cfg_entry_t *entry; + + this->props[property].type = attr.type; + this->props[property].min = attr.min_value; + this->props[property].max = attr.max_value; + int_default = attr.value; + this->props[property].atom = 1; + + if (config_name) { + /* is this a boolean property ? */ + if ((attr.min_value == 0) && (attr.max_value == 1)) { + this->config->register_bool (this->config, config_name, int_default, + config_desc, + config_help, 20, vaapi_property_callback, &this->props[property]); + + } else { + this->config->register_range (this->config, config_name, int_default, + this->props[property].min, this->props[property].max, + config_desc, + config_help, 20, vaapi_property_callback, &this->props[property]); + } + + entry = this->config->lookup_entry (this->config, config_name); + if((entry->num_value < this->props[property].min) || + (entry->num_value > this->props[property].max)) { + + this->config->update_num(this->config, config_name, + ((this->props[property].min + this->props[property].max) >> 1)); + + entry = this->config->lookup_entry (this->config, config_name); + } + + this->props[property].entry = entry; + + vaapi_set_property(&this->vo_driver, property, entry->num_value); + } else { + this->props[property].value = int_default; + } +} + +static void vaapi_show_display_props(vo_driver_t *this_gen) { + /* + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if(this->capabilities & VO_CAP_BRIGHTNESS) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : brightness : %d\n", this->props[VO_PROP_BRIGHTNESS].value); + if(this->capabilities & VO_CAP_CONTRAST) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : contrast : %d\n", this->props[VO_PROP_CONTRAST].value); + if(this->capabilities & VO_CAP_HUE) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : hue : %d\n", this->props[VO_PROP_HUE].value); + if(this->capabilities & VO_CAP_SATURATION) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : saturation : %d\n", this->props[VO_PROP_SATURATION].value); + */ +} + +/* VAAPI display attributes. */ +static void vaapi_display_attribs(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int num_display_attrs, max_display_attrs; + VAStatus vaStatus; + VADisplayAttribute *display_attrs; + int i; + + max_display_attrs = vaMaxNumDisplayAttributes(va_context->va_display); + display_attrs = calloc(max_display_attrs, sizeof(*display_attrs)); + + if (display_attrs) { + num_display_attrs = 0; + vaStatus = vaQueryDisplayAttributes(va_context->va_display, + display_attrs, &num_display_attrs); + if(vaapi_check_status(this_gen, vaStatus, "vaQueryDisplayAttributes()")) { + for (i = 0; i < num_display_attrs; i++) { + switch (display_attrs[i].type) { + case VADisplayAttribBrightness: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_BRIGHTNESS; + vaapi_check_capability(this, VO_PROP_BRIGHTNESS, display_attrs[i], "video.output.vaapi_brightness", "Brightness setting", "Brightness setting"); + } + break; + case VADisplayAttribContrast: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_CONTRAST; + vaapi_check_capability(this, VO_PROP_CONTRAST, display_attrs[i], "video.output.vaapi_contrast", "Contrast setting", "Contrast setting"); + } + break; + case VADisplayAttribHue: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_HUE; + vaapi_check_capability(this, VO_PROP_HUE, display_attrs[i], "video.output.vaapi_hue", "Hue setting", "Hue setting"); + } + break; + case VADisplayAttribSaturation: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_SATURATION; + vaapi_check_capability(this, VO_PROP_SATURATION, display_attrs[i], "video.output.vaapi_saturation", "Saturation setting", "Saturation setting"); + } + break; + default: + break; + } + } + } + free(display_attrs); + } + vaapi_show_display_props(this_gen); +} + +static void vaapi_set_background_color(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context->valid_context) + return; + + VADisplayAttribute attr; + memset( &attr, 0, sizeof(attr) ); + + attr.type = VADisplayAttribBackgroundColor; + attr.value = 0x000000; + + vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + vaapi_check_status(this_gen, vaStatus, "vaSetDisplayAttributes()"); +} + +static VAStatus vaapi_destroy_render_surfaces(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + VAStatus vaStatus; + + for(i = 0; i < RENDER_SURFACES; i++) { + if(va_surface_ids[i] != VA_INVALID_SURFACE) { + vaStatus = vaSyncSurface(va_context->va_display, va_surface_ids[i]); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + vaStatus = vaDestroySurfaces(va_context->va_display, &va_surface_ids[i], 1); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaces()"); + va_surface_ids[i] = VA_INVALID_SURFACE; + + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = va_surface_ids[i]; + } + } + + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_destroy_soft_surfaces(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + VAStatus vaStatus; + + + for(i = 0; i < SOFT_SURFACES; i++) { + if(va_soft_images[i].image_id != VA_INVALID_ID) + vaapi_destroy_image((vo_driver_t *)this, &va_soft_images[i]); + va_soft_images[i].image_id = VA_INVALID_ID; + + if(va_soft_surface_ids[i] != VA_INVALID_SURFACE) { +#ifdef DEBUG_SURFACE + printf("vaapi_close destroy render surface 0x%08x\n", va_soft_surface_ids[i]); +#endif + vaStatus = vaSyncSurface(va_context->va_display, va_soft_surface_ids[i]); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + vaStatus = vaDestroySurfaces(va_context->va_display, &va_soft_surface_ids[i], 1); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaces()"); + va_soft_surface_ids[i] = VA_INVALID_SURFACE; + } + } + + va_context->sw_width = 0; + va_context->sw_height = 0; + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_init_soft_surfaces(vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + int i; + + vaapi_destroy_soft_surfaces(this_gen); + + vaStatus = vaCreateSurfaces(va_context->va_display, width, height, VA_RT_FORMAT_YUV420, SOFT_SURFACES, va_soft_surface_ids); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaces()")) + goto error; + + /* allocate software surfaces */ + for(i = 0; i < SOFT_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + + vaStatus = vaapi_create_image((vo_driver_t *)this, va_soft_surface_ids[i], &va_soft_images[i], width, height, 1); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_create_image()")) { + va_soft_images[i].image_id = VA_INVALID_ID; + goto error; + } + + va_surface->index = i; + + vaStatus = vaPutImage(va_context->va_display, va_soft_surface_ids[i], va_soft_images[i].image_id, + 0, 0, va_soft_images[i].width, va_soft_images[i].height, + 0, 0, va_soft_images[i].width, va_soft_images[i].height); + vaapi_check_status(this_gen, vaStatus, "vaPutImage()"); +#ifdef DEBUG_SURFACE + printf("vaapi_init_soft_surfaces 0x%08x\n", va_soft_surface_ids[i]); +#endif + } + + va_context->sw_width = width; + va_context->sw_height = height; + return VA_STATUS_SUCCESS; + +error: + va_context->sw_width = 0; + va_context->sw_height = 0; + vaapi_destroy_soft_surfaces(this_gen); + return VA_STATUS_ERROR_UNKNOWN; +} + +static VAStatus vaapi_init_internal(vo_driver_t *this_gen, int va_profile, int width, int height, int softrender) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAConfigAttrib va_attrib; + int maj, min, i; + VAStatus vaStatus; + + vaapi_close(this_gen); + vaapi_init_va_context(this); + + this->va_context->va_display = vaapi_get_display(this->display, this->opengl_render); + + if(!this->va_context->va_display) + goto error; + + vaStatus = vaInitialize(this->va_context->va_display, &maj, &min); + if(!vaapi_check_status((vo_driver_t *)this, vaStatus, "vaInitialize()")) + goto error; + + lprintf("libva: %d.%d\n", maj, min); + + va_context->valid_context = 1; + + int fmt_count = 0; + fmt_count = vaMaxNumImageFormats( va_context->va_display ); + va_context->va_image_formats = calloc( fmt_count, sizeof(*va_context->va_image_formats) ); + + vaStatus = vaQueryImageFormats(va_context->va_display, va_context->va_image_formats, &va_context->va_num_image_formats); + if(!vaapi_check_status(this_gen, vaStatus, "vaQueryImageFormats()")) + goto error; + + fmt_count = vaMaxNumSubpictureFormats( va_context->va_display ); + va_context->va_subpic_formats = calloc( fmt_count, sizeof(*va_context->va_subpic_formats) ); + + vaStatus = vaQuerySubpictureFormats( va_context->va_display , va_context->va_subpic_formats, 0, &va_context->va_num_subpic_formats ); + if(!vaapi_check_status(this_gen, vaStatus, "vaQuerySubpictureFormats()")) + goto error; + + const char *vendor = vaQueryVendorString(va_context->va_display); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Vendor : %s\n", vendor); + + this->query_va_status = 1; + char *p = (char *)vendor; + for(i = 0; i < strlen(vendor); i++, p++) { + if(strncmp(p, "VDPAU", strlen("VDPAU")) == 0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Enable Splitted-Desktop Systems VDPAU-VIDEO workarounds.\n"); + this->query_va_status = 0; + this->opengl_use_tfp = 0; + break; + } + } + + vaapi_set_background_color(this_gen); + vaapi_display_attribs((vo_driver_t *)this); + + va_context->width = width; + va_context->height = height; + va_context->va_profile = va_profile; + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : Context width %d height %d\n", va_context->width, va_context->height); + + /* allocate decoding surfaces */ + vaStatus = vaCreateSurfaces(va_context->va_display, va_context->width, va_context->height, VA_RT_FORMAT_YUV420, RENDER_SURFACES, va_surface_ids); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaces()")) + goto error; + + /* hardware decoding needs more setup */ + if(!softrender && va_profile >= 0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : Profile: %d (%s) Entrypoint %d (%s) Surfaces %d\n", va_context->va_profile, vaapi_profile_to_string(va_context->va_profile), VAEntrypointVLD, vaapi_entrypoint_to_string(VAEntrypointVLD), RENDER_SURFACES); + + memset( &va_attrib, 0, sizeof(va_attrib) ); + va_attrib.type = VAConfigAttribRTFormat; + + vaStatus = vaGetConfigAttributes(va_context->va_display, va_context->va_profile, VAEntrypointVLD, &va_attrib, 1); + if(!vaapi_check_status(this_gen, vaStatus, "vaGetConfigAttributes()")) + goto error; + + if( (va_attrib.value & VA_RT_FORMAT_YUV420) == 0 ) + goto error; + + vaStatus = vaCreateConfig(va_context->va_display, va_context->va_profile, VAEntrypointVLD, &va_attrib, 1, &va_context->va_config_id); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateConfig()")) { + va_context->va_config_id = VA_INVALID_ID; + goto error; + } + + vaStatus = vaCreateContext(va_context->va_display, va_context->va_config_id, va_context->width, va_context->height, + VA_PROGRESSIVE, va_surface_ids, RENDER_SURFACES, &va_context->va_context_id); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateContext()")) { + va_context->va_context_id = VA_INVALID_ID; + goto error; + } + } + + /* xine was told to allocate RENDER_SURFACES frames. assign the frames the rendering surfaces. */ + for(i = 0; i < RENDER_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = va_surface_ids[i]; + + if(this->frames[i]) { + vaapi_frame_t *frame = this->frames[i]; + frame->vaapi_accel_data.index = i; + + VAImage va_image; + vaStatus = vaapi_create_image(va_context->driver, va_surface_ids[i], &va_image, width, height, 1); + if(vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + vaStatus = vaPutImage(va_context->va_display, va_surface_ids[i], va_image.image_id, + 0, 0, va_image.width, va_image.height, + 0, 0, va_image.width, va_image.height); + vaapi_destroy_image(va_context->driver, &va_image); + } + } +#ifdef DEBUG_SURFACE + printf("vaapi_init_internal 0x%08x\n", va_surface_ids[i]); +#endif + } + + vaStatus = vaapi_init_soft_surfaces(this_gen, width, height); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_init_soft_surfaces()")) { + vaapi_destroy_soft_surfaces(this_gen); + goto error; + } + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : guarded render : %d\n", this->guarded_render); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : glxrender : %d\n", this->opengl_render); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : glxrender tfp : %d\n", this->opengl_use_tfp); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : is_bound : %d\n", va_context->is_bound); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : scaling level : name %s value 0x%08x\n", scaling_level_enum_names[this->scaling_level_enum], this->scaling_level); + + this->init_opengl_render = 1; + + return VA_STATUS_SUCCESS; + +error: + vaapi_close(this_gen); + vaapi_init_va_context(this); + va_context->valid_context = 0; + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : error init vaapi\n"); + + return VA_STATUS_ERROR_UNKNOWN; +} + +/* + * Init VAAPI. This function is called from the decoder side. + * When the decoder uses software decoding vaapi_init is not called. + * Therefore we do it in vaapi_display_frame to get a valid VAAPI context + */ +static VAStatus vaapi_init(vo_frame_t *frame_gen, int va_profile, int width, int height, int softrender) { + if(!frame_gen) + return VA_STATUS_ERROR_UNKNOWN; + + vo_driver_t *this_gen = (vo_driver_t *) frame_gen->driver; + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + VAStatus vaStatus; + + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + if(!this->guarded_render) { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + } + + vaStatus = vaapi_init_internal(this_gen, va_profile, width, height, softrender); + + if(!this->guarded_render) { + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + } + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + return vaStatus; +} + +static void vaapi_frame_proc_slice (vo_frame_t *vo_img, uint8_t **src) +{ + vo_img->proc_called = 1; +} + +static void vaapi_frame_field (vo_frame_t *vo_img, int which_field) +{ +} + +static void vaapi_frame_dispose (vo_frame_t *vo_img) { + vaapi_driver_t *this = (vaapi_driver_t *) vo_img->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) vo_img ; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + + lprintf("vaapi_frame_dispose\n"); + + av_free (frame->vo_frame.base[0]); + av_free (frame->vo_frame.base[1]); + av_free (frame->vo_frame.base[2]); + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + va_surface->status = SURFACE_FREE; + } + + free (frame); +} + +static vo_frame_t *vaapi_alloc_frame (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame; + + frame = (vaapi_frame_t *) calloc(1, sizeof(vaapi_frame_t)); + + if (!frame) + return NULL; + + this->frames[this->num_frame_buffers++] = frame; + + frame->vo_frame.base[0] = frame->vo_frame.base[1] = frame->vo_frame.base[2] = NULL; + frame->width = frame->height = frame->format = frame->flags = 0; + + frame->vo_frame.accel_data = &frame->vaapi_accel_data; + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + + /* + * supply required functions + */ + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + frame->vo_frame.proc_slice = vaapi_frame_proc_slice; + frame->vo_frame.proc_frame = NULL; + frame->vo_frame.field = vaapi_frame_field; + frame->vo_frame.dispose = vaapi_frame_dispose; + frame->vo_frame.driver = this_gen; + + frame->vaapi_accel_data.vo_frame = &frame->vo_frame; + frame->vaapi_accel_data.vaapi_init = &vaapi_init; + frame->vaapi_accel_data.profile_from_imgfmt = &profile_from_imgfmt; + frame->vaapi_accel_data.get_context = &get_context; + +#if AVVIDEO > 1 + frame->vaapi_accel_data.avcodec_decode_video2 = &guarded_avcodec_decode_video2; +#else + frame->vaapi_accel_data.avcodec_decode_video = &guarded_avcodec_decode_video; +#endif + + frame->vaapi_accel_data.get_vaapi_surface = &get_vaapi_surface; + frame->vaapi_accel_data.render_vaapi_surface = &render_vaapi_surface; + frame->vaapi_accel_data.release_vaapi_surface = &release_vaapi_surface; + frame->vaapi_accel_data.guarded_render = &guarded_render; + + lprintf("alloc frame\n"); + + return (vo_frame_t *) frame; +} + + +/* Display OSD */ +static int vaapi_ovl_associate(vo_driver_t *this_gen, int format, int bShow) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context->valid_context) + return 0; + + if(va_context->last_sub_image_fmt && !bShow) { + if(va_context->va_subpic_id != VA_INVALID_ID) { + if(va_context->last_sub_image_fmt == XINE_IMGFMT_VAAPI) { + vaStatus = vaDeassociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_surface_ids, RENDER_SURFACES); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } else if(va_context->last_sub_image_fmt == XINE_IMGFMT_YV12 || + va_context->last_sub_image_fmt == XINE_IMGFMT_YUY2) { + vaStatus = vaDeassociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_soft_surface_ids, SOFT_SURFACES); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } + } + va_context->last_sub_image_fmt = 0; + return 1; + } + + if(!va_context->last_sub_image_fmt && bShow) { + unsigned int flags = 0; + unsigned int output_width = va_context->width; + unsigned int output_height = va_context->height; + void *p_base = NULL; + + VAStatus vaStatus; + + vaapi_destroy_subpicture(this_gen); + vaStatus = vaapi_create_subpicture(this_gen, this->overlay_bitmap_width, this->overlay_bitmap_height); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_create_subpicture()")) + return 0; + + vaStatus = vaMapBuffer(va_context->va_display, va_context->va_subpic_image.buf, &p_base); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + return 0; + + xine_fast_memcpy((uint32_t *)p_base, this->overlay_bitmap, this->overlay_bitmap_width * this->overlay_bitmap_height * sizeof(uint32_t)); + + vaStatus = vaUnmapBuffer(va_context->va_display, va_context->va_subpic_image.buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + lprintf( "vaapi_ovl_associate: overlay_width=%d overlay_height=%d unscaled %d va_subpic_id 0x%08x ovl_changed %d has_overlay %d bShow %d overlay_bitmap_width %d overlay_bitmap_height %d va_context->width %d va_context->height %d\n", + this->overlay_output_width, this->overlay_output_height, this->has_overlay, + va_context->va_subpic_id, this->ovl_changed, this->has_overlay, bShow, + this->overlay_bitmap_width, this->overlay_bitmap_height, + va_context->width, va_context->height); + + if(format == XINE_IMGFMT_VAAPI) { + lprintf("vaapi_ovl_associate hw\n"); + vaStatus = vaAssociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_surface_ids, RENDER_SURFACES, + 0, 0, va_context->va_subpic_image.width, va_context->va_subpic_image.height, + 0, 0, output_width, output_height, flags); + } else { + lprintf("vaapi_ovl_associate sw\n"); + vaStatus = vaAssociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_soft_surface_ids, SOFT_SURFACES, + 0, 0, va_context->va_subpic_image.width, va_context->va_subpic_image.height, + 0, 0, va_soft_images[0].width, va_soft_images[0].height, flags); + } + + if(vaapi_check_status(this_gen, vaStatus, "vaAssociateSubpicture()")) { + va_context->last_sub_image_fmt = format; + return 1; + } + } + return 0; +} + +static void vaapi_overlay_clut_yuv2rgb(vaapi_driver_t *this, vo_overlay_t *overlay, vaapi_frame_t *frame) +{ + int i; + clut_t* clut = (clut_t*) overlay->color; + + if (!overlay->rgb_clut) { + for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++ ) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->rgb_clut++; + } + if (!overlay->hili_rgb_clut) { + clut = (clut_t*) overlay->hili_color; + for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->hili_rgb_clut++; + } +} + +static void vaapi_overlay_begin (vo_driver_t *this_gen, + vo_frame_t *frame_gen, int changed) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if ( !changed ) + return; + + this->has_overlay = 0; + ++this->ovl_changed; + + /* Apply OSD layer. */ + if(va_context->valid_context) { + lprintf("vaapi_overlay_begin chaned %d\n", changed); + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + } +} + +static void vaapi_overlay_blend (vo_driver_t *this_gen, + vo_frame_t *frame_gen, vo_overlay_t *overlay) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + int i = this->ovl_changed; + + if (!i) + return; + + if (--i >= XINE_VORAW_MAX_OVL) + return; + + if (overlay->width <= 0 || overlay->height <= 0 || (!overlay->rle && (!overlay->argb_layer || !overlay->argb_layer->buffer))) + return; + + if (overlay->rle) + lprintf("overlay[%d] rle %s%s %dx%d@%d,%d hili rect %d,%d-%d,%d\n", i, + overlay->unscaled ? " unscaled ": " scaled ", + (overlay->rgb_clut > 0 || overlay->hili_rgb_clut > 0) ? " rgb ": " ycbcr ", + overlay->width, overlay->height, overlay->x, overlay->y, + overlay->hili_left, overlay->hili_top, + overlay->hili_right, overlay->hili_bottom); + if (overlay->argb_layer && overlay->argb_layer->buffer) + lprintf("overlay[%d] argb %s %dx%d@%d,%d dirty rect %d,%d-%d,%d\n", i, + overlay->unscaled ? " unscaled ": " scaled ", + overlay->width, overlay->height, overlay->x, overlay->y, + overlay->argb_layer->x1, overlay->argb_layer->y1, + overlay->argb_layer->x2, overlay->argb_layer->y2); + + + this->overlays[i] = overlay; + + ++this->ovl_changed; +} + +static void vaapi_overlay_end (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int novls = this->ovl_changed; + if (novls < 2) { + this->ovl_changed = 0; + return; + } + --novls; + + uint32_t output_width = frame->width; + uint32_t output_height = frame->height; + uint32_t unscaled_width = 0, unscaled_height = 0; + vo_overlay_t *first_scaled = NULL, *first_unscaled = NULL; + vaapi_rect_t dirty_rect, unscaled_dirty_rect; + int has_rle = 0; + + int i; + for (i = 0; i < novls; ++i) { + vo_overlay_t *ovl = this->overlays[i]; + + if (ovl->rle) + has_rle = 1; + + if (ovl->unscaled) { + if (first_unscaled) { + if (ovl->x < unscaled_dirty_rect.x1) + unscaled_dirty_rect.x1 = ovl->x; + if (ovl->y < unscaled_dirty_rect.y1) + unscaled_dirty_rect.y1 = ovl->y; + if ((ovl->x + ovl->width) > unscaled_dirty_rect.x2) + unscaled_dirty_rect.x2 = ovl->x + ovl->width; + if ((ovl->y + ovl->height) > unscaled_dirty_rect.y2) + unscaled_dirty_rect.y2 = ovl->y + ovl->height; + } else { + first_unscaled = ovl; + unscaled_dirty_rect.x1 = ovl->x; + unscaled_dirty_rect.y1 = ovl->y; + unscaled_dirty_rect.x2 = ovl->x + ovl->width; + unscaled_dirty_rect.y2 = ovl->y + ovl->height; + } + + unscaled_width = unscaled_dirty_rect.x2; + unscaled_height = unscaled_dirty_rect.y2; + } else { + if (first_scaled) { + if (ovl->x < dirty_rect.x1) + dirty_rect.x1 = ovl->x; + if (ovl->y < dirty_rect.y1) + dirty_rect.y1 = ovl->y; + if ((ovl->x + ovl->width) > dirty_rect.x2) + dirty_rect.x2 = ovl->x + ovl->width; + if ((ovl->y + ovl->height) > dirty_rect.y2) + dirty_rect.y2 = ovl->y + ovl->height; + } else { + first_scaled = ovl; + dirty_rect.x1 = ovl->x; + dirty_rect.y1 = ovl->y; + dirty_rect.x2 = ovl->x + ovl->width; + dirty_rect.y2 = ovl->y + ovl->height; + } + + if (dirty_rect.x2 > output_width) + output_width = dirty_rect.x2; + if (dirty_rect.y2 > output_height) + output_height = dirty_rect.y2; + + } + } + + int need_init = 0; + + lprintf("dirty_rect.x0 %d dirty_rect.y0 %d dirty_rect.x2 %d dirty_rect.y2 %d output_width %d output_height %d\n", + dirty_rect.x0, dirty_rect.y0, dirty_rect.x2, dirty_rect.y2, output_width, output_height); + + if (first_scaled) { + vaapi_rect_t dest; + dest.x1 = first_scaled->x; + dest.y1 = first_scaled->y; + dest.x2 = first_scaled->x + first_scaled->width; + dest.y2 = first_scaled->y + first_scaled->height; + if (!RECT_IS_EQ(dest, dirty_rect)) + need_init = 1; + } + + int need_unscaled_init = (first_unscaled && + (first_unscaled->x != unscaled_dirty_rect.x1 || + first_unscaled->y != unscaled_dirty_rect.y1 || + (first_unscaled->x + first_unscaled->width) != unscaled_dirty_rect.x2 || + (first_unscaled->y + first_unscaled->height) != unscaled_dirty_rect.y2)); + + if (first_scaled) { + this->overlay_output_width = output_width; + this->overlay_output_height = output_height; + + need_init = 1; + + this->overlay_dirty_rect = dirty_rect; + } + + if (first_unscaled) { + this->overlay_unscaled_width = unscaled_width; + this->overlay_unscaled_height = unscaled_height; + + need_unscaled_init = 1; + this->overlay_unscaled_dirty_rect = unscaled_dirty_rect; + } + + if (has_rle || need_init || need_unscaled_init) { + lprintf("has_rle %d need_init %d need_unscaled_init %d unscaled_width %d unscaled_height %d output_width %d output_height %d\n", + has_rle, need_init, need_unscaled_init, unscaled_width, unscaled_height, output_width, output_height); + if (need_init) { + this->overlay_bitmap_width = output_width; + this->overlay_bitmap_height = output_height; + } + if (need_unscaled_init) { + + if(this->vdr_osd_width) + this->overlay_bitmap_width = (this->vdr_osd_width > this->sc.gui_width) ? this->vdr_osd_width : this->sc.gui_width; + else + this->overlay_bitmap_width = (unscaled_width > this->sc.gui_width) ? unscaled_width : this->sc.gui_width; + + if(this->vdr_osd_height) + this->overlay_bitmap_height = (this->vdr_osd_height > this->sc.gui_height) ? this->vdr_osd_height : this->sc.gui_height; + else + this->overlay_bitmap_height = (unscaled_height > this->sc.gui_height) ? unscaled_height : this->sc.gui_height; + + } else if (need_init) { + + if(this->vdr_osd_width) + this->overlay_bitmap_width = (this->vdr_osd_width > this->sc.gui_width) ? this->vdr_osd_width : this->sc.gui_width; + else + this->overlay_bitmap_width = (output_width > this->sc.gui_width) ? output_width : this->sc.gui_width; + + if(this->vdr_osd_height) + this->overlay_bitmap_height = (this->vdr_osd_height > this->sc.gui_height) ? this->vdr_osd_height : this->sc.gui_height; + else + this->overlay_bitmap_height = (output_height > this->sc.gui_height) ? output_height : this->sc.gui_height; + + } + } + + if ((this->overlay_bitmap_width * this->overlay_bitmap_height) > this->overlay_bitmap_size) { + this->overlay_bitmap_size = this->overlay_bitmap_width * this->overlay_bitmap_height; + free(this->overlay_bitmap); + this->overlay_bitmap = calloc( this->overlay_bitmap_size, sizeof(uint32_t)); + } else { + memset(this->overlay_bitmap, 0x0, this->overlay_bitmap_size * sizeof(uint32_t)); + } + + for (i = 0; i < novls; ++i) { + vo_overlay_t *ovl = this->overlays[i]; + uint32_t *bitmap = NULL; + uint32_t *rgba = NULL; + + if (ovl->rle) { + if(ovl->width<=0 || ovl->height<=0) + continue; + + if (!ovl->rgb_clut || !ovl->hili_rgb_clut) + vaapi_overlay_clut_yuv2rgb (this, ovl, frame); + + bitmap = rgba = calloc(ovl->width * ovl->height * 4, sizeof(uint32_t)); + + int num_rle = ovl->num_rle; + rle_elem_t *rle = ovl->rle; + uint32_t red, green, blue, alpha; + clut_t *low_colors = (clut_t*)ovl->color; + clut_t *hili_colors = (clut_t*)ovl->hili_color; + uint8_t *low_trans = ovl->trans; + uint8_t *hili_trans = ovl->hili_trans; + clut_t *colors; + uint8_t *trans; + int rlelen = 0; + uint8_t clr = 0; + int i, pos=0, x, y; + + while (num_rle > 0) { + x = pos % ovl->width; + y = pos / ovl->width; + + if ( (x>=ovl->hili_left && x<=ovl->hili_right) && (y>=ovl->hili_top && y<=ovl->hili_bottom) ) { + colors = hili_colors; + trans = hili_trans; + } + else { + colors = low_colors; + trans = low_trans; + } + rlelen = rle->len; + clr = rle->color; + for ( i=0; i<rlelen; ++i ) { + if ( trans[clr] == 0 ) { + alpha = red = green = blue = 0; + } + else { + red = colors[clr].y; // red + green = colors[clr].cr; // green + blue = colors[clr].cb; // blue + alpha = trans[clr]*255/15; + } + *rgba = (alpha<<24) | (red<<16) | (green<<8) | blue; + rgba++; + ++pos; + } + ++rle; + --num_rle; + } + lprintf("width %d height %d pos %d %d\n", ovl->width, ovl->height, pos, ovl->width * ovl->height); + } else { + pthread_mutex_lock(&ovl->argb_layer->mutex); + bitmap = ovl->argb_layer->buffer; + } + + /* Blit overlay to destination */ + uint32_t pitch = ovl->width * sizeof(uint32_t); + uint32_t *copy_dst = this->overlay_bitmap; + uint32_t *copy_src = NULL; + uint32_t height = 0; + + copy_src = bitmap; + + copy_dst += ovl->y * this->overlay_bitmap_width; + + lprintf("overlay_bitmap_width %d overlay_bitmap_height %d ovl->x %d ovl->y %d ovl->width %d ovl->height %d width %d height %d\n", + this->overlay_bitmap_width, this->overlay_bitmap_height, ovl->x, ovl->y, ovl->width, ovl->height, this->overlay_bitmap_width, this->overlay_bitmap_height); + + for(height = 0; height < ovl->height; height++) { + if((height + ovl->y) >= this->overlay_bitmap_height) + break; + + xine_fast_memcpy(copy_dst + ovl->x, copy_src, pitch); + copy_dst += this->overlay_bitmap_width; + copy_src += ovl->width; + } + + if (ovl->rle) { + if(bitmap) { + free(bitmap); + bitmap = NULL; + } + } + + if (!ovl->rle) + pthread_mutex_unlock(&ovl->argb_layer->mutex); + + } + + this->ovl_changed = 0; + this->has_overlay = (first_scaled != NULL) | (first_unscaled != NULL); + + lprintf("this->has_overlay %d\n", this->has_overlay); + /* Apply OSD layer. */ + if(va_context->valid_context) { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + } +} + +static void vaapi_resize_glx_window (vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if(this->valid_opengl_context) { + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(FOVY, ASPECT, Z_NEAR, Z_FAR); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(-0.5f, -0.5f, -Z_CAMERA); + glScalef(1.0f / (GLfloat)width, + -1.0f / (GLfloat)height, + 1.0f / (GLfloat)width); + glTranslatef(0.0f, -1.0f * (GLfloat)height, 0.0f); + } +} + +static int vaapi_redraw_needed (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + int ret = 0; + + _x_vo_scale_compute_ideal_size( &this->sc ); + + if ( _x_vo_scale_redraw_needed( &this->sc ) ) { + _x_vo_scale_compute_output_size( &this->sc ); + + XMoveResizeWindow(this->display, this->window, + 0, 0, this->sc.gui_width, this->sc.gui_height); + + ret = 1; + } + + return ret; +} + +static void vaapi_provide_standard_frame_data (vo_frame_t *orig, xine_current_frame_data_t *data) +{ + vaapi_driver_t *driver = (vaapi_driver_t *) orig->driver; + ff_vaapi_context_t *va_context = driver->va_context; + + vaapi_accel_t *accel = (vaapi_accel_t *) orig->accel_data; + vo_frame_t *this = accel->vo_frame; + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + uint32_t pitches[3]; + uint8_t *base[3]; + + if(driver == NULL) { + return; + } + + if (this->format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_provide_standard_frame_data: unexpected frame format 0x%08x!\n", this->format); + return; + } + + if( !accel || va_surface->va_surface_id == VA_INVALID_SURFACE ) + return; + + lprintf("vaapi_provide_standard_frame_data %s 0x%08x width %d height %d\n", + (this->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((this->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + va_surface->va_surface_id, this->width, this->height); + + pthread_mutex_lock(&driver->vaapi_lock); + XLockDisplay(driver->display); + + int width = va_context->width; + int height = va_context->height; + + data->format = XINE_IMGFMT_YV12; + data->img_size = width * height + + ((width + 1) / 2) * ((height + 1) / 2) + + ((width + 1) / 2) * ((height + 1) / 2); + if (data->img) { + pitches[0] = width; + pitches[2] = width / 2; + pitches[1] = width / 2; + base[0] = data->img; + base[2] = data->img + width * height; + base[1] = data->img + width * height + width * this->height / 4; + + VAImage va_image; + VAStatus vaStatus; + void *p_base; + + vaStatus = vaSyncSurface(va_context->va_display, va_surface->va_surface_id); + vaapi_check_status(va_context->driver, vaStatus, "vaSyncSurface()"); + + VASurfaceStatus surf_status = 0; + + if(driver->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface->va_surface_id, &surf_status); + vaapi_check_status(va_context->driver, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status != VASurfaceReady) + goto error; + + vaStatus = vaapi_create_image(va_context->driver, va_surface->va_surface_id, &va_image, width, height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) + goto error; + + lprintf("vaapi_provide_standard_frame_data accel->va_surface_id 0x%08x va_image.image_id 0x%08x va_context->width %d va_context->height %d va_image.width %d va_image.height %d width %d height %d size1 %d size2 %d %d %d %d status %d num_planes %d\n", + va_surface->va_surface_id, va_image.image_id, va_context->width, va_context->height, va_image.width, va_image.height, width, height, va_image.data_size, data->img_size, + va_image.pitches[0], va_image.pitches[1], va_image.pitches[2], surf_status, va_image.num_planes); + + if(va_image.image_id == VA_INVALID_ID) + goto error; + + if(!va_context->is_bound) { + vaStatus = vaGetImage(va_context->va_display, va_surface->va_surface_id, 0, 0, + va_image.width, va_image.height, va_image.image_id); + } else { + vaStatus = VA_STATUS_SUCCESS; + } + + if(vaapi_check_status(va_context->driver, vaStatus, "vaGetImage()")) { + vaStatus = vaMapBuffer( va_context->va_display, va_image.buf, &p_base ) ; + if(vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) { + + /* + uint8_t *src[3] = { NULL, }; + src[0] = (uint8_t *)p_base + va_image.offsets[0]; + src[1] = (uint8_t *)p_base + va_image.offsets[1]; + src[2] = (uint8_t *)p_base + va_image.offsets[2]; + */ + + if( va_image.format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image.format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("VAAPI YV12 image\n"); + + yv12_to_yv12( + (uint8_t*)p_base + va_image.offsets[0], va_image.pitches[0], + base[0], pitches[0], + (uint8_t*)p_base + va_image.offsets[1], va_image.pitches[1], + base[1], pitches[1], + (uint8_t*)p_base + va_image.offsets[2], va_image.pitches[2], + base[2], pitches[2], + va_image.width, va_image.height); + + } else if( va_image.format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) ) { + lprintf("VAAPI NV12 image\n"); + + lprintf("va_image.offsets[0] %d va_image.offsets[1] %d va_image.offsets[2] %d size %d size %d size %d width %d height %d width %d height %d\n", + va_image.offsets[0], va_image.offsets[1], va_image.offsets[2], va_image.data_size, va_image.width * va_image.height, + data->img_size, width, height, va_image.width, va_image.height); + + base[0] = data->img; + base[1] = data->img + width * height; + base[2] = data->img + width * height + width * height / 4; + + nv12_to_yv12((uint8_t *)p_base + va_image.offsets[0], va_image.pitches[0], + (uint8_t *)p_base + va_image.offsets[1], va_image.pitches[1], + base[0], pitches[0], + base[1], pitches[1], + base[2], pitches[2], + va_image.width, va_image.height, + width, height, + va_image.data_size); + + } else { + printf("vaapi_provide_standard_frame_data unsupported image format\n"); + } + + vaStatus = vaUnmapBuffer(va_context->va_display, va_image.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + vaapi_destroy_image(va_context->driver, &va_image); + } + } + } + +error: + XUnlockDisplay(driver->display); + pthread_mutex_unlock(&driver->vaapi_lock); +} + +static void vaapi_duplicate_frame_data (vo_frame_t *this_gen, vo_frame_t *original) +{ + vaapi_driver_t *driver = (vaapi_driver_t *) original->driver; + ff_vaapi_context_t *va_context = driver->va_context; + + vaapi_frame_t *this = (vaapi_frame_t *)this_gen; + vaapi_frame_t *orig = (vaapi_frame_t *)original; + + vaapi_accel_t *accel_this = &this->vaapi_accel_data; + vaapi_accel_t *accel_orig = &orig->vaapi_accel_data; + + ff_vaapi_surface_t *va_surface_this = &va_render_surfaces[accel_this->index]; + ff_vaapi_surface_t *va_surface_orig = &va_render_surfaces[accel_orig->index]; + + lprintf("vaapi_duplicate_frame_data %s %s 0x%08x 0x%08x\n", + (this_gen->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((this_gen->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + (original->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((original->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + va_surface_this->va_surface_id, va_surface_orig->va_surface_id); + + if (orig->vo_frame.format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_duplicate_frame_data: unexpected frame format 0x%08x!\n", orig->format); + return; + } + + if (this->vo_frame.format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_duplicate_frame_data: unexpected frame format 0x%08x!\n", this->format); + return; + } + + pthread_mutex_lock(&driver->vaapi_lock); + XLockDisplay(driver->display); + + VAImage va_image_orig; + VAImage va_image_this; + VAStatus vaStatus; + void *p_base_orig = NULL; + void *p_base_this = NULL; + + vaStatus = vaSyncSurface(va_context->va_display, va_surface_orig->va_surface_id); + vaapi_check_status(va_context->driver, vaStatus, "vaSyncSurface()"); + + int this_width = va_context->width; + int this_height = va_context->height; + int orig_width = va_context->width; + int orig_height = va_context->height; + + vaStatus = vaapi_create_image(va_context->driver, va_surface_orig->va_surface_id, &va_image_orig, orig_width, orig_height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + va_image_orig.image_id = VA_INVALID_ID; + goto error; + } + + vaStatus = vaapi_create_image(va_context->driver, va_surface_this->va_surface_id, &va_image_this, this_width, this_height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + va_image_this.image_id = VA_INVALID_ID; + goto error; + } + + if(va_image_orig.image_id == VA_INVALID_ID || va_image_this.image_id == VA_INVALID_ID) { + printf("vaapi_duplicate_frame_data invalid image\n"); + goto error; + } + + lprintf("vaapi_duplicate_frame_data va_image_orig.image_id 0x%08x va_image_orig.width %d va_image_orig.height %d width %d height %d size %d %d %d %d\n", + va_image_orig.image_id, va_image_orig.width, va_image_orig.height, this->width, this->height, va_image_orig.data_size, + va_image_orig.pitches[0], va_image_orig.pitches[1], va_image_orig.pitches[2]); + + if(!va_context->is_bound) { + vaStatus = vaGetImage(va_context->va_display, va_surface_orig->va_surface_id, 0, 0, + va_image_orig.width, va_image_orig.height, va_image_orig.image_id); + } else { + vaStatus = VA_STATUS_SUCCESS; + } + + if(vaapi_check_status(va_context->driver, vaStatus, "vaGetImage()")) { + + if(!va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_surface_this->va_surface_id, va_image_orig.image_id, + 0, 0, va_image_orig.width, va_image_orig.height, + 0, 0, va_image_this.width, va_image_this.height); + vaapi_check_status(va_context->driver, vaStatus, "vaPutImage()"); + } else { + vaStatus = vaMapBuffer( va_context->va_display, va_image_orig.buf, &p_base_orig ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + goto error; + + vaStatus = vaMapBuffer( va_context->va_display, va_image_this.buf, &p_base_this ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + goto error; + + int size = (va_image_orig.data_size > va_image_this.data_size) ? va_image_this.data_size : va_image_orig.data_size; + xine_fast_memcpy((uint8_t *) p_base_this, (uint8_t *) p_base_orig, size); + + } + } + +error: + if(p_base_orig) { + vaStatus = vaUnmapBuffer(va_context->va_display, va_image_orig.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + } + if(p_base_this) { + vaStatus = vaUnmapBuffer(va_context->va_display, va_image_this.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + } + + vaapi_destroy_image(va_context->driver, &va_image_orig); + vaapi_destroy_image(va_context->driver, &va_image_this); + + XUnlockDisplay(driver->display); + pthread_mutex_unlock(&driver->vaapi_lock); +} + +static void vaapi_update_frame_format (vo_driver_t *this_gen, + vo_frame_t *frame_gen, + uint32_t width, uint32_t height, + double ratio, int format, int flags) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t*)frame_gen; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + + lprintf("vaapi_update_frame_format\n"); + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + lprintf("vaapi_update_frame_format %s %s width %d height %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + (format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + width, height); + + frame->vo_frame.width = width; + frame->vo_frame.height = height; + + if ((frame->width != width) + || (frame->height != height) + || (frame->format != format)) { + + // (re-) allocate render space + av_freep (&frame->vo_frame.base[0]); + av_freep (&frame->vo_frame.base[1]); + av_freep (&frame->vo_frame.base[2]); + + /* set init_vaapi on frame formats XINE_IMGFMT_YV12/XINE_IMGFMT_YUY2 only. + * for XINE_IMGFMT_VAAPI the init was already done. + */ + if (format == XINE_IMGFMT_YV12) { + frame->vo_frame.pitches[0] = 8*((width + 7) / 8); + frame->vo_frame.pitches[1] = 8*((width + 15) / 16); + frame->vo_frame.pitches[2] = 8*((width + 15) / 16); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.base[1] = av_mallocz (frame->vo_frame.pitches[1] * ((height+1)/2) + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.base[2] = av_mallocz (frame->vo_frame.pitches[2] * ((height+1)/2) + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + lprintf("XINE_IMGFMT_YV12 width %d height %d\n", width, height); + } else if (format == XINE_IMGFMT_YUY2){ + frame->vo_frame.pitches[0] = 8*((width + 3) / 4); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + lprintf("XINE_IMGFMT_YUY2 width %d height %d\n", width, height); + } else if (format == XINE_IMGFMT_VAAPI) { + frame->vo_frame.proc_duplicate_frame_data = vaapi_duplicate_frame_data; + frame->vo_frame.proc_provide_standard_frame_data = vaapi_provide_standard_frame_data; + lprintf("XINE_IMGFMT_VAAPI width %d height %d\n", width, height); + } + + frame->width = width; + frame->height = height; + frame->format = format; + frame->flags = flags; + vaapi_frame_field ((vo_frame_t *)frame, flags); + } + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + if(va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_update_frame_format 0x%08x\n", va_surface->va_surface_id); +#endif + } else if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RELEASE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_update_frame_format 0x%08x\n", va_surface->va_surface_id); +#endif + } + } + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + + frame->ratio = ratio; + frame->vo_frame.future_frame = NULL; +} + +static inline uint8_t clip_uint8_vlc( int32_t a ) +{ + if( a&(~255) ) return (-a)>>31; + else return a; +} + + +static void nv12_to_yv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *uv_src, int uv_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *u_dst, int u_dst_pitch, + uint8_t *v_dst, int v_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int src_data_size) { + + int y_src_size = src_height * y_src_pitch; + int y, x; + + int uv_src_size = src_height * uv_src_pitch / 2; + if((y_src_size + uv_src_size) != (src_data_size)) + printf("nv12_to_yv12 strange %d\n", (y_src_size + uv_src_size) - (src_data_size)); + + int height = (src_height > dst_height) ? dst_height : src_height; + int width = (src_width > dst_width) ? dst_width : src_width; + + for(y = 0; y < height; y++) { + xine_fast_memcpy(y_dst, y_src, width); + y_src += y_src_pitch; + y_dst += y_dst_pitch; + } + + for(y = 0; y < height; y++) { + const uint8_t *uv_src_tmp = uv_src; + for(x = 0; x < u_dst_pitch; x++) { + if(((y * uv_src_pitch) + x) < uv_src_size) { + *(u_dst + x) = *(uv_src_tmp ); + *(v_dst + x) = *(uv_src_tmp + 1); + } + uv_src_tmp += 2; + } + uv_src += uv_src_pitch; + u_dst += u_dst_pitch; + v_dst += v_dst_pitch; + } +} + +static void yv12_to_nv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *u_src, int u_src_pitch, + const uint8_t *v_src, int v_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *uv_dst, int uv_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int dst_data_size) { + + int y_dst_size = dst_height * y_dst_pitch; + int y, x; + + lprintf("yv12_to_nv12 converter\n"); + + int uv_dst_size = dst_height * uv_dst_pitch / 2; + if((y_dst_size + uv_dst_size) != (dst_data_size)) + printf("yv12_to_nv12 strange %d\n", (y_dst_size + uv_dst_size) - (dst_data_size)); + + int height = (src_height > dst_height) ? dst_height : src_height; + int width = (src_width > dst_width) ? dst_width : src_width; + + for(y = 0; y < height; y++) { + xine_fast_memcpy(y_dst, y_src, width); + y_src += y_src_pitch; + y_dst += y_dst_pitch; + } + + for(y = 0; y < height; y++) { + uint8_t *uv_dst_tmp = uv_dst; + for(x = 0; x < u_src_pitch; x++) { + if(((y * uv_dst_pitch) + x) < uv_dst_size) { + *(uv_dst_tmp ) = *(u_src + x); + *(uv_dst_tmp + 1) = *(v_src + x); + } + uv_dst_tmp += 2; + } + uv_dst += uv_dst_pitch; + u_src += u_src_pitch; + v_src += v_src_pitch; + } +} + +static VAStatus vaapi_software_render_frame(vo_driver_t *this_gen, vo_frame_t *frame_gen, + VAImage *va_image, VASurfaceID va_surface_id) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + void *p_base = NULL; + VAStatus vaStatus; + + if(va_image == NULL || va_image->image_id == VA_INVALID_ID || + va_surface_id == VA_INVALID_SURFACE || !va_context->valid_context) + return VA_STATUS_ERROR_UNKNOWN; + + lprintf("vaapi_software_render_frame : va_surface_id 0x%08x va_image.image_id 0x%08x width %d height %d f_width %d f_height %d sw_width %d sw_height %d\n", + va_surface_id, va_image->image_id, va_image->width, va_image->height, frame->width, frame->height, + va_context->sw_width, va_context->sw_height); + + if(frame->width != va_image->width || frame->height != va_image->height) + return VA_STATUS_SUCCESS; + + vaStatus = vaMapBuffer( va_context->va_display, va_image->buf, &p_base ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + return vaStatus; + + + uint8_t *dst[3] = { NULL, }; + uint32_t pitches[3]; + + if(this->swap_uv_planes) { + dst[0] = (uint8_t *)p_base + va_image->offsets[0]; pitches[0] = va_image->pitches[0]; + dst[1] = (uint8_t *)p_base + va_image->offsets[1]; pitches[1] = va_image->pitches[1]; + dst[2] = (uint8_t *)p_base + va_image->offsets[2]; pitches[2] = va_image->pitches[2]; + } else { + dst[0] = (uint8_t *)p_base + va_image->offsets[0]; pitches[0] = va_image->pitches[0]; + dst[1] = (uint8_t *)p_base + va_image->offsets[2]; pitches[1] = va_image->pitches[1]; + dst[2] = (uint8_t *)p_base + va_image->offsets[1]; pitches[2] = va_image->pitches[2]; + } + + /* Copy xine frames into VAAPI images */ + if(frame->format == XINE_IMGFMT_YV12) { + + if (va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("vaapi_software_render_frame yv12 -> yv12 convert\n"); + + yv12_to_yv12( + /* Y */ + frame_gen->base[0], frame_gen->pitches[0], + dst[0], pitches[0], + /* U */ + frame_gen->base[1], frame_gen->pitches[1], + dst[1], pitches[1], + /* V */ + frame_gen->base[2], frame_gen->pitches[2], + dst[2], pitches[2], + /* width x height */ + frame_gen->width, frame_gen->height); + + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' )) { + lprintf("vaapi_software_render_frame yv12 -> nv12 convert\n"); + + yv12_to_nv12(frame_gen->base[0], frame_gen->pitches[0], + frame_gen->base[1], frame_gen->pitches[1], + frame_gen->base[2], frame_gen->pitches[2], + (uint8_t *)p_base + va_image->offsets[0], va_image->pitches[0], + (uint8_t *)p_base + va_image->offsets[1], va_image->pitches[1], + frame_gen->width, frame_gen->height, + va_image->width, va_image->height, + va_image->data_size); + + } + } else if (frame->format == XINE_IMGFMT_YUY2) { + + if (va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("vaapi_software_render_frame yuy2 -> yv12 convert\n"); + + yuy2_to_yv12(frame_gen->base[0], frame_gen->pitches[0], + dst[0], pitches[0], + dst[1], pitches[1], + dst[2], pitches[2], + frame_gen->width, frame_gen->height); + + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' )) { + lprintf("vaapi_software_render_frame yuy2 -> nv12 convert\n"); + printf("yuy2 -> nv12 convert missing\n"); + } + + } + + vaStatus = vaUnmapBuffer(va_context->va_display, va_image->buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + if(!va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_surface_id, va_image->image_id, + 0, 0, va_image->width, va_image->height, + 0, 0, va_image->width, va_image->height); + } + + if(!vaapi_check_status(va_context->driver, vaStatus, "vaPutImage()")) + return vaStatus; + + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_hardware_render_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen, + VASurfaceID va_surface_id) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus = VA_STATUS_ERROR_UNKNOWN; + int i = 0; + int interlaced_frame = !frame->vo_frame.progressive_frame; + int top_field_first = frame->vo_frame.top_field_first; + int width, height; + + if(frame->format == XINE_IMGFMT_VAAPI) { + width = va_context->width; + height = va_context->height; + } else { + width = (frame->width > va_context->sw_width) ? va_context->sw_width : frame->width; + height = (frame->height > va_context->sw_height) ? va_context->sw_height : frame->height; + } + + if(!va_context->valid_context || va_surface_id == VA_INVALID_SURFACE) + return VA_STATUS_ERROR_UNKNOWN; + + if(this->opengl_render && !this->valid_opengl_context) + return VA_STATUS_ERROR_UNKNOWN; + + /* Final VAAPI rendering. The deinterlacing can be controled by xine config.*/ + unsigned int deint = this->deinterlace; + for(i = 0; i <= !!((deint > 1) && interlaced_frame); i++) { + unsigned int flags = (deint && (interlaced_frame) ? (((!!(top_field_first)) ^ i) == 0 ? VA_BOTTOM_FIELD : VA_TOP_FIELD) : VA_FRAME_PICTURE); + + //flags |= vaapi_get_colorspace_flags(this_gen); + + flags |= VA_CLEAR_DRAWABLE; + flags |= this->scaling_level; + + lprintf("Putsrfc srfc 0x%08X flags 0x%08x %dx%d -> %dx%d interlaced %d top_field_first %d\n", + va_surface_id, flags, width, height, + this->sc.output_width, this->sc.output_height, + interlaced_frame, top_field_first); + + if(this->opengl_render) { + + vaapi_x11_trap_errors(); + + if(this->opengl_use_tfp) { + lprintf("opengl render tfp\n"); + vaStatus = vaPutSurface(va_context->va_display, va_surface_id, this->gl_image_pixmap, + 0, 0, width, height, 0, 0, width, height, NULL, 0, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaPutSurface()")) + return vaStatus; + } else { + lprintf("opengl render\n"); + vaStatus = vaCopySurfaceGLX(va_context->va_display, va_context->gl_surface, va_surface_id, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaCopySurfaceGLX()")) + return vaStatus; + } + if(vaapi_x11_untrap_errors()) + return VA_STATUS_ERROR_UNKNOWN; + + vaapi_glx_flip_page(frame_gen, 0, 0, va_context->width, va_context->height); + + } else { + + vaStatus = vaPutSurface(va_context->va_display, va_surface_id, this->window, + 0, 0, width, height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height, + NULL, 0, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaPutSurface()")) + return vaStatus; + } + } + return VA_STATUS_SUCCESS; +} + +/* Used in vaapi_display_frame to determine how long displaying a frame takes + - if slower than 60fps, print a message +*/ +/* +static double timeOfDay() +{ + struct timeval t; + gettimeofday( &t, NULL ); + return ((double)t.tv_sec) + (((double)t.tv_usec)/1000000.0); +} +*/ + +static void vaapi_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + ff_vaapi_context_t *va_context = this->va_context; + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + VASurfaceID va_surface_id = VA_INVALID_SURFACE; + VAImage *va_image = NULL; + VAStatus vaStatus; + + lprintf("vaapi_display_frame\n"); + + /* + if((frame->height < 17 || frame->width < 17) && ((frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2))) { + frame->vo_frame.free( frame_gen ); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " frame size to small width %d height %d\n", frame->height, frame->width); + return; + } + */ + + /* + * let's see if this frame is different in size / aspect + * ratio from the previous one + */ + + if ( (frame->width != this->sc.delivered_width) + || (frame->height != this->sc.delivered_height) + || (frame->ratio != this->sc.delivered_ratio) + || (frame->vo_frame.crop_left != this->sc.crop_left) + || (frame->vo_frame.crop_right != this->sc.crop_right) + || (frame->vo_frame.crop_top != this->sc.crop_top) + || (frame->vo_frame.crop_bottom != this->sc.crop_bottom) ) { + lprintf("frame format changed\n"); + this->sc.force_redraw = 1; + } + + /* + * tell gui that we are about to display a frame, + * ask for offset and output size + */ + this->sc.delivered_height = frame->height; + this->sc.delivered_width = frame->width; + this->sc.delivered_ratio = frame->ratio; + this->sc.crop_left = frame->vo_frame.crop_left; + this->sc.crop_right = frame->vo_frame.crop_right; + this->sc.crop_top = frame->vo_frame.crop_top; + this->sc.crop_bottom = frame->vo_frame.crop_bottom; + + /* + * tell gui that we are about to display a frame, + * ask for offset and output size + */ + this->sc.delivered_height = frame->height; + this->sc.delivered_width = frame->width; + this->sc.delivered_ratio = frame->ratio; + + this->sc.crop_left = frame->vo_frame.crop_left; + this->sc.crop_right = frame->vo_frame.crop_right; + this->sc.crop_top = frame->vo_frame.crop_top; + this->sc.crop_bottom = frame->vo_frame.crop_bottom; + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + lprintf("vaapi_display_frame %s frame->width %d frame->height %d va_context->sw_width %d va_context->sw_height %d valid_context %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height, va_context->sw_width, va_context->sw_height, va_context->valid_context); + + if( ((frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2)) + && ((frame->width != va_context->sw_width) ||(frame->height != va_context->sw_height )) ) { + + lprintf("vaapi_display_frame %s frame->width %d frame->height %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height); + + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + if(!va_context->valid_context) { + lprintf("vaapi_display_frame init full context\n"); + vaapi_init_internal(frame_gen->driver, SW_CONTEXT_INIT_FORMAT, frame->width, frame->height, 0); + } else { + lprintf("vaapi_display_frame init soft surfaces\n"); + vaapi_init_soft_surfaces(frame_gen->driver, frame->width, frame->height); + } + + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + } + + XUnlockDisplay(this->display); + pthread_mutex_unlock(&this->vaapi_lock); + + vaapi_redraw_needed (this_gen); + + /* posible race could happen while the lock is opened */ + if(!this->va_context || !this->va_context->valid_context) + return; + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + /* initialize opengl rendering */ + if(this->opengl_render && this->init_opengl_render && va_context->valid_context) { + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + destroy_glx(this_gen); + + vaapi_glx_config_glx(frame_gen->driver, va_context->width, va_context->height); + + vaapi_resize_glx_window(frame_gen->driver, this->sc.gui_width, this->sc.gui_height); + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + this->sc.force_redraw = 1; + this->init_opengl_render = 0; + } + + /* + double start_time; + double end_time; + double elapse_time; + int factor; + + start_time = timeOfDay(); + */ + + if(va_context->valid_context && ( (frame->format == XINE_IMGFMT_VAAPI) || (frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2) )) { + + if((frame->format == XINE_IMGFMT_YUY2) || (frame->format == XINE_IMGFMT_YV12)) { + va_surface_id = va_soft_surface_ids[va_context->va_soft_head]; + va_image = &va_soft_images[va_context->va_soft_head]; + va_context->va_soft_head = (va_context->va_soft_head + 1) % (SOFT_SURFACES); + } + + if(this->guarded_render) { + if(frame->format == XINE_IMGFMT_VAAPI) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + if(va_surface->status == SURFACE_RENDER || va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface_id = va_surface->va_surface_id; + } + va_image = NULL; + } +#ifdef DEBUG_SURFACE + printf("vaapi_display_frame va_surface 0x%08x status %d index %d\n", va_surface_id, va_surface->status, accel->index); +#endif + } else { + if(frame->format == XINE_IMGFMT_VAAPI) { + va_surface_id = va_surface->va_surface_id; + va_image = NULL; + } + } + + lprintf("2: 0x%08x\n", va_surface_id); + + VASurfaceStatus surf_status = 0; + if(va_surface_id != VA_INVALID_SURFACE) { + + if(this->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface_id, &surf_status); + vaapi_check_status(this_gen, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status != VASurfaceReady) { + va_surface_id = VA_INVALID_SURFACE; + va_image = NULL; +#ifdef DEBUG_SURFACE + printf("Surface srfc 0x%08X not ready for render\n", va_surface_id); +#endif + } + } else { +#ifdef DEBUG_SURFACE + printf("Invalid srfc 0x%08X\n", va_surface_id); +#endif + } + + if(va_surface_id != VA_INVALID_SURFACE) { + + lprintf("vaapi_display_frame: 0x%08x %d %d\n", va_surface_id, va_context->width, va_context->height); + + vaStatus = vaSyncSurface(va_context->va_display, va_surface_id); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + + /* transfer image data to a VAAPI surface */ + if((frame->format == XINE_IMGFMT_YUY2 || frame->format == XINE_IMGFMT_YV12)) + vaapi_software_render_frame(this_gen, frame_gen, va_image, va_surface_id); + + vaapi_hardware_render_frame(this_gen, frame_gen, va_surface_id); + + } + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " unsupported image format %s width %d height %d valid_context %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height, va_context->valid_context); + } + + XSync(this->display, False); + + //end_time = timeOfDay(); + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + if(va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_display_frame 0x%08x\n", va_surface->va_surface_id); +#endif + } else if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RELEASE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_display_frame 0x%08x\n", va_surface->va_surface_id); +#endif + } + } + + XUnlockDisplay(this->display); + + frame->vo_frame.free( frame_gen ); + + pthread_mutex_unlock(&this->vaapi_lock); + + /* + elapse_time = end_time - start_time; + factor = (int)(elapse_time/(1.0/60.0)); + + if( factor > 1 ) + { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " PutImage %dX interval (%fs)\n", factor, elapse_time ); + } + */ +} + +static int vaapi_get_property (vo_driver_t *this_gen, int property) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + switch (property) { + case VO_PROP_WINDOW_WIDTH: + this->props[property].value = this->sc.gui_width; + break; + case VO_PROP_WINDOW_HEIGHT: + this->props[property].value = this->sc.gui_height; + break; + case VO_PROP_OUTPUT_WIDTH: + this->props[property].value = this->sc.output_width; + break; + case VO_PROP_OUTPUT_HEIGHT: + this->props[property].value = this->sc.output_height; + break; + case VO_PROP_OUTPUT_XOFFSET: + this->props[property].value = this->sc.output_xoffset; + break; + case VO_PROP_OUTPUT_YOFFSET: + this->props[property].value = this->sc.output_yoffset; + break; + case VO_PROP_MAX_NUM_FRAMES: + if(!this->guarded_render) + this->props[property].value = RENDER_SURFACES; + else + this->props[property].value = 2; + break; + } + + lprintf("vaapi_get_property property=%d, value=%d\n", property, this->props[property].value ); + + return this->props[property].value; +} + +static int vaapi_set_property (vo_driver_t *this_gen, int property, int value) { + + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + lprintf("vaapi_set_property property=%d, value=%d\n", property, value ); + + if(this->props[property].atom) { + VADisplayAttribute attr; + + if((value < this->props[property].min) || (value > this->props[property].max)) + value = (this->props[property].min + this->props[property].max) >> 1; + + this->props[property].value = value; + attr.type = this->props[property].type; + attr.value = value; + + if(va_context && va_context->valid_context) { + VAStatus vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + vaapi_check_status((vo_driver_t *)this, vaStatus, "vaSetDisplayAttributes()"); + } + + if (this->props[property].entry) + this->props[property].entry->num_value = this->props[property].value; + + vaapi_show_display_props((vo_driver_t*)this); + + return this->props[property].value; + } else { + switch (property) { + + case VO_PROP_ASPECT_RATIO: + if (value>=XINE_VO_ASPECT_NUM_RATIOS) + value = XINE_VO_ASPECT_AUTO; + this->props[property].value = value; + this->sc.user_ratio = value; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + break; + + case VO_PROP_ZOOM_X: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + this->sc.zoom_factor_x = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + } + break; + + case VO_PROP_ZOOM_Y: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + this->sc.zoom_factor_y = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + } + break; + } + } + return value; +} + +static void vaapi_get_property_min_max (vo_driver_t *this_gen, + int property, int *min, int *max) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + *min = this->props[property].min; + *max = this->props[property].max; +} + +static int vaapi_gui_data_exchange (vo_driver_t *this_gen, + int data_type, void *data) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + lprintf("vaapi_gui_data_exchange %d\n", data_type); + + switch (data_type) { +#ifndef XINE_DISABLE_DEPRECATED_FEATURES + case XINE_GUI_SEND_COMPLETION_EVENT: + break; +#endif + + case XINE_GUI_SEND_EXPOSE_EVENT: { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay( this->display ); + lprintf("XINE_GUI_SEND_EXPOSE_EVENT:\n"); + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + } + break; + + case XINE_GUI_SEND_WILL_DESTROY_DRAWABLE: { + printf("XINE_GUI_SEND_WILL_DESTROY_DRAWABLE\n"); + } + break; + + case XINE_GUI_SEND_DRAWABLE_CHANGED: { + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay( this->display ); + lprintf("XINE_GUI_SEND_DRAWABLE_CHANGED\n"); + + this->drawable = (Drawable) data; + + XReparentWindow(this->display, this->window, this->drawable, 0, 0); + + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + + XUnlockDisplay( this->display ); + pthread_mutex_unlock(&this->vaapi_lock); + } + break; + + case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO: { + int x1, y1, x2, y2; + x11_rectangle_t *rect = data; + + _x_vo_scale_translate_gui2video(&this->sc, rect->x, rect->y, &x1, &y1); + _x_vo_scale_translate_gui2video(&this->sc, rect->x + rect->w, rect->y + rect->h, &x2, &y2); + rect->x = x1; + rect->y = y1; + rect->w = x2-x1; + rect->h = y2-y1; + } + break; + + default: + return -1; + } + + return 0; +} + +static void vaapi_dispose (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + lprintf("vaapi_dispose\n"); + + pthread_mutex_lock(&this->vaapi_lock); + XLockDisplay(this->display); + + this->ovl_yuv2rgb->dispose(this->ovl_yuv2rgb); + this->yuv2rgb_factory->dispose (this->yuv2rgb_factory); + + vaapi_close(this_gen); + free(va_context); + + if(this->overlay_bitmap) + free(this->overlay_bitmap); + + if(va_surface_ids) + free(va_surface_ids); + if(va_soft_surface_ids) + free(va_soft_surface_ids); + if(va_render_surfaces) + free(va_render_surfaces); + if(va_soft_images) + free(va_soft_images); + + XDestroyWindow(this->display, this->window); + XUnlockDisplay(this->display); + + pthread_mutex_unlock(&this->vaapi_lock); + pthread_mutex_destroy(&this->vaapi_lock); + + free (this); +} + +static void vaapi_vdr_osd_width_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->vdr_osd_width = entry->num_value; +} + +static void vaapi_vdr_osd_height_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->vdr_osd_height = entry->num_value; +} + +static void vaapi_deinterlace_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->deinterlace = entry->num_value; + if(this->deinterlace > 2) + this->deinterlace = 2; +} + +static void vaapi_opengl_render( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->opengl_render = entry->num_value; +} + +static void vaapi_opengl_use_tfp( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->opengl_use_tfp = entry->num_value; +} + +static void vaapi_guarded_render( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->guarded_render = entry->num_value; +} + +static void vaapi_scaling_level( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->scaling_level = entry->num_value; +} + +static void vaapi_swap_uv_planes(void *this_gen, xine_cfg_entry_t *entry) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->swap_uv_planes = entry->num_value; +} + +static vo_driver_t *vaapi_open_plugin (video_driver_class_t *class_gen, const void *visual_gen) { + + vaapi_class_t *class = (vaapi_class_t *) class_gen; + x11_visual_t *visual = (x11_visual_t *) visual_gen; + vaapi_driver_t *this; + config_values_t *config = class->config; + XSetWindowAttributes xswa; + unsigned long xswa_mask; + XWindowAttributes wattr; + unsigned long black_pixel; + XVisualInfo visualInfo; + XVisualInfo *vi; + int depth; + const int x11_event_mask = ExposureMask | + StructureNotifyMask; + + this = (vaapi_driver_t *) calloc(1, sizeof(vaapi_driver_t)); + if (!this) + return NULL; + + this->config = config; + this->xine = class->xine; + + this->display = visual->display; + this->screen = visual->screen; + this->drawable = visual->d; + + this->va_context = calloc(1, sizeof(ff_vaapi_context_t)); + + /* number of video frames from config - register it with the default value. */ + int frame_num = config->register_num (config, "engine.buffers.video_num_frames", RENDER_SURFACES, /* default */ + _("default number of video frames"), + _("The default number of video frames to request " + "from xine video out driver. Some drivers will " + "override this setting with their own values."), + 20, NULL, this); + + /* now make sure we have at least 22 frames, to prevent + * locks with vdpau_h264 */ + if(frame_num != RENDER_SURFACES) + config->update_num(config,"engine.buffers.video_num_frames", RENDER_SURFACES); + + this->opengl_render = config->register_bool( config, "video.output.vaapi_opengl_render", 0, + _("vaapi: opengl output rendering"), + _("vaapi: opengl output rendering"), + 20, vaapi_opengl_render, this ); + + this->init_opengl_render = 1; + + this->opengl_use_tfp = config->register_bool( config, "video.output.vaapi_opengl_use_tfp", 0, + _("vaapi: opengl rendering tfp"), + _("vaapi: opengl rendering tfp"), + 20, vaapi_opengl_use_tfp, this ); + + if(this->opengl_render) { + this->opengl_render = vaapi_opengl_verify_direct ((x11_visual_t *)visual_gen); + if(!this->opengl_render) + xprintf (this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Opengl indirect/software rendering does not work. Fallback to plain VAAPI output !!!!\n"); + } + + this->valid_opengl_context = 0; + this->gl_vinfo = NULL; + this->gl_pixmap = None; + this->gl_image_pixmap = None; + this->gl_texture = GL_NONE; + + this->num_frame_buffers = 0; + + va_render_surfaces = calloc(RENDER_SURFACES + 1, sizeof(ff_vaapi_surface_t)); + va_surface_ids = calloc(RENDER_SURFACES + 1, sizeof(VASurfaceID)); + va_soft_surface_ids = calloc(SOFT_SURFACES + 1, sizeof(VASurfaceID)); + va_soft_images = calloc(SOFT_SURFACES + 1, sizeof(VAImage)); + + vaapi_init_va_context(this); + vaapi_init_subpicture(this); + + _x_vo_scale_init (&this->sc, 1, 0, config ); + + this->sc.frame_output_cb = visual->frame_output_cb; + this->sc.dest_size_cb = visual->dest_size_cb; + this->sc.user_data = visual->user_data; + this->sc.user_ratio = XINE_VO_ASPECT_AUTO; + + black_pixel = BlackPixel(this->display, this->screen); + + XGetWindowAttributes(this->display, this->drawable, &wattr); + + depth = wattr.depth; + if (depth != 15 && depth != 16 && depth != 24 && depth != 32) + depth = 24; + + vi = &visualInfo; + XMatchVisualInfo(this->display, this->screen, depth, TrueColor, vi); + + xswa_mask = CWBorderPixel | CWBackPixel | CWColormap; + xswa.border_pixel = black_pixel; + xswa.background_pixel = black_pixel; + xswa.colormap = CopyFromParent; + + this->window = XCreateWindow(this->display, this->drawable, + 0, 0, 1, 1, 0, depth, + InputOutput, vi->visual, xswa_mask, &xswa); + + if(this->window == None) + return NULL; + + XSelectInput(this->display, this->window, x11_event_mask); + + XMapWindow(this->display, this->window); + vaapi_x11_wait_event(this->display, this->window, MapNotify); + + if(vi != &visualInfo) + XFree(vi); + + this->capabilities = VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_CROP | VO_CAP_UNSCALED_OVERLAY | VO_CAP_ARGB_LAYER_OVERLAY | VO_CAP_VAAPI | VO_CAP_CUSTOM_EXTENT_OVERLAY; + + /* overlay converter */ + this->yuv2rgb_factory = yuv2rgb_factory_init (MODE_24_BGR, 0, NULL); + this->ovl_yuv2rgb = this->yuv2rgb_factory->create_converter( this->yuv2rgb_factory ); + + this->vo_driver.get_capabilities = vaapi_get_capabilities; + this->vo_driver.alloc_frame = vaapi_alloc_frame; + this->vo_driver.update_frame_format = vaapi_update_frame_format; + this->vo_driver.overlay_begin = vaapi_overlay_begin; + this->vo_driver.overlay_blend = vaapi_overlay_blend; + this->vo_driver.overlay_end = vaapi_overlay_end; + this->vo_driver.display_frame = vaapi_display_frame; + this->vo_driver.get_property = vaapi_get_property; + this->vo_driver.set_property = vaapi_set_property; + this->vo_driver.get_property_min_max = vaapi_get_property_min_max; + this->vo_driver.gui_data_exchange = vaapi_gui_data_exchange; + this->vo_driver.dispose = vaapi_dispose; + this->vo_driver.redraw_needed = vaapi_redraw_needed; + + this->deinterlace = 0; + this->vdr_osd_width = 0; + this->vdr_osd_height = 0; + + this->vdr_osd_width = config->register_num( config, "video.output.vaapi_vdr_osd_width", 0, + _("vaapi: VDR osd width workaround."), + _("vaapi: VDR osd width workaround."), + 10, vaapi_vdr_osd_width_flag, this ); + + this->vdr_osd_height = config->register_num( config, "video.output.vaapi_vdr_osd_height", 0, + _("vaapi: VDR osd height workaround."), + _("vaapi: VDR osd height workaround."), + 10, vaapi_vdr_osd_height_flag, this ); + + this->deinterlace = config->register_num( config, "video.output.vaapi_deinterlace", 0, + _("vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob )."), + _("vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob )."), + 10, vaapi_deinterlace_flag, this ); + + this->guarded_render = config->register_num( config, "video.output.vaapi_guarded_render", 1, + _("vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )"), + _("vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )"), + 10, vaapi_guarded_render, this ); + + this->scaling_level_enum = config->register_enum(config, "video.output.vaapi_scaling_level", 0, + (char **)scaling_level_enum_names, + _("vaapi: set scaling level to : default (default) fast (fast) hq (HQ) nla (anamorphic)"), + _("vaapi: set scaling level to : default (default) fast (fast) hq (HQ) nla (anamorphic)"), + 10, vaapi_scaling_level, this); + + this->scaling_level = scaling_level_enum_values[this->scaling_level_enum]; + + this->swap_uv_planes = config->register_bool( config, "video.output.vaapi_swap_uv_planes", 0, + _("vaapi: swap UV planes."), + _("vaapi: this is a workaround for buggy drivers ( intel IronLake ).\n" + "There the UV planes are swapped.\n"), + 10, vaapi_swap_uv_planes, this); + + + pthread_mutex_init(&this->vaapi_lock, NULL); + + pthread_mutex_lock(&this->vaapi_lock); + + int i; + for (i = 0; i < VO_NUM_PROPERTIES; i++) { + this->props[i].value = 0; + this->props[i].min = 0; + this->props[i].max = 0; + this->props[i].atom = 0; + this->props[i].entry = NULL; + this->props[i].this = this; + } + + this->sc.user_ratio = + this->props[VO_PROP_ASPECT_RATIO].value = XINE_VO_ASPECT_AUTO; + this->props[VO_PROP_ZOOM_X].value = 100; + this->props[VO_PROP_ZOOM_Y].value = 100; + + this->va_context->last_sub_surface_id = VA_INVALID_SURFACE; + this->va_context->last_sub_image_fmt = 0; + + if(vaapi_init_internal((vo_driver_t *)this, SW_CONTEXT_INIT_FORMAT, SW_WIDTH, SW_HEIGHT, 0) != VA_STATUS_SUCCESS) { + vaapi_dispose((vo_driver_t *)this); + return NULL; + } + vaapi_close((vo_driver_t *)this); + this->va_context->valid_context = 0; + this->va_context->driver = (vo_driver_t *)this; + + pthread_mutex_unlock(&this->vaapi_lock); + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Deinterlace : %d\n", this->deinterlace); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Render surfaces : %d\n", RENDER_SURFACES); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Opengl render : %d\n", this->opengl_render); + + return &this->vo_driver; +} + +/* + * class functions + */ +static void *vaapi_init_class (xine_t *xine, void *visual_gen) { + vaapi_class_t *this = (vaapi_class_t *) calloc(1, sizeof(vaapi_class_t)); + + this->driver_class.open_plugin = vaapi_open_plugin; + this->driver_class.identifier = "vaapi"; + this->driver_class.description = N_("xine video output plugin using the MIT X video extension"); + this->driver_class.dispose = default_video_driver_class_dispose; + this->config = xine->config; + this->xine = xine; + + return this; +} + +static const vo_info_t vo_info_vaapi = { + 9, /* priority */ + XINE_VISUAL_TYPE_X11 /* visual type */ +}; + +/* + * exported plugin catalog entry + */ + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_OUT, 22, "vaapi", XINE_VERSION_CODE, &vo_info_vaapi, vaapi_init_class }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/xine-engine/Makefile.am b/src/xine-engine/Makefile.am index a0131a5..baed5b7 100644 --- a/src/xine-engine/Makefile.am +++ b/src/xine-engine/Makefile.am @@ -11,7 +11,7 @@ XINEUTILS_LIB = $(top_builddir)/src/xine-utils/libxineutils.la YUV_LIB = $(top_builddir)/src/video_out/libyuv2rgb.la # FIXME: these are currently unused: -EXTRA_DIST = lrb.c lrb.h accel_vdpau.h accel_xvmc.h +EXTRA_DIST = lrb.c lrb.h accel_vdpau.h accel_vaapi.h accel_xvmc.h if WIN32 DEF_FILE = libxine-$(XINE_MAJOR).def diff --git a/src/xine-engine/accel_vaapi.h b/src/xine-engine/accel_vaapi.h new file mode 100644 index 0000000..666b23f --- /dev/null +++ b/src/xine-engine/accel_vaapi.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2008 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + * Common acceleration definitions for vdpau + * + * + */ + +#ifndef HAVE_XINE_ACCEL_VAAPI_H +#define HAVE_XINE_ACCEL_VAAPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <va/va_x11.h> +#include <pthread.h> +#ifdef HAVE_FFMPEG_AVUTIL_H +# include <avcodec.h> +#else +# include <libavcodec/avcodec.h> +#endif + +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 32) +# define AVVIDEO 2 +#else +# define AVVIDEO 1 +# define pp_context pp_context_t +# define pp_mode pp_mode_t +#endif + +#define NUM_OUTPUT_SURFACES 22 + +#define SURFACE_FREE 0 +#define SURFACE_ALOC 1 +#define SURFACE_RELEASE 2 +#define SURFACE_RENDER 3 +#define SURFACE_RENDER_RELEASE 5 + +struct vaapi_equalizer { + VADisplayAttribute brightness; + VADisplayAttribute contrast; + VADisplayAttribute hue; + VADisplayAttribute saturation; +}; + +typedef struct ff_vaapi_context_s ff_vaapi_context_t; + +struct ff_vaapi_context_s { + VADisplay va_display; + VAContextID va_context_id; + VAConfigID va_config_id; + int width; + int height; + int sw_width; + int sw_height; + int va_profile; + unsigned int va_colorspace; + VAImage va_subpic_image; + VASubpictureID va_subpic_id; + int va_subpic_width; + int va_subpic_height; + int is_bound; + void *gl_surface; + unsigned int soft_head; + unsigned int valid_context; + unsigned int va_head; + unsigned int va_soft_head; + vo_driver_t *driver; + unsigned int last_sub_image_fmt; + VASurfaceID last_sub_surface_id; + struct vaapi_equalizer va_equalizer; + VAImageFormat *va_image_formats; + int va_num_image_formats; + VAImageFormat *va_subpic_formats; + int va_num_subpic_formats; +}; + +typedef struct ff_vaapi_surface_s ff_vaapi_surface_t; +typedef struct vaapi_accel_s vaapi_accel_t; + +struct ff_vaapi_surface_s { + unsigned int index; + vaapi_accel_t *accel; + VASurfaceID va_surface_id; + unsigned int status; +}; + +struct vaapi_accel_s { + unsigned int index; + vo_frame_t *vo_frame; + +#if AVVIDEO > 1 + int (*avcodec_decode_video2)(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, AVPacket *avpkt); +#else + int (*avcodec_decode_video)(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, uint8_t *buf, int buf_size); +#endif + VAStatus (*vaapi_init)(vo_frame_t *frame_gen, int va_profile, int width, int height, int softrender); + int (*profile_from_imgfmt)(vo_frame_t *frame_gen, enum PixelFormat pix_fmt, int codec_id, int vaapi_mpeg_sofdec); + ff_vaapi_context_t *(*get_context)(vo_frame_t *frame_gen); + int (*guarded_render)(vo_frame_t *frame_gen); + ff_vaapi_surface_t *(*get_vaapi_surface)(vo_frame_t *frame_gen); + void (*render_vaapi_surface)(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface); + void (*release_vaapi_surface)(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface); +}; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/xine-engine/video_out.c b/src/xine-engine/video_out.c index cc42813..819e423 100644 --- a/src/xine-engine/video_out.c +++ b/src/xine-engine/video_out.c @@ -737,6 +737,13 @@ static vo_frame_t *vo_get_frame (xine_video_port_t *this_gen, return img; } +static double tt() +{ + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0; +} + static int vo_frame_draw (vo_frame_t *img, xine_stream_t *stream) { vos_t *this = (vos_t *) img->port; @@ -746,6 +753,30 @@ static int vo_frame_draw (vo_frame_t *img, xine_stream_t *stream) { int frames_to_skip; int duration; +/* +if (1) +{ + double t1 = tt(); + static double t0 = -1; + if (t0 == -1) t0 = t1; + double dt = t1 - t0; + t0 = t1; + static int64_t pts = 0; + fprintf(stderr, "img->pts: %12" PRId64 ", pts: %12" PRId64 ", img->duration: %4d, time since last vo_frame_draw: %7.3lf ms", img->pts, pts, img->duration, dt); + if (img->pts) + { + if (pts != img->pts) + fprintf(stderr, " ERROR: %12" PRId64 "", img->pts - pts); + pts = img->pts + img->duration; + } + else + { + pts += img->duration; + } + fprintf(stderr, "\n"); +} +*/ + /* handle anonymous streams like NULL for easy checking */ if (stream == XINE_ANON_STREAM) stream = NULL; ------------------------------------------------------------------------------ All the data continuously generated in your IT infrastructure contains a definitive record of customers, application performance, security threats, fraudulent activity and more. Splunk takes this data and makes sense of it. Business sense. IT sense. Common sense. http://p.sf.net/sfu/splunk-d2d-oct _______________________________________________ xine-devel mailing list xine-devel@... https://lists.sourceforge.net/lists/listinfo/xine-devel |
| Free embeddable forum powered by Nabble | Forum Help |