3325672d14c7f39a3a3e4630cb69ca30ddd5e59c
[vlc.git] / 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 <assert.h>
34 #include <signal.h>
35 #include <sys/types.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #ifdef __OS2__
41 #   include <io.h>      /* setmode() */
42 #endif
43
44 #include <vlc_common.h>
45 #include <vlc_plugin.h>
46 #include <vlc_sout.h>
47 #include <vlc_block.h>
48 #include <vlc_fs.h>
49 #include <vlc_network.h>
50 #include <vlc_strings.h>
51 #include <vlc_dialog.h>
52
53 #ifndef O_LARGEFILE
54 #   define O_LARGEFILE 0
55 #endif
56 #ifndef _POSIX_REALTIME_SIGNALS
57 # define _POSIX_REALTIME_SIGNALS (-1)
58 #endif
59
60 #define SOUT_CFG_PREFIX "sout-file-"
61
62 /*****************************************************************************
63  * Read: standard read on a file descriptor.
64  *****************************************************************************/
65 static ssize_t Read( sout_access_out_t *p_access, block_t *p_buffer )
66 {
67     ssize_t val;
68
69     do
70         val = read( (intptr_t)p_access->p_sys, p_buffer->p_buffer,
71                     p_buffer->i_buffer );
72     while (val == -1 && errno == EINTR);
73     return val;
74 }
75
76 /*****************************************************************************
77  * Write: standard write on a file descriptor.
78  *****************************************************************************/
79 static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
80 {
81     size_t i_write = 0;
82
83     while( p_buffer )
84     {
85         ssize_t val = write ((intptr_t)p_access->p_sys,
86                              p_buffer->p_buffer, p_buffer->i_buffer);
87         if (val <= 0)
88         {
89             if (errno == EINTR)
90                 continue;
91             block_ChainRelease (p_buffer);
92             msg_Err( p_access, "cannot write: %s", vlc_strerror_c(errno) );
93             return -1;
94         }
95
96         if ((size_t)val >= p_buffer->i_buffer)
97         {
98             block_t *p_next = p_buffer->p_next;
99             block_Release (p_buffer);
100             p_buffer = p_next;
101         }
102         else
103         {
104             p_buffer->p_buffer += val;
105             p_buffer->i_buffer -= val;
106         }
107         i_write += val;
108     }
109     return i_write;
110 }
111
112 static ssize_t WritePipe(sout_access_out_t *access, block_t *block)
113 {
114     int fd = (intptr_t)access->p_sys;
115     ssize_t total = 0;
116
117     while (block != NULL)
118     {
119         if (block->i_buffer == 0)
120         {
121             block_t *next = block->p_next;
122             block_Release(block);
123             block = next;
124             continue;
125         }
126
127         /* TODO: vectorized I/O with writev() */
128         ssize_t val = vlc_write(fd, block->p_buffer, block->i_buffer);
129         if (val < 0)
130         {
131             if (errno == EINTR)
132                 continue;
133
134             block_ChainRelease(block);
135             msg_Err(access, "cannot write: %s", vlc_strerror_c(errno));
136             total = -1;
137             break;
138         }
139
140         total += val;
141
142         assert((size_t)val <= block->i_buffer);
143         block->p_buffer += val;
144         block->i_buffer -= val;
145     }
146
147     return total;
148 }
149
150 #ifdef S_ISSOCK
151 static ssize_t Send(sout_access_out_t *access, block_t *block)
152 {
153     int fd = (intptr_t)access->p_sys;
154     size_t total = 0;
155
156     while (block != NULL)
157     {
158         if (block->i_buffer == 0)
159         {
160             block_t *next = block->p_next;
161             block_Release(block);
162             block = next;
163             continue;
164         }
165
166         /* TODO: vectorized I/O with sendmsg() */
167         ssize_t val = send(fd, block->p_buffer, block->i_buffer, MSG_NOSIGNAL);
168         if (val <= 0)
169         {   /* FIXME: errno is meaningless if val is zero */
170             if (errno == EINTR)
171                 continue;
172             block_ChainRelease(block);
173             msg_Err(access, "cannot write: %s", vlc_strerror_c(errno));
174             return -1;
175         }
176
177         total += val;
178
179         assert((size_t)val <= block->i_buffer);
180         block->p_buffer += val;
181         block->i_buffer -= val;
182     }
183     return total;
184 }
185 #endif
186
187 /*****************************************************************************
188  * Seek: seek to a specific location in a file
189  *****************************************************************************/
190 static int Seek( sout_access_out_t *p_access, off_t i_pos )
191 {
192     return lseek( (intptr_t)p_access->p_sys, i_pos, SEEK_SET );
193 }
194
195 static int NoSeek(sout_access_out_t *access, off_t pos)
196 {
197     (void) access; (void) pos;
198     return -1;
199 }
200
201 static int Control( sout_access_out_t *p_access, int i_query, va_list args )
202 {
203     switch( i_query )
204     {
205         case ACCESS_OUT_CONTROLS_PACE:
206         {
207             bool *pb = va_arg( args, bool * );
208             *pb = strcmp( p_access->psz_access, "stream" );
209             break;
210         }
211
212         case ACCESS_OUT_CAN_SEEK:
213         {
214             bool *pb = va_arg( args, bool * );
215             *pb = p_access->pf_seek == Seek;
216             break;
217         }
218
219         default:
220             return VLC_EGENERIC;
221     }
222     return VLC_SUCCESS;
223 }
224
225 static const char *const ppsz_sout_options[] = {
226     "append",
227     "format",
228     "overwrite",
229 #ifdef O_SYNC
230     "sync",
231 #endif
232     NULL
233 };
234
235 /*****************************************************************************
236  * Open: open the file
237  *****************************************************************************/
238 static int Open( vlc_object_t *p_this )
239 {
240     sout_access_out_t   *p_access = (sout_access_out_t*)p_this;
241     int                 fd;
242
243     config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
244
245     if( !p_access->psz_path )
246     {
247         msg_Err( p_access, "no file name specified" );
248         return VLC_EGENERIC;
249     }
250
251     bool overwrite = var_GetBool (p_access, SOUT_CFG_PREFIX"overwrite");
252     bool append = var_GetBool( p_access, SOUT_CFG_PREFIX "append" );
253
254     if (!strcmp (p_access->psz_access, "fd"))
255     {
256         char *end;
257
258         fd = strtol (p_access->psz_path, &end, 0);
259         if (!*p_access->psz_path || *end)
260         {
261             msg_Err (p_access, "invalid file descriptor: %s",
262                      p_access->psz_path);
263             return VLC_EGENERIC;
264         }
265         fd = vlc_dup (fd);
266         if (fd == -1)
267         {
268             msg_Err (p_access, "cannot use file descriptor: %s",
269                      vlc_strerror_c(errno));
270             return VLC_EGENERIC;
271         }
272     }
273     else
274     if( !strcmp( p_access->psz_path, "-" ) )
275     {
276 #if defined( _WIN32 ) || defined( __OS2__ )
277         setmode (STDOUT_FILENO, O_BINARY);
278 #endif
279         fd = vlc_dup (STDOUT_FILENO);
280         if (fd == -1)
281         {
282             msg_Err (p_access, "cannot use standard output: %s",
283                      vlc_strerror_c(errno));
284             return VLC_EGENERIC;
285         }
286         msg_Dbg( p_access, "using stdout" );
287     }
288     else
289     {
290         const char *path = p_access->psz_path;
291         char *buf = NULL;
292
293         if (var_InheritBool (p_access, SOUT_CFG_PREFIX"format"))
294         {
295             buf = vlc_strftime (path);
296             path = buf;
297         }
298
299         int flags = O_RDWR | O_CREAT | O_LARGEFILE;
300         if (!overwrite)
301             flags |= O_EXCL;
302         if (!append)
303             flags |= O_TRUNC;
304 #ifdef O_SYNC
305         if (var_GetBool (p_access, SOUT_CFG_PREFIX"sync"))
306             flags |= O_SYNC;
307 #endif
308         do
309         {
310             fd = vlc_open (path, flags, 0666);
311             if (fd != -1)
312                 break;
313             if (fd == -1)
314                 msg_Err (p_access, "cannot create %s: %s", path,
315                          vlc_strerror_c(errno));
316             if (overwrite || errno != EEXIST)
317                 break;
318             flags &= ~O_EXCL;
319         }
320         while (vlc_dialog_wait_question (p_access, VLC_DIALOG_QUESTION_NORMAL,
321                                          _("Keep existing file"),
322                                          _("Overwrite"), NULL, path,
323                                          _("The output file already exists. "
324                                          "If recording continues, the file will be "
325                                          "overridden and its content will be lost.")) == 1);
326         free (buf);
327         if (fd == -1)
328             return VLC_EGENERIC;
329     }
330
331     struct stat st;
332
333     if (fstat (fd, &st))
334     {
335         msg_Err (p_access, "write error: %s", vlc_strerror_c(errno));
336         vlc_close (fd);
337         return VLC_EGENERIC;
338     }
339
340     p_access->pf_read  = Read;
341
342     if (S_ISREG(st.st_mode) || S_ISBLK(st.st_mode))
343     {
344         p_access->pf_write = Write;
345         p_access->pf_seek  = Seek;
346     }
347 #ifdef S_ISSOCK
348     else if (S_ISSOCK(st.st_mode))
349     {
350         p_access->pf_write = Send;
351         p_access->pf_seek = NoSeek;
352     }
353 #endif
354     else
355     {
356         p_access->pf_write = WritePipe;
357         p_access->pf_seek = NoSeek;
358     }
359     p_access->pf_control = Control;
360     p_access->p_sys    = (void *)(intptr_t)fd;
361
362     msg_Dbg( p_access, "file access output opened (%s)", p_access->psz_path );
363     if (append)
364         lseek (fd, 0, SEEK_END);
365
366     return VLC_SUCCESS;
367 }
368
369 /*****************************************************************************
370  * Close: close the target
371  *****************************************************************************/
372 static void Close( vlc_object_t * p_this )
373 {
374     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
375
376     vlc_close( (intptr_t)p_access->p_sys );
377
378     msg_Dbg( p_access, "file access output closed" );
379 }
380
381 #define OVERWRITE_TEXT N_("Overwrite existing file")
382 #define OVERWRITE_LONGTEXT N_( \
383     "If the file already exists, it will be overwritten.")
384 #define APPEND_TEXT N_("Append to file")
385 #define APPEND_LONGTEXT N_( "Append to file if it exists instead " \
386                             "of replacing it.")
387 #define FORMAT_TEXT N_("Format time and date")
388 #define FORMAT_LONGTEXT N_("Perform ISO C time and date formatting " \
389     "on the file path")
390 #define SYNC_TEXT N_("Synchronous writing")
391 #define SYNC_LONGTEXT N_( "Open the file with synchronous writing.")
392
393 vlc_module_begin ()
394     set_description( N_("File stream output") )
395     set_shortname( N_("File" ))
396     set_capability( "sout access", 50 )
397     set_category( CAT_SOUT )
398     set_subcategory( SUBCAT_SOUT_ACO )
399     add_shortcut( "file", "stream", "fd" )
400     add_bool( SOUT_CFG_PREFIX "overwrite", true, OVERWRITE_TEXT,
401               OVERWRITE_LONGTEXT, true )
402     add_bool( SOUT_CFG_PREFIX "append", false, APPEND_TEXT,APPEND_LONGTEXT,
403               true )
404     add_bool( SOUT_CFG_PREFIX "format", false, FORMAT_TEXT, FORMAT_LONGTEXT,
405               true )
406 #ifdef O_SYNC
407     add_bool( SOUT_CFG_PREFIX "sync", false, SYNC_TEXT,SYNC_LONGTEXT,
408               false )
409 #endif
410     set_callbacks( Open, Close )
411 vlc_module_end ()