Browse code

lavf: add SFTP protocol via libssh

Signed-off-by: Lukasz Marek <lukasz.m.luki@gmail.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Lukasz Marek authored on 2013/09/18 21:08:11
Showing 6 changed files
... ...
@@ -30,6 +30,7 @@ version <next>
30 30
 - Error Resilient AAC syntax (ER AAC LC) decoding
31 31
 - Low Delay AAC (ER AAC LD) decoding
32 32
 - mux chapters in ASF files
33
+- SFTP protocol (via libssh)
33 34
 
34 35
 
35 36
 version 2.0:
... ...
@@ -225,6 +225,7 @@ External library support:
225 225
   --enable-libshine        enable fixed-point MP3 encoding via libshine [no]
226 226
   --enable-libsoxr         enable Include libsoxr resampling [no]
227 227
   --enable-libspeex        enable Speex de/encoding via libspeex [no]
228
+  --enable-libssh          enable SFTP protocol via libssh [no]
228 229
   --enable-libstagefright-h264  enable H.264 decoding via libstagefright [no]
229 230
   --enable-libtheora       enable Theora encoding via libtheora [no]
230 231
   --enable-libtwolame      enable MP2 encoding via libtwolame [no]
... ...
@@ -1191,6 +1192,7 @@ EXTERNAL_LIBRARY_LIST="
1191 1191
     libshine
1192 1192
     libsoxr
1193 1193
     libspeex
1194
+    libssh
1194 1195
     libstagefright_h264
1195 1196
     libtheora
1196 1197
     libtwolame
... ...
@@ -2158,6 +2160,7 @@ librtmpe_protocol_deps="librtmp"
2158 2158
 librtmps_protocol_deps="librtmp"
2159 2159
 librtmpt_protocol_deps="librtmp"
2160 2160
 librtmpte_protocol_deps="librtmp"
2161
+libssh_protocol_deps="libssh"
2161 2162
 mmsh_protocol_select="http_protocol"
2162 2163
 mmst_protocol_select="network"
2163 2164
 rtmp_protocol_deps="!librtmp_protocol"
... ...
@@ -4241,6 +4244,7 @@ enabled librtmp           && require_pkg_config librtmp librtmp/rtmp.h RTMP_Sock
4241 4241
 enabled libschroedinger   && require_pkg_config schroedinger-1.0 schroedinger/schro.h schro_init
4242 4242
 enabled libshine          && require_pkg_config shine shine/layer3.h shine_encode_buffer
4243 4243
 enabled libsoxr           && require libsoxr soxr.h soxr_create -lsoxr
4244
+enabled libssh            && require_pkg_config libssh libssh/sftp.h sftp_init
4244 4245
 enabled libspeex          && require libspeex speex/speex.h speex_decoder_init -lspeex
4245 4246
 enabled libstagefright_h264 && require_cpp libstagefright_h264 "binder/ProcessState.h media/stagefright/MetaData.h
4246 4247
     media/stagefright/MediaBufferGroup.h media/stagefright/MediaDebug.h media/stagefright/MediaDefs.h
... ...
@@ -520,6 +520,38 @@ The Real-Time Messaging Protocol tunneled through HTTPS (RTMPTS) is used
520 520
 for streaming multimedia content within HTTPS requests to traverse
521 521
 firewalls.
522 522
 
523
+@section libssh
524
+
525
+Secure File Transfer Protocol via libssh
526
+
527
+Allow to read from or write to remote resources using SFTP protocol.
528
+
529
+Following syntax is required.
530
+
531
+@example
532
+sftp://[user[:password]@@]server[:port]/path/to/remote/resource.mpeg
533
+@end example
534
+
535
+This protocol accepts the following options.
536
+
537
+@table @option
538
+@item timeout
539
+Set timeout of socket I/O operations used by the underlying low level
540
+operation. By default it is set to -1, which means that the timeout
541
+is not specified.
542
+
543
+@item truncate
544
+Truncate existing files on write, if set to 1. A value of 0 prevents
545
+truncating. Default value is 1.
546
+
547
+@end table
548
+
549
+Example: Play a file stored on remote server.
550
+
551
+@example
552
+ffplay sftp://user:password@@server_address:22/home/user/resource.mpeg
553
+@end example
554
+
523 555
 @section librtmp rtmp, rtmpe, rtmps, rtmpt, rtmpte
524 556
 
525 557
 Real-Time Messaging Protocol and its variants supported through
... ...
@@ -425,6 +425,7 @@ OBJS-$(CONFIG_LIBNUT_DEMUXER)            += libnut.o
425 425
 OBJS-$(CONFIG_LIBNUT_MUXER)              += libnut.o
