1 /*****************************************************************************
2 * avio.c: access using libavformat library
3 *****************************************************************************
4 * Copyright (C) 2009 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
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.
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.
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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_access.h>
33 #include <vlc_avcodec.h>
34 #include <vlc_interrupt.h>
37 #include "../codec/avcodec/avcommon.h"
39 #if LIBAVFORMAT_VERSION_MAJOR < 54
40 # define AVIOContext URLContext
42 # define avio_open url_open
43 # define avio_close url_close
44 # define avio_read url_read
45 # define avio_seek url_seek
46 # define avio_pause av_url_read_pause
48 # define AVIO_FLAG_READ URL_RDONLY
49 # define AVIO_FLAG_WRITE URL_WRONLY
50 # define avio_size url_filesize
53 /*****************************************************************************
55 *****************************************************************************/
62 /*****************************************************************************
64 *****************************************************************************/
65 static ssize_t
Read (access_t
*, void *, size_t);
66 static int Seek (access_t
*, uint64_t);
67 static int Control(access_t
*, int, va_list);
68 static ssize_t
Write(sout_access_out_t
*, block_t
*);
69 static int OutControl(sout_access_out_t
*, int, va_list);
70 static int OutSeek (sout_access_out_t
*, off_t
);
72 static int UrlInterruptCallback(void *access
)
74 /* NOTE: This works so long as libavformat invokes the callback from the
75 * same thread that invokes libavformat. Currently libavformat does not
76 * create internal threads at all. This is not proper event handling in any
77 * case; libavformat needs fixing. */
88 struct sout_access_out_sys_t
{
95 #if LIBAVFORMAT_VERSION_MAJOR < 54
96 static vlc_object_t
*current_access
= NULL
;
98 static int UrlInterruptCallbackSingle(void)
100 return UrlInterruptCallback(current_access
);
103 static int SetupAvioCb(vlc_object_t
*access
)
105 static vlc_mutex_t avio_lock
= VLC_STATIC_MUTEX
;
106 vlc_mutex_lock(&avio_lock
);
107 assert(!access
!= !current_access
);
108 if (access
&& current_access
) {
109 vlc_mutex_unlock(&avio_lock
);
112 url_set_interrupt_cb(access
? UrlInterruptCallbackSingle
: NULL
);
114 current_access
= access
;
116 vlc_mutex_unlock(&avio_lock
);
124 int OpenAvio(vlc_object_t
*object
)
126 access_t
*access
= (access_t
*)object
;
127 access_sys_t
*sys
= vlc_malloc(object
, sizeof(*sys
));
134 * - url (only a subset of available protocols).
137 if (!strcmp(access
->psz_name
, "avio"))
138 url
= strdup(access
->psz_location
);
139 else if (asprintf(&url
, "%s://%s", access
->psz_name
,
140 access
->psz_location
) < 0)
147 vlc_init_avformat(object
);
150 #if LIBAVFORMAT_VERSION_MAJOR < 54
151 ret
= avio_open(&sys
->context
, url
, AVIO_FLAG_READ
);
153 AVIOInterruptCB cb
= {
154 .callback
= UrlInterruptCallback
,
157 AVDictionary
*options
= NULL
;
158 char *psz_opts
= var_InheritString(access
, "avio-options");
160 vlc_av_get_options(psz_opts
, &options
);
163 ret
= avio_open2(&sys
->context
, url
, AVIO_FLAG_READ
, &cb
, &options
);
164 AVDictionaryEntry
*t
= NULL
;
165 while ((t
= av_dict_get(options
, "", t
, AV_DICT_IGNORE_SUFFIX
)))
166 msg_Err( access
, "unknown option \"%s\"", t
->key
);
167 av_dict_free(&options
);
170 msg_Err(access
, "Failed to open %s: %s", url
,
171 vlc_strerror_c(AVUNERROR(ret
)));
177 #if LIBAVFORMAT_VERSION_MAJOR < 54
178 /* We can accept only one active user at any time */
179 if (SetupAvioCb(VLC_OBJECT(access
))) {
180 msg_Err(access
, "Module already in use");
181 avio_close(sys
->context
);
186 int64_t size
= avio_size(sys
->context
);
188 #if LIBAVFORMAT_VERSION_MAJOR < 54
189 seekable
= !sys
->context
->is_streamed
;
191 seekable
= sys
->context
->seekable
;
193 msg_Dbg(access
, "%sseekable, size=%"PRIi64
, seekable
? "" : "not ", size
);
196 access
->pf_read
= Read
;
197 access
->pf_block
= NULL
;
198 access
->pf_control
= Control
;
199 access
->pf_seek
= Seek
;
207 static const char *const ppsz_sout_options
[] = {
212 int OutOpenAvio(vlc_object_t
*object
)
214 sout_access_out_t
*access
= (sout_access_out_t
*)object
;
216 config_ChainParse( access
, "sout-avio-", ppsz_sout_options
, access
->p_cfg
);
218 sout_access_out_sys_t
*sys
= vlc_malloc(object
, sizeof(*sys
));
224 vlc_init_avformat(object
);
226 if (!access
->psz_path
)
230 #if LIBAVFORMAT_VERSION_MAJOR < 54
231 ret
= avio_open(&sys
->context
, access
->psz_path
, AVIO_FLAG_WRITE
);
233 AVDictionary
*options
= NULL
;
234 char *psz_opts
= var_InheritString(access
, "sout-avio-options");
236 vlc_av_get_options(psz_opts
, &options
);
239 ret
= avio_open2(&sys
->context
, access
->psz_path
, AVIO_FLAG_WRITE
,
241 AVDictionaryEntry
*t
= NULL
;
242 while ((t
= av_dict_get(options
, "", t
, AV_DICT_IGNORE_SUFFIX
)))
243 msg_Err( access
, "unknown option \"%s\"", t
->key
);
244 av_dict_free(&options
);
247 errno
= AVUNERROR(ret
);
248 msg_Err(access
, "Failed to open %s", access
->psz_path
);
252 #if LIBAVFORMAT_VERSION_MAJOR < 54
253 /* We can accept only one active user at any time */
254 if (SetupAvioCb(VLC_OBJECT(access
))) {
255 msg_Err(access
, "Module already in use");
260 access
->pf_write
= Write
;
261 access
->pf_control
= OutControl
;
262 access
->pf_seek
= OutSeek
;
268 void CloseAvio(vlc_object_t
*object
)
270 access_t
*access
= (access_t
*)object
;
271 access_sys_t
*sys
= access
->p_sys
;
273 #if LIBAVFORMAT_VERSION_MAJOR < 54
277 avio_close(sys
->context
);
280 void OutCloseAvio(vlc_object_t
*object
)
282 sout_access_out_t
*access
= (sout_access_out_t
*)object
;
283 sout_access_out_sys_t
*sys
= access
->p_sys
;
285 #if LIBAVFORMAT_VERSION_MAJOR < 54
289 avio_close(sys
->context
);
292 static ssize_t
Read(access_t
*access
, void *data
, size_t size
)
294 access_sys_t
*sys
= access
->p_sys
;
296 int r
= avio_read(sys
->context
, data
, size
);
302 /*****************************************************************************
304 *****************************************************************************/
305 static ssize_t
Write(sout_access_out_t
*p_access
, block_t
*p_buffer
)
307 sout_access_out_sys_t
*p_sys
= (sout_access_out_sys_t
*)p_access
->p_sys
;
311 while (p_buffer
!= NULL
) {
312 block_t
*p_next
= p_buffer
->p_next
;
314 #if LIBAVFORMAT_VERSION_MAJOR < 54
315 val
= url_write(p_sys
->context
, p_buffer
->p_buffer
, p_buffer
->i_buffer
);
320 avio_write(p_sys
->context
, p_buffer
->p_buffer
, p_buffer
->i_buffer
);
321 avio_flush(p_sys
->context
);
322 if ((val
= p_sys
->context
->error
) != 0) {
323 p_sys
->context
->error
= 0; /* FIXME? */
326 i_write
+= p_buffer
->i_buffer
;
329 block_Release(p_buffer
);
337 msg_Err(p_access
, "Wrote only %zu bytes: %s", i_write
,
338 vlc_strerror_c(AVUNERROR(val
)));
339 block_ChainRelease( p_buffer
);
343 static int Seek(access_t
*access
, uint64_t position
)
345 access_sys_t
*sys
= access
->p_sys
;
349 # define EOVERFLOW EFBIG
352 if (position
> INT64_MAX
)
353 ret
= AVERROR(EOVERFLOW
);
355 ret
= avio_seek(sys
->context
, position
, SEEK_SET
);
357 msg_Err(access
, "Seek to %"PRIu64
" failed: %s", position
,
358 vlc_strerror_c(AVUNERROR(ret
)));
359 if (sys
->size
< 0 || position
!= sys
->size
)
365 static int OutSeek(sout_access_out_t
*p_access
, off_t i_pos
)
367 sout_access_out_sys_t
*sys
= p_access
->p_sys
;
369 if (avio_seek(sys
->context
, i_pos
, SEEK_SET
) < 0)
374 static int OutControl(sout_access_out_t
*p_access
, int i_query
, va_list args
)
376 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
380 case ACCESS_OUT_CONTROLS_PACE
: {
381 bool *pb
= va_arg(args
, bool *);
382 //*pb = strcmp(p_access->psz_name, "stream");
393 static int Control(access_t
*access
, int query
, va_list args
)
395 access_sys_t
*sys
= access
->p_sys
;
399 case STREAM_CAN_SEEK
:
400 case STREAM_CAN_FASTSEEK
: /* FIXME how to do that ? */
401 b
= va_arg(args
, bool *);
402 #if LIBAVFORMAT_VERSION_MAJOR < 54
403 *b
= !sys
->context
->is_streamed
;
405 *b
= sys
->context
->seekable
;
408 case STREAM_CAN_PAUSE
:
409 b
= va_arg(args
, bool *);
410 #if LIBAVFORMAT_VERSION_MAJOR < 54
411 *b
= sys
->context
->prot
->url_read_pause
!= NULL
;
413 *b
= sys
->context
->read_pause
!= NULL
;
416 case STREAM_CAN_CONTROL_PACE
:
417 b
= va_arg(args
, bool *);
418 *b
= true; /* FIXME */
420 case STREAM_GET_SIZE
:
423 *va_arg(args
, uint64_t *) = sys
->size
;
425 case STREAM_GET_PTS_DELAY
: {
426 int64_t *delay
= va_arg(args
, int64_t *);
427 *delay
= INT64_C(1000) * var_InheritInteger(access
, "network-caching");
430 case STREAM_SET_PAUSE_STATE
: {
431 bool is_paused
= va_arg(args
, int);
432 if (avio_pause(sys
->context
, is_paused
)< 0)