Initial implementation of ADGStreamer - #1
Conversation
e568679 to
61c27fe
Compare
gustavosr8
left a comment
There was a problem hiding this comment.
Partial review, for now.
Overall, I think that a great improve for this project could be to add a .clang-format rule set.
For references on how to do it, you can refer to lnls-dig/uhal#67.
| 1, | ||
| 1, | ||
| maxBuffers, | ||
| maxMemory, | ||
| 0, | ||
| 0, | ||
| ASYN_CANBLOCK, | ||
| 1, | ||
| priority, | ||
| stackSize) |
There was a problem hiding this comment.
It would be nice to document which parameter the literal values refer to, similar to what is being done in ADSpecsPEEM
| ADGStreamer(const char *portName, int maxBuffers=0, size_t maxMemory=0, int priority=0, int stackSize=0); | ||
| virtual ~ADGStreamer(); | ||
|
|
||
| virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); |
There was a problem hiding this comment.
It would be nice to add the override identifier to explicit the function purpose.
| public: | ||
| ADGStreamer(const char *portName, int maxBuffers=0, size_t maxMemory=0, int priority=0, int stackSize=0); | ||
| virtual ~ADGStreamer(); | ||
|
|
||
| protected: | ||
|
|
||
| private: | ||
| bool initializeGStreamer(); |
There was a problem hiding this comment.
Since classes have private as default, you can declare all private members first, and the members which require specifiers like public and protected after that. This way, it is not necessary to specify private.
Since private has no members, I think that it is also not necessary to be specified.
| if (error) | ||
| { | ||
|
|
||
| } |
| if (!pipeline_) | ||
| { | ||
| if (error) | ||
| { | ||
|
|
||
| } | ||
| return false; | ||
| } | ||
|
|
||
| if (error) | ||
| { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
What about unifying this checks in a single if statement?
| return true; | ||
| } | ||
|
|
||
| bool ADGStreamer::createPipeline() |
There was a problem hiding this comment.
I'm missing some explanation in the commit message about the required steps when creating a new pipeline
There was a problem hiding this comment.
Same for creating/stopping the pipeline
| pPvt->acquisitionTask(); | ||
| } | ||
|
|
||
| void ADGStreamer::acquisitionTask() |
There was a problem hiding this comment.
I see no reason on creating an empty acquisition task. You can create the task without it
| if (!bus_) | ||
| { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Is this checking really needed once you are already checking for bus_ in createPipeline()?
| } | ||
| else | ||
| { | ||
| processBusMessage(); |
There was a problem hiding this comment.
If I get it right, the bus messages warns us about pipelines states. If it is the case, the message processing shouldn't be a standalone thread instead of being attached to the acquisition one?
Implemented the pipeline start and stop logic through the `startPipeline()` and `stopPipeline()` routines. When an acquisition starts, samples are received by the `onNewSampleCallback()` callback function. At this stage, the pipeline string configuration is hardcoded.
Implemented the `processSample()` routine to process samples received by the GStreamer callback. The function extracts the image data from the `GstSample` and prepares it for delivery to the AreaDetector driver and plugins.
An epicsThread has been added to support the future implementation of a GstBus monitoring.
Added `GstBus` monitoring for pipeline events such as state changes, errors, and End Of Stream (EOS), keeping the driver updated with the current pipeline execution state.
construction Introduce the PipelineBuilder architecture to separate GStreamer pipeline construction from the ADGStreamer driver implementation. Common pipeline stages, such as queue, decoder, converter, and sink, are implemented in PipelineBuilder, allowing them to be shared across different communication protocols. Protocol-specific implementations, such as PipelineBuilderRTSP, are responsible only for configuring protocol-related pipeline elements, making it easier to support additional protocols such as HTTP, RTMP, and SRT without requiring changes to the driver implementation.
Add the reportStatus() helper method to centralize driver status updates and status message reporting.
|
Just another overall suggestion, I've notice you are using a lot of booleans to handling the status of different operations. I suggest changing it to use |
| -include $(TOP)/../configure/RELEASE_LIBS_INCLUDE | ||
| -include $(TOP)/RELEASE.local |
There was a problem hiding this comment.
Are those related to AREA_DETECTOR?
| ADGStreamer::ADGStreamer(const char *portName, int maxBuffers, size_t maxMemory, | ||
| int priority, int stackSize) | ||
| : ADDriver(portName, 1, 1, maxBuffers, maxMemory, 0, 0, ASYN_CANBLOCK, 1, | ||
| priority, stackSize) | ||
| { | ||
| initializeGStreamer(); | ||
| } | ||
|
|
||
| ADGStreamer::~ADGStreamer() { } | ||
|
|
||
| bool ADGStreamer::initializeGStreamer() | ||
| { | ||
| gst_init(nullptr, nullptr); | ||
| return true; | ||
| } |
There was a problem hiding this comment.
Why not simply call gst_init() directly in the constructor?
| GstStateChangeReturn ret; | ||
| ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING); | ||
|
|
||
| if (ret == GST_STATE_CHANGE_FAILURE) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
What about
if (gst_element_set_state(pipeline_, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
...
}| GstState state; | ||
| gst_element_get_state(pipeline_, &state, nullptr, GST_CLOCK_TIME_NONE); | ||
|
|
||
| if (state != GST_STATE_PLAYING) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; |
There was a problem hiding this comment.
if (gst_element_get_state(pipeline_, &state, nullptr, GST_CLOCK_TIME_NONE) != GST_STATE_PLAYING) {
...
}| GstStateChangeReturn ret; | ||
| ret = gst_element_set_state(pipeline_, GST_STATE_NULL); | ||
|
|
||
| if (ret == GST_STATE_CHANGE_FAILURE) { | ||
| return false; | ||
| } | ||
|
|
||
| GstState state; | ||
| gst_element_get_state(pipeline_, &state, nullptr, GST_CLOCK_TIME_NONE); | ||
|
|
||
| if (state != GST_STATE_NULL) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Same as commented in startPipeline()
| = std::string("rtspsrc " | ||
| "location=rtsp://admin:r00tr00t@10.20.21.40:554/cam/" | ||
| "realmonitor?channel=1&subtype=0 protocols=udp latency=0 " | ||
| "drop-on-latency=true") | ||
| + std::string(" ! rtph264depay ! h264parse config-interval=-1") | ||
| + std::string( | ||
| " ! queue max-size-buffers=1 leaky=downstream silent=true") | ||
| + std::string(" ! avdec_h264 ! videoconvert ! video/x-raw,format=RGB ! " | ||
| "appsink name=appsink sync=false"); |
There was a problem hiding this comment.
Missing some explanation in commit message about the reasoning around this string.
| if (!pipeline_) { | ||
| if (error) { } | ||
| return false; | ||
| } | ||
|
|
||
| if (error) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Why not simply
if ( !pipeline_ || error)| if (!sink_) { | ||
| return GST_FLOW_ERROR; | ||
| } |
There was a problem hiding this comment.
As far as I understand, this function in registered as the callback of sink when receiving a new sample. Is there any way to get here without sink_ being defined? Is this check really needed?
| int imageCounter_; | ||
| int numImagesCounter_; |
There was a problem hiding this comment.
What is the difference between those two?
| getIntegerParam(NDArrayCounter, &imageCounter_); | ||
| getIntegerParam(ADNumImagesCounter, &numImagesCounter_); | ||
| getIntegerParam(NDArrayCallbacks, &arrayCallbacks_); | ||
| imageCounter_++; | ||
| numImagesCounter_++; | ||
| setIntegerParam(NDArrayCounter, imageCounter_); | ||
| setIntegerParam(ADNumImagesCounter, numImagesCounter_); | ||
| pImage_->uniqueId = imageCounter_; | ||
| pImage_->timeStamp = startTime.secPastEpoch + startTime.nsec / 1.e9; | ||
| updateTimeStamp(&pImage_->epicsTS); |
There was a problem hiding this comment.
Isn't missing a callParamCallbacks() after those operations?
Hi everyone,
I'm currently developing an AreaDetector driver based on GStreamer to acquire images from network video streams. The current version supports RTSP communication only, and it has been working very well.
My goal is to extend the driver to support additional streaming protocols, such as HTTP, RTMP, and SRT. To make future development and maintenance easier, I introduced a PipelineBuilder class that encapsulates the pipeline construction and provides a scalable foundation for adding new protocols.