426 426
 OBJS-$(CONFIG_LIBQUVI_DEMUXER)           += libquvi.o
427 427
 OBJS-$(CONFIG_LIBRTMP)                   += librtmp.o
428
+OBJS-$(CONFIG_LIBSSH_PROTOCOL)           += libssh.o
428 429
 
429 430
 # protocols I/O
430 431
 OBJS-$(CONFIG_APPLEHTTP_PROTOCOL)        += hlsproto.o
... ...
@@ -348,4 +348,5 @@ void av_register_all(void)
348 348
     REGISTER_PROTOCOL(LIBRTMPS,         librtmps);
349 349
     REGISTER_PROTOCOL(LIBRTMPT,         librtmpt);
350 350
     REGISTER_PROTOCOL(LIBRTMPTE,        librtmpte);
351
+    REGISTER_PROTOCOL(LIBSSH,           libssh);
351 352
 }
352 353
new file mode 100644
... ...
@@ -0,0 +1,229 @@
0
+/*
1
+ * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+#include <fcntl.h>
21
+#include <libssh/sftp.h>
22
+#include "libavutil/avstring.h"
23
+#include "libavutil/opt.h"
24
+#include "avformat.h"
25
+#include "internal.h"
26
+#include "url.h"
27
+
28
+typedef struct {
29
+    const AVClass *class;
30
+    ssh_session session;
31
+    sftp_session sftp;
32
+    sftp_file file;
33
+    int64_t filesize;
34
+    int rw_timeout;
35
+    int trunc;
36
+} LIBSSHContext;
37
+
38
+static int libssh_close(URLContext *h)
39
+{
40
+    LIBSSHContext *s = h->priv_data;
41
+    if (s->file)
42
+        sftp_close(s->file);
43
+    if (s->sftp)
44
+        sftp_free(s->sftp);
45
+    if (s->session) {
46
+        ssh_disconnect(s->session);
47
+        ssh_free(s->session);
48
+    }
49
+    return 0;
50
+}
51
+
52
+static int libssh_open(URLContext *h, const char *url, int flags)
53
+{
54
+    static const int verbosity = SSH_LOG_NOLOG;
55
+    LIBSSHContext *s = h->priv_data;
56
+    char proto[10], path[MAX_URL_SIZE], hostname[1024], credencials[1024];
57
+    int port = 22, access, ret;
58
+    long timeout = s->rw_timeout * 1000;
59
+    const char *user = NULL, *pass = NULL;
60
+    char *end = NULL;
61
+    sftp_attributes stat;
62
+
63
+    av_url_split(proto, sizeof(proto),
64
+                 credencials, sizeof(credencials),
65
+                 hostname, sizeof(hostname),
66
+                 &port,
67
+                 path, sizeof(path),
68
+                 url);
69
+
70
+    if (port <= 0 || port > 65535)
71
+        port = 22;
72
+
73
+    if (!(s->session = ssh_new())) {
74
+        ret = AVERROR(ENOMEM);
75
+        goto fail;
76
+    }
77
+    user = av_strtok(credencials, ":", &end);
78
+    pass = av_strtok(end, ":", &end);
79
+    ssh_options_set(s->session, SSH_OPTIONS_HOST, hostname);
80
+    ssh_options_set(s->session, SSH_OPTIONS_PORT, &port);
81
+    ssh_options_set(s->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
82
+    if (timeout > 0)
83
+        ssh_options_set(s->session, SSH_OPTIONS_TIMEOUT_USEC, &timeout);
84
+    if (user)
85
+        ssh_options_set(s->session, SSH_OPTIONS_USER, user);
86
+
87
+    if (ssh_connect(s->session) != SSH_OK) {
88
+        av_log(h, AV_LOG_ERROR, "Connection failed. %s\n", ssh_get_error(s->session));
89
+        ret = AVERROR(EIO);
90
+        goto fail;
91
+    }
92
+
93
+    if (pass && ssh_userauth_password(s->session, NULL, pass) != SSH_AUTH_SUCCESS) {
94
+        av_log(h, AV_LOG_ERROR, "Error authenticating with password: %s\n", ssh_get_error(s->session));
95
+        ret = AVERROR(EACCES);
96
+        goto fail;
97
+    }
98
+
99
+    if (!(s->sftp = sftp_new(s->session))) {
100
+        av_log(h, AV_LOG_ERROR, "SFTP session creation failed: %s\n", ssh_get_error(s->session));
101
+        ret = AVERROR(ENOMEM);
102
+        goto fail;
103
+    }
104
+
105
+    if (sftp_init(s->sftp) != SSH_OK) {
106
+        av_log(h, AV_LOG_ERROR, "Error initializing sftp session: %s\n", ssh_get_error(s->session));
107
+        ret = AVERROR(EIO);
108
+        goto fail;
109
+    }
110
+
111
+    if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) {
112
+        access = O_CREAT | O_RDWR;
113
+        if (s->trunc)
114
+            access |= O_TRUNC;
115
+    } else if (flags & AVIO_FLAG_WRITE) {
116
+        access = O_CREAT | O_WRONLY;
117
+        if (s->trunc)
118
+            access |= O_TRUNC;
119
+    } else {
120
+        access = O_RDONLY;
121
+    }
122
+
123
+    if (!(s->file = sftp_open(s->sftp, path, access, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH))) {
124
+        av_log(h, AV_LOG_ERROR, "Error opening sftp file: %s\n", ssh_get_error(s->session));
125
+        ret = AVERROR(EIO);
126
+        goto fail;
127
+    }
128
+
129
+    if (!(stat = sftp_fstat(s->file))) {
130
+        av_log(h, AV_LOG_WARNING, "Cannot stat remote file %s.\n", path);
131
+        s->filesize = -1;
132
+    } else {
133
+        s->filesize = stat->size;
134
+        sftp_attributes_free(stat);
135
+    }
136
+
137
+    return 0;
138
+
139
+  fail:
140
+    libssh_close(h);
141
+    return ret;
142
+}
143
+
144
+static int64_t libssh_seek(URLContext *h, int64_t pos, int whence)
145
+{
146
+    LIBSSHContext *s = h->priv_data;
147
+    int64_t newpos;
148
+
149
+    if (s->filesize == -1 && (whence == AVSEEK_SIZE || whence == SEEK_END)) {
150
+        av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
151
+        return AVERROR(EIO);
152
+    }
153
+
154
+    switch(whence) {
155
+    case AVSEEK_SIZE:
156
+        return s->filesize;
157
+    case SEEK_SET:
158
+        newpos = pos;
159
+        break;
160
+    case SEEK_CUR:
161
+        newpos = sftp_tell64(s->file);
162
+        break;
163
+    case SEEK_END:
164
+        newpos = s->filesize + pos;
165
+        break;
166
+    default:
167
+        return AVERROR(EINVAL);
168
+    }
169
+
170
+    if (sftp_seek64(s->file, newpos)) {
171
+        av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
172
+        return AVERROR(EIO);
173
+    }
174
+
175
+    return newpos;
176
+}
177
+
178
+static int libssh_read(URLContext *h, unsigned char *buf, int size)
179
+{
180
+    LIBSSHContext *s = h->priv_data;
181
+    int bytes_read;
182
+
183
+    if ((bytes_read = sftp_read(s->file, buf, size)) < 0) {
184
+        av_log(h, AV_LOG_ERROR, "Read error.\n");
185
+        return AVERROR(EIO);
186
+    }
187
+    return bytes_read;
188
+}
189
+
190
+static int libssh_write(URLContext *h, const unsigned char *buf, int size)
191
+{
192
+    LIBSSHContext *s = h->priv_data;
193
+    int bytes_written;
194
+
195
+    if ((bytes_written = sftp_write(s->file, buf, size)) < 0) {
196
+        av_log(h, AV_LOG_ERROR, "Write error.\n");
197
+        return AVERROR(EIO);
198
+    }
199
+    return bytes_written;
200
+}
201
+
202
+#define OFFSET(x) offsetof(LIBSSHContext, x)
203
+#define D AV_OPT_FLAG_DECODING_PARAM
204
+#define E AV_OPT_FLAG_ENCODING_PARAM
205
+static const AVOption options[] = {
206
+    {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
207
+    {"truncate", "Truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
208
+    {NULL}
209
+};
210
+
211
+static const AVClass libssh_context_class = {
212
+    .class_name     = "libssh",
213
+    .item_name      = av_default_item_name,
214
+    .option         = options,
215
+    .version        = LIBAVUTIL_VERSION_INT,
216
+};
217
+
218
+URLProtocol ff_libssh_protocol = {
219
+    .name                = "sftp",
220
+    .url_open            = libssh_open,
221
+    .url_read            = libssh_read,
222
+    .url_write           = libssh_write,
223
+    .url_seek            = libssh_seek,
224
+    .url_close           = libssh_close,
225
+    .priv_data_size      = sizeof(LIBSSHContext),
226
+    .priv_data_class     = &libssh_context_class,
227
+    .flags               = URL_PROTOCOL_FLAG_NETWORK,
228
+};