1
0
Fork 0
mirror of https://github.com/albfan/miraclecast.git synced 2025-02-13 10:22:04 +00:00

source implement + sender talk via dbus

This commit is contained in:
Derek Dai 2016-12-05 08:34:03 +08:00
parent fb37a53dec
commit b3b51120e0
No known key found for this signature in database
GPG key ID: E109CC97553EF009
11 changed files with 797 additions and 176 deletions

View file

@ -1,12 +1,28 @@
########### install files ###############
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=session_bus_services_dir dbus-1
OUTPUT_VARIABLE DBUS_SESSION_SERVICES_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
configure_file(
org.freedesktop.miracle.Sender.service.cmake
org.freedesktop.miracle.Sender.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.miracle.Sender.service
DESTINATION ${DBUS_SESSION_SERVICES_DIR}/
)
install(
PROGRAMS miracle-gst gstplayer uibc-viewer
DESTINATION bin
)
INSTALL(
install(
FILES org.freedesktop.miracle.conf
DESTINATION /etc/dbus-1/system.d
)

View file

@ -0,0 +1,3 @@
[D-BUS Service]
Name=org.freedesktop.miracle.Sender
Exec=@CMAKE_INSTALL_PREFIX@/bin/miracle-sender

View file

@ -44,17 +44,23 @@ endif(READLINE_FOUND)
target_link_libraries(miracle-sinkctl miracle-shared)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
########### next target ###############
pkg_check_modules(GIO REQUIRED gio-2.0>=2.30)
set(miracle-srcctl_SRCS ctl.h
ctl-cli.c
ctl-src.h
ctl-src.c
ctl-wifi.c
srcctl.c
wfd.c)
set(miracle-srcctl_SRCS ctl.h
ctl-cli.c
ctl-src.h
ctl-src.c
ctl-wifi.c
srcctl.c
wfd.c)
include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/shared
${CMAKE_BINARY_DIR}/src/stream
${GIO_INCLUDE_DIRS})
add_executable(miracle-srcctl ${miracle-srcctl_SRCS})
@ -63,14 +69,14 @@ install(TARGETS miracle-srcctl DESTINATION bin)
if(READLINE_FOUND)
message(STATUS "Compiling with Readline support")
set_property(TARGET miracle-srcctl
APPEND
PROPERTY COMPILE_DEFINITIONS HAVE_READLINE)
APPEND
PROPERTY COMPILE_DEFINITIONS HAVE_READLINE)
target_link_libraries(miracle-srcctl ${READLINE_LIBRARY})
endif(READLINE_FOUND)
target_link_libraries(miracle-srcctl miracle-shared)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
target_link_libraries(miracle-srcctl miracle-shared
sender-iface
${GIO_LIBRARIES})
########### install files ###############

View file

