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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * record.c: record stream output module
  3.  *****************************************************************************
  4.  * Copyright (C) 2008 the VideoLAN team
  5.  * $Id: ca68231161afbea957c120df47ba0d8277837bcd $
  6.  *
  7.  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  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 <limits.h>
  30. #include <vlc_common.h>
  31. #include <vlc_plugin.h>
  32. #include <vlc_block.h>
  33. #include <vlc_sout.h>
  34. #include <vlc_charset.h>
  35. #include <assert.h>
  36. /*****************************************************************************
  37.  * Exported prototypes
  38.  *****************************************************************************/
  39. static int      Open    ( vlc_object_t * );
  40. static void     Close   ( vlc_object_t * );
  41. /*****************************************************************************
  42.  * Module descriptor
  43.  *****************************************************************************/
  44. #define DST_PREFIX_TEXT N_("Destination prefix")
  45. #define DST_PREFIX_LONGTEXT N_( 
  46.     "Prefix of the destination file automatically generated" )
  47. #define SOUT_CFG_PREFIX "sout-record-"
  48. vlc_module_begin ()
  49.     set_description( N_("Record stream output") )
  50.     set_capability( "sout stream", 0 )
  51.     add_shortcut( "record" )
  52.     set_shortname( N_("Record") )
  53.     set_category( CAT_SOUT )
  54.     set_subcategory( SUBCAT_SOUT_STREAM )
  55.     add_string( SOUT_CFG_PREFIX "dst-prefix", "", NULL, DST_PREFIX_TEXT,
  56.                 DST_PREFIX_LONGTEXT, true )
  57.     set_callbacks( Open, Close )
  58. vlc_module_end ()
  59. /* */
  60. static const char *const ppsz_sout_options[] = {
  61.     "dst-prefix",
  62.     NULL
  63. };
  64. /* */
  65. static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
  66. static int               Del ( sout_stream_t *, sout_stream_id_t * );
  67. static int               Send( sout_stream_t *, sout_stream_id_t *, block_t* );
  68. /* */
  69. struct sout_stream_id_t
  70. {
  71.     es_format_t fmt;
  72.     block_t *p_first;
  73.     block_t **pp_last;
  74.     sout_stream_id_t *id;
  75.     bool b_wait_key;
  76.     bool b_wait_start;
  77. };
  78. struct sout_stream_sys_t
  79. {
  80.     char *psz_prefix;
  81.     sout_stream_t *p_out;
  82.     mtime_t     i_date_start;
  83.     size_t      i_size;
  84.     mtime_t     i_max_wait;
  85.     size_t      i_max_size;
  86.     bool        b_drop;
  87.     int              i_id;
  88.     sout_stream_id_t **id;
  89.     mtime_t     i_dts_start;
  90. };
  91. static void OutputStart( sout_stream_t *p_stream );
  92. static void OutputSend( sout_stream_t *p_stream, sout_stream_id_t *id, block_t * );
  93. /*****************************************************************************
  94.  * Open:
  95.  *****************************************************************************/
  96. static int Open( vlc_object_t *p_this )
  97. {
  98.     sout_stream_t *p_stream = (sout_stream_t*)p_this;
  99.     sout_stream_sys_t *p_sys;
  100.     p_stream->pf_add    = Add;
  101.     p_stream->pf_del    = Del;
  102.     p_stream->pf_send   = Send;
  103.     p_stream->p_sys = p_sys = malloc( sizeof(*p_sys) );
  104.     if( !p_sys )
  105.         return VLC_ENOMEM;
  106.     config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg );
  107.     p_sys->p_out = NULL;
  108.     p_sys->psz_prefix = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "dst-prefix" );
  109.     if( !p_sys->psz_prefix  )
  110.     {
  111.         p_sys->psz_prefix = strdup( "sout-record-" );
  112.         if( !p_sys->psz_prefix )
  113.         {
  114.             free( p_sys );
  115.             return VLC_ENOMEM;
  116.         }
  117.     }
  118.     p_sys->i_date_start = -1;
  119.     p_sys->i_size = 0;
  120. #ifdef OPTIMIZE_MEMORY
  121.     p_sys->i_max_wait = 5*1000000; /* 5s */
  122.     p_sys->i_max_size = 1*1000000; /* 1 Mbyte */
  123. #else
  124.     p_sys->i_max_wait = 30*1000000; /* 30s */
  125.     p_sys->i_max_size = 20*1000000; /* 20 Mbyte */
  126. #endif
  127.     p_sys->b_drop = false;
  128.     p_sys->i_dts_start = 0;
  129.     TAB_INIT( p_sys->i_id, p_sys->id );
  130.     return VLC_SUCCESS;
  131. }
  132. /*****************************************************************************
  133.  * Close:
  134.  *****************************************************************************/
  135. static void Close( vlc_object_t * p_this )
  136. {
  137.     sout_stream_t *p_stream = (sout_stream_t*)p_this;
  138.     sout_stream_sys_t *p_sys = p_stream->p_sys;
  139.     if( p_sys->p_out )
  140.         sout_StreamDelete( p_sys->p_out );
  141.     TAB_CLEAN( p_sys->i_id, p_sys->id );
  142.     free( p_sys->psz_prefix );
  143.     free( p_sys );
  144. }
  145. /*****************************************************************************
  146.  *
  147.  *****************************************************************************/
  148. static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
  149. {
  150.     sout_stream_sys_t *p_sys = p_stream->p_sys;
  151.     sout_stream_id_t *id;
  152.     id = malloc( sizeof(*id) );
  153.     if( !id )
  154.         return NULL;
  155.     es_format_Copy( &id->fmt, p_fmt );
  156.     id->p_first = NULL;
  157.     id->pp_last = &id->p_first;
  158.     id->id = NULL;
  159.     id->b_wait_key = true;
  160.     id->b_wait_start = true;
  161.     TAB_APPEND( p_sys->i_id, p_sys->id, id );
  162.     return id;
  163. }
  164. static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
  165. {
  166.     sout_stream_sys_t *p_sys = p_stream->p_sys;
  167.     if( !p_sys->p_out )
  168.         OutputStart( p_stream );
  169.     if( id->p_first )
  170.         block_ChainRelease( id->p_first );
  171.     assert( !id->id || p_sys->p_out );
  172.     if( id->id )
  173.         sout_StreamIdDel( p_sys->p_out, id->id );
  174.     es_format_Clean( &id->fmt );
  175.     TAB_REMOVE( p_sys->i_id, p_sys->id, id );
  176.     if( p_sys->i_id <= 0 )
  177.     {
  178.         if( !p_sys->p_out )
  179.             p_sys->b_drop = false;
  180.     }
  181.     free( id );
  182.     return VLC_SUCCESS;
  183. }
  184. static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
  185.                  block_t *p_buffer )
  186. {
  187.     sout_stream_sys_t *p_sys = p_stream->p_sys;
  188.     if( p_sys->i_date_start < 0 )
  189.         p_sys->i_date_start = mdate();
  190.     if( !p_sys->p_out &&
  191.         ( mdate() - p_sys->i_date_start > p_sys->i_max_wait ||
  192.           p_sys->i_size > p_sys->i_max_size ) )
  193.     {
  194.         msg_Dbg( p_stream, "Starting recording, waited %ds and %dbyte",
  195.                  (int)((mdate() - p_sys->i_date_start)/1000000), (int)p_sys->i_size );
  196.         OutputStart( p_stream );
  197.     }
  198.     OutputSend( p_stream, id, p_buffer );
  199.     return VLC_SUCCESS;
  200. }
  201. /*****************************************************************************
  202.  *
  203.  *****************************************************************************/
  204. typedef struct
  205. {
  206.     const char  *psz_muxer;
  207.     const char  *psz_extension;
  208.     int         i_es_max;
  209.     vlc_fourcc_t codec[128];
  210. } muxer_properties_t;
  211. #define M(muxer, ext, count, ... ) { .psz_muxer = muxer, .psz_extension = ext, .i_es_max = count, .codec = { __VA_ARGS__, 0 } }
  212. /* Table of native codec support,
  213.  * Do not do non native and non standard association !
  214.  * Muxer will be probe if no entry found */
  215. static const muxer_properties_t p_muxers[] = {
  216.     M( "raw", "mp3", 1,         VLC_FOURCC('m','p','g','a'), VLC_FOURCC('m','p','3',' ') ),
  217.     M( "raw", "a52", 1,         VLC_FOURCC('a','5','2',' ') ),
  218.     M( "raw", "dts", 1,         VLC_FOURCC('d','t','s',' ') ),
  219.     M( "raw", "mpc", 1,         VLC_FOURCC('m','p','c',' ') ),
  220.     M( "raw", "ape", 1,         VLC_FOURCC('A','P','E',' ') ),
  221.     M( "wav", "wav", 1,         VLC_FOURCC('a','r','a','w'), VLC_FOURCC('u','8',' ',' '), VLC_FOURCC('s','1','6','l'),
  222.                                 VLC_FOURCC('s','2','4','l'), VLC_FOURCC('s','3','2','l'), VLC_FOURCC('f','l','3','2') ),
  223.     //M( "ffmpeg{mux=flac}", "flac", 1, VLC_FOURCC('f','l','a','c') ), BROKEN
  224.     M( "ogg", "ogg", INT_MAX,   VLC_FOURCC('v','o','r','b'), VLC_FOURCC('s','p','x',' '), VLC_FOURCC('f','l','a','c'),
  225.                                 VLC_FOURCC('s','u','b','t'), VLC_FOURCC('t','h','e','o'), VLC_FOURCC('d','r','a','c')  ),
  226.     M( "asf", "asf", 127,       VLC_FOURCC('w','m','a','1'), VLC_FOURCC('w','m','a','2'), VLC_FOURCC('w','m','a',' '),
  227.                                 VLC_FOURCC('w','m','a','p'), VLC_FOURCC('w','m','a','l'),
  228.                                 VLC_FOURCC('W','M','V','1'), VLC_FOURCC('W','M','V','2'), VLC_FOURCC('W','M','V','3'),
  229.                                 VLC_FOURCC('W','V','C','1')),
  230.     M( "mp4", "mp4", INT_MAX,   VLC_FOURCC('m','p','4','a'), VLC_FOURCC('h','2','6','4'), VLC_FOURCC('m','p','4','v'),
  231.                                 VLC_FOURCC('s','u','b','t') ),
  232.     M( "ps", "mpg", 16/* FIXME*/,VLC_FOURCC('m','p','g','v'), VLC_FOURCC('m','p','1','v'), VLC_FOURCC('m','p','2','v'),
  233.                                 VLC_FOURCC('m','p','g','a'), VLC_FOURCC('m','p','3',' '), VLC_FOURCC('l','p','c','m'), VLC_FOURCC('a','5','2',' '),
  234.                                 VLC_FOURCC('d','t','s',' '),
  235.                                 VLC_FOURCC('s','p','u',' ') ),
  236.     M( "ts", "ts", 8000,        VLC_FOURCC('m','p','g','v'), VLC_FOURCC('m','p','1','v'), VLC_FOURCC('m','p','2','v'),
  237.                                 VLC_FOURCC('h','2','6','4'),
  238.                                 VLC_FOURCC('m','p','g','a'), VLC_FOURCC('m','p','3',' '), VLC_FOURCC('l','p','c','m'), VLC_FOURCC('a','5','2',' '),
  239.                                 VLC_FOURCC('d','t','s',' '), VLC_FOURCC('m','p','4','a'),
  240.                                 VLC_FOURCC('d','v','b','s'), VLC_FOURCC('t','e','l','x') ),
  241.     M( NULL, NULL, 0, 0 )
  242. };
  243. #undef M
  244. static int OutputNew( sout_stream_t *p_stream,
  245.                       const char *psz_muxer, const char *psz_prefix, const char *psz_extension  )
  246. {
  247.     sout_stream_sys_t *p_sys = p_stream->p_sys;
  248.     char *psz_output;
  249.     int i_count;
  250.     if( asprintf( &psz_output, "std{access=file,mux='%s',dst='%s%s%s'}",
  251.                   psz_muxer, psz_prefix, psz_extension ? "." : "", psz_extension ? psz_extension : "" ) < 0 )
  252.         return -1;
  253.     /* Create the output */
  254.     msg_Dbg( p_stream, "Using record output `%s'", psz_output );
  255.     p_sys->p_out = sout_StreamNew( p_stream->p_sout, psz_output );
  256.     free( psz_output );
  257.     if( !p_sys->p_out )
  258.         return -1;
  259.     /* Add es */
  260.     i_count = 0;
  261.     for( int i = 0; i < p_sys->i_id; i++ )
  262.     {
  263.         sout_stream_id_t *id = p_sys->id[i];
  264.         id->id = sout_StreamIdAdd( p_sys->p_out, &id->fmt );
  265.         if( id->id )
  266.             i_count++;
  267.     }
  268.     return i_count;
  269. }
  270. static void OutputStart( sout_stream_t *p_stream )
  271. {
  272.     sout_stream_sys_t *p_sys = p_stream->p_sys;
  273.     /* */
  274.     if( p_sys->b_drop )
  275.         return;
  276.     /* From now on drop packet that cannot be handled */
  277.     p_sys->b_drop = true;
  278.     /* Detect streams to smart select muxer */
  279.     const char *psz_muxer = NULL;
  280.     const char *psz_extension = NULL;
  281.     /* Look for prefered muxer
  282.      * TODO we could insert transcode in a few cases like
  283.      * s16l <-> s16b
  284.      */
  285.     for( int i = 0; p_muxers[i].psz_muxer != NULL; i++ )
  286.     {
  287.         bool b_ok;
  288.         if( p_sys->i_id > p_muxers[i].i_es_max )
  289.             continue;
  290.         b_ok = true;
  291.         for( int j = 0; j < p_sys->i_id; j++ )
  292.         {
  293.             es_format_t *p_fmt = &p_sys->id[j]->fmt;
  294.             b_ok = false;
  295.             for( int k = 0; p_muxers[i].codec[k] != 0; k++ )
  296.             {
  297.                 if( p_fmt->i_codec == p_muxers[i].codec[k] )
  298.                 {
  299.                     b_ok = true;
  300.                     break;
  301.                 }
  302.             }
  303.             if( !b_ok )
  304.                 break;
  305.         }
  306.         if( !b_ok )
  307.             continue;
  308.         psz_muxer = p_muxers[i].psz_muxer;
  309.         psz_extension = p_muxers[i].psz_extension;
  310.         break;
  311.     }
  312.     /* If failed, brute force our demuxers and select the one that
  313.      * keeps most of our stream */
  314.     if( !psz_muxer || !psz_extension )
  315.     {
  316.         static const char *ppsz_muxers[][2] = {
  317.             { "avi", "avi" }, { "mp4", "mp4" }, { "ogg", "ogg" },
  318.             { "asf", "asf" }, {  "ts",  "ts" }, {  "ps", "mpg" },
  319. #if 0
  320.             // XXX ffmpeg sefault really easily if you try an unsupported codec
  321.             // mov and avi at least segfault
  322.             { "ffmpeg{mux=avi}", "avi" },
  323.             { "ffmpeg{mux=mov}", "mov" },
  324.             { "ffmpeg{mux=mp4}", "mp4" },
  325.             { "ffmpeg{mux=nsv}", "nsv" },
  326.             { "ffmpeg{mux=flv}", "flv" },
  327. #endif
  328.             { NULL, NULL }
  329.         };
  330.         int i_best = 0;
  331.         int i_best_es = 0;
  332.         msg_Warn( p_stream, "failed to find an adequate muxer, probing muxers" );
  333.         for( int i = 0; ppsz_muxers[i][0] != NULL; i++ )
  334.         {
  335.             char *psz_file;
  336.             int i_es;
  337.             psz_file = tempnam( NULL, "vlc" );
  338.             if( !psz_file )
  339.                 continue;
  340.             msg_Dbg( p_stream, "probing muxer %s", ppsz_muxers[i][0] );
  341.             i_es = OutputNew( p_stream, ppsz_muxers[i][0], psz_file, NULL );
  342.             if( i_es < 0 )
  343.             {
  344.                 utf8_unlink( psz_file );
  345.                 free( psz_file );
  346.                 continue;
  347.             }
  348.             /* */
  349.             for( int i = 0; i < p_sys->i_id; i++ )
  350.             {
  351.                 sout_stream_id_t *id = p_sys->id[i];
  352.                 if( id->id )
  353.                     sout_StreamIdDel( p_sys->p_out, id->id );
  354.                 id->id = NULL;
  355.             }
  356.             if( p_sys->p_out )
  357.                 sout_StreamDelete( p_sys->p_out );
  358.             p_sys->p_out = NULL;
  359.             if( i_es > i_best_es )
  360.             {
  361.                 i_best_es = i_es;
  362.                 i_best = i;
  363.                 if( i_best_es >= p_sys->i_id )
  364.                     break;
  365.             }
  366.             utf8_unlink( psz_file );
  367.             free( psz_file );
  368.         }
  369.         /* */
  370.         psz_muxer = ppsz_muxers[i_best][0];
  371.         psz_extension = ppsz_muxers[i_best][1];
  372.         msg_Dbg( p_stream, "using muxer %s with extension %s (%d/%d streams accepted)",
  373.                  psz_muxer, psz_extension, i_best_es, p_sys->i_id );
  374.     }
  375.     /* Create the output */
  376.     if( OutputNew( p_stream, psz_muxer, p_sys->psz_prefix, psz_extension ) < 0 )
  377.     {
  378.         msg_Err( p_stream, "failed to open output");
  379.         return;
  380.     }
  381.     /* Compute highest timestamp of first I over all streams */
  382.     p_sys->i_dts_start = 0;
  383.     for( int i = 0; i < p_sys->i_id; i++ )
  384.     {
  385.         sout_stream_id_t *id = p_sys->id[i];
  386.         block_t *p_block;
  387.         if( !id->id || !id->p_first )
  388.             continue;
  389.         mtime_t i_dts = id->p_first->i_dts;
  390.         for( p_block = id->p_first; p_block != NULL; p_block = p_block->p_next )
  391.         {
  392.             if( p_block->i_flags & BLOCK_FLAG_TYPE_I )
  393.             {
  394.                 i_dts = p_block->i_dts;
  395.                 break;
  396.             }
  397.         }
  398.         if( i_dts > p_sys->i_dts_start )
  399.             p_sys->i_dts_start = i_dts;
  400.     }
  401.     /* Send buffered data */
  402.     for( int i = 0; i < p_sys->i_id; i++ )
  403.     {
  404.         sout_stream_id_t *id = p_sys->id[i];
  405.         if( !id->id )
  406.             continue;
  407.         block_t *p_block = id->p_first;
  408.         while( p_block )
  409.         {
  410.             block_t *p_next = p_block->p_next;
  411.             p_block->p_next = NULL;
  412.             OutputSend( p_stream, id, p_block );
  413.             p_block = p_next;
  414.         }
  415.         id->p_first = NULL;
  416.         id->pp_last = &id->p_first;
  417.     }
  418. }
  419. static void OutputSend( sout_stream_t *p_stream, sout_stream_id_t *id, block_t *p_block )
  420. {
  421.     sout_stream_sys_t *p_sys = p_stream->p_sys;
  422.     if( id->id )
  423.     {
  424.         /* We wait until the first key frame (if needed) and
  425.          * to be beyong i_dts_start (for stream without key frame) */
  426.         if( id->b_wait_key )
  427.         {
  428.             if( p_block->i_flags & BLOCK_FLAG_TYPE_I )
  429.             {
  430.                 id->b_wait_key = false;
  431.                 id->b_wait_start = false;
  432.             }
  433.             if( ( p_block->i_flags & BLOCK_FLAG_TYPE_MASK ) == 0 )
  434.                 id->b_wait_key = false;
  435.         }
  436.         if( id->b_wait_start )
  437.         {
  438.             if( p_block->i_dts >=p_sys->i_dts_start )
  439.                 id->b_wait_start = false;
  440.         }
  441.         if( id->b_wait_key || id->b_wait_start )
  442.             block_ChainRelease( p_block );
  443.         else
  444.             sout_StreamIdSend( p_sys->p_out, id->id, p_block );
  445.     }
  446.     else if( p_sys->b_drop )
  447.     {
  448.         block_ChainRelease( p_block );
  449.     }
  450.     else
  451.     {
  452.         size_t i_size;
  453.         block_ChainProperties( p_block, NULL, &i_size, NULL );
  454.         p_sys->i_size += i_size;
  455.         block_ChainLastAppend( &id->pp_last, p_block );
  456.     }
  457. }