aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukasz Marek <lukasz.m.luki@gmail.com>2013-09-18 14:08:11 +0200
committerMichael Niedermayer <michaelni@gmx.at>2013-09-21 03:26:54 +0200
commit5b153f81645e9e5e88ffeee51ff3f414f70d8e05 (patch)
treea8eceeff56819610935643f48f2f2cccdd6daeb2
parent705b30e24f9ba88d36bb887fbe0cf5218914b160 (diff)
downloadffmpeg-5b153f81645e9e5e88ffeee51ff3f414f70d8e05.tar.gz
lavf: add SFTP protocol via libssh
Signed-off-by: Lukasz Marek <lukasz.m.luki@gmail.com> Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r--Changelog1
-rwxr-xr-xconfigure4
-rw-r--r--doc/protocols.texi32
-rw-r--r--libavformat/Makefile1
-rw-r--r--libavformat/allformats.c1
-rw-r--r--libavformat/libssh.c229
6 files changed, 268 insertions, 0 deletions
diff --git a/Changelog b/Changelog
index fe069ff175..cab8323699 100644
--- a/Changelog
+++ b/Changelog
@@ -30,6 +30,7 @@ version <next>
- Error Resilient AAC syntax (ER AAC LC) decoding
- Low Delay AAC (ER AAC LD) decoding
- mux chapters in ASF files
+- SFTP protocol (via libssh)
version 2.0:
diff --git a/configure b/configure
index fd777cf1ea..a219c26bf9 100755
--- a/configure
+++ b/configure
@@ -225,6 +225,7 @@ External library support:
--enable-libshine enable fixed-point MP3 encoding via libshine [no]
--enable-libsoxr enable Include libsoxr resampling [no]
--enable-libspeex enable Speex de/encoding via libspeex [no]
+ --enable-libssh enable SFTP protocol via libssh [no]
--enable-libstagefright-h264 enable H.264 decoding via libstagefright [no]
--enable-libtheora enable Theora encoding via libtheora [no]
--enable-libtwolame enable MP2 encoding via libtwolame [no]
@@ -1191,6 +1192,7 @@ EXTERNAL_LIBRARY_LIST="
libshine
libsoxr
libspeex
+ libssh
libstagefright_h264
libtheora
libtwolame
@@ -2158,6 +2160,7 @@ librtmpe_protocol_deps="librtmp"
librtmps_protocol_deps="librtmp"
librtmpt_protocol_deps="librtmp"
librtmpte_protocol_deps="librtmp"
+libssh_protocol_deps="libssh"
mmsh_protocol_select="http_protocol"
mmst_protocol_select="network"
rtmp_protocol_deps="!librtmp_protocol"
@@ -4241,6 +4244,7 @@ enabled librtmp && require_pkg_config librtmp librtmp/rtmp.h RTMP_Sock
enabled libschroedinger && require_pkg_config schroedinger-1.0 schroedinger/schro.h schro_init
enabled libshine && require_pkg_config shine shine/layer3.h shine_encode_buffer
enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr
+enabled libssh && require_pkg_config libssh libssh/sftp.h sftp_init
enabled libspeex && require libspeex speex/speex.h speex_decoder_init -lspeex
enabled libstagefright_h264 && require_cpp libstagefright_h264 "binder/ProcessState.h media/stagefright/MetaData.h
media/stagefright/MediaBufferGroup.h media/stagefright/MediaDebug.h media/stagefright/MediaDefs.h
diff --git a/doc/protocols.texi b/doc/protocols.texi
index 5c43f01198..d2bdf23bab 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -520,6 +520,38 @@ The Real-Time Messaging Protocol tunneled through HTTPS (RTMPTS) is used
for streaming multimedia content within HTTPS requests to traverse
firewalls.
+@section libssh
+
+Secure File Transfer Protocol via libssh
+
+Allow to read from or write to remote resources using SFTP protocol.
+
+Following syntax is required.
+
+@example
+sftp://[user[:password]@@]server[:port]/path/to/remote/resource.mpeg
+@end example
+
+This protocol accepts the following options.
+
+@table @option
+@item timeout
+Set timeout of socket I/O operations used by the underlying low level
+operation. By default it is set to -1, which means that the timeout
+is not specified.
+
+@item truncate
+Truncate existing files on write, if set to 1. A value of 0 prevents
+truncating. Default value is 1.
+
+@end table
+
+Example: Play a file stored on remote server.
+
+@example
+ffplay sftp://user:password@@server_address:22/home/user/resource.mpeg
+@end example
+
@section librtmp rtmp, rtmpe, rtmps, rtmpt, rtmpte
Real-Time Messaging Protocol and its variants supported through
diff --git a/libavformat/Makefile b/libavformat/Makefile
index ceffa783a7..f56cc72bfe 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -425,6 +425,7 @@ OBJS-$(CONFIG_LIBNUT_DEMUXER) += libnut.o
OBJS-$(CONFIG_LIBNUT_MUXER) += libnut.o
OBJS-$(CONFIG_LIBQUVI_DEMUXER) += libquvi.o
OBJS-$(CONFIG_LIBRTMP) += librtmp.o
+OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o
# protocols I/O
OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 03c883bc8e..e9eafb4de7 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -348,4 +348,5 @@ void av_register_all(void)
REGISTER_PROTOCOL(LIBRTMPS, librtmps);
REGISTER_PROTOCOL(LIBRTMPT, librtmpt);
REGISTER_PROTOCOL(LIBRTMPTE, librtmpte);
+ REGISTER_PROTOCOL(LIBSSH, libssh);
}
diff --git a/libavformat/libssh.c b/libavformat/libssh.c
new file mode 100644
index 0000000000..4a9b8674cf
--- /dev/null
+++ b/libavformat/libssh.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <fcntl.h>
+#include <libssh/sftp.h>
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+
+typedef struct {
+ const AVClass *class;
+ ssh_session session;
+ sftp_session sftp;
+ sftp_file file;
+ int64_t filesize;
+ int rw_timeout;
+ int trunc;
+} LIBSSHContext;
+
+static int libssh_close(URLContext *h)
+{
+ LIBSSHContext *s = h->priv_data;
+ if (s->file)
+ sftp_close(s->file);
+ if (s->sftp)
+ sftp_free(s->sftp);
+ if (s->session) {
+ ssh_disconnect(s->session);
+ ssh_free(s->session);
+ }
+ return 0;
+}
+
+static int libssh_open(URLContext *h, const char *url, int flags)
+{
+ static const int verbosity = SSH_LOG_NOLOG;
+ LIBSSHContext *s = h->priv_data;
+ char proto[10], path[MAX_URL_SIZE], hostname[1024], credencials[1024];
+ int port = 22, access, ret;
+ long timeout = s->rw_timeout * 1000;
+ const char *user = NULL, *pass = NULL;
+ char *end = NULL;
+ sftp_attributes stat;
+
+ av_url_split(proto, sizeof(proto),
+ credencials, sizeof(credencials),
+ hostname, sizeof(hostname),
+ &port,
+ path, sizeof(path),
+ url);
+
+ if (port <= 0 || port > 65535)
+ port = 22;
+
+ if (!(s->session = ssh_new())) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ user = av_strtok(credencials, ":", &end);
+ pass = av_strtok(end, ":", &end);
+ ssh_options_set(s->session, SSH_OPTIONS_HOST, hostname);
+ ssh_options_set(s->session, SSH_OPTIONS_PORT, &port);
+ ssh_options_set(s->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ if (timeout > 0)
+ ssh_options_set(s->session, SSH_OPTIONS_TIMEOUT_USEC, &timeout);
+ if (user)
+ ssh_options_set(s->session, SSH_OPTIONS_USER, user);
+
+ if (ssh_connect(s->session) != SSH_OK) {
+ av_log(h, AV_LOG_ERROR, "Connection failed. %s\n", ssh_get_error(s->session));
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ if (pass && ssh_userauth_password(s->session, NULL, pass) != SSH_AUTH_SUCCESS) {
+ av_log(h, AV_LOG_ERROR, "Error authenticating with password: %s\n", ssh_get_error(s->session));
+ ret = AVERROR(EACCES);
+ goto fail;
+ }
+
+ if (!(s->sftp = sftp_new(s->session))) {
+ av_log(h, AV_LOG_ERROR, "SFTP session creation failed: %s\n", ssh_get_error(s->session));
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (sftp_init(s->sftp) != SSH_OK) {
+ av_log(h, AV_LOG_ERROR, "Error initializing sftp session: %s\n", ssh_get_error(s->session));
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) {
+ access = O_CREAT | O_RDWR;
+ if (s->trunc)
+ access |= O_TRUNC;
+ } else if (flags & AVIO_FLAG_WRITE) {
+ access = O_CREAT | O_WRONLY;
+ if (s->trunc)
+ access |= O_TRUNC;
+ } else {
+ access = O_RDONLY;
+ }
+
+ if (!(s->file = sftp_open(s->sftp, path, access, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH))) {
+ av_log(h, AV_LOG_ERROR, "Error opening sftp file: %s\n", ssh_get_error(s->session));
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ if (!(stat = sftp_fstat(s->file))) {
+ av_log(h, AV_LOG_WARNING, "Cannot stat remote file %s.\n", path);
+ s->filesize = -1;
+ } else {
+ s->filesize = stat->size;
+ sftp_attributes_free(stat);
+ }
+
+ return 0;
+
+ fail:
+ libssh_close(h);
+ return ret;
+}
+
+static int64_t libssh_seek(URLContext *h, int64_t pos, int whence)
+{
+ LIBSSHContext *s = h->priv_data;
+ int64_t newpos;
+
+ if (s->filesize == -1 && (whence == AVSEEK_SIZE || whence == SEEK_END)) {
+ av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
+ return AVERROR(EIO);
+ }
+
+ switch(whence) {
+ case AVSEEK_SIZE:
+ return s->filesize;
+ case SEEK_SET:
+ newpos = pos;
+ break;
+ case SEEK_CUR:
+ newpos = sftp_tell64(s->file);
+ break;
+ case SEEK_END:
+ newpos = s->filesize + pos;
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ if (sftp_seek64(s->file, newpos)) {
+ av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
+ return AVERROR(EIO);
+ }
+
+ return newpos;
+}
+
+static int libssh_read(URLContext *h, unsigned char *buf, int size)
+{
+ LIBSSHContext *s = h->priv_data;
+ int bytes_read;
+
+ if ((bytes_read = sftp_read(s->file, buf, size)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Read error.\n");
+ return AVERROR(EIO);
+ }
+ return bytes_read;
+}
+
+static int libssh_write(URLContext *h, const unsigned char *buf, int size)
+{
+ LIBSSHContext *s = h->priv_data;
+ int bytes_written;
+
+ if ((bytes_written = sftp_write(s->file, buf, size)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Write error.\n");
+ return AVERROR(EIO);
+ }
+ return bytes_written;
+}
+
+#define OFFSET(x) offsetof(LIBSSHContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
+ {"truncate", "Truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
+ {NULL}
+};
+
+static const AVClass libssh_context_class = {
+ .class_name = "libssh",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+URLProtocol ff_libssh_protocol = {
+ .name = "sftp",
+ .url_open = libssh_open,
+ .url_read = libssh_read,
+ .url_write = libssh_write,
+ .url_seek = libssh_seek,
+ .url_close = libssh_close,
+ .priv_data_size = sizeof(LIBSSHContext),
+ .priv_data_class = &libssh_context_class,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+};