1 /*****************************************************************************
2  * gstvlcpictureplaneallocator.c: VLC pictures wrapped by GstAllocator
3  *****************************************************************************
4  * Copyright (C) 2016 VLC authors and VideoLAN
5  * $Id:
6  *
7  * Author: Vikram Fugro <vikram.fugro@gmail.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
22  *****************************************************************************/
23 
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <gst/gst.h>
28 
29 #include "gstvlcpictureplaneallocator.h"
30 
31 #include <vlc_common.h>
32 
33 #define gst_vlc_picture_plane_allocator_parent_class parent_class
34 G_DEFINE_TYPE (GstVlcPicturePlaneAllocator, gst_vlc_picture_plane_allocator, \
35         GST_TYPE_ALLOCATOR);
36 
37 static void gst_vlc_picture_plane_allocator_finalize( GObject *p_object );
38 static GstMemory* gst_vlc_picture_plane_allocator_dummy_alloc(
39         GstAllocator* p_allocator, gsize i_size,
40         GstAllocationParams *p_params );
41 static void gst_vlc_picture_plane_allocator_free( GstAllocator *p_allocator,
42         GstMemory *p_gmem);
43 static gpointer gst_vlc_picture_plane_map( GstMemory *p_gmem,
44         gsize i_maxsize, GstMapFlags flags );
45 static gboolean gst_vlc_picture_plane_unmap( GstVlcPicturePlane *p_mem );
46 static GstMemory* gst_vlc_picture_plane_copy(
47         GstVlcPicturePlane *p_mem, gssize i_offset, gssize i_size );
48 
49 #define GST_VLC_PICTURE_PLANE_ALLOCATOR_NAME "vlcpictureplane"
50 
gst_vlc_picture_plane_allocator_class_init(GstVlcPicturePlaneAllocatorClass * p_klass)51 static void gst_vlc_picture_plane_allocator_class_init(
52     GstVlcPicturePlaneAllocatorClass *p_klass )
53 {
54     GObjectClass *p_gobject_class;
55     GstAllocatorClass *p_allocator_class;
56 
57     p_gobject_class = (GObjectClass*) p_klass;
58     p_allocator_class = (GstAllocatorClass*) p_klass;
59 
60     p_gobject_class->finalize = gst_vlc_picture_plane_allocator_finalize;
61 
62     p_allocator_class->alloc = gst_vlc_picture_plane_allocator_dummy_alloc;
63     p_allocator_class->free = gst_vlc_picture_plane_allocator_free;
64 }
65 
gst_vlc_picture_plane_allocator_init(GstVlcPicturePlaneAllocator * p_allocator)66 static void gst_vlc_picture_plane_allocator_init(
67         GstVlcPicturePlaneAllocator *p_allocator )
68 {
69     GstAllocator *p_alloc = GST_ALLOCATOR_CAST( p_allocator );
70 
71     p_alloc->mem_type = GST_VLC_PICTURE_PLANE_ALLOCATOR_NAME;
72     p_alloc->mem_map = (GstMemoryMapFunction) gst_vlc_picture_plane_map;
73     p_alloc->mem_unmap = (GstMemoryUnmapFunction) gst_vlc_picture_plane_unmap;
74     p_alloc->mem_copy = (GstMemoryShareFunction) gst_vlc_picture_plane_copy;
75     /* fallback is_span */
76 
77     GST_OBJECT_FLAG_SET( p_allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC );
78 }
79 
gst_vlc_picture_plane_allocator_finalize(GObject * p_object)80 static void gst_vlc_picture_plane_allocator_finalize( GObject *p_object )
81 {
82     GstVlcPicturePlaneAllocator *p_alloc = GST_VLC_PICTURE_PLANE_ALLOCATOR(
83             p_object );
84     VLC_UNUSED( p_alloc );
85 
86     G_OBJECT_CLASS (parent_class)->finalize( p_object );
87 }
88 
gst_vlc_picture_plane_allocator_dummy_alloc(GstAllocator * p_allocator,gsize i_size,GstAllocationParams * p_params)89 static GstMemory* gst_vlc_picture_plane_allocator_dummy_alloc(
90         GstAllocator* p_allocator, gsize i_size, GstAllocationParams *p_params )
91 {
92     VLC_UNUSED( p_allocator );
93     VLC_UNUSED( i_size );
94     VLC_UNUSED( p_params );
95     return NULL;
96 }
97 
gst_vlc_picture_plane_allocator_free(GstAllocator * p_allocator,GstMemory * p_gmem)98 static void gst_vlc_picture_plane_allocator_free( GstAllocator *p_allocator,
99         GstMemory *p_gmem)
100 {
101     VLC_UNUSED( p_allocator );
102     GstVlcPicturePlane *p_mem = (GstVlcPicturePlane*) p_gmem;
103     g_slice_free( GstVlcPicturePlane, p_mem );
104 }
105 
gst_vlc_picture_plane_map(GstMemory * p_gmem,gsize i_maxsize,GstMapFlags flags)106 static gpointer gst_vlc_picture_plane_map( GstMemory *p_gmem,
107     gsize i_maxsize, GstMapFlags flags )
108 {
109     VLC_UNUSED( i_maxsize );
110     VLC_UNUSED( flags );
111     GstVlcPicturePlane* p_mem = (GstVlcPicturePlane*) p_gmem;
112 
113     if( p_mem->p_pic )
114         return (gpointer) (p_mem->p_plane->p_pixels + p_mem->parent.offset);
115     else
116         return NULL;
117 }
118 
gst_vlc_picture_plane_unmap(GstVlcPicturePlane * p_mem)119 static gboolean gst_vlc_picture_plane_unmap(
120         GstVlcPicturePlane *p_mem )
121 {
122     VLC_UNUSED( p_mem );
123     return TRUE;
124 }
125 
gst_vlc_picture_plane_copy(GstVlcPicturePlane * p_mem,gssize i_offset,gssize i_size)126 static GstMemory* gst_vlc_picture_plane_copy(
127         GstVlcPicturePlane *p_mem, gssize i_offset, gssize i_size )
128 {
129     VLC_UNUSED( p_mem );
130     VLC_UNUSED( i_offset );
131     VLC_UNUSED( i_size );
132     return NULL;
133 }
134 
gst_vlc_picture_plane_allocator_release(GstVlcPicturePlaneAllocator * p_allocator,GstBuffer * p_buffer)135 void gst_vlc_picture_plane_allocator_release(
136     GstVlcPicturePlaneAllocator *p_allocator, GstBuffer *p_buffer )
137 {
138     VLC_UNUSED( p_allocator );
139 
140     GstVlcPicturePlane* p_mem =
141         (GstVlcPicturePlane*) gst_buffer_peek_memory( p_buffer, 0 );
142     guint i_plane;
143 
144     if( p_mem->p_pic )
145     {
146         picture_Release( p_mem->p_pic );
147 
148         for( i_plane = 0; i_plane < gst_buffer_n_memory( p_buffer );
149                 i_plane++ )
150         {
151             p_mem = (GstVlcPicturePlane*) gst_buffer_peek_memory ( p_buffer,
152                     i_plane );
153             p_mem->p_pic = NULL;
154             p_mem->p_plane = NULL;
155         }
156     }
157 }
158 
gst_vlc_picture_plane_allocator_hold(GstVlcPicturePlaneAllocator * p_allocator,GstBuffer * p_buffer)159 bool gst_vlc_picture_plane_allocator_hold(
160     GstVlcPicturePlaneAllocator *p_allocator, GstBuffer *p_buffer )
161 {
162     picture_t* p_pic = NULL;
163     decoder_t* p_dec = p_allocator->p_dec;
164     GstVlcPicturePlane *p_mem;
165     int i_plane;
166 
167     if( !decoder_UpdateVideoFormat( p_dec ) )
168         p_pic = decoder_NewPicture( p_dec );
169     if( !p_pic )
170     {
171         msg_Err( p_allocator->p_dec, "failed to acquire picture from vout" );
172         return false;
173     }
174 
175     for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
176     {
177         p_mem = (GstVlcPicturePlane*) gst_buffer_peek_memory ( p_buffer,
178                 i_plane );
179         p_mem->p_pic = p_pic;
180         p_mem->p_plane = &p_pic->p[ i_plane ];
181     }
182 
183     return true;
184 }
185 
gst_vlc_picture_plane_allocator_alloc(GstVlcPicturePlaneAllocator * p_allocator,GstBuffer * p_buffer)186 bool gst_vlc_picture_plane_allocator_alloc(
187         GstVlcPicturePlaneAllocator *p_allocator,
188         GstBuffer *p_buffer )
189 {
190     int i_plane;
191     gsize i_max_size, i_align, i_offset, i_size;
192     picture_t *p_pic;
193 
194     p_pic = &p_allocator->pic_info;
195 
196     for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
197     {
198         GstVlcPicturePlane *p_mem =
199             (GstVlcPicturePlane*) g_slice_new0( GstVlcPicturePlane );
200 
201         i_size = p_pic->p[ i_plane ].i_pitch *
202             p_pic->p[ i_plane ].i_lines;
203         i_max_size = p_pic->p[ i_plane ].i_pitch *
204             p_pic->p[ i_plane ].i_lines;
205         i_align = 0;
206         i_offset = 0;
207 
208         gst_memory_init( GST_MEMORY_CAST( p_mem ), GST_MEMORY_FLAG_NO_SHARE,
209                 GST_ALLOCATOR_CAST( p_allocator ), NULL, i_max_size,
210                 i_align, i_offset, i_size );
211         gst_buffer_append_memory( p_buffer, (GstMemory*) p_mem );
212     }
213 
214     return true;
215 }
216 
gst_vlc_set_vout_fmt(GstVideoInfo * p_info,GstVideoAlignment * p_align,GstCaps * p_caps,decoder_t * p_dec)217 bool gst_vlc_set_vout_fmt( GstVideoInfo *p_info, GstVideoAlignment *p_align,
218         GstCaps *p_caps, decoder_t *p_dec )
219 {
220     es_format_t *p_outfmt = &p_dec->fmt_out;
221     video_format_t *p_voutfmt = &p_dec->fmt_out.video;
222     GstStructure *p_str = gst_caps_get_structure( p_caps, 0 );
223     vlc_fourcc_t i_chroma;
224     int i_padded_width, i_padded_height;
225 
226     i_chroma = p_outfmt->i_codec = vlc_fourcc_GetCodecFromString(
227             VIDEO_ES,
228             gst_structure_get_string( p_str, "format" ) );
229     if( !i_chroma )
230     {
231         msg_Err( p_dec, "video chroma type not supported" );
232         return false;
233     }
234 
235     i_padded_width = GST_VIDEO_INFO_WIDTH( p_info ) + p_align->padding_left +
236         p_align->padding_right;
237     i_padded_height = GST_VIDEO_INFO_HEIGHT( p_info ) + p_align->padding_top +
238         p_align->padding_bottom;
239 
240     video_format_Setup( p_voutfmt, i_chroma, i_padded_width, i_padded_height,
241             GST_VIDEO_INFO_WIDTH( p_info ), GST_VIDEO_INFO_HEIGHT( p_info ),
242             GST_VIDEO_INFO_PAR_N( p_info ), GST_VIDEO_INFO_PAR_D( p_info ));
243     p_voutfmt->i_x_offset = p_align->padding_left;
244     p_voutfmt->i_y_offset = p_align->padding_top;
245 
246     p_voutfmt->i_frame_rate = GST_VIDEO_INFO_FPS_N( p_info );
247     p_voutfmt->i_frame_rate_base = GST_VIDEO_INFO_FPS_D( p_info );
248 
249     return true;
250 }
251 
gst_vlc_video_info_from_vout(GstVideoInfo * p_info,GstVideoAlignment * p_align,GstCaps * p_caps,decoder_t * p_dec,picture_t * p_pic_info)252 static bool gst_vlc_video_info_from_vout( GstVideoInfo *p_info,
253         GstVideoAlignment *p_align, GstCaps *p_caps, decoder_t *p_dec,
254         picture_t *p_pic_info )
255 {
256     const GstVideoFormatInfo *p_vinfo = p_info->finfo;
257     picture_t *p_pic = NULL;
258     int i;
259 
260     /* Ensure the queue is empty */
261     gst_vlc_dec_ensure_empty_queue( p_dec );
262     gst_video_info_align( p_info, p_align );
263 
264     if( !gst_vlc_set_vout_fmt( p_info, p_align, p_caps, p_dec ))
265     {
266         msg_Err( p_dec, "failed to set output format to vout" );
267         return false;
268     }
269 
270     /* Acquire a picture and release it. This is to get the picture
271      * stride/offsets info for the Gstreamer decoder looking to use
272      * downstream bufferpool directly; Zero-Copy */
273     if( !decoder_UpdateVideoFormat( p_dec ) )
274         p_pic = decoder_NewPicture( p_dec );
275     if( !p_pic )
276     {
277         msg_Err( p_dec, "failed to acquire picture from vout; for pic info" );
278         return false;
279     }
280 
281     /* reject if strides don't match */
282     for( i = 0; i < p_pic->i_planes; i++ )
283         if( p_info->stride[i] != p_pic->p[i].i_pitch )
284             goto strides_mismatch;
285 
286     p_info->offset[0] = 0;
287     for( i = 1; i < p_pic->i_planes; i++ )
288     {
289         p_info->offset[i] = p_info->offset[i-1] +
290             p_pic->p[i-1].i_pitch * p_pic->p[i-1].i_lines;
291     }
292     GST_VIDEO_INFO_SIZE( p_info ) = p_info->offset[i-1] +
293         p_pic->p[i-1].i_pitch * p_pic->p[i-1].i_lines;
294 
295     for( i = 0; i < p_pic->i_planes; i++ )
296     {
297         int i_v_edge, i_h_edge;
298 
299         i_h_edge =
300             GST_VIDEO_FORMAT_INFO_SCALE_WIDTH( p_vinfo, i,
301                     p_align->padding_left);
302         i_v_edge =
303             GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT( p_vinfo, i,
304                     p_align->padding_top);
305 
306         p_info->offset[i] += ( i_v_edge * p_info->stride[i] ) +
307             ( i_h_edge * GST_VIDEO_FORMAT_INFO_PSTRIDE( p_vinfo, i ));
308     }
309 
310     memcpy( p_pic_info, p_pic, sizeof( picture_t ));
311     picture_Release( p_pic );
312 
313     return true;
314 
315 strides_mismatch:
316     msg_Err( p_dec, "strides mismatch" );
317     picture_Release( p_pic );
318     return false;
319 }
320 
gst_vlc_picture_plane_allocator_query_format(GstVlcPicturePlaneAllocator * p_allocator,GstVideoInfo * p_info,GstVideoAlignment * p_align,GstCaps * p_caps)321 bool gst_vlc_picture_plane_allocator_query_format(
322         GstVlcPicturePlaneAllocator *p_allocator, GstVideoInfo *p_info,
323         GstVideoAlignment *p_align, GstCaps *p_caps )
324 {
325     decoder_t *p_dec = p_allocator->p_dec;
326     video_format_t v_fmt;
327     picture_t *p_pic_info = &p_allocator->pic_info;
328 
329     /* Back up the original format; as this is just a query  */
330     v_fmt = p_dec->fmt_out.video;
331     video_format_Init( &p_dec->fmt_out.video, 0 );
332 
333     bool b_ret =
334         gst_vlc_video_info_from_vout( p_info, p_align, p_caps, p_dec,
335                 p_pic_info );
336 
337     video_format_Clean( &p_dec->fmt_out.video );
338 
339     /* Restore the original format; as this was just a query  */
340     p_dec->fmt_out.video = v_fmt;
341 
342     if( !b_ret )
343     {
344         msg_Err( p_allocator->p_dec, "failed to get the vout info" );
345         return false;
346     }
347 
348     return true;
349 }
350 
gst_vlc_picture_plane_allocator_new(decoder_t * p_dec)351 GstVlcPicturePlaneAllocator* gst_vlc_picture_plane_allocator_new(
352         decoder_t *p_dec )
353 {
354     GstVlcPicturePlaneAllocator *p_allocator;
355 
356     p_allocator = g_object_new( GST_TYPE_VLC_PICTURE_PLANE_ALLOCATOR, NULL);
357     p_allocator->p_dec = p_dec;
358 
359     return p_allocator;
360 }
361