Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qgroundcontrol
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Valentin Platzgummer
qgroundcontrol
Commits
efe64000
Commit
efe64000
authored
Jan 03, 2017
by
Jacob Walser
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Hide recording icon if no video stream present at startup
Other minor formatting and stability improvements
parent
9565d524
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
109 additions
and
83 deletions
+109
-83
VideoReceiver.cc
src/VideoStreaming/VideoReceiver.cc
+86
-60
VideoReceiver.h
src/VideoStreaming/VideoReceiver.h
+20
-21
MainToolBarIndicators.qml
src/ui/toolbar/MainToolBarIndicators.qml
+3
-2
No files found.
src/VideoStreaming/VideoReceiver.cc
View file @
efe64000
...
@@ -20,124 +20,143 @@
...
@@ -20,124 +20,143 @@
#include <QDir>
#include <QDir>
#include <QDateTime>
#include <QDateTime>
VideoReceiver
::
Sink
*
VideoReceiver
::
sink
=
NULL
;
VideoReceiver
::
Sink
*
VideoReceiver
::
_
sink
=
NULL
;
GstElement
*
VideoReceiver
::
_pipeline
=
NULL
;
GstElement
*
VideoReceiver
::
_pipeline
=
NULL
;
GstElement
*
VideoReceiver
::
_pipeline2
=
NULL
;
GstElement
*
VideoReceiver
::
_pipeline2
=
NULL
;
GstElement
*
VideoReceiver
::
tee
=
NULL
;
GstElement
*
VideoReceiver
::
_
tee
=
NULL
;
// -EOS has appeared on the bus of the temporary pipeline
// -At this point all of the recoring elements have been flushed, and the video file has been finalized
// -Now we can remove the temporary pipeline and its elements
gboolean
VideoReceiver
::
_eosCB
(
GstBus
*
bus
,
GstMessage
*
message
,
gpointer
user_data
)
gboolean
VideoReceiver
::
_eosCB
(
GstBus
*
bus
,
GstMessage
*
message
,
gpointer
user_data
)
{
{
Q_UNUSED
(
bus
);
Q_UNUSED
(
bus
);
Q_UNUSED
(
message
);
Q_UNUSED
(
message
);
Q_UNUSED
(
user_data
);
Q_UNUSED
(
user_data
);
gst_bin_remove
(
GST_BIN
(
_pipeline2
),
sink
->
queue
);
gst_bin_remove
(
GST_BIN
(
_pipeline2
),
_
sink
->
queue
);
gst_bin_remove
(
GST_BIN
(
_pipeline2
),
sink
->
mux
);
gst_bin_remove
(
GST_BIN
(
_pipeline2
),
_
sink
->
mux
);
gst_bin_remove
(
GST_BIN
(
_pipeline2
),
sink
->
filesink
);
gst_bin_remove
(
GST_BIN
(
_pipeline2
),
_
sink
->
filesink
);
gst_element_set_state
(
_pipeline2
,
GST_STATE_NULL
);
gst_element_set_state
(
_pipeline2
,
GST_STATE_NULL
);
gst_object_unref
(
_pipeline2
);
gst_object_unref
(
_pipeline2
);
gst_element_set_state
(
sink
->
filesink
,
GST_STATE_NULL
);
gst_element_set_state
(
_
sink
->
filesink
,
GST_STATE_NULL
);
gst_element_set_state
(
sink
->
mux
,
GST_STATE_NULL
);
gst_element_set_state
(
_
sink
->
mux
,
GST_STATE_NULL
);
gst_element_set_state
(
sink
->
queue
,
GST_STATE_NULL
);
gst_element_set_state
(
_
sink
->
queue
,
GST_STATE_NULL
);
gst_object_unref
(
sink
->
queue
);
gst_object_unref
(
_
sink
->
queue
);
gst_object_unref
(
sink
->
mux
);
gst_object_unref
(
_
sink
->
mux
);
gst_object_unref
(
sink
->
filesink
);
gst_object_unref
(
_
sink
->
filesink
);
delete
sink
;
delete
_
sink
;
sink
=
NULL
;
_
sink
=
NULL
;
return
true
;
return
true
;
}
}
// -Unlink the recording branch from the tee in the main pipeline
// -Create a second temporary pipeline, and place the recording branch elements into that pipeline
// -Setup watch and handler for EOS event on the temporary pipeline's bus
// -Send an EOS event at the beginning of that pipeline and set up a callback for
GstPadProbeReturn
VideoReceiver
::
_unlinkCB
(
GstPad
*
pad
,
GstPadProbeInfo
*
info
,
gpointer
user_data
)
GstPadProbeReturn
VideoReceiver
::
_unlinkCB
(
GstPad
*
pad
,
GstPadProbeInfo
*
info
,
gpointer
user_data
)
{
{
Q_UNUSED
(
pad
);
Q_UNUSED
(
pad
);
Q_UNUSED
(
info
);
Q_UNUSED
(
info
);
Q_UNUSED
(
user_data
);
Q_UNUSED
(
user_data
);
if
(
!
g_atomic_int_compare_and_exchange
(
&
sink
->
removing
,
FALSE
,
TRUE
))
// We will only execute once
if
(
!
g_atomic_int_compare_and_exchange
(
&
_sink
->
removing
,
FALSE
,
TRUE
))
return
GST_PAD_PROBE_OK
;
return
GST_PAD_PROBE_OK
;
GstPad
*
sinkpad
=
gst_element_get_static_pad
(
sink
->
queue
,
"sink"
);
gst_pad_unlink
(
sink
->
teepad
,
sinkpad
);
gst_object_unref
(
sinkpad
);
gst_element_release_request_pad
(
tee
,
sink
->
teepad
);
gst_object_unref
(
sink
->
teepad
);
// Also unlinks and unrefs
// Also unlinks and unrefs
gst_bin_remove
(
GST_BIN
(
_pipeline
),
sink
->
queue
);
gst_bin_remove_many
(
GST_BIN
(
_pipeline
),
_sink
->
queue
,
_sink
->
mux
,
_sink
->
filesink
,
NULL
);
gst_bin_remove
(
GST_BIN
(
_pipeline
),
sink
->
mux
);
gst_bin_remove
(
GST_BIN
(
_pipeline
),
sink
->
filesink
);
// Give tee its pad back
gst_element_release_request_pad
(
_tee
,
_sink
->
teepad
);
gst_object_unref
(
_sink
->
teepad
);
// Create temporary pipeline
_pipeline2
=
gst_pipeline_new
(
"pipe2"
);
_pipeline2
=
gst_pipeline_new
(
"pipe2"
);
gst_bin_add_many
(
GST_BIN
(
_pipeline2
),
sink
->
queue
,
sink
->
mux
,
sink
->
filesink
,
NULL
);
// Put our elements from the recording branch into the temporary pipeline
gst_element_link_many
(
sink
->
queue
,
sink
->
mux
,
sink
->
filesink
,
NULL
);
gst_bin_add_many
(
GST_BIN
(
_pipeline2
),
_sink
->
queue
,
_sink
->
mux
,
_sink
->
filesink
,
NULL
);
gst_element_link_many
(
_sink
->
queue
,
_sink
->
mux
,
_sink
->
filesink
,
NULL
);
// Add watch for EOS event
GstBus
*
bus
=
gst_pipeline_get_bus
(
GST_PIPELINE
(
_pipeline2
));
GstBus
*
bus
=
gst_pipeline_get_bus
(
GST_PIPELINE
(
_pipeline2
));
gst_bus_add_signal_watch
(
bus
);
gst_bus_add_signal_watch
(
bus
);
g_signal_connect
(
bus
,
"message::eos"
,
G_CALLBACK
(
_eosCB
),
NULL
);
g_signal_connect
(
bus
,
"message::eos"
,
G_CALLBACK
(
_eosCB
),
NULL
);
gst_object_unref
(
bus
);
if
(
gst_element_set_state
(
_pipeline2
,
GST_STATE_PLAYING
)
==
GST_STATE_CHANGE_FAILURE
)
{
if
(
gst_element_set_state
(
_pipeline2
,
GST_STATE_PLAYING
)
==
GST_STATE_CHANGE_FAILURE
)
{
qDebug
()
<<
"problem starting pipeline2"
;
qDebug
()
<<
"problem starting pipeline2"
;
}
}
GstPad
*
eosInjectPad
=
gst_element_get_static_pad
(
sink
->
queue
,
"sink"
);
// Send EOS at the beginning of the pipeline
gst_pad_send_event
(
eosInjectPad
,
gst_event_new_eos
());
GstPad
*
sinkpad
=
gst_element_get_static_pad
(
_sink
->
queue
,
"sink"
);
gst_object_unref
(
eosInjectPad
);
gst_pad_send_event
(
sinkpad
,
gst_event_new_eos
());
gst_object_unref
(
sinkpad
);
return
GST_PAD_PROBE_REMOVE
;
return
GST_PAD_PROBE_REMOVE
;
}
}
void
VideoReceiver
::
_startRecording
(
void
)
// When we finish our pipeline will look like this:
//
// +-->queue-->decoder-->_videosink
// |
// datasource-->demux-->parser-->tee
// |
// | +--------------_sink-------------------+
// | | |
// we are adding these elements-> +->teepad-->queue-->matroskamux-->_filesink |
// | |
// +--------------------------------------+
void
VideoReceiver
::
startRecording
(
void
)
{
{
// exit immediately if we are already recording
// exit immediately if we are already recording
if
(
_pipeline
==
NULL
||
_recording
)
{
if
(
_pipeline
==
NULL
||
_recording
)
{
return
;
return
;
}
}
sink
=
g_new0
(
Sink
,
1
);
_
sink
=
g_new0
(
Sink
,
1
);
sink
->
teepad
=
gst_element_get_request_pad
(
tee
,
"src_%u"
);
_sink
->
teepad
=
gst_element_get_request_pad
(
_
tee
,
"src_%u"
);
sink
->
queue
=
gst_element_factory_make
(
"queue"
,
NULL
);
_
sink
->
queue
=
gst_element_factory_make
(
"queue"
,
NULL
);
sink
->
mux
=
gst_element_factory_make
(
"matroskamux"
,
NULL
);
_
sink
->
mux
=
gst_element_factory_make
(
"matroskamux"
,
NULL
);
sink
->
filesink
=
gst_element_factory_make
(
"filesink"
,
NULL
);
_
sink
->
filesink
=
gst_element_factory_make
(
"filesink"
,
NULL
);
sink
->
removing
=
false
;
_
sink
->
removing
=
false
;
QString
filename
=
QDir
::
homePath
()
+
"/"
+
QDateTime
::
currentDateTime
().
toString
()
+
".mkv"
;
QString
filename
=
QDir
::
homePath
()
+
"/"
+
QDateTime
::
currentDateTime
().
toString
()
+
".mkv"
;
g_object_set
(
G_OBJECT
(
sink
->
filesink
),
"location"
,
qPrintable
(
filename
),
NULL
);
g_object_set
(
G_OBJECT
(
_
sink
->
filesink
),
"location"
,
qPrintable
(
filename
),
NULL
);
gst_object_ref
(
sink
->
queue
);
gst_object_ref
(
_
sink
->
queue
);
gst_object_ref
(
sink
->
mux
);
gst_object_ref
(
_
sink
->
mux
);
gst_object_ref
(
sink
->
filesink
);
gst_object_ref
(
_
sink
->
filesink
);
gst_bin_add_many
(
GST_BIN
(
_pipeline
),
sink
->
queue
,
sink
->
mux
,
sink
->
filesink
,
NULL
);
gst_bin_add_many
(
GST_BIN
(
_pipeline
),
_sink
->
queue
,
_sink
->
mux
,
_
sink
->
filesink
,
NULL
);
gst_element_link_many
(
sink
->
queue
,
sink
->
mux
,
sink
->
filesink
,
NULL
);
gst_element_link_many
(
_sink
->
queue
,
_sink
->
mux
,
_
sink
->
filesink
,
NULL
);
gst_element_sync_state_with_parent
(
sink
->
queue
);
gst_element_sync_state_with_parent
(
_
sink
->
queue
);
gst_element_sync_state_with_parent
(
sink
->
mux
);
gst_element_sync_state_with_parent
(
_
sink
->
mux
);
gst_element_sync_state_with_parent
(
sink
->
filesink
);
gst_element_sync_state_with_parent
(
_
sink
->
filesink
);
GstPad
*
sinkpad
=
gst_element_get_static_pad
(
sink
->
queue
,
"sink"
);
GstPad
*
sinkpad
=
gst_element_get_static_pad
(
_
sink
->
queue
,
"sink"
);
gst_pad_link
(
sink
->
teepad
,
sinkpad
);
gst_pad_link
(
_
sink
->
teepad
,
sinkpad
);
gst_object_unref
(
sinkpad
);
gst_object_unref
(
sinkpad
);
_recording
=
true
;
_recording
=
true
;
emit
recordingChanged
();
emit
recordingChanged
();
}
}
void
VideoReceiver
::
_
stopRecording
(
void
)
void
VideoReceiver
::
stopRecording
(
void
)
{
{
// exit immediately if we are not recording
// exit immediately if we are not recording
if
(
_pipeline
==
NULL
||
!
_recording
)
{
if
(
_pipeline
==
NULL
||
!
_recording
)
{
return
;
return
;
}
}
gst_pad_add_probe
(
sink
->
teepad
,
GST_PAD_PROBE_TYPE_IDLE
,
_unlinkCB
,
sink
,
NULL
);
gst_pad_add_probe
(
_sink
->
teepad
,
GST_PAD_PROBE_TYPE_IDLE
,
_unlinkCB
,
_
sink
,
NULL
);
_recording
=
false
;
_recording
=
false
;
emit
recordingChanged
();
emit
recordingChanged
();
...
@@ -246,6 +265,16 @@ void VideoReceiver::_timeout()
...
@@ -246,6 +265,16 @@ void VideoReceiver::_timeout()
}
}
#endif
#endif
// When we finish our pipeline will look like this:
//
// +-->queue-->decoder-->_videosink
// |
// datasource-->demux-->parser-->tee
//
// ^
// |
// +-Here we will later link elements for recording
void
VideoReceiver
::
start
()
void
VideoReceiver
::
start
()
{
{
#if defined(QGC_GST_STREAMING)
#if defined(QGC_GST_STREAMING)
...
@@ -281,10 +310,6 @@ void VideoReceiver::start()
...
@@ -281,10 +310,6 @@ void VideoReceiver::start()
GstPad
*
teeSrc1
=
NULL
;
// tee source pad 1
GstPad
*
teeSrc1
=
NULL
;
// tee source pad 1
GstPad
*
q1Sink
=
NULL
;
// queue1 sink pad
GstPad
*
q1Sink
=
NULL
;
// queue1 sink pad
// /queue1---decoder---_videosink
//datasource---demux---parser---tee
// \queue2---matroskamux---filesink
do
{
do
{
if
((
_pipeline
=
gst_pipeline_new
(
"receiver"
))
==
NULL
)
{
if
((
_pipeline
=
gst_pipeline_new
(
"receiver"
))
==
NULL
)
{
qCritical
()
<<
"VideoReceiver::start() failed. Error with gst_pipeline_new()"
;
qCritical
()
<<
"VideoReceiver::start() failed. Error with gst_pipeline_new()"
;
...
@@ -331,7 +356,7 @@ void VideoReceiver::start()
...
@@ -331,7 +356,7 @@ void VideoReceiver::start()
break
;
break
;
}
}
if
((
tee
=
gst_element_factory_make
(
"tee"
,
"stream-file-tee"
))
==
NULL
)
{
if
((
_
tee
=
gst_element_factory_make
(
"tee"
,
"stream-file-tee"
))
==
NULL
)
{
qCritical
()
<<
"VideoReceiver::start() failed. Error with gst_element_factory_make('tee')"
;
qCritical
()
<<
"VideoReceiver::start() failed. Error with gst_element_factory_make('tee')"
;
break
;
break
;
}
}
...
@@ -341,7 +366,7 @@ void VideoReceiver::start()
...
@@ -341,7 +366,7 @@ void VideoReceiver::start()
break
;
break
;
}
}
gst_bin_add_many
(
GST_BIN
(
_pipeline
),
dataSource
,
demux
,
parser
,
tee
,
queue1
,
decoder
,
_videoSink
,
NULL
);
gst_bin_add_many
(
GST_BIN
(
_pipeline
),
dataSource
,
demux
,
parser
,
_
tee
,
queue1
,
decoder
,
_videoSink
,
NULL
);
// if(isUdp) {
// if(isUdp) {
// res = gst_element_link_many(dataSource, demux, parser, decoder, tee, _videoSink, NULL);
// res = gst_element_link_many(dataSource, demux, parser, decoder, tee, _videoSink, NULL);
...
@@ -350,7 +375,7 @@ void VideoReceiver::start()
...
@@ -350,7 +375,7 @@ void VideoReceiver::start()
// }
// }
// Link the pipeline in front of the tee
// Link the pipeline in front of the tee
if
(
!
gst_element_link_many
(
dataSource
,
demux
,
parser
,
tee
,
NULL
))
{
if
(
!
gst_element_link_many
(
dataSource
,
demux
,
parser
,
_
tee
,
NULL
))
{
qCritical
()
<<
"Unable to link datasource and tee."
;
qCritical
()
<<
"Unable to link datasource and tee."
;
break
;
break
;
}
}
...
@@ -362,7 +387,7 @@ void VideoReceiver::start()
...
@@ -362,7 +387,7 @@ void VideoReceiver::start()
}
}
// Link the queues to the tee
// Link the queues to the tee
teeSrc1
=
gst_element_get_request_pad
(
tee
,
"src_%u"
);
teeSrc1
=
gst_element_get_request_pad
(
_
tee
,
"src_%u"
);
q1Sink
=
gst_element_get_static_pad
(
queue1
,
"sink"
);
q1Sink
=
gst_element_get_static_pad
(
queue1
,
"sink"
);
// Link the tee to queue1
// Link the tee to queue1
...
@@ -418,8 +443,8 @@ void VideoReceiver::start()
...
@@ -418,8 +443,8 @@ void VideoReceiver::start()
dataSource
=
NULL
;
dataSource
=
NULL
;
}
}
if
(
tee
!=
NULL
)
{
if
(
_
tee
!=
NULL
)
{
gst_object_unref
(
tee
);
gst_object_unref
(
_
tee
);
dataSource
=
NULL
;
dataSource
=
NULL
;
}
}
...
@@ -445,8 +470,9 @@ void VideoReceiver::EOS() {
...
@@ -445,8 +470,9 @@ void VideoReceiver::EOS() {
void
VideoReceiver
::
stop
()
void
VideoReceiver
::
stop
()
{
{
#if defined(QGC_GST_STREAMING)
#if defined(QGC_GST_STREAMING)
qDebug
()
<<
"stop()"
;
if
(
_pipeline
!=
NULL
)
{
if
(
_pipeline
!=
NULL
)
{
q
Critical
()
<<
"s
topping pipeline"
;
q
Debug
()
<<
"S
topping pipeline"
;
gst_element_set_state
(
_pipeline
,
GST_STATE_NULL
);
gst_element_set_state
(
_pipeline
,
GST_STATE_NULL
);
gst_object_unref
(
_pipeline
);
gst_object_unref
(
_pipeline
);
_pipeline
=
NULL
;
_pipeline
=
NULL
;
...
...
src/VideoStreaming/VideoReceiver.h
View file @
efe64000
...
@@ -35,7 +35,7 @@ public:
...
@@ -35,7 +35,7 @@ public:
~
VideoReceiver
();
~
VideoReceiver
();
#if defined(QGC_GST_STREAMING)
#if defined(QGC_GST_STREAMING)
void
setVideoSink
(
GstElement
*
sink
);
void
setVideoSink
(
GstElement
*
_
sink
);
#endif
#endif
bool
recording
()
{
return
_recording
;
}
bool
recording
()
{
return
_recording
;
}
...
@@ -48,8 +48,8 @@ public slots:
...
@@ -48,8 +48,8 @@ public slots:
void
EOS
();
void
EOS
();
void
stop
();
void
stop
();
void
setUri
(
const
QString
&
uri
);
void
setUri
(
const
QString
&
uri
);
void
_stopRecording
();
void
stopRecording
();
void
_startRecording
();
void
startRecording
();
private
slots
:
private
slots
:
#if defined(QGC_GST_STREAMING)
#if defined(QGC_GST_STREAMING)
...
@@ -60,6 +60,7 @@ private slots:
...
@@ -60,6 +60,7 @@ private slots:
private:
private:
#if defined(QGC_GST_STREAMING)
#if defined(QGC_GST_STREAMING)
typedef
struct
typedef
struct
{
{
GstPad
*
teepad
;
GstPad
*
teepad
;
...
@@ -69,16 +70,15 @@ private:
...
@@ -69,16 +70,15 @@ private:
gboolean
removing
;
gboolean
removing
;
}
Sink
;
}
Sink
;
static
Sink
*
sink
;
bool
_recording
;
static
Sink
*
_sink
;
static
GstElement
*
_tee
;
void
_onBusMessage
(
GstMessage
*
message
);
void
_onBusMessage
(
GstMessage
*
message
);
static
gboolean
_onBusMessage
(
GstBus
*
bus
,
GstMessage
*
msg
,
gpointer
user_data
);
static
gboolean
_onBusMessage
(
GstBus
*
bus
,
GstMessage
*
msg
,
gpointer
user_data
);
static
gboolean
_eosCB
(
GstBus
*
bus
,
GstMessage
*
message
,
gpointer
user_data
);
static
gboolean
_eosCB
(
GstBus
*
bus
,
GstMessage
*
message
,
gpointer
user_data
);
static
GstPadProbeReturn
_unlinkCB
(
GstPad
*
pad
,
GstPadProbeInfo
*
info
,
gpointer
user_data
);
static
GstPadProbeReturn
_unlinkCB
(
GstPad
*
pad
,
GstPadProbeInfo
*
info
,
gpointer
user_data
);
bool
_recording
;
static
GstElement
*
tee
;
#endif
#endif
QString
_uri
;
QString
_uri
;
...
@@ -86,7 +86,6 @@ private:
...
@@ -86,7 +86,6 @@ private:
#if defined(QGC_GST_STREAMING)
#if defined(QGC_GST_STREAMING)
static
GstElement
*
_pipeline
;
static
GstElement
*
_pipeline
;
static
GstElement
*
_pipeline2
;
static
GstElement
*
_pipeline2
;
GstElement
*
_videoSink
;
GstElement
*
_videoSink
;
#endif
#endif
...
...
src/ui/toolbar/MainToolBarIndicators.qml
View file @
efe64000
...
@@ -495,6 +495,7 @@ Item {
...
@@ -495,6 +495,7 @@ Item {
anchors.top
:
parent
.
top
anchors.top
:
parent
.
top
anchors.bottom
:
parent
.
bottom
anchors.bottom
:
parent
.
bottom
width
:
height
width
:
height
visible
:
QGroundControl
.
videoManager
.
videoRunning
Rectangle
{
Rectangle
{
anchors.top
:
parent
.
top
anchors.top
:
parent
.
top
...
@@ -506,7 +507,7 @@ Item {
...
@@ -506,7 +507,7 @@ Item {
MouseArea
{
MouseArea
{
anchors.fill
:
parent
anchors.fill
:
parent
onClicked
:
QGroundControl
.
videoManager
.
videoReceiver
.
recording
?
QGroundControl
.
videoManager
.
videoReceiver
.
_stopRecording
()
:
QGroundControl
.
videoManager
.
videoReceiver
.
_
startRecording
()
onClicked
:
QGroundControl
.
videoManager
.
videoReceiver
.
recording
?
QGroundControl
.
videoManager
.
videoReceiver
.
stopRecording
()
:
QGroundControl
.
videoManager
.
videoReceiver
.
startRecording
()
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment