access: merge stream_access.c and access.c
[vlc.git] / src / input / access.c
1 /*****************************************************************************
2  * access.c
3  *****************************************************************************
4  * Copyright (C) 1999-2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ 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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <vlc_common.h>
33 #include <vlc_url.h>
34 #include <vlc_modules.h>
35 #include <vlc_interrupt.h>
36
37 #include <libvlc.h>
38 #include "stream.h"
39 #include "input_internal.h"
40
41 /* Decode URL (which has had its scheme stripped earlier) to a file path. */
42 char *get_path(const char *location)
43 {
44     char *url, *path;
45
46     /* Prepending "file://" is a bit hackish. But then again, we do not want
47      * to hard-code the list of schemes that use file paths in make_path().
48      */
49     if (asprintf(&url, "file://%s", location) == -1)
50         return NULL;
51
52     path = make_path (url);
53     free (url);
54     return path;
55 }
56
57 /*****************************************************************************
58  * access_New:
59  *****************************************************************************/
60 static access_t *access_New(vlc_object_t *parent, input_thread_t *input,
61                             const char *mrl)
62 {
63     const char *p = strstr(mrl, "://");
64     if (p == NULL)
65         return NULL;
66
67     access_t *access = vlc_custom_create(parent, sizeof (*access), "access");
68     char *scheme = strndup(mrl, p - mrl);
69     char *location = strdup(p + 3);
70
71     if (unlikely(access == NULL || scheme == NULL || location == NULL))
72     {
73         free(location);
74         free(scheme);
75         vlc_object_release(access);
76         return NULL;
77     }
78
79     access->p_input = input;
80     access->psz_access = scheme;
81     access->psz_location = location;
82     access->psz_filepath = get_path(location);
83     access->pf_read    = NULL;
84     access->pf_block   = NULL;
85     access->pf_readdir = NULL;
86     access->pf_seek    = NULL;
87     access->pf_control = NULL;
88     access->p_sys      = NULL;
89     access_InitFields(access);
90
91     msg_Dbg(access, "creating access '%s' location='%s', path='%s'", scheme,
92             location, access->psz_filepath ? access->psz_filepath : "(null)");
93
94     access->p_module = module_need(access, "access", scheme, true);
95     if (access->p_module == NULL)
96     {
97         free(access->psz_filepath);
98         free(access->psz_location);
99         free(access->psz_access);
100         vlc_object_release(access);
101         access = NULL;
102     }
103     assert(access == NULL || access->pf_control != NULL);
104     return access;
105 }
106
107 access_t *vlc_access_NewMRL(vlc_object_t *parent, const char *mrl)
108 {
109     return access_New(parent, NULL, mrl);
110 }
111
112 void vlc_access_Delete(access_t *access)
113 {
114     module_unneed(access, access->p_module);
115
116     free(access->psz_access);
117     free(access->psz_location);
118     free(access->psz_filepath);
119     vlc_object_release(access);
120 }
121
122 /*****************************************************************************
123  * access_vaDirectoryControlHelper:
124  *****************************************************************************/
125 int access_vaDirectoryControlHelper( access_t *p_access, int i_query, va_list args )
126 {
127     VLC_UNUSED( p_access );
128
129     switch( i_query )
130     {
131         case ACCESS_CAN_SEEK:
132         case ACCESS_CAN_FASTSEEK:
133         case ACCESS_CAN_PAUSE:
134         case ACCESS_CAN_CONTROL_PACE:
135             *va_arg( args, bool* ) = false;
136             break;
137         case ACCESS_GET_PTS_DELAY:
138             *va_arg( args, int64_t * ) = 0;
139             break;
140         default:
141             return VLC_EGENERIC;
142      }
143      return VLC_SUCCESS;
144 }
145
146 struct stream_sys_t
147 {
148     access_t *access;
149     block_t  *block;
150 };
151
152 /* Block access */
153 static ssize_t AStreamReadBlock(stream_t *s, void *buf, size_t len)
154 {
155     stream_sys_t *sys = s->p_sys;
156     input_thread_t *input = s->p_input;
157     block_t *block = sys->block;
158
159     while (block == NULL)
160     {
161         if (vlc_access_Eof(sys->access))
162             return 0;
163         if (vlc_killed())
164             return -1;
165
166         block = vlc_access_Block(sys->access);
167     }
168
169     if (input != NULL)
170     {
171         uint64_t total;
172
173         vlc_mutex_lock(&input->p->counters.counters_lock);
174         stats_Update(input->p->counters.p_read_bytes, block->i_buffer, &total);
175         stats_Update(input->p->counters.p_input_bitrate, total, NULL);
176         stats_Update(input->p->counters.p_read_packets, 1, NULL);
177         vlc_mutex_unlock(&input->p->counters.counters_lock);
178     }
179
180     size_t copy = block->i_buffer < len ? block->i_buffer : len;
181
182     if (likely(copy > 0) && buf != NULL /* skipping data? */)
183         memcpy(buf, block->p_buffer, copy);
184
185     block->p_buffer += copy;
186     block->i_buffer -= copy;
187
188     if (block->i_buffer == 0)
189         block_Release(block);
190     else
191         sys->block = block;
192
193     return copy;
194 }
195
196 /* Read access */
197 static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
198 {
199     stream_sys_t *sys = s->p_sys;
200     input_thread_t *input = s->p_input;
201     ssize_t val = 0;
202
203     do
204     {
205         if (vlc_access_Eof(sys->access))
206             return 0;
207         if (vlc_killed())
208             return -1;
209
210         val = vlc_access_Read(sys->access, buf, len);
211         if (val == 0)
212             return 0; /* EOF */
213     }
214     while (val < 0);
215
216     if (input != NULL)
217     {
218         uint64_t total;
219
220         vlc_mutex_lock(&input->p->counters.counters_lock);
221         stats_Update(input->p->counters.p_read_bytes, val, &total);
222         stats_Update(input->p->counters.p_input_bitrate, total, NULL);
223         stats_Update(input->p->counters.p_read_packets, 1, NULL);
224         vlc_mutex_unlock(&input->p->counters.counters_lock);
225     }
226
227     return val;
228 }
229
230 /* Directory */
231 static input_item_t *AStreamReadDir(stream_t *s)
232 {
233     stream_sys_t *sys = s->p_sys;
234
235     if (sys->access->pf_readdir == NULL)
236         return NULL;
237     return sys->access->pf_readdir(sys->access);
238 }
239
240 /* Common */
241 #define static_control_match(foo) \
242     static_assert((unsigned) STREAM_##foo == ACCESS_##foo, "Mismatch")
243
244 static int AStreamControl(stream_t *s, int cmd, va_list args)
245 {
246     stream_sys_t *sys = s->p_sys;
247     access_t *access = sys->access;
248
249     static_control_match(CAN_SEEK);
250     static_control_match(CAN_FASTSEEK);
251     static_control_match(CAN_PAUSE);
252     static_control_match(CAN_CONTROL_PACE);
253     static_control_match(GET_SIZE);
254     static_control_match(GET_PTS_DELAY);
255     static_control_match(GET_TITLE_INFO);
256     static_control_match(GET_TITLE);
257     static_control_match(GET_SEEKPOINT);
258     static_control_match(GET_META);
259     static_control_match(GET_CONTENT_TYPE);
260     static_control_match(GET_SIGNAL);
261     static_control_match(SET_PAUSE_STATE);
262     static_control_match(SET_TITLE);
263     static_control_match(SET_SEEKPOINT);
264     static_control_match(SET_PRIVATE_ID_STATE);
265     static_control_match(SET_PRIVATE_ID_CA);
266     static_control_match(GET_PRIVATE_ID_STATE);
267
268     switch (cmd)
269     {
270         case STREAM_CAN_SEEK:
271         case STREAM_CAN_FASTSEEK:
272         case STREAM_CAN_PAUSE:
273         case STREAM_CAN_CONTROL_PACE:
274         case STREAM_GET_SIZE:
275         case STREAM_GET_PTS_DELAY:
276         case STREAM_GET_TITLE_INFO:
277         case STREAM_GET_TITLE:
278         case STREAM_GET_SEEKPOINT:
279         case STREAM_GET_META:
280         case STREAM_GET_CONTENT_TYPE:
281         case STREAM_GET_SIGNAL:
282         case STREAM_SET_PAUSE_STATE:
283         case STREAM_SET_TITLE:
284         case STREAM_SET_SEEKPOINT:
285         case STREAM_SET_PRIVATE_ID_STATE:
286         case STREAM_SET_PRIVATE_ID_CA:
287         case STREAM_GET_PRIVATE_ID_STATE:
288             return access_vaControl(access, cmd, args);
289
290         case STREAM_IS_DIRECTORY:
291         {
292             bool *b;
293
294             *va_arg(args, bool *) = access->pf_readdir != NULL;
295             b = va_arg(args, bool *);
296             if (b != NULL)
297                 *b = access->info.b_dir_sorted;
298             b = va_arg(args, bool *);
299             if (b != NULL)
300                 *b = access->info.b_dir_can_loop;
301             break;
302         }
303
304         case STREAM_GET_POSITION:
305         {
306             uint64_t *ppos =va_arg(args, uint64_t *);
307
308             *ppos = access->info.i_pos;
309             if (sys->block != NULL)
310             {
311                 assert(sys->block->i_buffer <= *ppos);
312                 *ppos -= sys->block->i_buffer;
313             }
314             break;
315         }
316
317         case STREAM_SET_POSITION:
318         {
319             uint64_t pos = va_arg(args, uint64_t);
320
321             if (sys->block != NULL)
322             {
323                 block_Release(sys->block);
324                 sys->block = NULL;
325             }
326             return vlc_access_Seek(sys->access, pos);
327         }
328
329         default:
330             return VLC_EGENERIC;
331     }
332     return VLC_SUCCESS;
333 }
334
335 static void AStreamDestroy(stream_t *s)
336 {
337     stream_sys_t *sys = s->p_sys;
338
339     if (sys->block != NULL)
340         block_Release(sys->block);
341     vlc_access_Delete(sys->access);
342     free(sys);
343 }
344
345 stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input,
346                            const char *url)
347 {
348     stream_t *s = stream_CommonNew(parent);
349     if (unlikely(s == NULL))
350         return NULL;
351
352     s->p_input = input;
353     s->psz_url = strdup(url);
354
355     stream_sys_t *sys = malloc(sizeof (*sys));
356     if (unlikely(s->psz_url == NULL || sys == NULL))
357         goto error;
358
359     sys->access = access_New(VLC_OBJECT(s), input, url);
360     if (sys->access == NULL)
361         goto error;
362
363     sys->block = NULL;
364
365     s->p_sys      = sys;
366     if (sys->access->pf_block != NULL)
367         s->pf_read = AStreamReadBlock;
368     else
369         s->pf_read = AStreamReadStream;
370     s->pf_readdir = AStreamReadDir;
371     s->pf_control = AStreamControl;
372     s->pf_destroy = AStreamDestroy;
373
374     return s;
375 error:
376     free(sys);
377     stream_Delete(s);
378     return NULL;
379 }