]> git.sesse.net Git - vlc/blob - modules/access_output/file.c
Qt is GPLv2+
[vlc] / modules / access_output / file.c
1 /*****************************************************************************
2  * file.c
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Eric Petit <titer@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <sys/types.h>
34 #include <time.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40
41 #include <vlc_common.h>
42 #include <vlc_plugin.h>
43 #include <vlc_sout.h>
44 #include <vlc_block.h>
45 #include <vlc_fs.h>
46 #include <vlc_strings.h>
47 #include <vlc_dialog.h>
48
49 #if defined( _WIN32 ) || defined( __OS2__ )
50 #   include <io.h>
51 #endif
52
53 #ifndef _WIN32
54 #   include <unistd.h>
55 #endif
56
57 #ifndef O_LARGEFILE
58 #   define O_LARGEFILE 0
59 #endif
60
61 /*****************************************************************************
62  * Module descriptor
63  *****************************************************************************/
64 static int  Open ( vlc_object_t * );
65 static void Close( vlc_object_t * );
66
67 #define SOUT_CFG_PREFIX "sout-file-"
68 #define OVERWRITE_TEXT N_("Overwrite existing file")
69 #define OVERWRITE_LONGTEXT N_( \
70     "If the file already exists, it will be overwritten.")
71 #define APPEND_TEXT N_("Append to file")
72 #define APPEND_LONGTEXT N_( "Append to file if it exists instead " \
73                             "of replacing it.")
74 #define FORMAT_TEXT N_("Format time and date")
75 #define FORMAT_LONGTEXT N_("Perform ISO C time and date formatting " \
76     "on the file path")
77 #define SYNC_TEXT N_("Synchronous writing")
78 #define SYNC_LONGTEXT N_( "Open the file with synchronous writing.")
79
80 vlc_module_begin ()
81     set_description( N_("File stream output") )
82     set_shortname( N_("File" ))
83     set_capability( "sout access", 50 )
84     set_category( CAT_SOUT )
85     set_subcategory( SUBCAT_SOUT_ACO )
86     add_shortcut( "file", "stream", "fd" )
87     add_bool( SOUT_CFG_PREFIX "overwrite", true, OVERWRITE_TEXT,
88               OVERWRITE_LONGTEXT, true )
89     add_bool( SOUT_CFG_PREFIX "append", false, APPEND_TEXT,APPEND_LONGTEXT,
90               true )
91     add_bool( SOUT_CFG_PREFIX "format", false, FORMAT_TEXT, FORMAT_LONGTEXT,
92               true )
93 #ifdef O_SYNC
94     add_bool( SOUT_CFG_PREFIX "sync", false, SYNC_TEXT,SYNC_LONGTEXT,
95               false )
96 #endif
97     set_callbacks( Open, Close )
98 vlc_module_end ()
99
100
101 /*****************************************************************************
102  * Exported prototypes
103  *****************************************************************************/
104 static const char *const ppsz_sout_options[] = {
105     "append",
106     "format",
107     "overwrite",
108 #ifdef O_SYNC
109     "sync",
110 #endif
111     NULL
112 };
113
114 static ssize_t Write( sout_access_out_t *, block_t * );
115 static int Seek ( sout_access_out_t *, off_t  );
116 static ssize_t Read ( sout_access_out_t *, block_t * );
117 static int Control( sout_access_out_t *, int, va_list );
118
119 /*****************************************************************************
120  * Open: open the file
121  *****************************************************************************/
122 static int Open( vlc_object_t *p_this )
123 {
124     sout_access_out_t   *p_access = (sout_access_out_t*)p_this;
125     int                 fd;
126
127     config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
128
129     if( !p_access->psz_path )
130     {
131         msg_Err( p_access, "no file name specified" );
132         return VLC_EGENERIC;
133     }
134
135     bool overwrite = var_GetBool (p_access, SOUT_CFG_PREFIX"overwrite");
136     bool append = var_GetBool( p_access, SOUT_CFG_PREFIX "append" );
137
138     if (!strcmp (p_access->psz_access, "fd"))
139     {
140         char *end;
141
142         fd = strtol (p_access->psz_path, &end, 0);
143         if (!*p_access->psz_path || *end)
144         {
145             msg_Err (p_access, "invalid file descriptor: %s",
146                      p_access->psz_path);
147             return VLC_EGENERIC;
148         }
149         fd = vlc_dup (fd);
150         if (fd == -1)
151         {
152             msg_Err (p_access, "cannot use file descriptor: %s",
153                      vlc_strerror_c(errno));
154             return VLC_EGENERIC;
155         }
156     }
157     else
158     if( !strcmp( p_access->psz_path, "-" ) )
159     {
160 #if defined( _WIN32 ) || defined( __OS2__ )
161         setmode (STDOUT_FILENO, O_BINARY);
162 #endif
163         fd = vlc_dup (STDOUT_FILENO);
164         if (fd == -1)
165         {
166             msg_Err (p_access, "cannot use standard output: %s",
167                      vlc_strerror_c(errno));
168             return VLC_EGENERIC;
169         }
170         msg_Dbg( p_access, "using stdout" );
171     }
172     else
173     {
174         const char *path = p_access->psz_path;
175         char *buf = NULL;
176
177         if (var_InheritBool (p_access, SOUT_CFG_PREFIX"format"))
178         {
179             buf = str_format_time (path);
180             path_sanitize (buf);
181             path = buf;
182         }
183
184         int flags = O_RDWR | O_CREAT | O_LARGEFILE;
185         if (!overwrite)
186             flags |= O_EXCL;
187         if (!append)
188             flags |= O_TRUNC;
189 #ifdef O_SYNC
190         if (var_GetBool (p_access, SOUT_CFG_PREFIX"sync"))
191             flags |= O_SYNC;
192 #endif
193         do
194         {
195             fd = vlc_open (path, flags, 0666);
196             if (fd != -1)
197                 break;
198             if (fd == -1)
199                 msg_Err (p_access, "cannot create %s: %s", path,
200                          vlc_strerror_c(errno));
201             if (overwrite || errno != EEXIST)
202                 break;
203             flags &= ~O_EXCL;
204         }
205         while (dialog_Question (p_access, path,
206                                 _("The output file already exists. "
207                                 "If recording continues, the file will be "
208                                 "overridden and its content will be lost."),
209                                 _("Keep existing file"),
210                                 _("Overwrite"), NULL) == 2);
211         free (buf);
212         if (fd == -1)
213             return VLC_EGENERIC;
214     }
215
216     p_access->pf_write = Write;
217     p_access->pf_read  = Read;
218     p_access->pf_seek  = Seek;
219     p_access->pf_control = Control;
220     p_access->p_sys    = (void *)(intptr_t)fd;
221
222     msg_Dbg( p_access, "file access output opened (%s)", p_access->psz_path );
223     if (append)
224         lseek (fd, 0, SEEK_END);
225
226     return VLC_SUCCESS;
227 }
228
229 /*****************************************************************************
230  * Close: close the target
231  *****************************************************************************/
232 static void Close( vlc_object_t * p_this )
233 {
234     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
235
236     close( (intptr_t)p_access->p_sys );
237
238     msg_Dbg( p_access, "file access output closed" );
239 }
240
241 static int Control( sout_access_out_t *p_access, int i_query, va_list args )
242 {
243     switch( i_query )
244     {
245         case ACCESS_OUT_CONTROLS_PACE:
246         {
247             bool *pb = va_arg( args, bool * );
248             *pb = strcmp( p_access->psz_access, "stream" );
249             break;
250         }
251
252         case ACCESS_OUT_CAN_SEEK:
253         {
254             bool *pb = va_arg( args, bool * );
255             struct stat st;
256             if( fstat( (intptr_t)p_access->p_sys, &st ) == -1 )
257                 *pb = false;
258             else
259                 *pb = S_ISREG( st.st_mode ) || S_ISBLK( st.st_mode );
260             break;
261         }
262
263         default:
264             return VLC_EGENERIC;
265     }
266     return VLC_SUCCESS;
267 }
268
269 /*****************************************************************************
270  * Read: standard read on a file descriptor.
271  *****************************************************************************/
272 static ssize_t Read( sout_access_out_t *p_access, block_t *p_buffer )
273 {
274     ssize_t val;
275
276     do
277         val = read( (intptr_t)p_access->p_sys, p_buffer->p_buffer,
278                     p_buffer->i_buffer );
279     while (val == -1 && errno == EINTR);
280     return val;
281 }
282
283 /*****************************************************************************
284  * Write: standard write on a file descriptor.
285  *****************************************************************************/
286 static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
287 {
288     size_t i_write = 0;
289
290     while( p_buffer )
291     {
292         ssize_t val = write ((intptr_t)p_access->p_sys,
293                              p_buffer->p_buffer, p_buffer->i_buffer);
294         if (val <= 0)
295         {
296             if (errno == EINTR)
297                 continue;
298             block_ChainRelease (p_buffer);
299             msg_Err( p_access, "cannot write: %s", vlc_strerror_c(errno) );
300             return -1;
301         }
302
303         if ((size_t)val >= p_buffer->i_buffer)
304         {
305             block_t *p_next = p_buffer->p_next;
306             block_Release (p_buffer);
307             p_buffer = p_next;
308         }
309         else
310         {
311             p_buffer->p_buffer += val;
312             p_buffer->i_buffer -= val;
313         }
314         i_write += val;
315     }
316     return i_write;
317 }
318
319 /*****************************************************************************
320  * Seek: seek to a specific location in a file
321  *****************************************************************************/
322 static int Seek( sout_access_out_t *p_access, off_t i_pos )
323 {
324     return lseek( (intptr_t)p_access->p_sys, i_pos, SEEK_SET );
325 }