decomp.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:12k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * decomp.c : Decompression module for vlc
  3.  *****************************************************************************
  4.  * Copyright © 2008-2009 Rémi Denis-Courmont
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU Lesser General Public License as published by
  8.  * the Free Software Foundation; either version 2.1 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  19.  *****************************************************************************/
  20. #ifdef HAVE_CONFIG_H
  21. # include "config.h"
  22. #endif
  23. #include <vlc_common.h>
  24. #include <vlc_plugin.h>
  25. #include <vlc_stream.h>
  26. #include <vlc_network.h>
  27. #include <assert.h>
  28. #include <unistd.h>
  29. #ifndef _POSIX_SPAWN
  30. # define _POSIX_SPAWN (-1)
  31. #endif
  32. #include <fcntl.h>
  33. #if (_POSIX_SPAWN >= 0)
  34. # include <spawn.h>
  35. #endif
  36. #include <sys/wait.h>
  37. #include <sys/ioctl.h>
  38. #if defined (__linux__) && defined (HAVE_VMSPLICE)
  39. # include <sys/uio.h>
  40. # include <sys/mman.h>
  41. #else
  42. # undef HAVE_VMSPLICE
  43. #endif
  44. static int  OpenGzip (vlc_object_t *);
  45. static int  OpenBzip2 (vlc_object_t *);
  46. static int  OpenXZ (vlc_object_t *);
  47. static void Close (vlc_object_t *);
  48. vlc_module_begin ()
  49.     set_description (N_("Decompression"))
  50.     set_category (CAT_INPUT)
  51.     set_subcategory (SUBCAT_INPUT_STREAM_FILTER)
  52.     set_capability ("stream_filter", 20)
  53.     set_callbacks (OpenXZ, Close)
  54.     add_submodule ()
  55.     set_callbacks (OpenBzip2, Close)
  56.     /* TODO: access shortnames for stream_UrlNew() */
  57.     add_submodule ()
  58.     set_callbacks (OpenGzip, Close)
  59. vlc_module_end ()
  60. struct stream_sys_t
  61. {
  62.     block_t      *peeked;
  63.     uint64_t     offset;
  64.     vlc_thread_t thread;
  65.     pid_t        pid;
  66.     int          write_fd, read_fd;
  67. };
  68. static void cloexec (int fd)
  69. {
  70.     int flags = fcntl (fd, F_GETFD);
  71.     fcntl (fd, F_SETFD, FD_CLOEXEC | ((flags != -1) ? flags : 0));
  72. }
  73. extern char **environ;
  74. static const size_t bufsize = 65536;
  75. #ifdef HAVE_VMSPLICE
  76. static void cleanup_mmap (void *addr)
  77. {
  78.     munmap (addr, bufsize);
  79. }
  80. #endif
  81. static void *Thread (void *data)
  82. {
  83.     stream_t *stream = data;
  84.     stream_sys_t *p_sys = stream->p_sys;
  85. #ifdef HAVE_VMSPLICE
  86.     ssize_t page_mask = sysconf (_SC_PAGE_SIZE) - 1;
  87. #endif
  88.     int fd = p_sys->write_fd;
  89.     bool error = false;
  90.     do
  91.     {
  92.         ssize_t len;
  93.         int canc = vlc_savecancel ();
  94. #ifdef HAVE_VMSPLICE
  95.         unsigned char *buf = mmap (NULL, bufsize, PROT_READ|PROT_WRITE,
  96.                                    MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  97.         vlc_cleanup_push (cleanup_mmap, buf);
  98. #else
  99.         unsigned char buf[bufsize];
  100. #endif
  101.         len = stream_Read (stream->p_source, buf, bufsize);
  102.         vlc_restorecancel (canc);
  103.         error = len <= 0;
  104.         for (ssize_t i = 0, j; i < len; i += j)
  105.         {
  106. #ifdef HAVE_VMSPLICE
  107.             if ((len - i) <= page_mask) /* incomplete last page */
  108.                 j = write (fd, buf + i, len - i);
  109.             else
  110.             {
  111.                 struct iovec iov = { buf + i, (len - i) & ~page_mask, };
  112.                 j = vmsplice (fd, &iov, 1, SPLICE_F_GIFT);
  113.             }
  114.             if (j == -1 && errno == ENOSYS) /* vmsplice() not supported */
  115. #endif
  116.             j = write (fd, buf + i, len - i);
  117.             if (j <= 0)
  118.             {
  119.                 if (j == 0)
  120.                     errno = EPIPE;
  121.                 msg_Err (stream, "cannot write data (%m)");
  122.                 error = true;
  123.                 break;
  124.             }
  125.         }
  126. #ifdef HAVE_VMSPLICE
  127.         vlc_cleanup_run (); /* munmap (buf, bufsize) */
  128. #endif
  129.     }
  130.     while (!error);
  131.     msg_Dbg (stream, "compressed stream at EOF");
  132.     return NULL;
  133. }
  134. static int Peek (stream_t *, const uint8_t **, unsigned int);
  135. #define MIN_BLOCK (1 << 10)
  136. #define MAX_BLOCK (1 << 20)
  137. /**
  138.  * Reads decompressed from the decompression program
  139.  * @return -1 for EAGAIN, 0 for EOF, byte count otherwise.
  140.  */
  141. static int Read (stream_t *stream, void *buf, unsigned int buflen)
  142. {
  143.     stream_sys_t *p_sys = stream->p_sys;
  144.     block_t *peeked;
  145.     ssize_t length;
  146.     if (buf == NULL) /* caller skips data, get big enough peek buffer */
  147.         buflen = Peek (stream, &(const uint8_t *){ NULL }, buflen);
  148.     if ((peeked = p_sys->peeked) != NULL)
  149.     {   /* dequeue peeked data */
  150.         length = (buflen > peeked->i_buffer) ? peeked->i_buffer : buflen;
  151.         if (buf != NULL)
  152.         {
  153.             memcpy (buf, peeked->p_buffer, length);
  154.             buf = ((char *)buf) + length;
  155.         }
  156.         buflen -= length;
  157.         peeked->p_buffer += length;
  158.         peeked->i_buffer -= length;
  159.         if (peeked->i_buffer == 0)
  160.         {
  161.             block_Release (peeked);
  162.             p_sys->peeked = NULL;
  163.         }
  164.         p_sys->offset += length;
  165.         if (buflen > 0)
  166.             length += Read (stream, ((char *)buf) + length, buflen - length);
  167.         return length;
  168.     }
  169.     assert ((buf != NULL) || (buflen == 0));
  170.     length = net_Read (stream, p_sys->read_fd, NULL, buf, buflen, false);
  171.     if (length < 0)
  172.         return 0;
  173.     p_sys->offset += length;
  174.     return length;
  175. }
  176. /**
  177.  *
  178.  */
  179. static int Peek (stream_t *stream, const uint8_t **pbuf, unsigned int len)
  180. {
  181.     stream_sys_t *p_sys = stream->p_sys;
  182.     block_t *peeked = p_sys->peeked;
  183.     size_t curlen = 0;
  184.     int fd = p_sys->read_fd;
  185.     if (peeked == NULL)
  186.         peeked = block_Alloc (len);
  187.     else if ((curlen = peeked->i_buffer) < len)
  188.         peeked = block_Realloc (peeked, 0, len);
  189.     if ((p_sys->peeked = peeked) == NULL)
  190.         return 0;
  191.     if (curlen < len)
  192.     {
  193.         ssize_t val = net_Read (stream, fd, NULL, peeked->p_buffer + curlen,
  194.                                 len - curlen, true);
  195.         if (val >= 0)
  196.         {
  197.             curlen += val;
  198.             peeked->i_buffer = curlen;
  199.         }
  200.     }
  201.     *pbuf = peeked->p_buffer;
  202.     return curlen;
  203. }
  204. /**
  205.  *
  206.  */
  207. static int Control (stream_t *stream, int query, va_list args)
  208. {
  209.     stream_sys_t *p_sys = stream->p_sys;
  210.     switch (query)
  211.     {
  212.         case STREAM_CAN_SEEK:
  213.         case STREAM_CAN_FASTSEEK:
  214.             *(va_arg (args, bool *)) = false;
  215.             break;
  216.         case STREAM_GET_POSITION:
  217.             *(va_arg (args, int64_t *)) = p_sys->offset;
  218.             break;
  219.         case STREAM_GET_SIZE:
  220.             *(va_arg (args, int64_t *)) = 0;
  221.             break;
  222.         default:
  223.             return VLC_EGENERIC;
  224.     }
  225.     return VLC_SUCCESS;
  226. }
  227. /**
  228.  * Pipe data through an external executable.
  229.  * @param stream the stream filter object.
  230.  * @param path path to the executable.
  231.  */
  232. static int Open (stream_t *stream, const char *path)
  233. {
  234.     stream_sys_t *p_sys = stream->p_sys = malloc (sizeof (*p_sys));
  235.     if (p_sys == NULL)
  236.         return VLC_ENOMEM;
  237.     stream->pf_read = Read;
  238.     stream->pf_peek = Peek;
  239.     stream->pf_control = Control;
  240.     p_sys->peeked = NULL;
  241.     p_sys->offset = 0;
  242.     p_sys->pid = -1;
  243.     /* I am not a big fan of the pyramid style, but I cannot think of anything
  244.      * better here. There are too many failure cases. */
  245.     int ret = VLC_EGENERIC;
  246.     int comp[2];
  247.     /* We use two pipes rather than one stream socket pair, so that we can
  248.      * use vmsplice() on Linux. */
  249.     if (pipe (comp) == 0)
  250.     {
  251.         cloexec (comp[1]);
  252.         p_sys->write_fd = comp[1];
  253.         int uncomp[2];
  254.         if (pipe (uncomp) == 0)
  255.         {
  256.             cloexec (uncomp[0]);
  257.             p_sys->read_fd = uncomp[0];
  258. #if (_POSIX_SPAWN >= 0)
  259.             posix_spawn_file_actions_t actions;
  260.             if (posix_spawn_file_actions_init (&actions) == 0)
  261.             {
  262.                 char *const argv[] = { (char *)path, NULL };
  263.                 if (!posix_spawn_file_actions_adddup2 (&actions, comp[0], 0)
  264.                  && !posix_spawn_file_actions_addclose (&actions, comp[0])
  265.                  && !posix_spawn_file_actions_adddup2 (&actions, uncomp[1], 1)
  266.                  && !posix_spawn_file_actions_addclose (&actions, uncomp[1])
  267.                  && !posix_spawnp (&p_sys->pid, path, &actions, NULL, argv,
  268.                                    environ))
  269.                 {
  270.                     if (vlc_clone (&p_sys->thread, Thread, stream,
  271.                                    VLC_THREAD_PRIORITY_INPUT) == 0)
  272.                         ret = VLC_SUCCESS;
  273.                 }
  274.                 else
  275.                 {
  276.                     msg_Err (stream, "Cannot execute %s", path);
  277.                     p_sys->pid = -1;
  278.                 }
  279.                 posix_spawn_file_actions_destroy (&actions);
  280.             }
  281. #else /* _POSIX_SPAWN */
  282.             switch (p_sys->pid = fork ())
  283.             {
  284.                 case -1:
  285.                     msg_Err (stream, "Cannot fork (%m)");
  286.                     break;
  287.                 case 0:
  288.                     dup2 (comp[0], 0);
  289.                     close (comp[0]);
  290.                     dup2 (uncomp[1], 1);
  291.                     close (uncomp[1]);
  292.                     execlp (path, path, (char *)NULL);
  293.                     exit (1); /* if we get, execlp() failed! */
  294.                 default:
  295.                     if (vlc_clone (&p_sys->thread, Thread, stream,
  296.                                    VLC_THREAD_PRIORITY_INPUT) == 0)
  297.                         ret = VLC_SUCCESS;
  298.             }
  299. #endif /* _POSIX_SPAWN < 0 */
  300.             close (uncomp[1]);
  301.             if (ret != VLC_SUCCESS)
  302.                 close (uncomp[0]);
  303.         }
  304.         close (comp[0]);
  305.         if (ret != VLC_SUCCESS)
  306.         {
  307.             close (comp[1]);
  308.             if (p_sys->pid != -1)
  309.                 while (waitpid (p_sys->pid, &(int){ 0 }, 0) == -1);
  310.         }
  311.     }
  312.     return ret;
  313. }
  314. /**
  315.  * Releases allocate resources.
  316.  */
  317. static void Close (vlc_object_t *obj)
  318. {
  319.     stream_t *stream = (stream_t *)obj;
  320.     stream_sys_t *p_sys = stream->p_sys;
  321.     int status;
  322.     vlc_cancel (p_sys->thread);
  323.     close (p_sys->read_fd);
  324.     vlc_join (p_sys->thread, NULL);
  325.     close (p_sys->write_fd);
  326.     msg_Dbg (obj, "waiting for PID %u", (unsigned)p_sys->pid);
  327.     while (waitpid (p_sys->pid, &status, 0) == -1);
  328.     msg_Dbg (obj, "exit status %d", status);
  329.     if (p_sys->peeked)
  330.         block_Release (p_sys->peeked);
  331.     free (p_sys);
  332. }
  333. /**
  334.  * Detects gzip file format
  335.  */
  336. static int OpenGzip (vlc_object_t *obj)
  337. {
  338.     stream_t      *stream = (stream_t *)obj;
  339.     const uint8_t *peek;
  340.     if (stream_Peek (stream->p_source, &peek, 3) < 3)
  341.         return VLC_EGENERIC;
  342.     if (memcmp (peek, "x1fx8bx08", 3))
  343.         return VLC_EGENERIC;
  344.     msg_Dbg (obj, "detected gzip compressed stream");
  345.     return Open (stream, "zcat");
  346. }
  347. /**
  348.  * Detects bzip2 file format
  349.  */
  350. static int OpenBzip2 (vlc_object_t *obj)
  351. {
  352.     stream_t      *stream = (stream_t *)obj;
  353.     const uint8_t *peek;
  354.     /* (Try to) parse the bzip2 header */
  355.     if (stream_Peek (stream->p_source, &peek, 10) < 10)
  356.         return VLC_EGENERIC;
  357.     if (memcmp (peek, "BZh", 3) || (peek[3] < '1') || (peek[3] > '9')
  358.      || memcmp (peek + 4, "x31x41x59x26x53x59", 6))
  359.         return VLC_EGENERIC;
  360.     msg_Dbg (obj, "detected bzip2 compressed stream");
  361.     return Open (stream, "bzcat");
  362. }
  363. /**
  364.  * Detects xz file format
  365.  */
  366. static int OpenXZ (vlc_object_t *obj)
  367. {
  368.     stream_t      *stream = (stream_t *)obj;
  369.     const uint8_t *peek;
  370.     /* (Try to) parse the xz stream header */
  371.     if (stream_Peek (stream->p_source, &peek, 8) < 8)
  372.         return VLC_EGENERIC;
  373.     if (memcmp (peek, "xfdx37x7ax58x5a", 6))
  374.         return VLC_EGENERIC;
  375.     msg_Dbg (obj, "detected xz compressed stream");
  376.     return Open (stream, "xzcat");
  377. }