mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-28 01:59:24 +01:00
Initial commit of an afc gstreamer source
This commit is contained in:
parent
7a6499abde
commit
cc1728ddf0
@ -208,6 +208,10 @@ add_subdirectory(tests)
|
||||
add_subdirectory(dist)
|
||||
add_subdirectory(debian)
|
||||
|
||||
if(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
|
||||
add_subdirectory(gstafcsrc)
|
||||
endif(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
|
||||
|
||||
# Uninstall support
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
|
28
gstafcsrc/CMakeLists.txt
Normal file
28
gstafcsrc/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
set(CMAKE_C_FLAGS "-Wall")
|
||||
set(CMAKE_CXX_FLAGS "-Woverloaded-virtual -Wall")
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
include_directories(${GLIB_INCLUDE_DIRS})
|
||||
include_directories(${GOBJECT_INCLUDE_DIRS})
|
||||
include_directories(${GSTREAMER_INCLUDE_DIRS})
|
||||
include_directories(${IMOBILEDEVICE_INCLUDE_DIRS})
|
||||
|
||||
set(SOURCES
|
||||
gstafcsrc.cpp
|
||||
main.c
|
||||
)
|
||||
|
||||
add_library(gstafcsrc SHARED
|
||||
${SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(gstafcsrc
|
||||
${GOBJECT_LIBRARIES}
|
||||
${GLIB_LIBRARIES}
|
||||
${GSTREAMER_LIBRARIES}
|
||||
${GSTREAMER_BASE_LIBRARIES}
|
||||
${IMOBILEDEVICE_LIBRARIES}
|
||||
)
|
276
gstafcsrc/gstafcsrc.cpp
Normal file
276
gstafcsrc/gstafcsrc.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
/* This file is part of Clementine.
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "gstafcsrc.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <gst/gst.h>
|
||||
|
||||
|
||||
// Signals
|
||||
enum {
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
// Properties
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_UUID,
|
||||
PROP_PATH,
|
||||
};
|
||||
|
||||
|
||||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("ANY")
|
||||
);
|
||||
|
||||
GST_BOILERPLATE (GstAfcSrc, gst_afc_src, GstBaseSrc, GST_TYPE_BASE_SRC);
|
||||
|
||||
static void gst_afc_src_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_afc_src_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
static void gst_afc_src_finalize(GObject* object);
|
||||
static gboolean gst_afc_src_start(GstBaseSrc* src);
|
||||
static gboolean gst_afc_src_stop(GstBaseSrc* src);
|
||||
static GstFlowReturn gst_afc_src_create(GstBaseSrc* src, guint64 offset, guint length, GstBuffer** buffer);
|
||||
static gboolean gst_afc_src_is_seekable(GstBaseSrc* src);
|
||||
static gboolean gst_afc_src_get_size(GstBaseSrc* src, guint64* size);
|
||||
|
||||
|
||||
|
||||
static void gst_afc_src_base_init(gpointer gclass) {
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
||||
|
||||
gst_element_class_set_details_simple(element_class,
|
||||
"iPod/iPhone Source",
|
||||
"Source/Afc",
|
||||
"Read using libimobiledevice",
|
||||
"David Sansome <me@davidsansome.com>");
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&src_factory));
|
||||
}
|
||||
|
||||
static void gst_afc_src_class_init (GstAfcSrcClass* klass) {
|
||||
GObjectClass* gobject_class = (GObjectClass*) klass;
|
||||
GstBaseSrcClass* gstbasesrc_class = (GstBaseSrcClass*) klass;
|
||||
|
||||
gobject_class->set_property = gst_afc_src_set_property;
|
||||
gobject_class->get_property = gst_afc_src_get_property;
|
||||
gobject_class->finalize = gst_afc_src_finalize;
|
||||
|
||||
g_object_class_install_property(gobject_class, PROP_UUID,
|
||||
g_param_spec_string(
|
||||
"uuid", "AFC Device UUID",
|
||||
"The UUID of the device to connect to", NULL,
|
||||
GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)));
|
||||
g_object_class_install_property(gobject_class, PROP_PATH,
|
||||
g_param_spec_string(
|
||||
"path", "File path",
|
||||
"The absolute path of the file on the device", NULL,
|
||||
GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)));
|
||||
|
||||
gstbasesrc_class->start = gst_afc_src_start;
|
||||
gstbasesrc_class->stop = gst_afc_src_stop;
|
||||
gstbasesrc_class->create = gst_afc_src_create;
|
||||
gstbasesrc_class->is_seekable = gst_afc_src_is_seekable;
|
||||
gstbasesrc_class->get_size = gst_afc_src_get_size;
|
||||
}
|
||||
|
||||
static void gst_afc_src_init(GstAfcSrc* element, GstAfcSrcClass* gclass) {
|
||||
element->uuid_ = NULL;
|
||||
element->path_ = NULL;
|
||||
element->afc_ = NULL;
|
||||
element->afc_port_ = 0;
|
||||
element->device_ = NULL;
|
||||
element->file_handle_ = 0;
|
||||
element->lockdown_ = NULL;
|
||||
}
|
||||
|
||||
static void gst_afc_src_finalize(GObject* object) {
|
||||
GstAfcSrc* src = GST_AFCSRC(object);
|
||||
free(src->uuid_);
|
||||
free(src->path_);
|
||||
|
||||
G_OBJECT_CLASS(parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static void gst_afc_src_set_property(
|
||||
GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) {
|
||||
GstAfcSrc* self = GST_AFCSRC(object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_UUID:
|
||||
self->uuid_ = g_strdup(g_value_get_string(value));
|
||||
break;
|
||||
|
||||
case PROP_PATH:
|
||||
self->path_ = g_strdup(g_value_get_string(value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gst_afc_src_get_property(
|
||||
GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) {
|
||||
GstAfcSrc* self = GST_AFCSRC(object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_UUID:
|
||||
g_value_set_string(value, self->uuid_);
|
||||
break;
|
||||
|
||||
case PROP_PATH:
|
||||
g_value_set_string(value, self->path_);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_start(GstBaseSrc* src) {
|
||||
GstAfcSrc* self = GST_AFCSRC(src);
|
||||
|
||||
// Check that a UUID and path have been passed
|
||||
if (!self->uuid_ || self->uuid_[0] == '\0') {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("No UUID specified"), (NULL));
|
||||
return false;
|
||||
}
|
||||
if (!self->path_ || self->path_[0] == '\0') {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("No path specified"), (NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the device
|
||||
idevice_error_t err = idevice_new(&self->device_, self->uuid_);
|
||||
if (err != IDEVICE_E_SUCCESS) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("idevice error: %d", err), (NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
lockdownd_error_t lockdown_err =
|
||||
lockdownd_client_new_with_handshake(self->device_, &self->lockdown_, "GstAfcSrc");
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("lockdown error: %d", lockdown_err), (NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
lockdown_err = lockdownd_start_service(self->lockdown_, "com.apple.afc", &self->afc_port_);
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("lockdown error: %d", lockdown_err), (NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
afc_error_t afc_err = afc_client_new(self->device_, self->afc_port_, &self->afc_);
|
||||
if (afc_err != 0) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", afc_err), (NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Try opening the file
|
||||
afc_err = afc_file_open(self->afc_, self->path_, AFC_FOPEN_RDONLY, &self->file_handle_);
|
||||
if (afc_err != 0) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", afc_err), (NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_stop(GstBaseSrc* src) {
|
||||
GstAfcSrc* self = GST_AFCSRC(src);
|
||||
|
||||
if (self->file_handle_) {
|
||||
afc_file_close(self->afc_, self->file_handle_);
|
||||
}
|
||||
|
||||
if (self->afc_) {
|
||||
afc_client_free(self->afc_);
|
||||
}
|
||||
if (self->lockdown_) {
|
||||
lockdownd_client_free(self->lockdown_);
|
||||
}
|
||||
if (self->device_) {
|
||||
idevice_free(self->device_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static GstFlowReturn gst_afc_src_create(GstBaseSrc* src, guint64 offset, guint length, GstBuffer** buffer) {
|
||||
GstAfcSrc* self = GST_AFCSRC(src);
|
||||
|
||||
GstBuffer* buf = gst_buffer_try_new_and_alloc(length);
|
||||
if (buf == NULL && length > 0) {
|
||||
GST_ERROR_OBJECT(src, "Failed to allocate %u bytes", length);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
uint32_t bytes_read = 0;
|
||||
if (length > 0) {
|
||||
afc_file_seek(self->afc_, self->file_handle_, offset, SEEK_SET);
|
||||
afc_file_read(self->afc_, self->file_handle_, (char*)(GST_BUFFER_DATA(buf)), length, &bytes_read);
|
||||
}
|
||||
|
||||
*buffer = buf;
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_is_seekable(GstBaseSrc* src) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_get_size(GstBaseSrc* src, guint64* size) {
|
||||
GstAfcSrc* self = GST_AFCSRC(src);
|
||||
|
||||
char** infolist = NULL;
|
||||
afc_error_t err = afc_get_file_info(self->afc_, self->path_, &infolist);
|
||||
if (err != AFC_E_SUCCESS || !infolist) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", err), (NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found_size = false;
|
||||
|
||||
char** p = infolist;
|
||||
while (*p != NULL) {
|
||||
if (g_strcmp0(*p, "st_size") == 0) {
|
||||
*size = strtoll(*(p+1), NULL, 0);
|
||||
found_size = true;
|
||||
}
|
||||
free(*p); ++p;
|
||||
free(*p); ++p;
|
||||
}
|
||||
free(infolist);
|
||||
|
||||
if (!found_size) {
|
||||
*size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
66
gstafcsrc/gstafcsrc.h
Normal file
66
gstafcsrc/gstafcsrc.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* This file is part of Clementine.
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GST_AFCSRC_H__
|
||||
#define __GST_AFCSRC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasesrc.h>
|
||||
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_AFCSRC \
|
||||
(gst_afc_src_get_type())
|
||||
#define GST_AFCSRC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AFCSRC,GstAfcSrc))
|
||||
#define GST_AFCSRC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AFCSRC,GstAfcSrcClass))
|
||||
#define GST_IS_AFCSRC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AFCSRC))
|
||||
#define GST_IS_AFCSRC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AFCSRC))
|
||||
|
||||
typedef struct _GstAfcSrc GstAfcSrc;
|
||||
typedef struct _GstAfcSrcClass GstAfcSrcClass;
|
||||
|
||||
struct _GstAfcSrc {
|
||||
GstBaseSrc element;
|
||||
|
||||
char* uuid_;
|
||||
char* path_;
|
||||
|
||||
idevice_t device_;
|
||||
lockdownd_client_t lockdown_;
|
||||
afc_client_t afc_;
|
||||
|
||||
uint16_t afc_port_;
|
||||
|
||||
uint64_t file_handle_;
|
||||
};
|
||||
|
||||
struct _GstAfcSrcClass {
|
||||
GstBaseSrcClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_afc_src_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
42
gstafcsrc/main.c
Normal file
42
gstafcsrc/main.c
Normal file
@ -0,0 +1,42 @@
|
||||
/* This file is part of Clementine.
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gstafcsrc.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC(gst_afc_src_debug);
|
||||
#define GST_CAT_DEFAULT gst_afc_src_debug
|
||||
|
||||
static gboolean afcsrc_init(GstPlugin* afcsrc) {
|
||||
GST_DEBUG_CATEGORY_INIT (gst_afc_src_debug, "afcsrc", 0, "iPod/iPhone Source");
|
||||
|
||||
return gst_element_register(afcsrc, "afcsrc", GST_RANK_NONE, GST_TYPE_AFCSRC);
|
||||
}
|
||||
|
||||
#define PACKAGE "Clementine"
|
||||
|
||||
GST_PLUGIN_DEFINE(
|
||||
GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
"afcsrc",
|
||||
"iPod/iPhone Source",
|
||||
afcsrc_init,
|
||||
"0.1",
|
||||
"GPL",
|
||||
"Clementine",
|
||||
"http://www.clementine-player.org/"
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user