mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-17 05:09:58 +01:00
Re-implementation of MediaPlayer and Stream update
Avoid using gst video frames, and simply copy buffer instead. Use gst memory map to access pointer to RGBA data. unreferencing the buffer frees the memory (apparently). Also free OpenGL objects on close to free memory. Overall, memory consumption seems to be reduced.
This commit is contained in:
@@ -96,14 +96,6 @@ MediaPlayer::~MediaPlayer()
|
||||
{
|
||||
close();
|
||||
|
||||
// cleanup opengl texture
|
||||
if (textureindex_)
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
|
||||
#ifdef MEDIA_PLAYER_DEBUG
|
||||
g_printerr("MediaPlayer %s deleted\n", std::to_string(id_).c_str());
|
||||
#endif
|
||||
@@ -426,7 +418,6 @@ void MediaPlayer::execute_open()
|
||||
else {
|
||||
g_object_set( G_OBJECT (pipeline_), "video-filter", effect, nullptr);
|
||||
Log::Info("MediaPlayer %s Added filter '%s'", std::to_string(id_).c_str(), video_filter_.c_str());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,14 +741,6 @@ bool MediaPlayer::failed() const
|
||||
return failed_;
|
||||
}
|
||||
|
||||
void MediaPlayer::Frame::unmap()
|
||||
{
|
||||
if (full)
|
||||
gst_video_frame_unmap(&vframe);
|
||||
|
||||
full = false;
|
||||
}
|
||||
|
||||
void MediaPlayer::pipeline_terminate( GstElement *p, GstBus *b )
|
||||
{
|
||||
gchar *name = gst_element_get_name(p);
|
||||
@@ -819,7 +802,8 @@ void MediaPlayer::close()
|
||||
// cleanup eventual remaining frame memory
|
||||
for(guint i = 0; i < N_VFRAME; i++) {
|
||||
frame_[i].access.lock();
|
||||
frame_[i].unmap();
|
||||
if (frame_[i].buffer)
|
||||
gst_buffer_unref(frame_[i].buffer);
|
||||
frame_[i].status = INVALID;
|
||||
frame_[i].access.unlock();
|
||||
}
|
||||
@@ -834,6 +818,18 @@ void MediaPlayer::close()
|
||||
pipeline_ = nullptr;
|
||||
bus_ = nullptr;
|
||||
}
|
||||
|
||||
// cleanup opengl texture
|
||||
if (textureindex_) {
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
textureindex_ = 0;
|
||||
}
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0]) {
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1155,15 +1151,23 @@ void MediaPlayer::init_texture(guint index)
|
||||
glGenTextures(1, &textureindex_);
|
||||
glBindTexture(GL_TEXTURE_2D, textureindex_);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, media_.width, media_.height);
|
||||
|
||||
// fill texture frame with frame at given index
|
||||
if (frame_[index].buffer) {
|
||||
GstMapInfo map;
|
||||
gst_buffer_map(frame_[index].buffer, &map, GST_MAP_READ);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, media_.width, media_.height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, map.data);
|
||||
gst_buffer_unmap (frame_[index].buffer, &map);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// use Pixel Buffer Objects only for performance needs of videos
|
||||
if ( !singleFrame() ) {
|
||||
|
||||
// set pbo image size
|
||||
pbo_size_ = media_.height * media_.width * 4;
|
||||
|
||||
@@ -1172,31 +1176,7 @@ void MediaPlayer::init_texture(guint index)
|
||||
glDeleteBuffers(2, pbo_);
|
||||
glGenBuffers(2, pbo_);
|
||||
|
||||
for(int i = 0; i < 2; i++ ) {
|
||||
// create 2 PBOs
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[i]);
|
||||
// glBufferDataARB with NULL pointer reserves only memory space.
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
|
||||
// fill in with reset picture
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr) {
|
||||
// update data directly on the mapped buffer
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
}
|
||||
else {
|
||||
// did not work, disable PBO
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_[0] = pbo_[1] = 0;
|
||||
pbo_size_ = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// should be good to go, wrap it up
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
// should be good to go
|
||||
pbo_index_ = 0;
|
||||
pbo_next_index_ = 1;
|
||||
|
||||
@@ -1214,14 +1194,21 @@ void MediaPlayer::fill_texture(guint index)
|
||||
// is this the first frame ?
|
||||
if (textureindex_ < 1)
|
||||
{
|
||||
// initialize texture
|
||||
// initialize texture on first run
|
||||
// (this also fills the texture with frame at index)
|
||||
init_texture(index);
|
||||
}
|
||||
else {
|
||||
// Use GST mapping to access pointer to RGBA data
|
||||
GstMapInfo map;
|
||||
gst_buffer_map(frame_[index].buffer, &map, GST_MAP_READ);
|
||||
|
||||
// bind texture for writing
|
||||
glBindTexture(GL_TEXTURE_2D, textureindex_);
|
||||
|
||||
// use dual Pixel Buffer Object
|
||||
if (pbo_size_ > 0) {
|
||||
// use dual Pixel Buffer Object (faster)
|
||||
if (pbo_size_ > 0 && map.size == pbo_size_) {
|
||||
|
||||
// In dual PBO mode, increment current index first then get the next index
|
||||
pbo_index_ = (pbo_index_ + 1) % 2;
|
||||
pbo_next_index_ = (pbo_index_ + 1) % 2;
|
||||
@@ -1232,8 +1219,9 @@ void MediaPlayer::fill_texture(guint index)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, media_.width, media_.height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
// bind the next PBO to write pixels
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_next_index_]);
|
||||
|
||||
#ifdef USE_GL_BUFFER_SUBDATA
|
||||
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, pbo_size_, frame_[index].vframe.data[0]);
|
||||
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, pbo_size_, map.data);
|
||||
#else
|
||||
// update data directly on the mapped buffer
|
||||
// NB : equivalent but faster than glBufferSubData (memmove instead of memcpy ?)
|
||||
@@ -1242,7 +1230,8 @@ void MediaPlayer::fill_texture(guint index)
|
||||
// map the buffer object into client's memory
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr)
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
memmove(ptr, map.data, pbo_size_);
|
||||
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
#endif
|
||||
@@ -1252,9 +1241,15 @@ void MediaPlayer::fill_texture(guint index)
|
||||
else {
|
||||
// without PBO, use standard opengl (slower)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, media_.width, media_.height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, map.data);
|
||||
}
|
||||
|
||||
// unbind texture
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// unmap buffer to let it free
|
||||
gst_buffer_unmap (frame_[index].buffer, &map);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1318,7 +1313,7 @@ void MediaPlayer::update()
|
||||
need_loop = true;
|
||||
}
|
||||
// otherwise just fill non-empty SAMPLE or PREROLL
|
||||
else if (frame_[read_index].full)
|
||||
else if (frame_[read_index].is_new)
|
||||
{
|
||||
// fill the texture with the frame at reading index
|
||||
fill_texture(read_index);
|
||||
@@ -1327,8 +1322,9 @@ void MediaPlayer::update()
|
||||
if ( (frame_[read_index].status == PREROLL || seeking_ ) && pbo_size_ > 0)
|
||||
fill_texture(read_index);
|
||||
|
||||
// free frame
|
||||
frame_[read_index].unmap();
|
||||
// // free frame
|
||||
// frame_[read_index].unmap();
|
||||
frame_[read_index].is_new = false;
|
||||
}
|
||||
|
||||
// we just displayed a vframe : set position time to frame PTS
|
||||
@@ -1586,7 +1582,10 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
|
||||
frame_[write_index_].access.lock();
|
||||
|
||||
// always empty frame before filling it again
|
||||
frame_[write_index_].unmap();
|
||||
if (frame_[write_index_].buffer) {
|
||||
gst_buffer_unref(frame_[write_index_].buffer);
|
||||
frame_[write_index_].buffer = NULL;
|
||||
}
|
||||
|
||||
// accept status of frame received
|
||||
frame_[write_index_].status = status;
|
||||
@@ -1594,24 +1593,12 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
|
||||
// a buffer is given (not EOS)
|
||||
if (buf != NULL) {
|
||||
|
||||
// get the frame from buffer
|
||||
if ( !gst_video_frame_map (&frame_[write_index_].vframe, &v_frame_video_info_, buf, GST_MAP_READ ) )
|
||||
{
|
||||
#ifdef MEDIA_PLAYER_DEBUG
|
||||
Log::Info("MediaPlayer %s Failed to map the video buffer", std::to_string(id_).c_str());
|
||||
#endif
|
||||
// free access to frame & exit
|
||||
frame_[write_index_].status = INVALID;
|
||||
frame_[write_index_].access.unlock();
|
||||
return false;
|
||||
}
|
||||
// copy the buffer in the frame
|
||||
frame_[write_index_].buffer = gst_buffer_copy(buf);
|
||||
|
||||
// successfully filled the frame
|
||||
frame_[write_index_].full = true;
|
||||
// indicate to update loop that buffer is new
|
||||
frame_[write_index_].is_new = true;
|
||||
|
||||
// validate frame format
|
||||
if( GST_VIDEO_INFO_IS_RGB(&(frame_[write_index_].vframe).info) && GST_VIDEO_INFO_N_PLANES(&(frame_[write_index_].vframe).info) == 1)
|
||||
{
|
||||
// set presentation time stamp
|
||||
frame_[write_index_].position = buf->pts;
|
||||
|
||||
@@ -1622,18 +1609,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
|
||||
if (buf->pts > 0 && !timeline_.gapAt( buf->pts ))
|
||||
timeline_.addGap(0, buf->pts);
|
||||
}
|
||||
}
|
||||
// full but invalid frame : will be deleted next iteration
|
||||
// (should never happen)
|
||||
else {
|
||||
#ifdef MEDIA_PLAYER_DEBUG
|
||||
Log::Info("MediaPlayer %s Received an Invalid frame", std::to_string(id_).c_str());
|
||||
#endif
|
||||
// free access to frame & exit
|
||||
frame_[write_index_].status = INVALID;
|
||||
frame_[write_index_].access.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
// else; null buffer for EOS: give a position
|
||||
else {
|
||||
|
||||
@@ -368,18 +368,18 @@ private:
|
||||
} FrameStatus;
|
||||
|
||||
struct Frame {
|
||||
GstVideoFrame vframe;
|
||||
GstBuffer *buffer;
|
||||
FrameStatus status;
|
||||
bool full;
|
||||
bool is_new;
|
||||
GstClockTime position;
|
||||
std::mutex access;
|
||||
|
||||
Frame() {
|
||||
full = false;
|
||||
buffer = NULL;
|
||||
is_new = false;
|
||||
status = INVALID;
|
||||
position = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
void unmap();
|
||||
};
|
||||
Frame frame_[N_VFRAME];
|
||||
guint write_index_;
|
||||
|
||||
142
src/Stream.cpp
142
src/Stream.cpp
@@ -77,14 +77,6 @@ Stream::~Stream()
|
||||
{
|
||||
Stream::close();
|
||||
|
||||
// cleanup opengl texture
|
||||
if (textureindex_)
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
|
||||
#ifdef STREAM_DEBUG
|
||||
g_printerr("Stream %s deleted\n", std::to_string(id_).c_str());
|
||||
#endif
|
||||
@@ -137,6 +129,7 @@ GstFlowReturn callback_stream_discoverer (GstAppSink *sink, gpointer p)
|
||||
// release info to let StreamDiscoverer go forward
|
||||
info->discovered.notify_all();
|
||||
}
|
||||
gst_caps_unref(caps);
|
||||
}
|
||||
else
|
||||
ret = GST_FLOW_FLUSHING;
|
||||
@@ -265,8 +258,6 @@ void Stream::execute_open()
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
// ref gst_object (unrefed on close)
|
||||
gst_object_ref(pipeline_);
|
||||
|
||||
// set pipeline name
|
||||
g_object_set(G_OBJECT(pipeline_), "name", std::to_string(id_).c_str(), NULL);
|
||||
@@ -290,6 +281,7 @@ void Stream::execute_open()
|
||||
|
||||
// instruct sink to use the required caps
|
||||
gst_app_sink_set_caps (GST_APP_SINK(sink), caps);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
|
||||
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 30);
|
||||
@@ -340,10 +332,6 @@ void Stream::execute_open()
|
||||
gst_bus_set_sync_handler(bus_, stream_signal_handler, this, NULL);
|
||||
#endif
|
||||
|
||||
// done with refs
|
||||
gst_object_unref (sink);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
// all good
|
||||
Log::Info("Stream %s Opened '%s' (%d x %d)", std::to_string(id_).c_str(), description.c_str(), width_, height_);
|
||||
opened_ = true;
|
||||
@@ -382,14 +370,6 @@ bool Stream::failed() const
|
||||
return failed_;
|
||||
}
|
||||
|
||||
void Stream::Frame::unmap()
|
||||
{
|
||||
if (full)
|
||||
gst_video_frame_unmap(&vframe);
|
||||
|
||||
full = false;
|
||||
}
|
||||
|
||||
|
||||
void Stream::pipeline_terminate( GstElement *p, GstBus *b )
|
||||
{
|
||||
@@ -444,7 +424,8 @@ void Stream::close()
|
||||
// cleanup eventual remaining frame memory
|
||||
for(guint i = 0; i < N_FRAME; ++i){
|
||||
frame_[i].access.lock();
|
||||
frame_[i].unmap();
|
||||
if (frame_[i].buffer)
|
||||
gst_buffer_unref(frame_[i].buffer);
|
||||
frame_[i].status = INVALID;
|
||||
frame_[i].access.unlock();
|
||||
}
|
||||
@@ -460,6 +441,17 @@ void Stream::close()
|
||||
bus_ = nullptr;
|
||||
}
|
||||
|
||||
// cleanup opengl texture
|
||||
if (textureindex_) {
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
textureindex_ = 0;
|
||||
}
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0]) {
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -607,13 +599,21 @@ void Stream::init_texture(guint index)
|
||||
glGenTextures(1, &textureindex_);
|
||||
glBindTexture(GL_TEXTURE_2D, textureindex_);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width_, height_);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
|
||||
// fill texture with frame at given index
|
||||
if (frame_[index].buffer) {
|
||||
GstMapInfo map;
|
||||
gst_buffer_map(frame_[index].buffer, &map, GST_MAP_READ);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, map.data);
|
||||
gst_buffer_unmap(frame_[index].buffer, &map);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// use Pixel Buffer Objects only for performance needs of videos
|
||||
if (!single_frame_) {
|
||||
|
||||
// set pbo image size
|
||||
@@ -624,31 +624,7 @@ void Stream::init_texture(guint index)
|
||||
glDeleteBuffers(2, pbo_);
|
||||
glGenBuffers(2, pbo_);
|
||||
|
||||
for(int i = 0; i < 2; i++ ) {
|
||||
// create 2 PBOs
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[i]);
|
||||
// glBufferDataARB with NULL pointer reserves only memory space.
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
|
||||
// fill in with reset picture
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr) {
|
||||
// update data directly on the mapped buffer
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
}
|
||||
else {
|
||||
// did not work, disable PBO
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_[0] = pbo_[1] = 0;
|
||||
pbo_size_ = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// should be good to go, wrap it up
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
// should be good to go
|
||||
pbo_index_ = 0;
|
||||
pbo_next_index_ = 1;
|
||||
|
||||
@@ -670,13 +646,20 @@ void Stream::fill_texture(guint index)
|
||||
if ( !textureinitialized_ || !textureindex_)
|
||||
{
|
||||
// initialize texture
|
||||
// (this also fills the texture with frame at index)
|
||||
init_texture(index);
|
||||
}
|
||||
else {
|
||||
// Use GST mapping to access pointer to RGBA data
|
||||
GstMapInfo map;
|
||||
gst_buffer_map(frame_[index].buffer, &map, GST_MAP_READ);
|
||||
|
||||
// bind texture for writing
|
||||
glBindTexture(GL_TEXTURE_2D, textureindex_);
|
||||
|
||||
// use dual Pixel Buffer Object
|
||||
if (pbo_size_ > 0) {
|
||||
if (pbo_size_ > 0 && map.size == pbo_size_) {
|
||||
|
||||
// In dual PBO mode, increment current index first then get the next index
|
||||
pbo_index_ = (pbo_index_ + 1) % 2;
|
||||
pbo_next_index_ = (pbo_index_ + 1) % 2;
|
||||
@@ -688,30 +671,37 @@ void Stream::fill_texture(guint index)
|
||||
// bind the next PBO to write pixels
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_next_index_]);
|
||||
#ifdef USE_GL_BUFFER_SUBDATA
|
||||
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, pbo_size_, frame_[index].vframe.data[0]);
|
||||
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, pbo_size_, map.data);
|
||||
#else
|
||||
// update data directly on the mapped buffer
|
||||
// NB : equivalent but faster than glBufferSubData (memmove instead of memcpy ?)
|
||||
// update data directly on the mapped buffer \
|
||||
// NB : equivalent but faster than glBufferSubData (memmove instead of memcpy ?) \
|
||||
// See http://www.songho.ca/opengl/gl_pbo.html#map for more details
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
|
||||
|
||||
// map the buffer object into client's memory
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr)
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
memmove(ptr, map.data, pbo_size_);
|
||||
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
#endif
|
||||
|
||||
// done with PBO
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
else {
|
||||
// without PBO, use standard opengl (slower)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, map.data);
|
||||
}
|
||||
|
||||
// unbind texture
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// unmap buffer to let it free
|
||||
gst_buffer_unmap (frame_[index].buffer, &map);
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::update()
|
||||
@@ -778,7 +768,7 @@ void Stream::update()
|
||||
need_loop = true;
|
||||
}
|
||||
// otherwise just fill non-empty SAMPLE or PREROLL
|
||||
else if (frame_[read_index].full)
|
||||
else if (frame_[read_index].is_new)
|
||||
{
|
||||
// fill the texture with the frame at reading index
|
||||
fill_texture(read_index);
|
||||
@@ -788,7 +778,7 @@ void Stream::update()
|
||||
fill_texture(read_index);
|
||||
|
||||
// free frame
|
||||
frame_[read_index].unmap();
|
||||
frame_[read_index].is_new = false;
|
||||
}
|
||||
|
||||
// we just displayed a vframe : set position time to frame PTS
|
||||
@@ -833,43 +823,25 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
|
||||
frame_[write_index_].access.lock();
|
||||
|
||||
// always empty frame before filling it again
|
||||
frame_[write_index_].unmap();
|
||||
if (frame_[write_index_].buffer) {
|
||||
gst_buffer_unref(frame_[write_index_].buffer);
|
||||
frame_[write_index_].buffer = NULL;
|
||||
}
|
||||
|
||||
// accept status of frame received
|
||||
frame_[write_index_].status = status;
|
||||
|
||||
// a buffer is given (not EOS)
|
||||
if (buf != NULL) {
|
||||
// get the frame from buffer
|
||||
if ( !gst_video_frame_map (&frame_[write_index_].vframe, &v_frame_video_info_, buf, GST_MAP_READ ) )
|
||||
{
|
||||
Log::Info("Stream %s Failed to map the video buffer", std::to_string(id_).c_str());
|
||||
// free access to frame & exit
|
||||
frame_[write_index_].status = INVALID;
|
||||
frame_[write_index_].access.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
// successfully filled the frame
|
||||
frame_[write_index_].full = true;
|
||||
// copy the buffer in the frame
|
||||
frame_[write_index_].buffer = gst_buffer_copy(buf);
|
||||
|
||||
// indicate to update loop that buffer is new
|
||||
frame_[write_index_].is_new = true;
|
||||
|
||||
// validate frame format
|
||||
if( GST_VIDEO_INFO_IS_RGB(&(frame_[write_index_].vframe).info) && GST_VIDEO_INFO_N_PLANES(&(frame_[write_index_].vframe).info) == 1)
|
||||
{
|
||||
// set presentation time stamp
|
||||
frame_[write_index_].position = buf->pts;
|
||||
|
||||
}
|
||||
// full but invalid frame : will be deleted next iteration
|
||||
// (should never happen)
|
||||
else {
|
||||
#ifdef STREAM_DEBUG
|
||||
Log::Info("Stream %s Received an Invalid frame", std::to_string(id_).c_str());
|
||||
#endif
|
||||
frame_[write_index_].status = INVALID;
|
||||
frame_[write_index_].access.unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// else; null buffer for EOS: give a position
|
||||
else {
|
||||
|
||||
@@ -213,18 +213,18 @@ protected:
|
||||
} FrameStatus;
|
||||
|
||||
struct Frame {
|
||||
GstVideoFrame vframe;
|
||||
GstBuffer *buffer;
|
||||
FrameStatus status;
|
||||
bool full;
|
||||
bool is_new;
|
||||
GstClockTime position;
|
||||
std::mutex access;
|
||||
|
||||
Frame() {
|
||||
full = false;
|
||||
buffer = NULL;
|
||||
is_new = false;
|
||||
status = INVALID;
|
||||
position = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
void unmap();
|
||||
};
|
||||
Frame frame_[N_FRAME];
|
||||
guint write_index_;
|
||||
|
||||
@@ -118,6 +118,7 @@ void TextContents::execute_open()
|
||||
|
||||
// instruct sink to use the required caps
|
||||
gst_app_sink_set_caps(GST_APP_SINK(sink), caps);
|
||||
gst_caps_unref(caps);
|
||||
|
||||
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
|
||||
gst_app_sink_set_max_buffers(GST_APP_SINK(sink), 30);
|
||||
@@ -203,9 +204,9 @@ void TextContents::execute_open()
|
||||
// instruct the sink to send samples synched in time if not live source
|
||||
gst_base_sink_set_sync(GST_BASE_SINK(sink), !live_);
|
||||
|
||||
// done with refs
|
||||
gst_object_unref(sink);
|
||||
gst_caps_unref(caps);
|
||||
bus_ = gst_element_get_bus(pipeline_);
|
||||
// avoid filling up bus with messages
|
||||
gst_bus_set_flushing(bus_, true);
|
||||
|
||||
// all good
|
||||
Log::Info("TextContents: %s Opened '%s' (%d x %d)",
|
||||
|
||||
Reference in New Issue
Block a user