1 /***************************************************************************
2                           \fn     libvaEnc_plugin
3                           \brief  Plugin to use libva hw encoder (intel mostly)
4                              -------------------
5 
6     copyright            : (C) 2018 by mean
7     email                : fixounet@free.fr
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18  /***************************************************************************/
19 /* Derived from libva sample code */
20 /*
21  * Copyright (c) 2007-2013 Intel Corporation. All Rights Reserved.
22  *
23  * Permission is hereby granted, free of charge, to any person obtaining a
24  * copy of this software and associated documentation files (the
25  * "Software"), to deal in the Software without restriction, including
26  * without limitation the rights to use, copy, modify, merge, publish,
27  * distribute, sub license, and/or sell copies of the Software, and to
28  * permit persons to whom the Software is furnished to do so, subject to
29  * the following conditions:
30  *
31  * The above copyright notice and this permission notice (including the
32  * next paragraph) shall be included in all copies or substantial portions
33  * of the Software.
34  *
35  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
36  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
38  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
39  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
40  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
41  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42  */
43 /***************************************************************************
44  *                                                                         *
45  *   This program is free software; you can redistribute it and/or modify  *
46  *   it under the terms of the GNU General Public License as published by  *
47  *   the Free Software Foundation; either version 2 of the License, or     *
48  *   (at your option) any later version.                                   *
49  *                                                                         *
50  ***************************************************************************/
51 #include "ADM_default.h"
52 #include "ADM_bitstream.h"
53 #include "ADM_coreVideoEncoder.h"
54 #include "ADM_videoInfoExtractor.h"
55 
56 
57 #include "va/va.h"
58 #include "va/va_enc_h264.h"
59 #include "ADM_coreLibVA_buffer.h"
60 #include "ADM_libVaEncodingContextH264.h"
61 
62 
63 /**
64  *
65  */
ADM_vaEncodingContextH264Base()66 ADM_vaEncodingContextH264Base::ADM_vaEncodingContextH264Base()
67 {
68     aprintf("vaH264 ctor\n");
69     context_id=VA_INVALID;
70     config_id=VA_INVALID;
71 
72     current_frame_encoding=0;
73 
74     for(int i=0;i<VA_ENC_NB_SURFACE;i++)
75         vaEncodingBuffers[i]=NULL;;
76     for(int i=0;i<VA_ENC_NB_SURFACE;i++)
77     {
78         vaSurface[i]=NULL;
79         vaRefSurface[i]=NULL;
80     }
81     memset(&seq_param, 0, sizeof(seq_param));
82     memset(&pic_param, 0, sizeof(pic_param));
83     memset(&slice_param, 0, sizeof(slice_param));
84 
85     num_ref_frames = 1;
86 
87 
88     numShortTerm = 0;
89     MaxPicOrderCntLsb = (2<<8);
90     Log2MaxFrameNum = 16;
91     Log2MaxPicOrderCntLsb = 8;
92 
93 
94     // RC
95     initial_qp = 15;
96     minimal_qp = 0;
97     rc_mode = VA_RC_CBR; //VA_RC_CQP;
98     aprintf("/vaH264 ctor\n");
99     tmpBuffer=NULL;
100 }
101 /**
102  *
103  */
~ADM_vaEncodingContextH264Base()104 ADM_vaEncodingContextH264Base::~ADM_vaEncodingContextH264Base()
105 {
106     aprintf("vaH264 dtor\n");
107     if(context_id!=VA_INVALID)
108     {
109         vaDestroyContext(admLibVA::getDisplay(),context_id);
110         context_id=VA_INVALID;
111     }
112     if(config_id!=VA_INVALID)
113     {
114         vaDestroyConfig(admLibVA::getDisplay(),config_id);
115         config_id=VA_INVALID;
116     }
117     for(int i=0;i<VA_ENC_NB_SURFACE;i++)
118     {
119         if(vaSurface[i])
120         {
121             delete vaSurface[i];
122             vaSurface[i]=NULL;
123         }
124         if(vaRefSurface[i])
125         {
126             delete vaRefSurface[i];
127             vaRefSurface[i]=NULL;
128         }
129 
130     }
131     aprintf("/vaH264 dtor\n");
132 }
133 /**
134  *
135  * @param width
136  * @param height
137  * @param knownSurfaces
138  * @return
139  */
setup(int width,int height,int frameInc,std::vector<ADM_vaSurface * > knownSurfaces)140 bool ADM_vaEncodingContextH264Base::setup( int width, int height, int frameInc,std::vector<ADM_vaSurface *>knownSurfaces)
141 {
142         ADM_info("vaH264 setup\n");
143 
144         h264=vaGetH264EncoderProfile();
145         if(h264->profile==VAProfileNone)
146         {
147             ADM_error("No H264 encoding support\n");
148             return false;
149         }
150 
151 
152         VAStatus va_status;
153         frame_width=width;
154         frame_height=height;
155         frame_width_mbaligned=(width+15)&~15;
156         frame_height_mbaligned=(height+15)&~15;
157         int  i;
158         usSecondsToFrac(frameInc,&frameNum,&frameDen);
159         ADM_info("xFps : %d : %d\n",frameNum,frameDen);
160         // marshall new config...
161 
162         // copy common part
163         int nAttrib=h264->newAttributes.count();
164         int outAttrib=0;
165         VAConfigAttrib *ttrib=new VAConfigAttrib[nAttrib+1];
166         const VAConfigAttrib *old=h264->newAttributes.getPointer();
167 
168         for(int i=0;i<nAttrib;i++)
169         {
170             ttrib[outAttrib]=old[i];
171             if(ttrib[outAttrib].type==VAConfigAttribEncPackedHeaders) // remove packed
172                 ttrib[outAttrib].value=getPackedAttributes();
173             outAttrib++;
174 
175         }
176         // add rate control, it is per instance
177         ttrib[outAttrib].type=VAConfigAttribRateControl;
178         ttrib[outAttrib].value=VA_RC_CBR;
179         outAttrib++;
180 
181         CHECK_VA_STATUS_BOOL( vaCreateConfig(admLibVA::getDisplay(), h264->profile, VAEntrypointEncSlice, ttrib, outAttrib, &config_id));
182 
183 
184         int n=knownSurfaces.size();
185         VASurfaceID *tmp_surfaceId = new VASurfaceID[n];
186         for(int i=0;i<n;i++)
187         {
188             tmp_surfaceId[i]=knownSurfaces[i]->surface;
189         }
190 
191         /* Create a context for this encode pipe */
192         CHECK_VA_STATUS_BOOL( vaCreateContext(admLibVA::getDisplay(), config_id,
193                                     frame_width_mbaligned, frame_height_mbaligned,
194                                     VA_PROGRESSIVE,
195                                     tmp_surfaceId, n,
196                                     &context_id));
197 
198         delete [] ttrib;
199         delete [] tmp_surfaceId;
200         tmp_surfaceId=NULL;
201 
202         int codedbuf_size = (frame_width_mbaligned * frame_height_mbaligned * 400) / (16*16);
203 
204         for (i = 0; i < SURFACE_NUM; i++)
205         {
206             vaEncodingBuffers[i]= ADM_vaEncodingBuffers::allocate(context_id,codedbuf_size);
207             if(!vaEncodingBuffers[i])
208             {
209                 ADM_warning("Cannot create encoding buffer %d\n",i);
210                 return false;;
211             }
212         }
213 
214         // Allocate VAImage
215 
216         for(int i=0;i<VA_ENC_NB_SURFACE;i++)
217         {
218             vaSurface[i]=ADM_vaSurface::allocateWithSurface(width,height);
219             if(!vaSurface[i])
220             {
221                 ADM_warning("Cannot allocate surface\n");
222                 return false;
223             }
224 
225             vaRefSurface[i]=ADM_vaSurface::allocateWithSurface(width,height);
226             if(!vaRefSurface[i])
227             {
228                 ADM_warning("Cannot allocate ref surface\n");
229                 return false;
230             }
231         }
232         tmpBuffer=new uint8_t[codedbuf_size];
233         render_sequence();
234         ADM_info("/vaH264 setup\n");
235         return true;
236 }
237 
238 
239 //-- Global Header
240 
generateExtraData(int * size,uint8_t ** data)241 bool ADM_vaEncodingContextH264Base::generateExtraData(int *size, uint8_t **data)
242 {
243     aprintf("vaH264 extraData\n");
244     vaBitstream sps,pps;
245 
246     fillSeqParam();
247     sps_rbsp(&sps);
248 
249     fillPPS(0,FRAME_IDR);
250     pps_rbsp(&pps);
251 
252     sps.stop();
253     pps.stop();
254 
255     int spsLen=sps.lengthInBytes();
256     int ppsLen=pps.lengthInBytes();
257 
258     *data =new uint8_t[spsLen+ppsLen+20];
259 
260     uint8_t *p=*data;
261     *p++=1;
262     *p++=sps.getPointer()[0];
263     *p++=sps.getPointer()[1];
264     *p++=sps.getPointer()[2];
265     *p++=0xff;
266     // SPS
267     *p++=0xe1;
268     *p++=(1+spsLen)>>8;
269     *p++=(1+spsLen)&0xff;
270     *p++=NAL_SPS; // SPS NALU
271     memcpy(p,sps.getPointer(),spsLen);
272     p+=spsLen;
273     // PPS
274     *p++=1;
275     *p++=(1+ppsLen)>>8;
276     *p++=(1+ppsLen)&0xff;
277     *p++=NAL_PPS;
278     memcpy(p,pps.getPointer(),ppsLen);
279     p+=ppsLen;
280     *size=(intptr_t)(p)-(intptr_t)*data;
281 
282     mixDump(*data,*size);
283 
284     aprintf("/vaH264 extraData\n");
285     return true;
286 }
287 /**
288  *
289  * @param in
290  * @param out
291  * @return
292  */
encode(ADMImage * in,ADMBitstream * out)293 bool ADM_vaEncodingContextH264Base::encode(ADMImage *in, ADMBitstream *out)
294 {
295     aprintf("Encoding frame %d, H264 AVC\n",current_frame_encoding);
296     vaFrameType current_frame_type;
297     if(!vaSurface[current_frame_encoding%SURFACE_NUM]->fromAdmImage(in))
298     {
299         ADM_warning("Failed to upload image to vaSurface\n");
300         return false;
301     }
302 
303     encoding2display_order(current_frame_encoding, vaH264Settings.IntraPeriod,    &current_frame_type);
304     aprintf("Encoding order = %d,  frame type=%d\n",(int)current_frame_encoding,current_frame_type);
305     int current_slot= (current_frame_encoding % SURFACE_NUM);
306 
307     CHECK_VA_STATUS_BOOL(vaBeginPicture(admLibVA::getDisplay(), context_id, vaSurface[current_slot]->surface));
308 
309 
310 
311     if (current_frame_type == FRAME_IDR)
312     {
313         out->flags = AVI_KEY_FRAME;
314     }else
315     {
316         out->flags = AVI_P_FRAME;
317     }
318     render_picture(current_frame_encoding,current_frame_type);
319     render_slice(current_frame_encoding,current_frame_type);
320     CHECK_VA_STATUS_BOOL( vaEndPicture(admLibVA::getDisplay(),context_id));
321     //--
322 
323     CHECK_VA_STATUS_BOOL( vaSyncSurface(admLibVA::getDisplay(), vaSurface[current_frame_encoding % SURFACE_NUM]->surface));
324 
325     #if 0 // Heavy convert
326 
327         int len=vaEncodingBuffers[current_frame_encoding % SURFACE_NUM]->read(tmpBuffer, out->bufferSize);
328         int l=ADM_unescapeH264(len-4,tmpBuffer+4,out->data+4);
329         out->data[0]=l>>24;
330         out->data[1]=l>>16;
331         out->data[2]=l>>8;
332         out->data[3]=l>>0;
333         out->len=l+4;
334 
335     #else            // light convert, no escape
336         out->len=vaEncodingBuffers[current_frame_encoding % SURFACE_NUM]->read(out->data, out->bufferSize);
337         ADM_assert(out->len>=0);
338 
339         // Set NAL Size (wtf ?)
340         int l=out->len-4;
341         out->data[0]=l>>24;
342         out->data[1]=l>>16;
343         out->data[2]=l>>8;
344         out->data[3]=l>>0;
345     #endif
346     /* reload a new frame data */
347 
348     update_ReferenceFrames(current_frame_type);
349     current_frame_encoding++;
350     out->pts=in->Pts;
351     out->dts=out->pts;
352     return true;
353 }
354 
355 // EOF
356