mirror of
https://github.com/CircusMonkey/ros_rtsp.git
synced 2025-01-05 15:38:35 -04:00
Init commit
This commit is contained in:
parent
c527ba284b
commit
6836d66abc
225
CMakeLists.txt
Normal file
225
CMakeLists.txt
Normal file
@ -0,0 +1,225 @@
|
||||
cmake_minimum_required(VERSION 2.8.3)
|
||||
project(ros_rtsp)
|
||||
|
||||
## Compile as C++11, supported in ROS Kinetic and newer
|
||||
add_compile_options(-std=c++11)
|
||||
|
||||
## Find catkin macros and libraries
|
||||
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
|
||||
## is used, also find other catkin packages
|
||||
find_package(OpenCV REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GST REQUIRED
|
||||
gstreamer-1.0
|
||||
gstreamer-app-1.0
|
||||
gstreamer-rtsp-server-1.0
|
||||
)
|
||||
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS
|
||||
cv_bridge
|
||||
image_transport
|
||||
roscpp
|
||||
sensor_msgs
|
||||
std_msgs
|
||||
)
|
||||
|
||||
## System dependencies are found with CMake's conventions
|
||||
# find_package(Boost REQUIRED COMPONENTS system)
|
||||
|
||||
|
||||
## Uncomment this if the package has a setup.py. This macro ensures
|
||||
## modules and global scripts declared therein get installed
|
||||
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
|
||||
# catkin_python_setup()
|
||||
|
||||
################################################
|
||||
## Declare ROS messages, services and actions ##
|
||||
################################################
|
||||
|
||||
## To declare and build messages, services or actions from within this
|
||||
## package, follow these steps:
|
||||
## * Let MSG_DEP_SET be the set of packages whose message types you use in
|
||||
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
|
||||
## * In the file package.xml:
|
||||
## * add a build_depend tag for "message_generation"
|
||||
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
|
||||
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
|
||||
## but can be declared for certainty nonetheless:
|
||||
## * add a exec_depend tag for "message_runtime"
|
||||
## * In this file (CMakeLists.txt):
|
||||
## * add "message_generation" and every package in MSG_DEP_SET to
|
||||
## find_package(catkin REQUIRED COMPONENTS ...)
|
||||
## * add "message_runtime" and every package in MSG_DEP_SET to
|
||||
## catkin_package(CATKIN_DEPENDS ...)
|
||||
## * uncomment the add_*_files sections below as needed
|
||||
## and list every .msg/.srv/.action file to be processed
|
||||
## * uncomment the generate_messages entry below
|
||||
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
|
||||
|
||||
## Generate messages in the 'msg' folder
|
||||
# add_message_files(
|
||||
# FILES
|
||||
# Message1.msg
|
||||
# Message2.msg
|
||||
# )
|
||||
|
||||
## Generate services in the 'srv' folder
|
||||
# add_service_files(
|
||||
# FILES
|
||||
# Service1.srv
|
||||
# Service2.srv
|
||||
# )
|
||||
|
||||
## Generate actions in the 'action' folder
|
||||
# add_action_files(
|
||||
# FILES
|
||||
# Action1.action
|
||||
# Action2.action
|
||||
# )
|
||||
|
||||
## Generate added messages and services with any dependencies listed here
|
||||
# generate_messages(
|
||||
# DEPENDENCIES
|
||||
# sensor_msgs# std_msgs
|
||||
# )
|
||||
|
||||
################################################
|
||||
## Declare ROS dynamic reconfigure parameters ##
|
||||
################################################
|
||||
|
||||
## To declare and build dynamic reconfigure parameters within this
|
||||
## package, follow these steps:
|
||||
## * In the file package.xml:
|
||||
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
|
||||
## * In this file (CMakeLists.txt):
|
||||
## * add "dynamic_reconfigure" to
|
||||
## find_package(catkin REQUIRED COMPONENTS ...)
|
||||
## * uncomment the "generate_dynamic_reconfigure_options" section below
|
||||
## and list every .cfg file to be processed
|
||||
|
||||
## Generate dynamic reconfigure parameters in the 'cfg' folder
|
||||
# generate_dynamic_reconfigure_options(
|
||||
# cfg/DynReconf1.cfg
|
||||
# cfg/DynReconf2.cfg
|
||||
# )
|
||||
|
||||
###################################
|
||||
## catkin specific configuration ##
|
||||
###################################
|
||||
## The catkin_package macro generates cmake config files for your package
|
||||
## Declare things to be passed to dependent projects
|
||||
## INCLUDE_DIRS: uncomment this if your package contains header files
|
||||
## LIBRARIES: libraries you create in this project that dependent projects also need
|
||||
## CATKIN_DEPENDS: catkin_packages dependent projects also need
|
||||
## DEPENDS: system dependencies of this project that dependent projects also need
|
||||
catkin_package(
|
||||
# INCLUDE_DIRS include
|
||||
# LIBRARIES ros_rtsp
|
||||
CATKIN_DEPENDS cv_bridge image_transport roscpp sensor_msgs std_msgs
|
||||
# DEPENDS system_lib
|
||||
)
|
||||
|
||||
###########
|
||||
## Build ##
|
||||
###########
|
||||
|
||||
## Specify additional locations of header files
|
||||
## Your package locations should be listed before other locations
|
||||
include_directories(
|
||||
include
|
||||
${catkin_INCLUDE_DIRS}
|
||||
${GST_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
## Declare a C++ library
|
||||
# add_library(${PROJECT_NAME}
|
||||
# src/${PROJECT_NAME}/ros_rtsp.cpp
|
||||
# )
|
||||
|
||||
## Add cmake target dependencies of the library
|
||||
## as an example, code may need to be generated before libraries
|
||||
## either from message generation or dynamic reconfigure
|
||||
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||
|
||||
## Declare a C++ executable
|
||||
## With catkin_make all packages are built within a single CMake context
|
||||
## The recommended prefix ensures that target names across packages don't collide
|
||||
add_executable(camera_publisher src/camera_publisher.cpp)
|
||||
add_executable(camera_subscriber src/camera_subscriber.cpp)
|
||||
add_executable(rtsp_server src/test-appsrc.cpp)
|
||||
|
||||
## Rename C++ executable without prefix
|
||||
## The above recommended prefix causes long target names, the following renames the
|
||||
## target back to the shorter version for ease of user use
|
||||
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
|
||||
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
|
||||
|
||||
## Add cmake target dependencies of the executable
|
||||
## same as for the library above
|
||||
add_dependencies(camera_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||
add_dependencies(camera_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||
add_dependencies(rtsp_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||
|
||||
## Specify libraries to link a library or executable target against
|
||||
target_link_libraries(camera_publisher
|
||||
${catkin_LIBRARIES}
|
||||
${OpenCV_LIBRARIES}
|
||||
)
|
||||
target_link_libraries(camera_subscriber
|
||||
${catkin_LIBRARIES}
|
||||
${OpenCV_LIBRARIES}
|
||||
)
|
||||
target_link_libraries(rtsp_server
|
||||
${catkin_LIBRARIES}
|
||||
${GST_LIBRARIES}
|
||||
)
|
||||
|
||||
|
||||
#############
|
||||
## Install ##
|
||||
#############
|
||||
|
||||
# all install targets should use catkin DESTINATION variables
|
||||
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
|
||||
|
||||
## Mark executable scripts (Python etc.) for installation
|
||||
## in contrast to setup.py, you can choose the destination
|
||||
# install(PROGRAMS
|
||||
# scripts/my_python_script
|
||||
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
# )
|
||||
|
||||
## Mark executables and/or libraries for installation
|
||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
|
||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
# )
|
||||
|
||||
## Mark cpp header files for installation
|
||||
# install(DIRECTORY include/${PROJECT_NAME}/
|
||||
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
|
||||
# FILES_MATCHING PATTERN "*.h"
|
||||
# PATTERN ".svn" EXCLUDE
|
||||
# )
|
||||
|
||||
## Mark other files for installation (e.g. launch and bag files, etc.)
|
||||
# install(FILES
|
||||
# # myfile1
|
||||
# # myfile2
|
||||
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||
# )
|
||||
|
||||
#############
|
||||
## Testing ##
|
||||
#############
|
||||
|
||||
## Add gtest based cpp test target and link libraries
|
||||
# catkin_add_gtest(${PROJECT_NAME}-test test/test_ros_rtsp.cpp)
|
||||
# if(TARGET ${PROJECT_NAME}-test)
|
||||
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
|
||||
# endif()
|
||||
|
||||
## Add folders to be run by python nosetests
|
||||
# catkin_add_nosetests(test)
|
74
package.xml
Normal file
74
package.xml
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0"?>
|
||||
<package format="2">
|
||||
<name>ros_rtsp</name>
|
||||
<version>0.0.0</version>
|
||||
<description>The ros_rtsp package</description>
|
||||
|
||||
<!-- One maintainer tag required, multiple allowed, one person per tag -->
|
||||
<!-- Example: -->
|
||||
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
|
||||
<maintainer email="samar@todo.todo">samar</maintainer>
|
||||
|
||||
|
||||
<!-- One license tag required, multiple allowed, one license per tag -->
|
||||
<!-- Commonly used license strings: -->
|
||||
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
|
||||
<license>TODO</license>
|
||||
|
||||
|
||||
<!-- Url tags are optional, but multiple are allowed, one per tag -->
|
||||
<!-- Optional attribute type can be: website, bugtracker, or repository -->
|
||||
<!-- Example: -->
|
||||
<!-- <url type="website">http://wiki.ros.org/ros_rtsp</url> -->
|
||||
|
||||
|
||||
<!-- Author tags are optional, multiple are allowed, one per tag -->
|
||||
<!-- Authors do not have to be maintainers, but could be -->
|
||||
<!-- Example: -->
|
||||
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
|
||||
|
||||
|
||||
<!-- The *depend tags are used to specify dependencies -->
|
||||
<!-- Dependencies can be catkin packages or system dependencies -->
|
||||
<!-- Examples: -->
|
||||
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
|
||||
<!-- <depend>roscpp</depend> -->
|
||||
<!-- Note that this is equivalent to the following: -->
|
||||
<!-- <build_depend>roscpp</build_depend> -->
|
||||
<!-- <exec_depend>roscpp</exec_depend> -->
|
||||
<!-- Use build_depend for packages you need at compile time: -->
|
||||
<!-- <build_depend>message_generation</build_depend> -->
|
||||
<!-- Use build_export_depend for packages you need in order to build against this package: -->
|
||||
<!-- <build_export_depend>message_generation</build_export_depend> -->
|
||||
<!-- Use buildtool_depend for build tool packages: -->
|
||||
<!-- <buildtool_depend>catkin</buildtool_depend> -->
|
||||
<!-- Use exec_depend for packages you need at runtime: -->
|
||||
<!-- <exec_depend>message_runtime</exec_depend> -->
|
||||
<!-- Use test_depend for packages you need only for testing: -->
|
||||
<!-- <test_depend>gtest</test_depend> -->
|
||||
<!-- Use doc_depend for packages you need only for building documentation: -->
|
||||
<!-- <doc_depend>doxygen</doc_depend> -->
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
<build_depend>cv_bridge</build_depend>
|
||||
<build_depend>image_transport</build_depend>
|
||||
<build_depend>roscpp</build_depend>
|
||||
<build_depend>sensor_msgs</build_depend>
|
||||
<build_depend>std_msgs</build_depend>
|
||||
<build_export_depend>cv_bridge</build_export_depend>
|
||||
<build_export_depend>image_transport</build_export_depend>
|
||||
<build_export_depend>roscpp</build_export_depend>
|
||||
<build_export_depend>sensor_msgs</build_export_depend>
|
||||
<build_export_depend>std_msgs</build_export_depend>
|
||||
<exec_depend>cv_bridge</exec_depend>
|
||||
<exec_depend>image_transport</exec_depend>
|
||||
<exec_depend>roscpp</exec_depend>
|
||||
<exec_depend>sensor_msgs</exec_depend>
|
||||
<exec_depend>std_msgs</exec_depend>
|
||||
|
||||
|
||||
<!-- The export tag contains other, unspecified, tags -->
|
||||
<export>
|
||||
<!-- Other tools can request additional information be placed here -->
|
||||
|
||||
</export>
|
||||
</package>
|
42
src/camera_publisher.cpp
Normal file
42
src/camera_publisher.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include <ros/ros.h>
|
||||
#include <image_transport/image_transport.h>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <sstream> // for converting the command line parameter to integer
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Check if video source has been passed as a parameter
|
||||
if(argv[1] == NULL) return 1;
|
||||
|
||||
ros::init(argc, argv, "image_publisher");
|
||||
ros::NodeHandle nh;
|
||||
image_transport::ImageTransport it(nh);
|
||||
image_transport::Publisher pub = it.advertise("camera/image", 1);
|
||||
|
||||
// Convert the passed as command line parameter index for the video device to an integer
|
||||
std::istringstream video_sourceCmd(argv[1]);
|
||||
int video_source;
|
||||
// Check if it is indeed a number
|
||||
if(!(video_sourceCmd >> video_source)) return 1;
|
||||
|
||||
cv::VideoCapture cap(video_source);
|
||||
// Check if video device can be opened with the given index
|
||||
if(!cap.isOpened()) return 1;
|
||||
cv::Mat frame;
|
||||
sensor_msgs::ImagePtr msg;
|
||||
|
||||
ros::Rate loop_rate(30);
|
||||
while (nh.ok()) {
|
||||
cap >> frame;
|
||||
// Check if grabbed frame is actually full with some content
|
||||
if(!frame.empty()) {
|
||||
msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", frame).toImageMsg();
|
||||
pub.publish(msg);
|
||||
cv::waitKey(1);
|
||||
}
|
||||
|
||||
ros::spinOnce();
|
||||
loop_rate.sleep();
|
||||
}
|
||||
}
|
29
src/camera_subscriber.cpp
Normal file
29
src/camera_subscriber.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <ros/ros.h>
|
||||
#include <image_transport/image_transport.h>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
|
||||
void imageCallback(const sensor_msgs::ImageConstPtr& msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
cv::imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image);
|
||||
cv::waitKey(30);
|
||||
}
|
||||
catch (cv_bridge::Exception& e)
|
||||
{
|
||||
ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ros::init(argc, argv, "image_listener");
|
||||
ros::NodeHandle nh;
|
||||
cv::namedWindow("view");
|
||||
cv::startWindowThread();
|
||||
image_transport::ImageTransport it(nh);
|
||||
image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback);
|
||||
ros::spin();
|
||||
cv::destroyWindow("view");
|
||||
}
|
268
src/test-appsrc.cpp
Normal file
268
src/test-appsrc.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "test-appsrc.h"
|
||||
|
||||
|
||||
GstElement *appsrc;
|
||||
GstBuffer *buffer;
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
timeout(GstRTSPServer *server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool(server);
|
||||
gst_rtsp_session_pool_cleanup(pool);
|
||||
g_object_unref(pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* called when we need to give data to appsrc */
|
||||
static void
|
||||
need_data (GstElement * appsrc, guint unused)
|
||||
{
|
||||
g_print("NEED DATA. Source is dry.");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* called when a new media pipeline is constructed. We can query the
|
||||
* pipeline and configure our appsrc */
|
||||
static void
|
||||
media_constructed(GstRTSPMediaFactory *factory, GstRTSPMedia *media,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstElement *element;
|
||||
g_print ("YAY MEDIA BEING CONSTRUCTED!!!\n");
|
||||
|
||||
/* Setup for multicasting */
|
||||
guint i, n_streams;
|
||||
|
||||
n_streams = gst_rtsp_media_n_streams(media);
|
||||
|
||||
for (i = 0; i < n_streams; i++)
|
||||
{
|
||||
GstRTSPAddressPool *pool;
|
||||
GstRTSPStream *stream;
|
||||
gchar *min, *max;
|
||||
|
||||
stream = gst_rtsp_media_get_stream(media, i);
|
||||
|
||||
/* make a new address pool */
|
||||
pool = gst_rtsp_address_pool_new();
|
||||
|
||||
min = g_strdup_printf("224.3.0.%d", (2 * i) + 1);
|
||||
max = g_strdup_printf("224.3.0.%d", (2 * i) + 2);
|
||||
gst_rtsp_address_pool_add_range(pool, min, max,
|
||||
5000 + (10 * i), 5010 + (10 * i), 1);
|
||||
g_free(min);
|
||||
g_free(max);
|
||||
|
||||
gst_rtsp_stream_set_address_pool(stream, pool);
|
||||
g_object_unref(pool);
|
||||
}
|
||||
|
||||
|
||||
/* get the element used for providing the streams of the media */
|
||||
element = gst_rtsp_media_get_element (media);
|
||||
|
||||
/* get our appsrc, we named it 'mysrc' with the name property */
|
||||
appsrc = gst_bin_get_by_name_recurse_up (GST_BIN (element), "mysrc");
|
||||
|
||||
/* this instructs appsrc that we will be dealing with timed buffer */
|
||||
gst_util_set_object_arg (G_OBJECT (appsrc), "format", "time");
|
||||
|
||||
g_signal_connect (appsrc, "need-data", (GCallback) need_data, NULL);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Borrowed from https://github.com/ProjectArtemis/gst_video_server/blob/master/src/server_nodelet.cpp */
|
||||
namespace enc = sensor_msgs::image_encodings;
|
||||
|
||||
GstCaps* gst_caps_new_from_image(const sensor_msgs::Image::ConstPtr &msg)
|
||||
{
|
||||
// http://gstreamer.freedesktop.org/data/doc/gstreamer/head/pwg/html/section-types-definitions.html
|
||||
static const ros::M_string known_formats = {{
|
||||
{enc::RGB8, "RGB"},
|
||||
{enc::RGB16, "RGB16"},
|
||||
{enc::RGBA8, "RGBA"},
|
||||
{enc::RGBA16, "RGBA16"},
|
||||
{enc::BGR8, "BGR"},
|
||||
{enc::BGR16, "BGR16"},
|
||||
{enc::BGRA8, "BGRA"},
|
||||
{enc::BGRA16, "BGRA16"},
|
||||
{enc::MONO8, "GRAY8"},
|
||||
{enc::MONO16, "GRAY16_LE"},
|
||||
}};
|
||||
|
||||
if (msg->is_bigendian) {
|
||||
ROS_ERROR("GST: big endian image format is not supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto format = known_formats.find(msg->encoding);
|
||||
g_print("Format found = %s\n", format);
|
||||
if (format == known_formats.end()) {
|
||||
ROS_ERROR("GST: image format '%s' unknown", msg->encoding.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return gst_caps_new_simple("video/x-raw",
|
||||
"format", G_TYPE_STRING, format->second.c_str(),
|
||||
"width", G_TYPE_INT, msg->width,
|
||||
"height", G_TYPE_INT, msg->height,
|
||||
"framerate", GST_TYPE_FRACTION, 0, 1, // 0/1 = dynamic
|
||||
nullptr);
|
||||
}
|
||||
|
||||
/* Borrowed from https://github.com/ProjectArtemis/gst_video_server/blob/master/src/server_nodelet.cpp */
|
||||
GstSample* gst_sample_new_from_image(const sensor_msgs::Image::ConstPtr &msg)
|
||||
{
|
||||
g_print("Image details: %s\n", msg->encoding.c_str());
|
||||
|
||||
|
||||
// unfortunately we may not move image data because it is immutable. copying.
|
||||
auto buffer = gst_buffer_new_allocate(nullptr, msg->data.size(), nullptr);
|
||||
g_assert(buffer);
|
||||
|
||||
/* NOTE(vooon):
|
||||
* I found that best is to use `do-timestamp=true` parameter
|
||||
* and forgot about stamping stamp problem (PTS).
|
||||
*/
|
||||
//auto ts = gst_time_from_stamp(msg->header.stamp);
|
||||
//NODELET_INFO("TS: %lld, %lld", ts, gst_util_uint64_scale_int(1, GST_SECOND, 40));
|
||||
|
||||
gst_buffer_fill(buffer, 0, msg->data.data(), msg->data.size());
|
||||
GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_LIVE);
|
||||
//GST_BUFFER_PTS(buffer) = ts;
|
||||
|
||||
auto caps = gst_caps_new_from_image(msg);
|
||||
g_print("Got caps from msg\n");
|
||||
if (caps == nullptr) {
|
||||
gst_object_unref(GST_OBJECT(buffer));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto sample = gst_sample_new(buffer, caps, nullptr, nullptr);
|
||||
g_print("Assigned a sample\n");
|
||||
gst_buffer_unref(buffer);
|
||||
gst_caps_unref(caps);
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void imageCallback(const sensor_msgs::ImageConstPtr& msg)
|
||||
{
|
||||
GstSample *sample;
|
||||
GstFlowReturn ret;
|
||||
|
||||
g_print("Got new ROS image callback\n");
|
||||
/* Create the sample from the ros msg */
|
||||
sample = gst_sample_new_from_image(msg);
|
||||
|
||||
/* Get the pipeline to push to */
|
||||
ret = gst_app_src_push_sample (GST_APP_SRC (appsrc), sample);
|
||||
gst_object_unref (sample);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init(&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new(NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points(server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new();
|
||||
gst_rtsp_media_factory_set_launch(factory, "( "
|
||||
"appsrc name=mysrc ! videoconvert ! videoscale ! video/x-raw,framerate=15/1,width=1280,height=720 ! videoconvert ! "
|
||||
"x264enc tune=zerolatency bitrate=1000 key-int-max=90 ! "
|
||||
"rtph264pay name=pay0 pt=96"
|
||||
" )");
|
||||
|
||||
gst_rtsp_media_factory_set_shared(factory, TRUE);
|
||||
|
||||
/* notify when our media is ready, This is called whenever someone asks for
|
||||
* the media and a new pipeline with our appsrc is created */
|
||||
g_signal_connect(factory, "media-constructed", (GCallback)media_constructed,
|
||||
NULL);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory(mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mounts anymore */
|
||||
g_object_unref(mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach(server, NULL) == 0)
|
||||
{
|
||||
g_print("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
g_timeout_add_seconds(2, (GSourceFunc)timeout, server);
|
||||
|
||||
/* start serving */
|
||||
g_print("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
|
||||
|
||||
/* Initialise the ros subscriber node */
|
||||
ros::init(argc, argv, "image_listener");
|
||||
ros::NodeHandle nh;
|
||||
image_transport::ImageTransport it(nh);
|
||||
image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback);
|
||||
|
||||
/* Spin up ROS, but use the non-blocking spinner so
|
||||
* our g_main_loop can still listen for RTSP connections */
|
||||
ros::AsyncSpinner spinner(1);
|
||||
spinner.start();
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
spinner.stop();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
24
src/test-appsrc.h
Normal file
24
src/test-appsrc.h
Normal file
@ -0,0 +1,24 @@
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
#include <gst/app/gstappsrc.h>
|
||||
|
||||
#include <ros/ros.h>
|
||||
#include <image_transport/image_transport.h>
|
||||
#include <sensor_msgs/image_encodings.h>
|
||||
|
||||
static gboolean
|
||||
timeout(GstRTSPServer *server);
|
||||
|
||||
static void
|
||||
media_constructed(GstRTSPMediaFactory *factory, GstRTSPMedia *media,
|
||||
gpointer user_data);
|
||||
|
||||
GstCaps*
|
||||
gst_caps_new_from_image(const sensor_msgs::Image::ConstPtr &msg);
|
||||
|
||||
GstSample*
|
||||
gst_sample_new_from_image(const sensor_msgs::Image::ConstPtr &msg);
|
||||
|
||||
|
||||
int
|
||||
ros_main(int argc, char *argv[]);
|
204
src/test-multicast2.cpp
Normal file
204
src/test-multicast2.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <ros/ros.h>
|
||||
#include <image_transport/image_transport.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/app/gstappsrc.h>
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
|
||||
static gboolean
|
||||
timeout(GstRTSPServer *server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool(server);
|
||||
gst_rtsp_session_pool_cleanup(pool);
|
||||
g_object_unref(pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
media_constructed(GstRTSPMediaFactory *factory, GstRTSPMedia *media)
|
||||
{
|
||||
guint i, n_streams;
|
||||
|
||||
n_streams = gst_rtsp_media_n_streams(media);
|
||||
|
||||
for (i = 0; i < n_streams; i++)
|
||||
{
|
||||
GstRTSPAddressPool *pool;
|
||||
GstRTSPStream *stream;
|
||||
gchar *min, *max;
|
||||
|
||||
stream = gst_rtsp_media_get_stream(media, i);
|
||||
|
||||
/* make a new address pool */
|
||||
pool = gst_rtsp_address_pool_new();
|
||||
|
||||
min = g_strdup_printf("224.3.0.%d", (2 * i) + 1);
|
||||
max = g_strdup_printf("224.3.0.%d", (2 * i) + 2);
|
||||
gst_rtsp_address_pool_add_range(pool, min, max,
|
||||
5000 + (10 * i), 5010 + (10 * i), 1);
|
||||
g_free(min);
|
||||
g_free(max);
|
||||
|
||||
gst_rtsp_stream_set_address_pool(stream, pool);
|
||||
g_object_unref(pool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* called when there is a new buffer ready for
|
||||
* processing */
|
||||
static void
|
||||
wrapper_imageCallback(void* pt2Object, const sensor_msgs::ImageConstPtr& msg)
|
||||
{
|
||||
myImages* mySelf = (myImages*) pt2Object; // explicitly cast to a pointer to class myImages
|
||||
mySelf->imageCallback(msg ); // call member
|
||||
}
|
||||
|
||||
|
||||
static GstFlowReturn
|
||||
imageCallback(const sensor_msgs::ImageConstPtr& msg)
|
||||
{
|
||||
GstSample *sample;
|
||||
GstFlowReturn ret;
|
||||
|
||||
GstCaps *caps;
|
||||
char *gst_type, *gst_format = (char *)"";
|
||||
|
||||
GstBuffer *buf;
|
||||
void *imgdata;
|
||||
GstMapInfo map;
|
||||
static GstClockTime timestamp=0;
|
||||
|
||||
appsrc_front = gst_bin_get_by_name(GST_BIN(data->sink), "appsrc_front");
|
||||
|
||||
if (appsrc_front != NULL)
|
||||
{
|
||||
// Set caps from message
|
||||
get_format(msg->encoding.c_str(), &gst_type, &gst_format);
|
||||
caps = gst_caps_new_simple (gst_type,
|
||||
"format", G_TYPE_STRING, gst_format,
|
||||
"width", G_TYPE_INT, msg->width,
|
||||
"height", G_TYPE_INT, msg->height,
|
||||
NULL);
|
||||
gst_app_src_set_caps(appsrc_front, caps);
|
||||
|
||||
buf = gst_buffer_new_and_alloc(msg->step*msg->height);
|
||||
|
||||
gst_buffer_map(buf, &map, GST_MAP_READ);
|
||||
imgdata = map.data;
|
||||
|
||||
GST_BUFFER_PTS(buf) = timestamp;
|
||||
GST_BUFFER_DURATION(buf) = gst_util_uint64_scale_int(1, GST_SECOND, 15);
|
||||
timestamp += GST_BUFFER_DURATION(buf);
|
||||
|
||||
|
||||
memcpy(imgdata, &msg->data[0], msg->step*msg->height);
|
||||
|
||||
gst_buffer_unmap(buf, &map);
|
||||
gst_app_src_push_buffer(appsrc_rgb, buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* get the sample from appsink */
|
||||
sample = msg; //Get from ROS
|
||||
|
||||
/* get source an push new sample */
|
||||
source = gst_bin_get_by_name(GST_BIN(source), "appsrc_front");
|
||||
ret = gst_app_src_push_sample(GST_APP_SRC(source), sample);
|
||||
gst_object_unref(source);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
std::thread rosthr(ros_main)
|
||||
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory_front, *factory_back;
|
||||
|
||||
gst_init(&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new(NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points(server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory_front = gst_rtsp_media_factory_new();
|
||||
gst_rtsp_media_factory_set_launch(factory_front, "( "
|
||||
"appsrc name=appsrc_front ! videoconvert ! videoscale ! video/x-raw,framerate=15/1,width=1280,height=720 ! "
|
||||
"x264enc tune=zerolatency bitrate=1000 key-int-max=90 ! "
|
||||
"rtph264pay name=pay0 pt=96 "
|
||||
")");
|
||||
|
||||
gst_rtsp_media_factory_set_shared(factory_front, TRUE);
|
||||
|
||||
g_signal_connect(factory_front, "media-constructed", (GCallback)media_constructed, NULL);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory(mounts, "/front", factory_front);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref(mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach(server, NULL) == 0)
|
||||
{
|
||||
g_print("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_timeout_add_seconds(2, (GSourceFunc)timeout, server);
|
||||
|
||||
/* start serving */
|
||||
g_print("stream ready at rtsp://127.0.0.1:8554/front\n");
|
||||
|
||||
// Use the ros::spin main loop instead.
|
||||
// g_main_loop_run(loop);
|
||||
|
||||
ros::init(argc, argv, "image_listener");
|
||||
ros::NodeHandle nh;
|
||||
image_transport::ImageTransport it(nh);
|
||||
image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback);
|
||||
ros::spin();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user