VlcInterface.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. /*
  2. * Copyright (C) 2015 iZc
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. package de.nplusc.izc.iZpl.Utils;
  18. import com.sun.jna.Native;
  19. import com.sun.jna.NativeLibrary;
  20. import de.nplusc.izc.iZpl.API.IZPLApi;
  21. import de.nplusc.izc.iZpl.API.PlaybackPlugin;
  22. import de.nplusc.izc.iZpl.API.UIPlugin;
  23. import de.nplusc.izc.iZpl.API.shared.MultiPlayListItem;
  24. import de.nplusc.izc.iZpl.API.shared.PlayListItem;
  25. import de.nplusc.izc.iZpl.API.shared.SinglePlayListItem;
  26. import de.nplusc.izc.tools.baseTools.Detectors;
  27. import net.java.truevfs.access.TFile;
  28. import java.awt.Canvas;
  29. import java.io.File;
  30. import java.io.IOException;
  31. import java.util.Arrays;
  32. import javax.swing.JPanel;
  33. import org.apache.logging.log4j.LogManager;
  34. import org.apache.logging.log4j.Logger;
  35. import uk.co.caprica.vlcj.binding.LibVlc;
  36. import uk.co.caprica.vlcj.binding.RuntimeUtil;
  37. import uk.co.caprica.vlcj.player.base.MediaPlayer;
  38. import uk.co.caprica.vlcj.player.base.MediaPlayerEventListener;
  39. import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
  40. import uk.co.caprica.vlcj.media.Media;
  41. import uk.co.caprica.vlcj.media.MediaRef;
  42. import uk.co.caprica.vlcj.media.TrackType;
  43. import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
  44. /**
  45. *
  46. * @author inge
  47. */
  48. public class VlcInterface implements PlaybackPlugin , MediaPlayerEventListener
  49. {
  50. private static final Logger l = LogManager.getLogger();
  51. //private static VlcInterface instance=null;
  52. private InternalInterface adapter=null;
  53. private PlayListItem currentTrack;
  54. private boolean isInMultiItem;
  55. private SinglePlayListItem[] multiItemStorage = null;
  56. private int multiIndex=0;
  57. private boolean isPlaying;
  58. private String vlcpath;
  59. private boolean suppressVideo = false;
  60. private static String jarschiv = VlcInterface.class.getProtectionDomain().getCodeSource().getLocation().getPath();
  61. private UIPlugin selectedUIPlugin;
  62. private final String vlcVout;
  63. private boolean voutRequired = false;
  64. private int length = 0;
  65. private int position = 0;
  66. public VlcInterface ()
  67. {
  68. this(new File(jarschiv).getParent() + "\\vlcbinaries",true);
  69. }
  70. private VlcInterface (String vlcpath,boolean bundledVersion)
  71. {
  72. this.vlcpath=vlcpath;
  73. boolean isOnWindows = Detectors.getSystemClassification()[0].equals("windows");
  74. vlcVout = "direct3d9";
  75. voutRequired = isOnWindows;
  76. }
  77. @Override
  78. public String getPluginName()
  79. {
  80. return "VLC Embedded";
  81. }
  82. @Override
  83. public void initializePlugin()
  84. {
  85. String[] osmetadata = Detectors.getSystemClassification();
  86. initializeInterface(null, osmetadata[0].equals("windows"));
  87. }
  88. @SuppressWarnings("CallToPrintStackTrace")
  89. private void initializeInterface(String vlcpath, boolean embedded)
  90. {
  91. String[] osmetadata = Detectors.getSystemClassification();
  92. l.info("Os-data:"+Arrays.toString(osmetadata));
  93. if (osmetadata[0].equals("windows"))
  94. {
  95. if(embedded)
  96. {
  97. File target = new File(new File(jarschiv).getParent() + "\\vlcbinaries");
  98. if (!target.exists())
  99. {
  100. TFile zippo = new TFile(jarschiv + "\\vlc\\vlc-3.0.3-" + osmetadata[0] + "-" + osmetadata[1] + ".zip");
  101. try
  102. {
  103. zippo.cp_rp(target);
  104. }
  105. catch (IOException ex)
  106. {
  107. ex.printStackTrace();
  108. }
  109. }
  110. NativeLibrary.addSearchPath(RuntimeUtil.getLibVlcLibraryName(), target.getPath());
  111. }
  112. else
  113. {
  114. throw new UnsupportedOperationException("ERROR! Host VLC is not supported yet on Windows");
  115. }
  116. try
  117. {
  118. TFile jarschcrap = new TFile(jarschiv + "\\vlc\\");
  119. if(jarschcrap.exists())
  120. {
  121. jarschcrap.rm_r();//removal of the embedded VLC data after connecting to the System
  122. }
  123. }
  124. catch (IOException ex)
  125. {
  126. ex.printStackTrace();
  127. }
  128. }
  129. else
  130. {
  131. if(embedded)
  132. {
  133. throw new UnsupportedOperationException("ERROR! invalid OSType detected, embedded VLC doesnt work here");
  134. }
  135. }
  136. Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
  137. }
  138. private JPanel cachedVideoSurface;
  139. @Override
  140. public void connectToPlayer()
  141. {
  142. selectedUIPlugin=IZPLApi.getUIPlugin();
  143. l.info("Connected the player");
  144. if(adapter==null)
  145. {
  146. l.info("Loading VLC with suppressVideo = {}",suppressVideo);
  147. adapter=new VlcJIterface(vlcpath,this,suppressVideo);
  148. adapter.suppressVideoOut(suppressVideo);
  149. if(cachedVideoSurface!=null)
  150. {
  151. adapter.setVideoSurface(cachedVideoSurface);
  152. }
  153. }
  154. }
  155. @Override
  156. public void registerVideoSurface(JPanel p)
  157. {
  158. if(adapter!=null)
  159. {
  160. adapter.setVideoSurface(p);
  161. }
  162. cachedVideoSurface=p;
  163. }
  164. @Override
  165. public int getLengthInSeconds()
  166. {
  167. return length;
  168. }
  169. @Override
  170. public int getPosition()
  171. {
  172. return position;
  173. }
  174. @Override
  175. public void seek(int sekunde)
  176. {
  177. if(adapter!=null)
  178. adapter.seek(sekunde);
  179. }
  180. @Override
  181. public void skipTitle()
  182. {
  183. if(adapter!=null)
  184. {
  185. if(isInMultiItem)
  186. {
  187. if(multiIndex==multiItemStorage.length-1)
  188. {
  189. isInMultiItem=false;
  190. }
  191. currentTrack=multiItemStorage[multiIndex++];
  192. adapter.addTitle(((SinglePlayListItem)currentTrack).getPath());
  193. }
  194. else
  195. {
  196. adapter.skipTitle();
  197. }
  198. }
  199. }
  200. @Override
  201. public void setTitleToPlay(PlayListItem i)
  202. {
  203. if(adapter!=null)
  204. {
  205. isPlaying=true;
  206. if(i instanceof SinglePlayListItem)
  207. {
  208. currentTrack = i;
  209. adapter.addTitle(((SinglePlayListItem)i).getPath());
  210. String t = ((SinglePlayListItem) i).getTitle();
  211. if (t != null && !t.equals(""))
  212. {
  213. selectedUIPlugin.setTrackName(t.split(",")[1]);
  214. }
  215. else
  216. {
  217. selectedUIPlugin.setTrackName(((SinglePlayListItem) i).getPath());
  218. }
  219. }
  220. else
  221. {
  222. isInMultiItem=true;
  223. multiIndex=0;
  224. multiItemStorage = ((MultiPlayListItem)i).getSinglePlayListElements();
  225. currentTrack=multiItemStorage[0];
  226. adapter.addTitle(multiItemStorage[0].getPath());
  227. multiIndex++;
  228. }
  229. }
  230. }
  231. @Override
  232. public PlayListItem getCurrentTitle()
  233. {
  234. return currentTrack;
  235. }
  236. @Override
  237. public void play()
  238. {
  239. if(!isPlaying&&adapter!=null)
  240. {
  241. adapter.play();
  242. isPlaying=true;
  243. }
  244. }
  245. @Override
  246. public void pause()
  247. {
  248. if(isPlaying&&adapter!=null)
  249. {
  250. adapter.pause();
  251. isPlaying=false;
  252. }
  253. }
  254. @Override
  255. public void mediaChanged(MediaPlayer mp, MediaRef mr)
  256. {
  257. Thread t = new Thread(() ->
  258. {
  259. grabMediaData(mp,mr);
  260. try
  261. {
  262. Thread.sleep(1000);
  263. }
  264. catch (InterruptedException devnulled)
  265. {
  266. }
  267. grabMediaData(mp,mr);
  268. });
  269. t.setName("VLC MediaDataGrabber");
  270. t.start();
  271. }
  272. private void grabMediaData(MediaPlayer mp,MediaRef mr)
  273. {
  274. Media m = mr.duplicateMedia();
  275. //IZplGUIBackend.setTitle(mp.getMediaMeta().getTitle());
  276. l.trace("Parsing media meta");
  277. selectedUIPlugin.setTrackLength((int) Math.ceil(m.info().duration()/1000));
  278. length = (int) Math.ceil(m.info().duration()/1000);
  279. selectedUIPlugin.setTrackLength(length);
  280. m.release();
  281. // TODO Media img.
  282. /*
  283. Image img = mp.getMediaMeta().getArtwork();
  284. if(img !=null)
  285. {
  286. l.info("got media img");
  287. }
  288. selectedUIPlugin.setAlbumArt(img);
  289. */
  290. }
  291. public void finished_internal(boolean skip)
  292. {
  293. Thread t = new Thread(()-> //HACK, VLC race conditions
  294. {
  295. try
  296. {
  297. if(!skip)
  298. {
  299. Thread.sleep(2000);
  300. }
  301. }
  302. catch (InterruptedException ex)
  303. {
  304. ex.printStackTrace();
  305. }
  306. if(isInMultiItem)
  307. {
  308. skipTitle();
  309. }
  310. else
  311. {
  312. IZPLApi.playNextTrack();
  313. }
  314. });
  315. t.setName("VLC-HACK");
  316. t.start();
  317. }
  318. //@Override
  319. public void error(MediaPlayer mp)
  320. {
  321. l.error("Some error happened on the current playlist segment:\n {}",currentTrack.getM3UElement());
  322. l.error("Skipping the track!");
  323. finished_internal(false);
  324. }
  325. @Override
  326. public boolean playingStatus()
  327. {
  328. return isPlaying;
  329. }
  330. @Override
  331. public void suppressVideoOut(boolean suppress)
  332. {
  333. suppressVideo = suppress;
  334. if(adapter !=null)
  335. {
  336. adapter.suppressVideoOut(suppress);
  337. }
  338. }
  339. @Override
  340. public void elementaryStreamAdded(MediaPlayer mp, TrackType tt, int i)
  341. {
  342. }
  343. @Override
  344. public void elementaryStreamDeleted(MediaPlayer mp, TrackType tt, int i)
  345. {
  346. }
  347. @Override
  348. public void elementaryStreamSelected(MediaPlayer mp, TrackType tt, int i)
  349. {
  350. }
  351. @Override
  352. public void opening(MediaPlayer mp)
  353. {
  354. }
  355. @Override
  356. public void buffering(MediaPlayer mp, float f)
  357. {
  358. }
  359. @Override
  360. public void playing(MediaPlayer mp)
  361. {
  362. }
  363. @Override
  364. public void paused(MediaPlayer mp)
  365. {
  366. }
  367. @Override
  368. public void stopped(MediaPlayer mp)
  369. {
  370. }
  371. @Override
  372. public void forward(MediaPlayer mp)
  373. {
  374. }
  375. @Override
  376. public void backward(MediaPlayer mp)
  377. {
  378. }
  379. @Override
  380. public void finished(MediaPlayer mp)
  381. {
  382. finished_internal(false);
  383. }
  384. @Override
  385. public void seekableChanged(MediaPlayer mp, int i)
  386. {
  387. }
  388. @Override
  389. public void pausableChanged(MediaPlayer mp, int i)
  390. {
  391. }
  392. @Override
  393. public void titleChanged(MediaPlayer mp, int i)
  394. {
  395. }
  396. @Override
  397. public void snapshotTaken(MediaPlayer mp, String string)
  398. {
  399. }
  400. @Override
  401. public void lengthChanged(MediaPlayer mp, long l)
  402. {
  403. length = (int) Math.ceil(l/1000);
  404. selectedUIPlugin.setTrackLength(length);
  405. }
  406. @Override
  407. public void videoOutput(MediaPlayer mp, int i)
  408. {
  409. }
  410. @Override
  411. public void scrambledChanged(MediaPlayer mp, int i)
  412. {
  413. }
  414. @Override
  415. public void corked(MediaPlayer mp, boolean bln)
  416. {
  417. }
  418. @Override
  419. public void muted(MediaPlayer mp, boolean bln)
  420. {
  421. }
  422. @Override
  423. public void volumeChanged(MediaPlayer mp, float f)
  424. {
  425. }
  426. @Override
  427. public void audioDeviceChanged(MediaPlayer mp, String string)
  428. {
  429. }
  430. @Override
  431. public void chapterChanged(MediaPlayer mp, int i)
  432. {
  433. }
  434. @Override
  435. public void positionChanged(MediaPlayer mediaPlayer, float newPosition)
  436. {
  437. }
  438. @Override
  439. public void timeChanged(MediaPlayer mediaPlayer, long newTime)
  440. {
  441. position = (int) newTime/1000;
  442. selectedUIPlugin.setPlaybackPositionInSeconds(position);
  443. }
  444. @Override
  445. public void mediaPlayerReady(MediaPlayer mediaPlayer)
  446. {
  447. }
  448. private interface InternalInterface
  449. {
  450. public void addTitle(String vlcpath);
  451. public void seek(int sekunde);
  452. public void skipTitle();
  453. public void play();
  454. public void pause();
  455. public default void setVideoSurface(JPanel p){};
  456. public default void suppressVideoOut(boolean suppress){};
  457. }
  458. private class VlcJIterface implements InternalInterface
  459. {
  460. //HACK, vlc logic..., visualizers only load on initial load
  461. private boolean requiresReload=false;
  462. private Canvas c = new Canvas();
  463. private final VlcInterface parent;
  464. private EmbeddedMediaPlayer mpaccess;
  465. private MediaPlayerFactory f;
  466. private boolean registeredVideoSurface=false;
  467. private boolean suppressVideoInternal = false;
  468. public VlcJIterface(String vlcpath,VlcInterface parent,boolean suppress)
  469. {
  470. suppressVideoInternal = suppress;
  471. l.info("Video suppression is {}",suppressVideoInternal);
  472. this.parent=parent;
  473. if(IZPLApi.isInHTTPStreamMode())
  474. {
  475. l.info("Stream mode on port="+IZPLApi.getHTTPStreamPort()+"...");
  476. f= new MediaPlayerFactory("--no-video-title-show",
  477. "--sout=#gather:transcode{vcodec=none,acodec=mp3,ab=256,channels=2,samplerate=44100}:udp{dst=localhost:"+IZPLApi.getHTTPStreamPort()+"} :sout-keep}"
  478. ,"--sout-all","--sout-keep",IZPLApi.getVerbosity()>2?"-vvv":""
  479. );
  480. }
  481. else
  482. {
  483. f= new MediaPlayerFactory("--no-video-title-show",IZPLApi.getVerbosity()>2?"-vvv":"",suppressVideo?"--no-video":"",voutRequired?"-V":"",voutRequired?vlcVout:""
  484. );
  485. }
  486. mpaccess = f.mediaPlayers().newEmbeddedMediaPlayer();
  487. mpaccess.events().addMediaPlayerEventListener(parent);
  488. }
  489. @Override
  490. public void suppressVideoOut(boolean suppress)
  491. {
  492. if(suppress!=suppressVideoInternal)
  493. {
  494. requiresReload=true;
  495. }
  496. suppressVideoInternal = suppress;
  497. }
  498. @Override
  499. public void addTitle(String path)
  500. {
  501. String[] options = new String[0];
  502. mpaccess.controls().stop();
  503. l.info("Next title:{}",path);
  504. if(registeredVideoSurface&&requiresReload)
  505. {
  506. l.info("Reloading LibVLC due to a limit of it");
  507. if(IZPLApi.isInHTTPStreamMode())
  508. {
  509. l.error("Video surface with streaming mode doesn't work... Quitting immediately!");
  510. IZPLApi.quickQuitWithoutSaving();
  511. }
  512. // "--projectm-preset-path="+IZPLApi.VISUALIZERPATH+File.separator,"--audio-visual=projectM","--novideo"
  513. options = new String[]{"--no-video-title-show",IZPLApi.getVerbosity()>2?"-vvv":"","--audio-visual=projectM","--effect-list=scope", "--projectm-preset-path="+IZPLApi.VISUALIZERPATH+File.separator+""
  514. , "--projectm-width="+videowidth,"--projectm-height="+videoheight ,"--novideo" //TODO flag for switching those flags for jukebox mode and similar stuff /*projectM*/
  515. };
  516. mpaccess.release();
  517. f.release();
  518. f= new MediaPlayerFactory(options);
  519. mpaccess = f.mediaPlayers().newEmbeddedMediaPlayer();
  520. mpaccess.events().addMediaPlayerEventListener(parent);
  521. mpaccess.videoSurface().set(f.videoSurfaces().newVideoSurface(c));
  522. requiresReload=false;
  523. }
  524. else
  525. {
  526. if(requiresReload) //stripping the video surface off, got to disable visualizing
  527. {
  528. l.info("Video suppression is {}",suppressVideo);
  529. l.info("Reloading LibVLC due to a limit of it");
  530. mpaccess.release();
  531. f.release();
  532. f= new MediaPlayerFactory("--no-video-title-show",IZPLApi.getVerbosity()>2?"-vvv":"",suppressVideoInternal?"--no-video":"",voutRequired?"-V":"",voutRequired?vlcVout:"");
  533. mpaccess = f.mediaPlayers().newEmbeddedMediaPlayer();
  534. mpaccess.events().addMediaPlayerEventListener(parent);
  535. requiresReload=false;
  536. }
  537. }
  538. mpaccess.media().play(path,options);
  539. }
  540. @Override
  541. public void seek(int sekunde)
  542. {
  543. mpaccess.controls().setPosition(((float)sekunde)/((float)getLengthInSeconds()));
  544. }
  545. @Override
  546. public void skipTitle()
  547. {
  548. parent.finished_internal(true);//über bande spielen
  549. }
  550. @Override
  551. public void play()
  552. {
  553. mpaccess.controls().play();
  554. }
  555. @Override
  556. public void pause()
  557. {
  558. mpaccess.controls().pause();
  559. }
  560. private int videowidth=0;
  561. private int videoheight=0;
  562. private JPanel oldPanel = null;
  563. @Override
  564. public void setVideoSurface(JPanel p)
  565. {
  566. if(p!=null)
  567. {
  568. registeredVideoSurface=true;
  569. p.removeAll();
  570. l.info("Surface size: ("+p.getWidth()+"|"+p.getHeight()+")");
  571. c.setBounds(0, 0, p.getWidth(), p.getHeight());
  572. p.add(c);
  573. int newvideowidth=p.getWidth();
  574. int newvideoheight=p.getHeight();
  575. if(p !=oldPanel)
  576. {
  577. oldPanel = p;
  578. requiresReload=true;
  579. }
  580. p.invalidate();
  581. videowidth = newvideowidth;
  582. videoheight = newvideoheight;
  583. mpaccess.videoSurface().set(f.videoSurfaces().newVideoSurface(c));
  584. }
  585. else
  586. {
  587. requiresReload=true;
  588. mpaccess.videoSurface().set(null);
  589. registeredVideoSurface=false;
  590. }
  591. }
  592. }
  593. private class VlcTelnetInterface implements InternalInterface
  594. {
  595. @Override
  596. public void addTitle(String path)
  597. {
  598. }
  599. @Override
  600. public void seek(int sekunde)
  601. {
  602. }
  603. @Override
  604. public void skipTitle()
  605. {
  606. }
  607. @Override
  608. public void play()
  609. {
  610. throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
  611. }
  612. @Override
  613. public void pause()
  614. {
  615. throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
  616. }
  617. }
  618. @Override
  619. @SuppressWarnings("CallToPrintStackTrace")
  620. public void prepareUpgrade()
  621. {
  622. if(new TFile(jarschiv + "\\vlc").exists()) //nullroute falls reinit nicht möglich da files schon ausgepackt wurden
  623. {
  624. try
  625. {
  626. //killen der alten vlc-natives
  627. TFile f = new TFile(IZPLApi.APPDIR+File.separator+"lib"+File.separator+"vlcbinaries");
  628. if(f.exists())
  629. f.rm_r();
  630. }
  631. catch (IOException ex)
  632. {
  633. ex.printStackTrace();
  634. }
  635. }
  636. };
  637. }