After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 778496 - vtenc: should support GLMemory
vtenc: should support GLMemory
Status: RESOLVED OBSOLETE
Product: GStreamer
Classification: Platform
Component: gst-plugins-bad
1.11.1
Other Mac OS
: Normal enhancement
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2017-02-11 15:09 UTC by Nick Kallen
Modified: 2018-11-03 14:04 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Allow GLMemory static caps (1.19 KB, patch)
2017-02-11 15:14 UTC, Nick Kallen
accepted-commit_now Details | Review
Support resizing; pass frame pointer to avoid calls to gst_video_encoder_get_frame in background threads; support gst_core_video buffers (9.44 KB, patch)
2017-02-17 12:22 UTC, Nick Kallen
needs-work Details | Review

Description Nick Kallen 2017-02-11 15:09:10 UTC
Currently vtenc caps disallow GLMemory. However, having tested this on iOS and reviewed the code, it actually works using GLMemory. Since the underlying pixel buffer is always a core media buffer,

    pbuf = gst_core_media_buffer_get_pixel_buffer (frame->input_buffer);

this statement is valid whether the input_buffer's pixel buffer is GL buffer or not.

Therefore, just changing the static caps works.
Comment 1 Nick Kallen 2017-02-11 15:14:30 UTC
Created attachment 345525 [details] [review]
Allow GLMemory static caps
Comment 2 Sebastian Dröge (slomo) 2017-02-14 10:22:01 UTC
Comment on attachment 345525 [details] [review]
Allow GLMemory static caps

You're removing support for caps *without* GL feature here. Also are you sure about the formats? I would've expected BGRA or a variant of that for GL.
Comment 3 Nick Kallen 2017-02-15 11:23:10 UTC
Correct me if I'm wrong, but doesn't

 	    "; " GST_VIDEO_CAPS_MAKE ("{ NV12, I420 }")

includes the old formats?

And yeah I'm 98% about NV12 and I420 -- these are the formats specific to the hardware encoding and decoding on the iphone.
Comment 4 Nick Kallen 2017-02-17 12:22:32 UTC
Created attachment 346061 [details] [review]
Support resizing; pass frame pointer to avoid calls to gst_video_encoder_get_frame in background threads; support gst_core_video buffers
Comment 5 Nick Kallen 2017-02-17 12:29:03 UTC
This latest patch is non-trivial and although it works correctly I wanted to get feedback on implementation details.

Firstly, the old code was extremely deadlock-prone, look at code like this:

  /* We need to unlock the stream lock here because
   * it can wait for gst_vtenc_enqueue_buffer() to
   * handle a buffer... which will take the stream
   * lock from another thread and then deadlock */
  GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
  vt_status =
      VTCompressionSessionCompleteFrames (self->session,
      kCMTimePositiveInfinity);

The main problem is the various VTC* methods may or may not block, but they usually execute code asynchronously. And the callback in this case always called gst_video_encoder_get_frame, which required the video encoder stream lock.

I haven't removed these UNLOCKs in this commit yet, just to be careful, but I'm pretty sure this removes all possibility of deadlock. But it relies on passing frame pointers between threads -- I wanted to double check that's safe. Seems like it should be, but it's surprising the old code didn't do that in the first place.

--

The resizing-related __unfix_resolution code is elaborate but turned out to be necessary to get caps to agree. The pipelines I have in mind are:

 uridecodebin ! vtenc ! video/x-h264,width=x,height=y
Comment 6 Nicolas Dufresne (ndufresne) 2017-02-17 14:49:04 UTC
Just a note that the encoder stream_lock is kind of historical. It's redundant with the pads stream locks, and does not provide the same level of flexibility.
Comment 7 Sebastian Dröge (slomo) 2017-02-26 10:23:47 UTC
(In reply to Nicolas Dufresne (stormer) from comment #6)
> Just a note that the encoder stream_lock is kind of historical. It's
> redundant with the pads stream locks, and does not provide the same level of
> flexibility.

That's not true, to be able to remove the lock there would have to be quite some refactoring in the base class.
Comment 8 Sebastian Dröge (slomo) 2017-02-26 10:31:27 UTC
Review of attachment 346061 [details] [review]:

This looks like it should be split into 3 different patches. You have 3 separate functional changes in here, even your commit message reads like that ;)