@ -136,6 +136,8 @@ static void src_handle_setup(struct ctl_src *s,
goto error;
}
ctl_fn_src_setup(s);
return;
error:
@ -389,7 +391,7 @@ static int src_send_set_parameter(struct ctl_src *s)
_rtsp_message_unref_ struct rtsp_message *req;
int r;
const static char tmp[] =
"wfd_video_formats: 00 00 02 02 00000002 00000000 00000000 00 0000 0000 00 none none\n"
"wfd_video_formats: 38 00 02 10 00000080 00000000 00000000 00 0000 0000 00 none none\n"
//"wfd_audio_codecs: AAC 00000001 00\n"
//"wfd_uibc_capability: input_category_list=GENERIC\n;generic_cap_list=SingleTouch;hidc_cap_list=none;port=5100\n"
//"wfd_uibc_setting: disable\n"
@ -690,7 +692,7 @@ static int src_io_fn(sd_event_source *source,
static int src_listen(struct ctl_src *s)
{
int fd, r;
int fd, r, enable = 1;
if (!s)
return cli_EINVAL();
@ -705,8 +707,15 @@ static int src_listen(struct ctl_src *s)
if (fd < 0)
return cli_ERRNO();
r = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable));
if(r < 0) {
r = -errno;
cli_vERR(r);
goto err_close;
}
r = bind(fd, (struct sockaddr*)&s->addr, s->addr_size);
if (fd < 0) {
if (r < 0) {
r = -errno;
cli_vERR(r);
goto err_close;

View file

@ -253,6 +253,7 @@ void ctl_fn_peer_disconnected(struct ctl_peer *p);
void ctl_fn_link_new(struct ctl_link *l);
void ctl_fn_link_free(struct ctl_link *l);
void ctl_fn_src_setup(struct ctl_src *s);
void ctl_fn_src_playing(struct ctl_src *s);
void ctl_fn_src_connected(struct ctl_src *s);
void ctl_fn_src_disconnected(struct ctl_src *s);

View file

@ -36,12 +36,16 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <gio/gio.h>
#include "ctl.h"
#include "ctl-src.h"
#include "wfd.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "config.h"
#include "sender-iface.h"
void launch_sender(struct ctl_src *s);
static sd_bus *bus;
static struct ctl_wifi *wifi;
@ -57,19 +61,20 @@ static struct ctl_link *running_link;
static struct ctl_peer *running_peer;
static struct ctl_peer *pending_peer;
void launch_sender(struct ctl_src *s);
//
//char *gst_scale_res;
int gst_audio_en = 1;
static const int DEFAULT_RSTP_PORT = 1991;
static int gst_audio_en = 1;
static const int DEFAULT_RTSP_PORT = 1991;
//bool uibc;
int rstp_port;
static int rtsp_port;
//int uibc_port;
//
//unsigned int wfd_supported_res_cea = 0x0000001f; /* up to 720x576 */
//unsigned int wfd_supported_res_vesa = 0x00000003; /* up to 800x600 */
//unsigned int wfd_supported_res_hh = 0x00000000; /* not supported */
static Sender *sender;
/*
* cmd: select
*/
@ -504,14 +509,14 @@ void launch_sender(struct ctl_src *s) {
int i = 0;
argv[i++] = "miracle-sender";
//if (gst_audio_en) {
// argv[i++] = "--acodec";
// argv[i++] = "aac";
//}
if (gst_audio_en) {
argv[i++] = "--acodec";
argv[i++] = "aac";
}
argv[i++] = "--host";
argv[i++] = inet_ntoa(((struct sockaddr_in *) &s->addr)->sin_addr);
argv[i++] = "-p";
sprintf(port, "%d", rstp_port);
sprintf(port, "%d", rtsp_port);
argv[i++] = port;
// if (s->hres && s->vres) {
@ -569,15 +574,80 @@ void ctl_fn_src_disconnected(struct ctl_src *s)
} else {
cli_notice("SRC disconnected");
src_connected = false;
if(sender) {
GError *error = NULL;
if(sender_call_stop_sync(sender, NULL, &error)) {
cli_error("SRC failed to stop sender: %s", error->message);
g_error_free(error);
}
g_object_unref(sender);
sender = NULL;
}
}
}
void ctl_fn_src_setup(struct ctl_src *s)
{
GError *error = NULL;
cli_printf("SRC got setup request\n");
if(!sender) {
sender = sender_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
"org.freedesktop.miracle.Sender",
"/org/freedesktop/miracle/Sender/0",
NULL,
&error);
if(!sender) {
cli_error("SRC failed to connect to sender: %s", error->message);
g_error_free(error);
return;
}
}
sender_call_prepare_sync(sender,
inet_ntoa(((struct sockaddr_in *) &s->addr)->sin_addr),
s->sink.rtp_ports.port0,
":0",
1920,
1080,
30,
FALSE,
NULL,
&error);
if(error) {
cli_error("SRC failed to setup: %s", error->message);
g_error_free(error);
return;
}
g_info("SRC sender prepared");
}
void ctl_fn_src_playing(struct ctl_src *s)
{
GError *error = NULL;
cli_printf("SRC got play request\n");
// TODO src_connected must be true, why if() failed?
//if (src_connected)
spawn_gst(s);
//spawn_gst(s);
if(!sender) {
cli_error("SRC not setup yet");
return;
}
if(!sender_call_play_sync(sender, NULL, &error)) {
cli_error("SRC failed to play: %s", error->message);
g_error_free(error);
return;
}
g_info("SRC sender playing");
}
void ctl_fn_peer_new(struct ctl_peer *p)
@ -834,7 +904,7 @@ static int parse_argv(int argc, char *argv[])
};
int c;
rstp_port = DEFAULT_RSTP_PORT;
rtsp_port = DEFAULT_RTSP_PORT;
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) {
@ -863,6 +933,7 @@ static int parse_argv(int argc, char *argv[])
int main(int argc, char **argv)
{
int r;
GError *error = NULL;
setlocale(LC_ALL, "");

View file

@ -1,19 +1,42 @@
pkg_check_modules(GTK REQUIRED gtk+-3.0)
pkg_check_modules(GDK REQUIRED gdk-3.0>=3.20)
pkg_check_modules(GIO REQUIRED gio-2.0>=2.30)
pkg_check_modules(GST REQUIRED gstreamer-1.0)
pkg_check_modules(GST_PBUTILS REQUIRED gstreamer-pbutils-1.0)
find_program(GDBUS_CODEGEN_EXEC NAMES gdbus-codegen)
if(NOT GDBUS_CODEGEN_EXEC)
message(ERROR gdbus-codegen not found)
endif()
include_directories(
${GTK_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
${GDK_INCLUDE_DIRS}
${GST_INCLUDE_DIRS}
${GST_PBUTILS_INCLUDE_DIRS}
${GIO_INCLUDE_DIRS}
)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sender-iface.c
${CMAKE_CURRENT_BINARY_DIR}/sender-iface.h
COMMAND ${GDBUS_CODEGEN_EXEC}
ARGS --generate-c-code sender-iface
--annotate org.freedesktop.miracle.Sender org.gtk.GDBus.C.Name Sender
--interface-prefix org.freedesktop.Miracle
${CMAKE_CURRENT_SOURCE_DIR}/sender-iface.xml
DEPENDS sender-iface.xml
COMMENT "generating sender interface"
)
add_definitions(-DG_LOG_USE_STRUCTURED)
add_library(sender-iface STATIC ${CMAKE_CURRENT_BINARY_DIR}/sender-iface.c)
add_executable(miracle-sender sender)
target_link_libraries(
miracle-sender
${GTK_LIBRARIES}
${GST_LIBRARIES}
${GST_PBUTILS_LIBRARIES})
target_link_libraries(miracle-sender sender-iface
${GDK_LIBRARIES}
${GIO_LIBRARIES}
${GST_LIBRARIES}
${GST_PBUTILS_LIBRARIES})
install(TARGETS miracle-sender DESTINATION bin)

View file

@ -0,0 +1,20 @@
<node>
<interface name="org.freedesktop.miracle.Sender">
<method name="Prepare">
<arg name="host" type="s" direction="in"/>
<arg name="port" type="q" direction="in"/>
<arg name="display" type="s" direction="in"/>
<arg name="width" type="q" direction="in"/>
<arg name="height" type="q" direction="in"/>
<arg name="refresh_rate" type="q" direction="in"/>
<arg name="interleave" type="b" direction="in"/>
</method>
<method name="Play">
</method>
<method name="Pause">
</method>
<method name="Stop">
</method>
<property name="State" type="s" access="read"/>
</interface>
</node>

View file

@ -16,6 +16,7 @@
*/
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <glib.h>
#include <glib-object.h>
#include <glib-unix.h>
@ -25,68 +26,24 @@
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
#include <gst/pbutils/encoding-profile.h>
#include "sender-iface.h"
#include "sender.h"
struct CmdChannel
struct SenderImpl
{
gint in;
gint out;
Sender *skeleton;
guint in_source;
gboolean controllee;
};
static gboolean on_req_in(gint fd, GIOCondition cond, struct CmdChannel *self)
{
}
static struct CmdChannel * cmd_channel_new()
{
int fds[2];
GError *error = NULL;
struct CmdChannel *self = g_slice_new(struct CmdChannel);
if(!self) {
goto end;
}
if(!g_unix_open_pipe(fds, 0, &error)) {
g_warning("%s", error->message);
goto free_self;
}
self->in = fds[0];
self->out = fds[1];
self->in_source = g_unix_fd_add(self->in,
G_IO_IN | G_IO_ERR | G_IO_HUP,
(GUnixFDSourceFunc) on_req_in,
self);
goto end;
free_self:
g_slice_free(struct CmdChannel, self);
self = NULL;
end:
return self;
}
static void cmd_channel_free(struct CmdChannel *self)
{
g_source_remove(self->in_source);
g_close(self->in, NULL);
g_close(self->out, NULL);
g_slice_free(struct CmdChannel, self);
}
struct Sender
{
GMainLoop *loop;
GstElement *pipeline;
struct CmdChannel *channel;
guint bus_owner_id;
guint bus_obj_id;
GDBusMethodInvocation *method_invoke;
};
static gchar *arg_host = NULL;
@ -101,18 +58,78 @@ static gint arg_screen = -1;
static gchar *arg_acodec = NULL;
static struct Sender *sender_new()
{
struct Sender *self = g_slice_new(struct Sender);
if(!self) {
goto end;
}
static gboolean arg_audio_only = FALSE;
static guint arg_refresh_rate = 30;
static gboolean arg_interleave = FALSE;
static char * arg_h264_profile = NULL;
static const char *vpipeline_desc =
"ximagesrc name=vsrc use-damage=false show-pointer=false do-timestamp=true starty=%d startx=%d endy=%d endx=%d "
"capsfilter name=caps_framerate caps=\"video/x-raw, framerate=%d/1\" "
"videoscale name=vscale "
"capsfilter name=caps_scale caps=\"video/x-raw, width=%d, height=%d\" "
"autovideoconvert name=vconv "
"capsfilter name=caps_format caps=\"video/x-raw, format=I420\" "
"encodebin name=vencoder "
"queue name=vqueue max-size-buffers=0 max-size-bytes=0 "
"mpegtsmux name=muxer alignment=7 "
"capsfilter name=caps_muxer caps=\"video/mpegts, packetsize=188, systemstream=true\" "
"rtpmp2tpay name=rtppay "
"udpsink name=sink host=\"%s\" port=%d ";
static const char *apipeline_desc =
//"pulsesrc name=asrc device=\"%s\" "
"audiotestsrc name=asrc "
"audioconvert name=aconv "
"audioresample name=aresample "
"encodebin name=aencoder "
"queue name=aqueue ";
static gboolean sender_impl_prepare(struct SenderImpl *self,
GDBusMethodInvocation *invocation,
const gchar *host,
guint16 port,
const gchar *display,
guint16 width,
guint16 height,
guint16 refresh_rate,
gboolean interleave);
static gboolean sender_impl_play(struct SenderImpl *sender, GDBusMethodInvocation *invocation);
static gboolean sender_impl_pause(struct SenderImpl *sender, GDBusMethodInvocation *invocation);
static gboolean sender_impl_stop(struct SenderImpl *sender, GDBusMethodInvocation *invocation);
#define SENDER_IMPL_ERROR_DOMAIN_NAME "miracle-sender-impl"
static const GDBusErrorEntry sender_dbus_error_entries[] = {
{ MIRACLE_SENDER_ERROR_UNKNOWN, "org.freedesktop.miracle.Sender.Error.Unknown" },
{ MIRACLE_SENDER_ERROR_NOT_PREPARED, "org.freedesktop.miracle.Sender.Error.NoPrepared" },
};
static GQuark sender_impl_error_quark()
{
static gsize registered = 0;
g_dbus_error_register_error_domain("miracle-sender",
&registered,
sender_dbus_error_entries,
G_N_ELEMENTS(sender_dbus_error_entries));
return g_quark_from_static_string("miracle-sender-impl");
}
static struct SenderImpl * sender_impl_new()
{
struct SenderImpl *self = g_slice_new0(struct SenderImpl);
end:
return self;
}
static void sender_free(struct Sender *self)
static void sender_impl_free(struct SenderImpl *self)
{
if(self->pipeline) {
gst_element_set_state(self->pipeline, GST_STATE_NULL);
@ -123,124 +140,461 @@ static void sender_free(struct Sender *self)
g_main_loop_unref(self->loop);
}
g_slice_free(struct Sender, self);
if(self->bus_owner_id) {
g_bus_unown_name(self->bus_owner_id);
}
g_slice_free(struct SenderImpl, self);
}
static int sender_prepare(struct Sender *self)
static void get_screen_dimension(gint *top,
gint *left,
gint *bottom,
gint *right)
{
GError *error = NULL;
GString *desc = g_string_new("ximagesrc ");
GstElement *encoder;
GstElement *vconv;
gint screen_no;
GdkRectangle rect;
if(arg_screen == -1) {
GdkScreen *screen = gdk_screen_get_default();
screen_no = gdk_screen_get_number(screen);
#if GDK_VERSION_MIN_REQUIRED > GDK_VERSION_3_20
GdkDisplay *display = gdk_display_get_default();
GdkMonitor *monitor;
if(arg_screen < 0 || arg_screen >= gdk_display_get_n_monitors(display)) {
monitor = gdk_display_get_primary_monitor(display);
}
else {
screen_no = arg_screen;
monitor = gdk_display_get_monitor(display, arg_screen);
}
g_string_append_printf(desc,
"screen-num=%d ! video/x-raw, framerate=30/1 ",
screen_no);
gdk_monitor_get_geometry(monitor, &rect);
#else
GdkScreen *screen = gdk_screen_get_default();
gint monitor;
if(arg_screen < 0 || arg_screen >= gdk_screen_get_n_monitors(screen)) {
monitor = gdk_screen_get_primary_monitor(screen);
}
else {
monitor = arg_screen;
}
gdk_screen_get_monitor_geometry(screen, monitor, &rect);
#endif
if(arg_width || arg_height) {
g_string_append(desc, "! videoscale ! video/x-raw, ");
*top = rect.y;
*left = rect.x;
*bottom = rect.y + rect.height - 1;
*right = rect.x + rect.width - 1;
}
static int link_elements(GstBin *bin, const char *name, ...)
{
va_list argv;
const char *name1, *name2;
va_start(argv, name);
name1 = name;
while((name2 = va_arg(argv, const char *))) {
GstElement *e1 = gst_bin_get_by_name(bin, name1);
GstElement *e2 = gst_bin_get_by_name(bin, name2);
gboolean linked = gst_element_link(e1, e2);
g_object_unref(G_OBJECT(e2));
g_object_unref(G_OBJECT(e1));
if(!linked) {
g_warning("failed to link %s to %s", name1, name2);
errno = EINVAL;
return -1;
}
name1 = name2;
}
if(arg_width) {
g_string_append_printf(desc, "width=%d ", arg_width);
}
if(arg_height) {
g_string_append_printf(desc, "height=%d ", arg_height);
va_end(argv);
return 0;
}
void on_gst_message_state_changed(struct SenderImpl *self,
GstMessage *message)
{
GstState old, curr, pending;
gst_message_parse_state_changed(message, &old, &curr, &pending);
const char *src = GST_MESSAGE_SRC_NAME(message);
if(strncmp("pipeline", src, 8) || !self->skeleton) {
return;
}
g_info("%s(%s) state changed: %s => %s",
GST_MESSAGE_SRC_NAME(message),
g_type_name(G_OBJECT_TYPE(GST_MESSAGE_SRC(message))),
gst_element_state_get_name(old),
gst_element_state_get_name(curr));
switch(curr) {
case GST_STATE_PLAYING:
g_object_set(self->skeleton, "state", "playing", NULL);
if(self->method_invoke) {
sender_complete_play(SENDER(self->skeleton),
self->method_invoke);
self->method_invoke = NULL;
}
break;
case GST_STATE_PAUSED:
g_object_set(self->skeleton, "state", "paused", NULL);
if(self->method_invoke) {
sender_complete_pause(SENDER(self->skeleton),
self->method_invoke);
self->method_invoke = NULL;
}
break;
default:
break;
}
}
void on_gst_message_error(struct SenderImpl *self,
GstMessage *message)
{
GError *error = NULL;
char *dbg = NULL;
gst_message_parse_error(message, &error, &dbg);
if(self->method_invoke) {
g_dbus_method_invocation_return_gerror(self->method_invoke, error);
self->method_invoke = NULL;
}
g_warning("%s", dbg);
g_error_free(error);
g_free(dbg);
}
void on_gst_message(struct SenderImpl *self,
GstMessage *message,
GstBus *bus)
{
g_debug("Gstreamer message: %s", GST_MESSAGE_TYPE_NAME(message));
switch(GST_MESSAGE_TYPE(message)) {
case GST_MESSAGE_ERROR:
on_gst_message_error(self, message);
break;
case GST_MESSAGE_STATE_CHANGED:
on_gst_message_state_changed(self, message);
break;
case GST_MESSAGE_LATENCY: {
GstClockTime latency = gst_pipeline_get_latency(GST_PIPELINE(self->pipeline));
g_info("New latency is: %lu", latency);
}
break;
default:
break;
}
}
static int prepare_pipeline(struct SenderImpl *self)
{
GError *error = NULL;
gint result;
gint screen_top, screen_left, screen_bottom, screen_right;
GString * desc;
desc = g_string_new(NULL);
if(!desc) {
result = -1;
goto end;
}
get_screen_dimension(&screen_top, &screen_left, &screen_bottom, &screen_right);
g_string_append_printf(desc,
"! videoconvert name=vconv "
"! video/x-raw, format=NV12 "
"! encodebin name=encoder "
"! rtpmp2tpay "
"! udpsink host=\"%s\" port=%d ",
arg_host,
arg_port);
vpipeline_desc,
screen_top,
screen_left,
screen_bottom,
screen_right,
arg_refresh_rate,
arg_width ? arg_width : screen_right - screen_left + 1,
arg_height ? arg_height : screen_bottom - screen_top + 1,
arg_host,
arg_port);
if(arg_acodec) {
g_string_append_printf(desc, "pulsesrc device=\"%s\" ! encoder.",
"alsa_output.pci-0000_00_1b.0.analog-stereo");
g_string_append_printf(desc,
apipeline_desc,
"alsa_output.pci-0000_00_1b.0.analog-stereo.monitor");
}
g_info("final pipeline: %s", desc->str);
self->pipeline = gst_parse_launch(desc->str, &error);
if(GST_PARSE_ERROR_LINK != error->code) {
if(error) {
g_error("%s", error->message);
goto free_desc;
}
gst_element_set_name(GST_ELEMENT(self->pipeline), "pipeline");
GstEncodingVideoProfile *vencode_profile = gst_encoding_video_profile_new(
gst_caps_from_string("video/x-h264, profile=high"),
NULL,
gst_caps_new_any(),
0);
GstElement *vencoder = gst_bin_get_by_name(GST_BIN(self->pipeline), "vencoder");
g_object_set(G_OBJECT(vencoder), "tune", 0x4, NULL);
g_object_set(G_OBJECT(vencoder), "profile", vencode_profile, NULL);
g_object_unref(G_OBJECT(vencoder));
if(arg_acodec) {
const char *format;
if(!strncmp("aac", arg_acodec, 3)) {
format = "audio/mpeg, framed=true, mpegversion=4, stream-format=adts";
}
else if(!strncmp("ac3", arg_acodec, 3)) {
format = "audio/x-ac3, framed=true";
}
else if(!strncmp("pcm", arg_acodec, 3)) {
format = "audio/x-lpcm";
}
GstEncodingAudioProfile *aencode_profile = gst_encoding_audio_profile_new(
gst_caps_from_string(format),
NULL,
gst_caps_new_any(),
0);
GstElement *aencoder = gst_bin_get_by_name(GST_BIN(self->pipeline), "aencoder");
g_object_set(G_OBJECT(aencoder), "profile", aencode_profile, NULL);
g_object_unref(G_OBJECT(aencoder));
}
result = link_elements(GST_BIN(self->pipeline),
"vsrc",
"caps_framerate",
"vscale",
"caps_scale",
"vconv",
"caps_format",
"vencoder",
"vqueue",
"muxer",
"caps_muxer",
"rtppay",
"sink",
NULL);
if(result < 0) {
goto error;
}
GstCaps *caps = gst_caps_from_string("video/mpegts, systemstream=true, packetsize=188");
GstEncodingContainerProfile *cprofile = gst_encoding_container_profile_new("mpeg-ts-profile",
NULL,
caps,
NULL);
gst_caps_unref(caps);
caps = gst_caps_from_string("video/x-h264, format=YV12, width=1280, height=720");
GstEncodingVideoProfile *vprofile = gst_encoding_video_profile_new(caps, NULL, NULL, 0);
gst_encoding_container_profile_add_profile(cprofile, GST_ENCODING_PROFILE(vprofile));
//gst_caps_unref(caps);
/*g_object_unref(vprofile);*/
if(arg_acodec) {
if(!strcmp("aac", arg_acodec)) {
caps = gst_caps_from_string("audio/aac");
}
GstEncodingAudioProfile *aprofile = gst_encoding_audio_profile_new(caps, NULL, NULL, 0);
//gst_caps_unref(caps);
gst_encoding_container_profile_add_profile(cprofile, GST_ENCODING_PROFILE(aprofile));
/*g_object_unref(aprofile);*/
link_elements(GST_BIN(self->pipeline),
"asrc",
"aconv",
"aresample",
"aencoder",
"aqueue",
"muxer",
NULL);
}
encoder = gst_bin_get_by_name(GST_BIN(self->pipeline), "encoder");
g_object_set(G_OBJECT(encoder), "profile", cprofile, NULL);
/*g_object_unref(cprofile);*/
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(self->pipeline));
g_signal_connect_swapped(bus, "message", G_CALLBACK(on_gst_message), self);
gst_bus_add_signal_watch(bus);
vconv = gst_bin_get_by_name(GST_BIN(self->pipeline), "vconv");
if(!gst_element_link(vconv, encoder)) {
printf("failed to link vconv to encoder\n");
}
gst_element_set_state(self->pipeline, GST_STATE_PAUSED);
self->loop = g_main_loop_new(NULL, FALSE);
goto end;
goto free_desc;
error:
if(self->pipeline) {
g_object_unref(self->pipeline);
self->pipeline = NULL;
}
end:
free_desc:
g_string_free(desc, TRUE);
return 0;
end:
return result;
}
static void sender_start(struct Sender *self)
static void sender_on_name_acquired(GDBusConnection *conn,
const char *name,
gpointer user_data)
{
struct SenderImpl *self = user_data;
GError *error = NULL;
gboolean result;
self->skeleton = sender_skeleton_new();
g_signal_connect_swapped(self->skeleton,
"handle-prepare",
G_CALLBACK(sender_impl_prepare),
self);
g_signal_connect_swapped(self->skeleton,
"handle-play",
G_CALLBACK(sender_impl_play),
self);
g_signal_connect_swapped(self->skeleton,
"handle-pause",
G_CALLBACK(sender_impl_pause),
self);
g_signal_connect_swapped(self->skeleton,
"handle-stop",
G_CALLBACK(sender_impl_stop),
self);
g_object_set(self->skeleton, "state", "stop", NULL);
result = g_dbus_interface_skeleton_export(
G_DBUS_INTERFACE_SKELETON(self->skeleton),
conn,
"/org/freedesktop/miracle/Sender/0",
&error);
if(!result) {
g_error("failed to expose object");
}
}
static void sender_on_name_lost(GDBusConnection *conn,
const char *name,
gpointer user_data)
{
struct SenderImpl *self = user_data;
if(self->skeleton) {
g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(self->skeleton));
g_signal_handlers_disconnect_by_data(self->skeleton, self);
g_object_unref(self->skeleton);
self->skeleton = NULL;
}
}
static gint sender_impl_init(struct SenderImpl *self)
{
gint result;
self->loop = g_main_loop_new(NULL, FALSE);
if(!self->loop) {
result = -1;
goto end;
}
self->bus_owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
"org.freedesktop.miracle.Sender",
G_BUS_NAME_OWNER_FLAGS_NONE,
NULL,
sender_on_name_acquired,
NULL,
self,
NULL);
end:
return result;
}
static gboolean sender_impl_prepare(struct SenderImpl *self,
GDBusMethodInvocation *invocation,
const gchar *host,
guint16 port,
const gchar *display,
guint16 width,
guint16 height,
guint16 refresh_rate,
gboolean interleave)
{
if(self->pipeline) {
sender_complete_prepare(SENDER(self->skeleton), invocation);
return TRUE;
}
if(self->method_invoke) {
g_dbus_method_invocation_return_error(invocation,
MIRACLE_SENDER_ERROR,
MIRACLE_SENDER_ERROR_AGAIN,
"request handling in progress");
return TRUE;
}
self->method_invoke = invocation;
if(arg_host) {
g_free(arg_host);
}
arg_host = g_strdup(host);
arg_port = port;
g_setenv("DISPLAY", display ? display : ":0", TRUE);
arg_width = width;
arg_height = height;
prepare_pipeline(self);
return TRUE;
}
static gboolean sender_impl_play(struct SenderImpl *self,
GDBusMethodInvocation *invocation)
{
if(!self->pipeline) {
g_dbus_method_invocation_return_error(invocation,
MIRACLE_SENDER_ERROR,
MIRACLE_SENDER_ERROR_NOT_PREPARED,
"sender not prepared");
return TRUE;
}
if(self->method_invoke) {
g_dbus_method_invocation_return_error(invocation,
MIRACLE_SENDER_ERROR,
MIRACLE_SENDER_ERROR_AGAIN,
"request handling in progress");
return TRUE;
}
self->method_invoke = invocation;
gst_element_set_state(self->pipeline, GST_STATE_PLAYING);
return TRUE;
}
static void sender_pause(struct Sender *self)
static gboolean sender_impl_pause(struct SenderImpl *self,
GDBusMethodInvocation *invocation)
{
if(!self->pipeline) {
g_dbus_method_invocation_return_error(invocation,
MIRACLE_SENDER_ERROR,
MIRACLE_SENDER_ERROR_NOT_PREPARED,
"sender not prepared");
return TRUE;
}
if(self->method_invoke) {
g_dbus_method_invocation_return_error(invocation,
MIRACLE_SENDER_ERROR,
MIRACLE_SENDER_ERROR_AGAIN,
"request handling in progress");
return TRUE;
}
self->method_invoke = invocation;
gst_element_set_state(self->pipeline, GST_STATE_PAUSED);
return TRUE;
}
static void sender_stop(struct Sender *self)
static gboolean sender_impl_stop(struct SenderImpl *self,
GDBusMethodInvocation *invocation)
{
if(!self->pipeline) {
goto end;
}
if(self->method_invoke) {
g_dbus_method_invocation_return_error(invocation,
MIRACLE_SENDER_ERROR,
MIRACLE_SENDER_ERROR_AGAIN,
"request handling in progress");
return TRUE;
}
g_object_set(self->skeleton, "state", "stop", NULL);
gst_element_set_state(self->pipeline, GST_STATE_NULL);
g_object_unref(self->pipeline);
self->pipeline = NULL;
end:
sender_complete_stop(SENDER(self->skeleton), invocation);
return TRUE;
}
static void sender_run(struct Sender *self)
static gboolean sender_impl_run(struct SenderImpl *self)
{
g_main_loop_run(self->loop);
}
@ -253,7 +607,9 @@ static GOptionEntry entries[] = {
{ "width", 'w', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &arg_width, "", "" },
{ "height", 'h', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &arg_height, "", "" },
{ "screen-num", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &arg_screen, "screen number to cast to", "" },
{ "acodec", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &arg_acodec, "codec to encode audio", "" },
{ "acodec", 'a', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &arg_acodec, "codec to encode audio", "" },
{ "audio-only", 'o', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &arg_audio_only, "no video, audio stream only", "" },
{ 0 }
};
static void arg_parse(int *argc, char ***args)
@ -280,30 +636,55 @@ static void arg_parse(int *argc, char ***args)
g_option_context_free(opt_context);
}
static void gst_rerank(const char *name, ...)
{
va_list names;
GstRegistry *reg = gst_registry_get();
va_start(names, name);
while(name) {
GstPluginFeature *plugin = gst_registry_lookup_feature(reg, name);
if(!plugin) {
goto next;
}
g_info("raising rank of plugin %s from %u to %u",
name,
gst_plugin_feature_get_rank(plugin),
GST_RANK_PRIMARY + 1);
gst_plugin_feature_set_rank(plugin, GST_RANK_PRIMARY + 1);
gst_object_unref(plugin);
next:
name = va_arg(names, const char *);
}
va_end(names);
}
int main(int argc, char *args[])
{
struct Sender *sender;
struct SenderImpl *sender;
arg_parse(&argc, &args);
gdk_init(&argc, &args);
gst_init(&argc, &args);
gst_pb_utils_init();
//gst_rerank("vaapih264enc", "vaapienc_h264", "glcolorconvert", NULL);
sender = sender_new();
sender = sender_impl_new();
if(!sender) {
g_error("%s", strerror(errno));
}
if(sender_prepare(sender) < 0) {
if(!sender_impl_init(sender)) {
g_error("%s", strerror(errno));
}
sender_start(sender);
sender_impl_run(sender);
sender_run(sender);
sender_free(sender);
g_object_unref(sender);
return 0;
}

25
src/stream/sender.h Normal file
View file

@ -0,0 +1,25 @@
/*
* =====================================================================================
*
* Filename: sender.h
*
* Description:
*
* Version: 1.0
* Created: 20161125 104220
* Revision: none
* Compiler: gcc
*
* Author: YOUR NAME (),
* Organization:
*
* =====================================================================================
*/
#define MIRACLE_SENDER_ERROR sender_impl_error_quark()
enum {
MIRACLE_SENDER_ERROR_UNKNOWN,
MIRACLE_SENDER_ERROR_NOT_PREPARED,
MIRACLE_SENDER_ERROR_AGAIN,
};

66
src/stream/sender.py Executable file
View file

@ -0,0 +1,66 @@
#!/usr/bin/python3
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
gi.require_version('GstPbutils', '1.0')
from gi.repository import GLib
from gi.repository import Gst, GstBase, GstPbutils
Gst.init(None)
GstPbutils.pb_utils_init()
pipeline = Gst.ElementFactory.make('pipeline')
ximagesrc = Gst.ElementFactory.make('ximagesrc')
ximagesrc.set_property('use-damage', False)
ximagesrc.set_property('show-pointer', False)
ximagesrc.set_property('do-timestamp', True)
ximagesrc.set_property('startx', 0)
ximagesrc.set_property('starty', 0)
ximagesrc.set_property('endx', 1919)
ximagesrc.set_property('endy', 1079)
pipeline.add(ximagesrc)
vscale = Gst.ElementFactory.make('videoscale')
pipeline.add(vscale)
ximagesrc.link(vscale)
vconvert = Gst.ElementFactory.make('videoconvert')
pipeline.add(vconvert)
vscale.link(vconvert)
filter = Gst.ElementFactory.make('capsfilter')
filter.set_property('caps', Gst.Caps.from_string('video/x-raw, format=I420, framerate=30/1, width=1920, height=1080'))
pipeline.add(filter)
vconvert.link(filter)
encoder = Gst.ElementFactory.make('encodebin')
#cont_profile = GstPbutils.EncodingContainerProfile.new('mpegts',
# None,
# Gst.Caps.from_string('video/mpegts, packetsize=188, systemstream=true'),
# None)
vencode_profile = GstPbutils.EncodingVideoProfile.new(Gst.Caps.from_string('video/x-h264'),
None,
Gst.Caps.new_any(),
1)
#cont_profile.add_profile(vencode_profile)
encoder.set_property('profile', vencode_profile)
pipeline.add(encoder)
filter.link(encoder)
mpegtsmux = Gst.ElementFactory.make('mpegtsmux')
mpegtsmux.set_property('alignment', 7)
pipeline.add(mpegtsmux)
encoder.link(mpegtsmux)
sink = Gst.ElementFactory.make('filesink')
sink.set_property('location', 'xxx')
pipeline.add(sink)
mpegtsmux.link(sink)
pipeline.set_state(Gst.State.PLAYING)
loop = GLib.MainLoop()
loop.run()