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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * es_out_timeshift.c: Es Out timeshift.
  3.  *****************************************************************************
  4.  * Copyright (C) 2008 Laurent Aimar
  5.  * $Id: 19f4d211056c8e09594239a8c28a7195f156f082 $
  6.  *
  7.  * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. /*****************************************************************************
  24.  * Preamble
  25.  *****************************************************************************/
  26. #ifdef HAVE_CONFIG_H
  27. # include "config.h"
  28. #endif
  29. #include <stdlib.h>
  30. #include <stdio.h>
  31. #include <errno.h>
  32. #include <assert.h>
  33. #if defined (WIN32) && !defined (UNDER_CE)
  34. #  include <direct.h>
  35. #endif
  36. #ifdef HAVE_SYS_STAT_H
  37. #   include <sys/stat.h>
  38. #endif
  39. #include <vlc_common.h>
  40. #include <vlc_charset.h>
  41. #include <vlc_input.h>
  42. #include <vlc_es_out.h>
  43. #include <vlc_block.h>
  44. #include "input_internal.h"
  45. #include "es_out.h"
  46. #include "es_out_timeshift.h"
  47. /*****************************************************************************
  48.  * Local prototypes
  49.  *****************************************************************************/
  50. /* XXX attribute_packed is (and MUST be) used ONLY to reduce memory usage */
  51. #ifdef HAVE_ATTRIBUTE_PACKED
  52. #   define attribute_packed __attribute__((__packed__))
  53. #else
  54. #   define attribute_packed
  55. #endif
  56. enum
  57. {
  58.     C_ADD,
  59.     C_SEND,
  60.     C_DEL,
  61.     C_CONTROL,
  62. };
  63. typedef struct attribute_packed
  64. {
  65.     es_out_id_t *p_es;
  66.     es_format_t *p_fmt;
  67. } ts_cmd_add_t;
  68. typedef struct attribute_packed
  69. {
  70.     es_out_id_t *p_es;
  71. } ts_cmd_del_t;
  72. typedef struct attribute_packed
  73. {
  74.     es_out_id_t *p_es;
  75.     block_t *p_block;
  76.     int     i_offset;  /* We do not use file > INT_MAX */
  77. } ts_cmd_send_t;
  78. typedef struct attribute_packed
  79. {
  80.     int  i_query;
  81.     union
  82.     {
  83.         bool b_bool;
  84.         int  i_int;
  85.         int64_t i_i64;
  86.         es_out_id_t *p_es;
  87.         struct
  88.         {
  89.             int     i_int;
  90.             int64_t i_i64;
  91.         } int_i64;
  92.         struct
  93.         {
  94.             int        i_int;
  95.             vlc_meta_t *p_meta;
  96.         } int_meta;
  97.         struct
  98.         {
  99.             int       i_int;
  100.             vlc_epg_t *p_epg;
  101.         } int_epg;
  102.         struct
  103.         {
  104.             es_out_id_t *p_es;
  105.             bool        b_bool;
  106.         } es_bool;
  107.         struct
  108.         {
  109.             es_out_id_t *p_es;
  110.             es_format_t *p_fmt;
  111.         } es_fmt;
  112.         struct
  113.         {
  114.             /* FIXME Really too big (double make the whole thing too big) */
  115.             double  f_position;
  116.             mtime_t i_time;
  117.             mtime_t i_length;
  118.         } times;
  119.         struct
  120.         {
  121.             mtime_t i_pts_delay;
  122.             int     i_cr_average;
  123.         } jitter;
  124.     };
  125. } ts_cmd_control_t;
  126. typedef struct attribute_packed
  127. {
  128.     int8_t  i_type;
  129.     mtime_t i_date;
  130.     union
  131.     {
  132.         ts_cmd_add_t     add;
  133.         ts_cmd_del_t     del;
  134.         ts_cmd_send_t    send;
  135.         ts_cmd_control_t control;
  136.     };
  137. } ts_cmd_t;
  138. typedef struct ts_storage_t ts_storage_t;
  139. struct ts_storage_t
  140. {
  141.     ts_storage_t *p_next;
  142.     /* */
  143.     char    *psz_file;  /* Filename */
  144.     size_t  i_file_max; /* Max size in bytes */
  145.     int64_t i_file_size;/* Current size in bytes */
  146.     FILE    *p_filew;   /* FILE handle for data writing */
  147.     FILE    *p_filer;   /* FILE handle for data reading */
  148.     /* */
  149.     int      i_cmd_r;
  150.     int      i_cmd_w;
  151.     int      i_cmd_max;
  152.     ts_cmd_t *p_cmd;
  153. };
  154. typedef struct
  155. {
  156.     VLC_COMMON_MEMBERS
  157.     /* */
  158.     input_thread_t *p_input;
  159.     es_out_t       *p_out;
  160.     int64_t        i_tmp_size_max;
  161.     const char     *psz_tmp_path;
  162.     /* Lock for all following fields */
  163.     vlc_mutex_t    lock;
  164.     vlc_cond_t     wait;
  165.     /* */
  166.     bool           b_paused;
  167.     mtime_t        i_pause_date;
  168.     /* */
  169.     int            i_rate;
  170.     int            i_rate_source;
  171.     mtime_t        i_rate_date;
  172.     mtime_t        i_rate_delay;
  173.     /* */
  174.     mtime_t        i_buffering_delay;
  175.     /* */
  176.     ts_storage_t   *p_storage_r;
  177.     ts_storage_t   *p_storage_w;
  178.     mtime_t        i_cmd_delay;
  179. } ts_thread_t;
  180. struct es_out_id_t
  181. {
  182.     es_out_id_t *p_es;
  183. };
  184. struct es_out_sys_t
  185. {
  186.     input_thread_t *p_input;
  187. es_out_t       *p_out;
  188.     /* Configuration */
  189.     int64_t        i_tmp_size_max;    /* Maximal temporary file size in byte */
  190.     char           *psz_tmp_path;     /* Path for temporary files */
  191.     /* Lock for all following fields */
  192.     vlc_mutex_t    lock;
  193.     /* */
  194.     bool           b_delayed;
  195.     ts_thread_t   *p_thread;
  196.     /* */
  197.     bool           b_input_paused;
  198.     bool           b_input_paused_source;
  199.     int            i_input_rate;
  200.     int            i_input_rate_source;
  201.     /* */
  202.     int            i_es;
  203.     es_out_id_t    **pp_es;
  204. };
  205. static es_out_id_t *Add    ( es_out_t *, const es_format_t * );
  206. static int          Send   ( es_out_t *, es_out_id_t *, block_t * );
  207. static void         Del    ( es_out_t *, es_out_id_t * );
  208. static int          Control( es_out_t *, int i_query, va_list );
  209. static void         Destroy( es_out_t * );
  210. static int          TsStart( es_out_t * );
  211. static void         TsAutoStop( es_out_t * );
  212. static void         TsStop( ts_thread_t * );
  213. static void         TsPushCmd( ts_thread_t *, ts_cmd_t * );
  214. static int          TsPopCmdLocked( ts_thread_t *, ts_cmd_t *, bool b_flush );
  215. static bool         TsHasCmd( ts_thread_t * );
  216. static bool         TsIsUnused( ts_thread_t * );
  217. static int          TsChangePause( ts_thread_t *, bool b_source_paused, bool b_paused, mtime_t i_date );
  218. static int          TsChangeRate( ts_thread_t *, int i_src_rate, int i_rate );
  219. static void         *TsRun( vlc_object_t * );
  220. static ts_storage_t *TsStorageNew( const char *psz_path, int64_t i_tmp_size_max );
  221. static void         TsStorageDelete( ts_storage_t * );
  222. static void         TsStoragePack( ts_storage_t *p_storage );
  223. static bool         TsStorageIsFull( ts_storage_t *, const ts_cmd_t *p_cmd );
  224. static bool         TsStorageIsEmpty( ts_storage_t * );
  225. static void         TsStoragePushCmd( ts_storage_t *, const ts_cmd_t *p_cmd, bool b_flush );
  226. static void         TsStoragePopCmd( ts_storage_t *p_storage, ts_cmd_t *p_cmd, bool b_flush );
  227. static void CmdClean( ts_cmd_t * );
  228. static void cmd_cleanup_routine( void *p ) { CmdClean( p ); }
  229. static int  CmdInitAdd    ( ts_cmd_t *, es_out_id_t *, const es_format_t *, bool b_copy );
  230. static void CmdInitSend   ( ts_cmd_t *, es_out_id_t *, block_t * );
  231. static int  CmdInitDel    ( ts_cmd_t *, es_out_id_t * );
  232. static int  CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
  233. /* */
  234. static void CmdCleanAdd    ( ts_cmd_t * );
  235. static void CmdCleanSend   ( ts_cmd_t * );
  236. static void CmdCleanControl( ts_cmd_t *p_cmd );
  237. /* XXX these functions will take the destination es_out_t */
  238. static void CmdExecuteAdd    ( es_out_t *, ts_cmd_t * );
  239. static int  CmdExecuteSend   ( es_out_t *, ts_cmd_t * );
  240. static void CmdExecuteDel    ( es_out_t *, ts_cmd_t * );
  241. static int  CmdExecuteControl( es_out_t *, ts_cmd_t * );
  242. /* File helpers */
  243. static char *GetTmpPath( char *psz_path );
  244. static FILE *GetTmpFile( char **ppsz_file, const char *psz_path );
  245. /*****************************************************************************
  246.  * input_EsOutTimeshiftNew:
  247.  *****************************************************************************/
  248. es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out, int i_rate )
  249. {
  250.     es_out_t *p_out = malloc( sizeof(*p_out) );
  251.     if( !p_out )
  252.         return NULL;
  253.     es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
  254.     if( !p_sys )
  255.     {
  256.         free( p_out );
  257.         return NULL;
  258.     }
  259.     /* */
  260.     p_out->pf_add     = Add;
  261.     p_out->pf_send    = Send;
  262.     p_out->pf_del     = Del;
  263.     p_out->pf_control = Control;
  264.     p_out->pf_destroy = Destroy;
  265.     p_out->p_sys      = p_sys;
  266.     p_out->b_sout     = p_input->p->p_sout != NULL;
  267.     /* */
  268.     p_sys->b_input_paused = false;
  269.     p_sys->b_input_paused_source = false;
  270.     p_sys->p_input = p_input;
  271.     p_sys->i_input_rate = i_rate;
  272.     p_sys->i_input_rate_source = i_rate;
  273.     p_sys->p_out = p_next_out;
  274.     vlc_mutex_init_recursive( &p_sys->lock );
  275.     p_sys->b_delayed = false;
  276.     p_sys->p_thread = NULL;
  277.     TAB_INIT( p_sys->i_es, p_sys->pp_es );
  278.     /* */
  279.     const int i_tmp_size_max = var_CreateGetInteger( p_input, "input-timeshift-granularity" );
  280.     if( i_tmp_size_max < 0 )
  281.         p_sys->i_tmp_size_max = 50*1024*1024;
  282.     else
  283.         p_sys->i_tmp_size_max = __MAX( i_tmp_size_max, 1*1024*1024 );
  284.     msg_Dbg( p_input, "using timeshift granularity of %d MBytes",
  285.              (int)p_sys->i_tmp_size_max/(1024*1024) );
  286.     char *psz_tmp_path = var_CreateGetNonEmptyString( p_input, "input-timeshift-path" );
  287.     p_sys->psz_tmp_path = GetTmpPath( psz_tmp_path );
  288.     msg_Dbg( p_input, "using timeshift path '%s'", p_sys->psz_tmp_path );
  289. #if 0
  290. #define S(t) msg_Err( p_input, "SIZEOF("#t")=%d", sizeof(t) )
  291.     S(ts_cmd_t);
  292.     S(ts_cmd_control_t);
  293.     S(ts_cmd_send_t);
  294.     S(ts_cmd_del_t);
  295.     S(ts_cmd_add_t);
  296. #undef S
  297. #endif
  298.     return p_out;
  299. }
  300. /*****************************************************************************
  301.  * Internal functions
  302.  *****************************************************************************/
  303. static void Destroy( es_out_t *p_out )
  304. {
  305.     es_out_sys_t *p_sys = p_out->p_sys;
  306.     if( p_sys->b_delayed )
  307.     {
  308.         TsStop( p_sys->p_thread );
  309.         p_sys->b_delayed = false;
  310.     }
  311.     while( p_sys->i_es > 0 )
  312.         Del( p_out, p_sys->pp_es[0] );
  313.     TAB_CLEAN( p_sys->i_es, p_sys->pp_es  );
  314.     free( p_sys->psz_tmp_path );
  315.     vlc_mutex_destroy( &p_sys->lock );
  316.     free( p_sys );
  317.     free( p_out );
  318. }
  319. static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
  320. {
  321.     es_out_sys_t *p_sys = p_out->p_sys;
  322.     ts_cmd_t cmd;
  323.     es_out_id_t *p_es = malloc( sizeof( *p_es ) );
  324.     if( !p_es )
  325.         return NULL;
  326.     vlc_mutex_lock( &p_sys->lock );
  327.     TsAutoStop( p_out );
  328.     if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
  329.     {
  330.         vlc_mutex_unlock( &p_sys->lock );
  331.         free( p_es );
  332.         return NULL;
  333.     }
  334.     TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
  335.     if( p_sys->b_delayed )
  336.         TsPushCmd( p_sys->p_thread, &cmd );
  337.     else
  338.         CmdExecuteAdd( p_sys->p_out, &cmd );
  339.     vlc_mutex_unlock( &p_sys->lock );
  340.     return p_es;
  341. }
  342. static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
  343. {
  344.     es_out_sys_t *p_sys = p_out->p_sys;
  345.     ts_cmd_t cmd;
  346.     int i_ret = VLC_SUCCESS;
  347.     vlc_mutex_lock( &p_sys->lock );
  348.     TsAutoStop( p_out );
  349.     CmdInitSend( &cmd, p_es, p_block );
  350.     if( p_sys->b_delayed )
  351.         TsPushCmd( p_sys->p_thread, &cmd );
  352.     else
  353.         i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
  354.     vlc_mutex_unlock( &p_sys->lock );
  355.     return i_ret;
  356. }
  357. static void Del( es_out_t *p_out, es_out_id_t *p_es )
  358. {
  359.     es_out_sys_t *p_sys = p_out->p_sys;
  360.     ts_cmd_t cmd;
  361.     vlc_mutex_lock( &p_sys->lock );
  362.     TsAutoStop( p_out );
  363.     CmdInitDel( &cmd, p_es );
  364.     if( p_sys->b_delayed )
  365.         TsPushCmd( p_sys->p_thread, &cmd );
  366.     else
  367.         CmdExecuteDel( p_sys->p_out, &cmd );
  368.     TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
  369.     vlc_mutex_unlock( &p_sys->lock );
  370. }
  371. static int ControlLockedGetEmpty( es_out_t *p_out, bool *pb_empty )
  372. {
  373.     es_out_sys_t *p_sys = p_out->p_sys;
  374.     if( p_sys->b_delayed && TsHasCmd( p_sys->p_thread ) )
  375.         *pb_empty = false;
  376.     else
  377.         *pb_empty = es_out_GetEmpty( p_sys->p_out );
  378.     return VLC_SUCCESS;
  379. }
  380. static int ControlLockedGetWakeup( es_out_t *p_out, mtime_t *pi_wakeup )
  381. {
  382.     es_out_sys_t *p_sys = p_out->p_sys;
  383.     if( p_sys->b_delayed )
  384.     {
  385.         assert( !p_sys->p_input->p->b_can_pace_control );
  386.         *pi_wakeup = 0;
  387.     }
  388.     else
  389.     {
  390.         *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
  391.     }
  392.     return VLC_SUCCESS;
  393. }
  394. static int ControlLockedGetBuffering( es_out_t *p_out, bool *pb_buffering )
  395. {
  396.     es_out_sys_t *p_sys = p_out->p_sys;
  397.     if( p_sys->b_delayed )
  398.         *pb_buffering = true;
  399.     else
  400.         *pb_buffering = es_out_GetBuffering( p_sys->p_out );
  401.     return VLC_SUCCESS;
  402. }
  403. static int ControlLockedSetPauseState( es_out_t *p_out, bool b_source_paused, bool b_paused, mtime_t i_date )
  404. {
  405.     es_out_sys_t *p_sys = p_out->p_sys;
  406.     int i_ret;
  407.     if( !p_sys->b_delayed && !b_source_paused == !b_paused )
  408.     {
  409.         i_ret = es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
  410.     }
  411.     else
  412.     {
  413.         i_ret = VLC_EGENERIC;
  414.         if( !p_sys->p_input->p->b_can_pace_control )
  415.         {
  416.             if( !p_sys->b_delayed )
  417.                 TsStart( p_out );
  418.             if( p_sys->b_delayed )
  419.                 i_ret = TsChangePause( p_sys->p_thread, b_source_paused, b_paused, i_date );
  420.         }
  421.         else
  422.         {
  423.             /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
  424.              * and so be able to advertize correctly pace control property in access
  425.              * module */
  426.             msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
  427.         }
  428.     }
  429.     if( !i_ret )
  430.     {
  431.         p_sys->b_input_paused_source = b_source_paused;
  432.         p_sys->b_input_paused = b_paused;
  433.     }
  434.     return i_ret;
  435. }
  436. static int ControlLockedSetRate( es_out_t *p_out, int i_src_rate, int i_rate )
  437. {
  438.     es_out_sys_t *p_sys = p_out->p_sys;
  439.     int i_ret;
  440.     if( !p_sys->b_delayed && i_src_rate == i_rate )
  441.     {
  442.         i_ret = es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
  443.     }
  444.     else
  445.     {
  446.         i_ret = VLC_EGENERIC;
  447.         if( !p_sys->p_input->p->b_can_pace_control )
  448.         {
  449.             if( !p_sys->b_delayed )
  450.                 TsStart( p_out );
  451.             if( p_sys->b_delayed )
  452.                 i_ret = TsChangeRate( p_sys->p_thread, i_src_rate, i_rate );
  453.         }
  454.         else
  455.         {
  456.             /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
  457.              * and so be able to advertize correctly pace control property in access
  458.              * module */
  459.             msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
  460.         }
  461.     }
  462.     if( !i_ret )
  463.     {
  464.         p_sys->i_input_rate_source = i_src_rate;
  465.         p_sys->i_input_rate = i_rate;
  466.     }
  467.     return i_ret;
  468. }
  469. static int ControlLockedSetTime( es_out_t *p_out, mtime_t i_date )
  470. {
  471.     es_out_sys_t *p_sys = p_out->p_sys;
  472.     if( !p_sys->b_delayed )
  473.         return es_out_SetTime( p_sys->p_out, i_date );
  474.     /* TODO */
  475.     msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support time change" );
  476.     return VLC_EGENERIC;
  477. }
  478. static int ControlLockedSetFrameNext( es_out_t *p_out )
  479. {
  480.     es_out_sys_t *p_sys = p_out->p_sys;
  481.     return es_out_SetFrameNext( p_sys->p_out );
  482. }
  483. static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
  484. {
  485.     es_out_sys_t *p_sys = p_out->p_sys;
  486.     switch( i_query )
  487.     {
  488.     /* Invalid query for this es_out level */
  489.     case ES_OUT_SET_ES_BY_ID:
  490.     case ES_OUT_RESTART_ES_BY_ID:
  491.     case ES_OUT_SET_ES_DEFAULT_BY_ID:
  492.     case ES_OUT_SET_DELAY:
  493.     case ES_OUT_SET_RECORD_STATE:
  494.         assert(0);
  495.         return VLC_EGENERIC;
  496.     /* Pass-through control */
  497.     case ES_OUT_SET_ACTIVE:
  498.     case ES_OUT_SET_MODE:
  499.     case ES_OUT_SET_GROUP:
  500.     case ES_OUT_SET_PCR:
  501.     case ES_OUT_SET_GROUP_PCR:
  502.     case ES_OUT_RESET_PCR:
  503.     case ES_OUT_SET_NEXT_DISPLAY_TIME:
  504.     case ES_OUT_SET_GROUP_META:
  505.     case ES_OUT_SET_GROUP_EPG:
  506.     case ES_OUT_SET_ES_SCRAMBLED_STATE:
  507.     case ES_OUT_DEL_GROUP:
  508.     case ES_OUT_SET_META:
  509.     case ES_OUT_SET_ES:
  510.     case ES_OUT_RESTART_ES:
  511.     case ES_OUT_SET_ES_DEFAULT:
  512.     case ES_OUT_SET_ES_STATE:
  513.     case ES_OUT_SET_ES_FMT:
  514.     case ES_OUT_SET_TIMES:
  515.     case ES_OUT_SET_JITTER:
  516.     {
  517.         ts_cmd_t cmd;
  518.         if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
  519.             return VLC_EGENERIC;
  520.         if( p_sys->b_delayed )
  521.         {
  522.             TsPushCmd( p_sys->p_thread, &cmd );
  523.             return VLC_SUCCESS;
  524.         }
  525.         return CmdExecuteControl( p_sys->p_out, &cmd );
  526.     }
  527.     /* Special control when delayed */
  528.     case ES_OUT_GET_ES_STATE:
  529.     {
  530.         es_out_id_t *p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
  531.         bool *pb_enabled = (bool*)va_arg( args, bool* );
  532.         if( p_sys->b_delayed )
  533.         {
  534.             *pb_enabled = true;
  535.             return VLC_SUCCESS;
  536.         }
  537.         return es_out_Control( p_sys->p_out, ES_OUT_GET_ES_STATE, p_es->p_es, pb_enabled );
  538.     }
  539.     /* Special internal input control */
  540.     case ES_OUT_GET_EMPTY:
  541.     {
  542.         bool *pb_empty = (bool*)va_arg( args, bool* );
  543.         return ControlLockedGetEmpty( p_out, pb_empty );
  544.     }
  545.     case ES_OUT_GET_WAKE_UP: /* TODO ? */
  546.     {
  547.         mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
  548.         return ControlLockedGetWakeup( p_out, pi_wakeup );
  549.     }
  550.     case ES_OUT_GET_BUFFERING:
  551.     {
  552.         bool *pb_buffering = (bool *)va_arg( args, bool* );
  553.         return ControlLockedGetBuffering( p_out, pb_buffering );
  554.     }
  555.     case ES_OUT_SET_PAUSE_STATE:
  556.     {
  557.         const bool b_source_paused = (bool)va_arg( args, int );
  558.         const bool b_paused = (bool)va_arg( args, int );
  559.         const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
  560.         return ControlLockedSetPauseState( p_out, b_source_paused, b_paused, i_date );
  561.     }
  562.     case ES_OUT_SET_RATE:
  563.     {
  564.         const int i_src_rate = (int)va_arg( args, int );
  565.         const int i_rate = (int)va_arg( args, int );
  566.         return ControlLockedSetRate( p_out, i_src_rate, i_rate );
  567.     }
  568.     case ES_OUT_SET_TIME:
  569.     {
  570.         const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
  571.         return ControlLockedSetTime( p_out, i_date );
  572.     }
  573.     case ES_OUT_SET_FRAME_NEXT:
  574.     {
  575.         return ControlLockedSetFrameNext( p_out );
  576.     }
  577.     default:
  578.         msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
  579.         assert(0);
  580.         return VLC_EGENERIC;
  581.     }
  582. }
  583. static int Control( es_out_t *p_out, int i_query, va_list args )
  584. {
  585.     es_out_sys_t *p_sys = p_out->p_sys;
  586.     int i_ret;
  587.     vlc_mutex_lock( &p_sys->lock );
  588.     TsAutoStop( p_out );
  589.     i_ret = ControlLocked( p_out, i_query, args );
  590.     vlc_mutex_unlock( &p_sys->lock );
  591.     return i_ret;
  592. }
  593. /*****************************************************************************
  594.  *
  595.  *****************************************************************************/
  596. static void TsDestructor( vlc_object_t *p_this )
  597. {
  598.     ts_thread_t *p_ts = (ts_thread_t*)p_this;
  599.     vlc_cond_destroy( &p_ts->wait );
  600.     vlc_mutex_destroy( &p_ts->lock );
  601. }
  602. static int TsStart( es_out_t *p_out )
  603. {
  604.     es_out_sys_t *p_sys = p_out->p_sys;
  605.     ts_thread_t *p_ts;
  606.     assert( !p_sys->b_delayed );
  607.     p_sys->p_thread = p_ts = vlc_custom_create( p_sys->p_input, sizeof(ts_thread_t),
  608.                                                 VLC_OBJECT_GENERIC, "es out timeshift" );
  609.     if( !p_ts )
  610.         return VLC_EGENERIC;
  611.     p_ts->i_tmp_size_max = p_sys->i_tmp_size_max;
  612.     p_ts->psz_tmp_path = p_sys->psz_tmp_path;
  613.     p_ts->p_input = p_sys->p_input;
  614.     p_ts->p_out = p_sys->p_out;
  615.     vlc_mutex_init( &p_ts->lock );
  616.     vlc_cond_init( &p_ts->wait );
  617.     p_ts->b_paused = p_sys->b_input_paused && !p_sys->b_input_paused_source;
  618.     p_ts->i_pause_date = p_ts->b_paused ? mdate() : -1;
  619.     p_ts->i_rate_source = p_sys->i_input_rate_source;
  620.     p_ts->i_rate        = p_sys->i_input_rate;
  621.     p_ts->i_rate_date = -1;
  622.     p_ts->i_rate_delay = 0;
  623.     p_ts->i_buffering_delay = 0;
  624.     p_ts->i_cmd_delay = 0;
  625.     p_ts->p_storage_r = NULL;
  626.     p_ts->p_storage_w = NULL;
  627.     vlc_object_set_destructor( p_ts, TsDestructor );
  628.     p_sys->b_delayed = true;
  629.     if( vlc_thread_create( p_ts, "es out timeshift",
  630.                            TsRun, VLC_THREAD_PRIORITY_INPUT ) )
  631.     {
  632.         msg_Err( p_sys->p_input, "cannot create input thread" );
  633.         vlc_object_release( p_ts );
  634.         p_sys->b_delayed = false;
  635.         return VLC_EGENERIC;
  636.     }
  637.     return VLC_SUCCESS;
  638. }
  639. static void TsAutoStop( es_out_t *p_out )
  640. {
  641.     es_out_sys_t *p_sys = p_out->p_sys;
  642.     if( !p_sys->b_delayed || !TsIsUnused( p_sys->p_thread ) )
  643.         return;
  644.     msg_Warn( p_sys->p_input, "es out timeshift: auto stop" );
  645.     TsStop( p_sys->p_thread );
  646.     p_sys->b_delayed = false;
  647. }
  648. static void TsStop( ts_thread_t *p_ts )
  649. {
  650.     vlc_object_kill( p_ts );
  651.     vlc_thread_join( p_ts );
  652.     vlc_mutex_lock( &p_ts->lock );
  653.     for( ;; )
  654.     {
  655.         ts_cmd_t cmd;
  656.         if( TsPopCmdLocked( p_ts, &cmd, true ) )
  657.             break;
  658.         CmdClean( &cmd );
  659.     }
  660.     assert( !p_ts->p_storage_r || !p_ts->p_storage_r->p_next );
  661.     if( p_ts->p_storage_r )
  662.         TsStorageDelete( p_ts->p_storage_r );
  663.     vlc_mutex_unlock( &p_ts->lock );
  664.     vlc_object_release( p_ts );
  665. }
  666. static void TsPushCmd( ts_thread_t *p_ts, ts_cmd_t *p_cmd )
  667. {
  668.     vlc_mutex_lock( &p_ts->lock );
  669.     if( !p_ts->p_storage_w || TsStorageIsFull( p_ts->p_storage_w, p_cmd ) )
  670.     {
  671.         ts_storage_t *p_storage = TsStorageNew( p_ts->psz_tmp_path, p_ts->i_tmp_size_max );
  672.         if( !p_storage )
  673.         {
  674.             CmdClean( p_cmd );
  675.             vlc_mutex_unlock( &p_ts->lock );
  676.             /* TODO warn the user (but only once) */
  677.             return;
  678.         }
  679.         if( !p_ts->p_storage_w )
  680.         {
  681.             p_ts->p_storage_r = p_ts->p_storage_w = p_storage;
  682.         }
  683.         else
  684.         {
  685.             TsStoragePack( p_ts->p_storage_w );
  686.             p_ts->p_storage_w->p_next = p_storage;
  687.             p_ts->p_storage_w = p_storage;
  688.         }
  689.     }
  690.     /* TODO return error and warn the user (but only once) */
  691.     TsStoragePushCmd( p_ts->p_storage_w, p_cmd, p_ts->p_storage_r == p_ts->p_storage_w );
  692.     vlc_cond_signal( &p_ts->wait );
  693.     vlc_mutex_unlock( &p_ts->lock );
  694. }
  695. static int TsPopCmdLocked( ts_thread_t *p_ts, ts_cmd_t *p_cmd, bool b_flush )
  696. {
  697.     vlc_assert_locked( &p_ts->lock );
  698.     if( TsStorageIsEmpty( p_ts->p_storage_r ) )
  699.         return VLC_EGENERIC;
  700.     TsStoragePopCmd( p_ts->p_storage_r, p_cmd, b_flush );
  701.     while( p_ts->p_storage_r && TsStorageIsEmpty( p_ts->p_storage_r ) )
  702.     {
  703.         ts_storage_t *p_next = p_ts->p_storage_r->p_next;
  704.         if( !p_next )
  705.             break;
  706.         TsStorageDelete( p_ts->p_storage_r );
  707.         p_ts->p_storage_r = p_next;
  708.     }
  709.     return VLC_SUCCESS;
  710. }
  711. static bool TsHasCmd( ts_thread_t *p_ts )
  712. {
  713.     bool b_cmd;
  714.     vlc_mutex_lock( &p_ts->lock );
  715.     b_cmd =  TsStorageIsEmpty( p_ts->p_storage_r );
  716.     vlc_mutex_unlock( &p_ts->lock );
  717.     return b_cmd;
  718. }
  719. static bool TsIsUnused( ts_thread_t *p_ts )
  720. {
  721.     bool b_unused;
  722.     vlc_mutex_lock( &p_ts->lock );
  723.     b_unused = !p_ts->b_paused &&
  724.                p_ts->i_rate == p_ts->i_rate_source &&
  725.                TsStorageIsEmpty( p_ts->p_storage_r );
  726.     vlc_mutex_unlock( &p_ts->lock );
  727.     return b_unused;
  728. }
  729. static int TsChangePause( ts_thread_t *p_ts, bool b_source_paused, bool b_paused, mtime_t i_date )
  730. {
  731.     vlc_mutex_lock( &p_ts->lock );
  732.     int i_ret;
  733.     if( b_paused )
  734.     {
  735.         assert( !b_source_paused );
  736.         i_ret = es_out_SetPauseState( p_ts->p_out, true, true, i_date );
  737.     }
  738.     else
  739.     {
  740.         i_ret = es_out_SetPauseState( p_ts->p_out, false, false, i_date );
  741.     }
  742.     if( !i_ret )
  743.     {
  744.         if( !b_paused )
  745.         {
  746.             assert( p_ts->i_pause_date > 0 );
  747.             p_ts->i_cmd_delay += i_date - p_ts->i_pause_date;
  748.         }
  749.         p_ts->b_paused = b_paused;
  750.         p_ts->i_pause_date = i_date;
  751.         vlc_cond_signal( &p_ts->wait );
  752.     }
  753.     vlc_mutex_unlock( &p_ts->lock );
  754.     return i_ret;
  755. }
  756. static int TsChangeRate( ts_thread_t *p_ts, int i_src_rate, int i_rate )
  757. {
  758.     int i_ret;
  759.     vlc_mutex_lock( &p_ts->lock );
  760.     p_ts->i_cmd_delay += p_ts->i_rate_delay;
  761.     p_ts->i_rate_date = -1;
  762.     p_ts->i_rate_delay = 0;
  763.     p_ts->i_rate = i_rate;
  764.     p_ts->i_rate_source = i_src_rate;
  765.     i_ret = es_out_SetRate( p_ts->p_out, i_rate, i_rate );
  766.     vlc_mutex_unlock( &p_ts->lock );
  767.     return i_ret;
  768. }
  769. static void *TsRun( vlc_object_t *p_thread )
  770. {
  771.     ts_thread_t *p_ts = (ts_thread_t*)p_thread;
  772.     mtime_t i_buffering_date = -1;
  773.     for( ;; )
  774.     {
  775.         ts_cmd_t cmd;
  776.         mtime_t  i_deadline;
  777.         bool b_buffering;
  778.         /* Pop a command to execute */
  779.         vlc_mutex_lock( &p_ts->lock );
  780.         mutex_cleanup_push( &p_ts->lock );
  781.         for( ;; )
  782.         {
  783.             const int canc = vlc_savecancel();
  784.             b_buffering = es_out_GetBuffering( p_ts->p_out );
  785.             if( ( !p_ts->b_paused || b_buffering ) && !TsPopCmdLocked( p_ts, &cmd, false ) )
  786.             {
  787.                 vlc_restorecancel( canc );
  788.                 break;
  789.             }
  790.             vlc_restorecancel( canc );
  791.             vlc_cond_wait( &p_ts->wait, &p_ts->lock );
  792.         }
  793.         if( b_buffering && i_buffering_date < 0 )
  794.         {
  795.             i_buffering_date = cmd.i_date;
  796.         }
  797.         else if( i_buffering_date > 0 )
  798.         {
  799.             p_ts->i_buffering_delay += i_buffering_date - cmd.i_date; /* It is < 0 */
  800.             if( b_buffering )
  801.                 i_buffering_date = cmd.i_date;
  802.             else
  803.                 i_buffering_date = -1;
  804.         }
  805.         if( p_ts->i_rate_date < 0 )
  806.             p_ts->i_rate_date = cmd.i_date;
  807.         p_ts->i_rate_delay = 0;
  808.         if( p_ts->i_rate_source != p_ts->i_rate )
  809.         {
  810.             const mtime_t i_duration = cmd.i_date - p_ts->i_rate_date;
  811.             p_ts->i_rate_delay = i_duration * p_ts->i_rate / p_ts->i_rate_source - i_duration;
  812.         }
  813.         if( p_ts->i_cmd_delay + p_ts->i_rate_delay + p_ts->i_buffering_delay < 0 && p_ts->i_rate != p_ts->i_rate_source )
  814.         {
  815.             const int canc = vlc_savecancel();
  816.             /* Auto reset to rate 1.0 */
  817.             msg_Warn( p_ts->p_input, "es out timeshift: auto reset rate to %d", p_ts->i_rate_source );
  818.             p_ts->i_cmd_delay = 0;
  819.             p_ts->i_buffering_delay = 0;
  820.             p_ts->i_rate_delay = 0;
  821.             p_ts->i_rate_date = -1;
  822.             p_ts->i_rate = p_ts->i_rate_source;
  823.             if( !es_out_SetRate( p_ts->p_out, p_ts->i_rate_source, p_ts->i_rate ) )
  824.             {
  825.                 vlc_value_t val = { .i_int = p_ts->i_rate };
  826.                 /* Warn back input
  827.                  * FIXME it is perfectly safe BUT it is ugly as it may hide a
  828.                  * rate change requested by user */
  829.                 input_ControlPush( p_ts->p_input, INPUT_CONTROL_SET_RATE, &val );
  830.             }
  831.             vlc_restorecancel( canc );
  832.         }
  833.         i_deadline = cmd.i_date + p_ts->i_cmd_delay + p_ts->i_rate_delay + p_ts->i_buffering_delay;
  834.         vlc_cleanup_run();
  835.         /* Regulate the speed of command processing to the same one than
  836.          * reading  */
  837.         vlc_cleanup_push( cmd_cleanup_routine, &cmd );
  838.         mwait( i_deadline );
  839.         vlc_cleanup_pop();
  840.         /* Execute the command  */
  841.         const int canc = vlc_savecancel();
  842.         switch( cmd.i_type )
  843.         {
  844.         case C_ADD:
  845.             CmdExecuteAdd( p_ts->p_out, &cmd );
  846.             CmdCleanAdd( &cmd );
  847.             break;
  848.         case C_SEND:
  849.             CmdExecuteSend( p_ts->p_out, &cmd );
  850.             CmdCleanSend( &cmd );
  851.             break;
  852.         case C_CONTROL:
  853.             CmdExecuteControl( p_ts->p_out, &cmd );
  854.             CmdCleanControl( &cmd );
  855.             break;
  856.         case C_DEL:
  857.             CmdExecuteDel( p_ts->p_out, &cmd );
  858.             break;
  859.         default:
  860.             assert(0);
  861.             break;
  862.         }
  863.         vlc_restorecancel( canc );
  864.     }
  865.     return NULL;
  866. }
  867. /*****************************************************************************
  868.  *
  869.  *****************************************************************************/
  870. static ts_storage_t *TsStorageNew( const char *psz_tmp_path, int64_t i_tmp_size_max )
  871. {
  872.     ts_storage_t *p_storage = calloc( 1, sizeof(ts_storage_t) );
  873.     if( !p_storage )
  874.         return NULL;
  875.     /* */
  876.     p_storage->p_next = NULL;
  877.     /* */
  878.     p_storage->i_file_max = i_tmp_size_max;
  879.     p_storage->i_file_size = 0;
  880.     p_storage->p_filew = GetTmpFile( &p_storage->psz_file, psz_tmp_path );
  881.     if( p_storage->psz_file )
  882.         p_storage->p_filer = utf8_fopen( p_storage->psz_file, "rb" );
  883.     /* */
  884.     p_storage->i_cmd_w = 0;
  885.     p_storage->i_cmd_r = 0;
  886.     p_storage->i_cmd_max = 30000;
  887.     p_storage->p_cmd = malloc( p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) );
  888.     //fprintf( stderr, "nSTORAGE name=%s size=%d kbytesn", p_storage->psz_file, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) /1024 );
  889.     if( !p_storage->p_cmd || !p_storage->p_filew || !p_storage->p_filer )
  890.     {
  891.         TsStorageDelete( p_storage );
  892.         return NULL;
  893.     }
  894.     return p_storage;
  895. }
  896. static void TsStorageDelete( ts_storage_t *p_storage )
  897. {
  898.     while( p_storage->i_cmd_r < p_storage->i_cmd_w )
  899.     {
  900.         ts_cmd_t cmd;
  901.         TsStoragePopCmd( p_storage, &cmd, true );
  902.         CmdClean( &cmd );
  903.     }
  904.     free( p_storage->p_cmd );
  905.     if( p_storage->p_filer )
  906.         fclose( p_storage->p_filer );
  907.     if( p_storage->p_filew )
  908.         fclose( p_storage->p_filew );
  909.     if( p_storage->psz_file )
  910.     {
  911.         utf8_unlink( p_storage->psz_file );
  912.         free( p_storage->psz_file );
  913.     }
  914.     free( p_storage );
  915. }
  916. static void TsStoragePack( ts_storage_t *p_storage )
  917. {
  918.     /* Try to release a bit of memory */
  919.     if( p_storage->i_cmd_w >= p_storage->i_cmd_max )
  920.         return;
  921.     p_storage->i_cmd_max = __MAX( p_storage->i_cmd_w, 1 );
  922.     ts_cmd_t *p_new = realloc( p_storage->p_cmd, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) );
  923.     if( p_new )
  924.         p_storage->p_cmd = p_new;
  925. }
  926. static bool TsStorageIsFull( ts_storage_t *p_storage, const ts_cmd_t *p_cmd )
  927. {
  928.     if( p_cmd && p_cmd->i_type == C_SEND && p_storage->i_cmd_w > 0 )
  929.     {
  930.         size_t i_size = sizeof(*p_cmd->send.p_block) + p_cmd->send.p_block->i_buffer;
  931.         if( p_storage->i_file_size + i_size >= p_storage->i_file_max )
  932.             return true;
  933.     }
  934.     return p_storage->i_cmd_w >= p_storage->i_cmd_max;
  935. }
  936. static bool TsStorageIsEmpty( ts_storage_t *p_storage )
  937. {
  938.     return !p_storage || p_storage->i_cmd_r >= p_storage->i_cmd_w;
  939. }
  940. static void TsStoragePushCmd( ts_storage_t *p_storage, const ts_cmd_t *p_cmd, bool b_flush )
  941. {
  942.     ts_cmd_t cmd = *p_cmd;
  943.     assert( !TsStorageIsFull( p_storage, p_cmd ) );
  944.     if( cmd.i_type == C_SEND )
  945.     {
  946.         block_t *p_block = cmd.send.p_block;
  947.         cmd.send.p_block = NULL;
  948.         cmd.send.i_offset = ftell( p_storage->p_filew );
  949.         if( fwrite( p_block, sizeof(*p_block), 1, p_storage->p_filew ) != 1 )
  950.         {
  951.             block_Release( p_block );
  952.             return;
  953.         }
  954.         p_storage->i_file_size += sizeof(*p_block);
  955.         if( p_block->i_buffer > 0 )
  956.         {
  957.             if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, p_storage->p_filew ) != 1 )
  958.             {
  959.                 block_Release( p_block );
  960.                 return;
  961.             }
  962.         }
  963.         p_storage->i_file_size += p_block->i_buffer;
  964.         block_Release( p_block );
  965.         if( b_flush )
  966.             fflush( p_storage->p_filew );
  967.     }
  968.     p_storage->p_cmd[p_storage->i_cmd_w++] = cmd;
  969. }
  970. static void TsStoragePopCmd( ts_storage_t *p_storage, ts_cmd_t *p_cmd, bool b_flush )
  971. {
  972.     assert( !TsStorageIsEmpty( p_storage ) );
  973.     *p_cmd = p_storage->p_cmd[p_storage->i_cmd_r++];
  974.     if( p_cmd->i_type == C_SEND )
  975.     {
  976.         block_t block;
  977.         if( !b_flush &&
  978.             !fseek( p_storage->p_filer, p_cmd->send.i_offset, SEEK_SET ) &&
  979.             fread( &block, sizeof(block), 1, p_storage->p_filer ) == 1 )
  980.         {
  981.             block_t *p_block = block_Alloc( block.i_buffer );
  982.             if( p_block )
  983.             {
  984.                 p_block->i_dts      = block.i_dts;
  985.                 p_block->i_pts      = block.i_pts;
  986.                 p_block->i_flags    = block.i_flags;
  987.                 p_block->i_length   = block.i_length;
  988.                 p_block->i_rate     = block.i_rate;
  989.                 p_block->i_samples  = block.i_samples;
  990.                 p_block->i_buffer = fread( p_block->p_buffer, 1, block.i_buffer, p_storage->p_filer );
  991.             }
  992.             p_cmd->send.p_block = p_block;
  993.         }
  994.         else
  995.         {
  996.             //fprintf( stderr, "TsStoragePopCmd: %mn" );
  997.             p_cmd->send.p_block = block_Alloc( 1 );
  998.         }
  999.     }
  1000. }
  1001. /*****************************************************************************
  1002.  *
  1003.  *****************************************************************************/
  1004. static void CmdClean( ts_cmd_t *p_cmd )
  1005. {
  1006.     switch( p_cmd->i_type )
  1007.     {
  1008.     case C_ADD:
  1009.         CmdCleanAdd( p_cmd );
  1010.         break;
  1011.     case C_SEND:
  1012.         CmdCleanSend( p_cmd );
  1013.         break;
  1014.     case C_CONTROL:
  1015.         CmdCleanControl( p_cmd );
  1016.         break;
  1017.     case C_DEL:
  1018.         break;
  1019.     default:
  1020.         assert(0);
  1021.         break;
  1022.     }
  1023. }
  1024. static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
  1025. {
  1026.     p_cmd->i_type = C_ADD;
  1027.     p_cmd->i_date = mdate();
  1028.     p_cmd->add.p_es = p_es;
  1029.     if( b_copy )
  1030.     {
  1031.         p_cmd->add.p_fmt = malloc( sizeof(*p_fmt) );
  1032.         if( !p_cmd->add.p_fmt )
  1033.             return VLC_EGENERIC;
  1034.         es_format_Copy( p_cmd->add.p_fmt, p_fmt );
  1035.     }
  1036.     else
  1037.     {
  1038.         p_cmd->add.p_fmt = (es_format_t*)p_fmt;
  1039.     }
  1040.     return VLC_SUCCESS;
  1041. }
  1042. static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
  1043. {
  1044.     p_cmd->add.p_es->p_es = es_out_Add( p_out, p_cmd->add.p_fmt );
  1045. }
  1046. static void CmdCleanAdd( ts_cmd_t *p_cmd )
  1047. {
  1048.     es_format_Clean( p_cmd->add.p_fmt );
  1049.     free( p_cmd->add.p_fmt );
  1050. }
  1051. static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
  1052. {
  1053.     p_cmd->i_type = C_SEND;
  1054.     p_cmd->i_date = mdate();
  1055.     p_cmd->send.p_es = p_es;
  1056.     p_cmd->send.p_block = p_block;
  1057. }
  1058. static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
  1059. {
  1060.     block_t *p_block = p_cmd->send.p_block;
  1061.     p_cmd->send.p_block = NULL;
  1062.     if( p_block )
  1063.     {
  1064.         if( p_cmd->send.p_es->p_es )
  1065.             return es_out_Send( p_out, p_cmd->send.p_es->p_es, p_block );
  1066.         block_Release( p_block );
  1067.     }
  1068.     return VLC_EGENERIC;
  1069. }
  1070. static void CmdCleanSend( ts_cmd_t *p_cmd )
  1071. {
  1072.     if( p_cmd->send.p_block )
  1073.         block_Release( p_cmd->send.p_block );
  1074. }
  1075. static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
  1076. {
  1077.     p_cmd->i_type = C_DEL;
  1078.     p_cmd->i_date = mdate();
  1079.     p_cmd->del.p_es = p_es;
  1080.     return VLC_SUCCESS;
  1081. }
  1082. static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
  1083. {
  1084.     if( p_cmd->del.p_es->p_es )
  1085.         es_out_Del( p_out, p_cmd->del.p_es->p_es );
  1086.     free( p_cmd->del.p_es );
  1087. }
  1088. static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
  1089. {
  1090.     p_cmd->i_type = C_CONTROL;
  1091.     p_cmd->i_date = mdate();
  1092.     p_cmd->control.i_query = i_query;
  1093.     switch( i_query )
  1094.     {
  1095.     /* Pass-through control */
  1096.     case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
  1097.         p_cmd->control.b_bool = (bool)va_arg( args, int );
  1098.         break;
  1099.     case ES_OUT_SET_MODE:    /* arg1= int                            */
  1100.     case ES_OUT_SET_GROUP:   /* arg1= int                            */
  1101.     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
  1102.         p_cmd->control.i_int = (int)va_arg( args, int );
  1103.         break;
  1104.     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
  1105.     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
  1106.         p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
  1107.         break;
  1108.     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
  1109.         p_cmd->control.int_i64.i_int = (int)va_arg( args, int );
  1110.         p_cmd->control.int_i64.i_i64 = (int64_t)va_arg( args, int64_t );
  1111.         break;
  1112.     case ES_OUT_SET_ES_SCRAMBLED_STATE:
  1113.         p_cmd->control.es_bool.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
  1114.         p_cmd->control.es_bool.b_bool = (bool)va_arg( args, int );
  1115.         break;
  1116.     case ES_OUT_RESET_PCR:           /* no arg */
  1117.         break;
  1118.     case ES_OUT_SET_META:        /* arg1=const vlc_meta_t* */
  1119.     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
  1120.     {
  1121.         if( i_query == ES_OUT_SET_GROUP_META )
  1122.             p_cmd->control.int_meta.i_int = (int)va_arg( args, int );
  1123.         vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
  1124.         if( b_copy )
  1125.         {
  1126.             p_cmd->control.int_meta.p_meta = vlc_meta_New();
  1127.             if( !p_cmd->control.int_meta.p_meta )
  1128.                 return VLC_EGENERIC;
  1129.             vlc_meta_Merge( p_cmd->control.int_meta.p_meta, p_meta );
  1130.         }
  1131.         else
  1132.         {
  1133.             p_cmd->control.int_meta.p_meta = p_meta;
  1134.         }
  1135.         break;
  1136.     }
  1137.     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
  1138.     {
  1139.         p_cmd->control.int_epg.i_int = (int)va_arg( args, int );
  1140.         vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
  1141.         if( b_copy )
  1142.         {
  1143.             p_cmd->control.int_epg.p_epg = vlc_epg_New( p_epg->psz_name );
  1144.             if( !p_cmd->control.int_epg.p_epg )
  1145.                 return VLC_EGENERIC;
  1146.             for( int i = 0; i < p_epg->i_event; i++ )
  1147.             {
  1148.                 vlc_epg_event_t *p_evt = p_epg->pp_event[i];
  1149.                 vlc_epg_AddEvent( p_cmd->control.int_epg.p_epg,
  1150.                                   p_evt->i_start, p_evt->i_duration,
  1151.                                   p_evt->psz_name,
  1152.                                   p_evt->psz_short_description, p_evt->psz_description );
  1153.             }
  1154.             vlc_epg_SetCurrent( p_cmd->control.int_epg.p_epg,
  1155.                                 p_epg->p_current ? p_epg->p_current->i_start : -1 );
  1156.         }
  1157.         else
  1158.         {
  1159.             p_cmd->control.int_epg.p_epg = p_epg;
  1160.         }
  1161.         break;
  1162.     }
  1163.     /* Modified control */
  1164.     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
  1165.     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
  1166.     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
  1167.         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
  1168.         break;
  1169.     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
  1170.         p_cmd->control.es_bool.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
  1171.         p_cmd->control.es_bool.b_bool = (bool)va_arg( args, int );
  1172.         break;
  1173.     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
  1174.     {
  1175.         p_cmd->control.es_fmt.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
  1176.         es_format_t *p_fmt = (es_format_t*)va_arg( args, es_format_t * );
  1177.         if( b_copy )
  1178.         {
  1179.             p_cmd->control.es_fmt.p_fmt = malloc( sizeof(*p_fmt) );
  1180.             if( !p_cmd->control.es_fmt.p_fmt )
  1181.                 return VLC_EGENERIC;
  1182.             es_format_Copy( p_cmd->control.es_fmt.p_fmt, p_fmt );
  1183.         }
  1184.         else
  1185.         {
  1186.             p_cmd->control.es_fmt.p_fmt = p_fmt;
  1187.         }
  1188.         break;
  1189.     }
  1190.     case ES_OUT_SET_TIMES:
  1191.     {
  1192.         double f_position = (double)va_arg( args, double );
  1193.         mtime_t i_time = (mtime_t)va_arg( args, mtime_t );
  1194.         mtime_t i_length = (mtime_t)va_arg( args, mtime_t );
  1195.         p_cmd->control.times.f_position = f_position;
  1196.         p_cmd->control.times.i_time = i_time;
  1197.         p_cmd->control.times.i_length = i_length;
  1198.         break;
  1199.     }
  1200.     case ES_OUT_SET_JITTER:
  1201.     {
  1202.         mtime_t i_pts_delay = (mtime_t)va_arg( args, mtime_t );
  1203.         int     i_cr_average = (int)va_arg( args, int );
  1204.         p_cmd->control.jitter.i_pts_delay = i_pts_delay;
  1205.         p_cmd->control.jitter.i_cr_average = i_cr_average;
  1206.         break;
  1207.     }
  1208.     default:
  1209.         assert(0);
  1210.         return VLC_EGENERIC;
  1211.     }
  1212.     return VLC_SUCCESS;
  1213. }
  1214. static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
  1215. {
  1216.     const int i_query = p_cmd->control.i_query;
  1217.     switch( i_query )
  1218.     {
  1219.     /* Pass-through control */
  1220.     case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
  1221.         return es_out_Control( p_out, i_query, p_cmd->control.b_bool );
  1222.     case ES_OUT_SET_MODE:    /* arg1= int                            */
  1223.     case ES_OUT_SET_GROUP:   /* arg1= int                            */
  1224.     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
  1225.         return es_out_Control( p_out, i_query, p_cmd->control.i_int );
  1226.     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
  1227.     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
  1228.         return es_out_Control( p_out, i_query, p_cmd->control.i_i64 );
  1229.     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
  1230.         return es_out_Control( p_out, i_query, p_cmd->control.int_i64.i_int,
  1231.                                                p_cmd->control.int_i64.i_i64 );
  1232.     case ES_OUT_RESET_PCR:           /* no arg */
  1233.         return es_out_Control( p_out, i_query );
  1234.     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
  1235.         return es_out_Control( p_out, i_query, p_cmd->control.int_meta.i_int,
  1236.                                                p_cmd->control.int_meta.p_meta );
  1237.     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
  1238.         return es_out_Control( p_out, i_query, p_cmd->control.int_epg.i_int,
  1239.                                                p_cmd->control.int_epg.p_epg );
  1240.     case ES_OUT_SET_ES_SCRAMBLED_STATE: /* arg1=int es_out_id_t* arg2=bool */
  1241.         return es_out_Control( p_out, i_query, p_cmd->control.es_bool.p_es->p_es,
  1242.                                                p_cmd->control.es_bool.b_bool );
  1243.     case ES_OUT_SET_META:  /* arg1=const vlc_meta_t* */
  1244.         return es_out_Control( p_out, i_query, p_cmd->control.int_meta.p_meta );
  1245.     /* Modified control */
  1246.     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
  1247.     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
  1248.     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
  1249.         return es_out_Control( p_out, i_query, p_cmd->control.p_es->p_es );
  1250.     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
  1251.         return es_out_Control( p_out, i_query, p_cmd->control.es_bool.p_es->p_es,
  1252.                                                p_cmd->control.es_bool.b_bool );
  1253.     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
  1254.         return es_out_Control( p_out, i_query, p_cmd->control.es_fmt.p_es->p_es,
  1255.                                                p_cmd->control.es_fmt.p_fmt );
  1256.     case ES_OUT_SET_TIMES:
  1257.         return es_out_Control( p_out, i_query, p_cmd->control.times.f_position,
  1258.                                                p_cmd->control.times.i_time,
  1259.                                                p_cmd->control.times.i_length );
  1260.     case ES_OUT_SET_JITTER:
  1261.         return es_out_Control( p_out, i_query, p_cmd->control.jitter.i_pts_delay,
  1262.                                                p_cmd->control.jitter.i_cr_average );
  1263.     default:
  1264.         assert(0);
  1265.         return VLC_EGENERIC;
  1266.     }
  1267. }
  1268. static void CmdCleanControl( ts_cmd_t *p_cmd )
  1269. {
  1270.     if( ( p_cmd->control.i_query == ES_OUT_SET_GROUP_META ||
  1271.           p_cmd->control.i_query == ES_OUT_SET_META ) &&
  1272.         p_cmd->control.int_meta.p_meta )
  1273.     {
  1274.         vlc_meta_Delete( p_cmd->control.int_meta.p_meta );
  1275.     }
  1276.     else if( p_cmd->control.i_query == ES_OUT_SET_GROUP_EPG &&
  1277.              p_cmd->control.int_epg.p_epg )
  1278.     {
  1279.         vlc_epg_Delete( p_cmd->control.int_epg.p_epg );
  1280.     }
  1281.     else if( p_cmd->control.i_query == ES_OUT_SET_ES_FMT &&
  1282.              p_cmd->control.es_fmt.p_fmt )
  1283.     {
  1284.         es_format_Clean( p_cmd->control.es_fmt.p_fmt );
  1285.         free( p_cmd->control.es_fmt.p_fmt );
  1286.     }
  1287. }
  1288. /*****************************************************************************
  1289.  * GetTmpFile/Path:
  1290.  *****************************************************************************/
  1291. static char *GetTmpPath( char *psz_path )
  1292. {
  1293.     if( psz_path && *psz_path )
  1294.     {
  1295.         /* Make sure that the path exists and is a directory */
  1296.         struct stat s;
  1297.         const int i_ret = utf8_stat( psz_path, &s );
  1298.         if( i_ret < 0 && !utf8_mkdir( psz_path, 0600 ) )
  1299.             return psz_path;
  1300.         else if( i_ret == 0 && ( s.st_mode & S_IFDIR ) )
  1301.             return psz_path;
  1302.     }
  1303.     free( psz_path );
  1304.     /* Create a suitable path */
  1305. #if defined (WIN32) && !defined (UNDER_CE)
  1306.     const DWORD dwCount = GetTempPathW( 0, NULL );
  1307.     wchar_t *psw_path = calloc( dwCount + 1, sizeof(wchar_t) );
  1308.     if( psw_path )
  1309.     {
  1310.         if( GetTempPathW( dwCount + 1, psw_path ) <= 0 )
  1311.         {
  1312.             free( psw_path );
  1313.             psw_path = _wgetcwd( NULL, 0 );
  1314.         }
  1315.     }
  1316.     psz_path = NULL;
  1317.     if( psw_path )
  1318.     {
  1319.         psz_path = FromWide( psw_path );
  1320.         while( psz_path && *psz_path && psz_path[strlen( psz_path ) - 1] == '\' )
  1321.             psz_path[strlen( psz_path ) - 1] = '';
  1322.         free( psw_path );
  1323.     }
  1324.     if( !psz_path || *psz_path == '' )
  1325.     {
  1326.         free( psz_path );
  1327.         return strdup( "C:" );
  1328.     }
  1329. #else
  1330.     psz_path = strdup( "/tmp" );
  1331. #endif
  1332.     return psz_path;
  1333. }
  1334. static FILE *GetTmpFile( char **ppsz_file, const char *psz_path )
  1335. {
  1336.     char *psz_name;
  1337.     int fd;
  1338.     FILE *f;
  1339.     /* */
  1340.     *ppsz_file = NULL;
  1341.     if( asprintf( &psz_name, "%s/vlc-timeshift.XXXXXX", psz_path ) < 0 )
  1342.         return NULL;
  1343.     /* */
  1344.     fd = utf8_mkstemp( psz_name );
  1345.     *ppsz_file = psz_name;
  1346.     if( fd < 0 )
  1347.         return NULL;
  1348.     /* */
  1349.     f = fdopen( fd, "w+b" );
  1350.     if( !f )
  1351.         close( fd );
  1352.     return f;
  1353. }