::: sys/applemedia/vtenc.c
@@ +652,3 @@
+  GstStructure *s;
+  gboolean peer_has_resolution;
+  gint peer_width, peer_height;

Variable declarations in C code should be at the beginning of the block

@@ +661,3 @@
+  if (peer_has_resolution) {
+    self->negotiated_width = peer_width;
+    self->negotiated_height = peer_height;

This might be better to do in negotiate()

@@ +745,3 @@
       self->input_state);
+  state->info.width = self->negotiated_width;
+  state->info.height = self->negotiated_height;

You need to correct the pixel-aspect-ratio here AFAIU. If the encoder was rescaling, it might've kept the display-aspect-ratio but you need to also update the pixel-aspect-ratio in the caps accordingly then

@@ +823,3 @@
+      g_value_init (&value, GST_TYPE_INT_RANGE);
+      gst_value_set_int_range (&value, 1, G_MAXINT);
+      gst_structure_set_value (caps_s, "width", &value);

Ideally g_value_unset(&value) after you're done with it. Doesn't make a difference here (int ranges don't occupy heap memory) but is cleaner

@@ +864,3 @@
+
+  if (new_filter != NULL)
+    gst_caps_unref (new_filter);

You need to intersect with the original filter before returning, otherwise you might return a wider range of resolutions than upstream wanted

@@ +1322,3 @@
   GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
   vt_status = VTCompressionSessionEncodeFrame (self->session,
+      pbuf, ts, duration, frame_props, frame, NULL);

Reason for going via the frame number here is that it can prevent access to already freed memory (or memory leaks if you pass a new reference here). When getting it again later by integer, we can handle properly if the frame was freed already (can probably happen during shutdown). (Or if you pass another reference, we can ensure that the encoder is never keeping the reference forever but we can free the frame when we want).
Comment 9 Nick Kallen 2017-03-04 09:48:42 UTC
OK I'm going to deal with all these changes this week.


> Reason for going via the frame number here is that it can prevent access to already
> freed memory (or memory leaks if you pass a new reference here). When getting it 
> again later by integer, we can handle properly if the frame was freed already (can 
> probably happen during shutdown). (Or if you pass another reference, we can ensure 
> that the encoder is never keeping the reference forever but we can free the frame 
> when we want).

OK I'll revert this change. It seems unfortunate because when getting by integer a lock is acquired, and you have all this deadlock avoidance code in vtenc which as I discovered was missing several cases. The issue is you can call apples encoder, and it will (sometimes) block, but it will actually do the work in another thread. And if you call it with a lock which it will later need in the other thread, you get a deadlock.

The code does a lot of this:

  GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
      VTCompressionSessionCompleteFrames (self->session,
      kCMTimePositiveInfinity);
  GST_VIDEO_ENCODER_STREAM_LOCK (self);
Comment 10 Sebastian Dröge (slomo) 2017-03-04 09:55:56 UTC
(In reply to Nick Kallen from comment #9)

> The code does a lot of this:
> 
>   GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
>       VTCompressionSessionCompleteFrames (self->session,
>       kCMTimePositiveInfinity);
>   GST_VIDEO_ENCODER_STREAM_LOCK (self);

Taking the encoder's stream lock is required from the other threads to do anything with the GstVideoEncoder. If you don't do anything else with it and you can guarantee that the GstVideoCodecFrame* you pass around directly is a) always ending up in your callback (so that you can unref it) and b) you always get it once and only the ones you provided (e.g. not some random other pointer sometimes), then you can also pass the frame directly. It's just more requirements on the VT API which you have to check to ensure everything is memory-safe.
Comment 11 Sebastian Dröge (slomo) 2017-06-07 07:22:19 UTC
Nick?
Comment 12 GStreamer system administrator 2018-11-03 14:04:53 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/522.