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