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         memcpy(ttrib,old,nAttrib*sizeof(VAConfigAttrib));
168 
169         // add rate control, it is per instance
170         ttrib[outAttrib].type=VAConfigAttribRateControl;
171         ttrib[outAttrib].value=VA_RC_CBR;
172         outAttrib++;
173 
174         CHECK_VA_STATUS_BOOL( vaCreateConfig(admLibVA::getDisplay(), h264->profile, VAEntrypointEncSlice, ttrib, outAttrib, &config_id));
175 
176         int n=knownSurfaces.size();
177         VASurfaceID *tmp_surfaceId = new VASurfaceID[n];
178         for(int i=0;i<n;i++)
179         {
180             tmp_surfaceId[i]=knownSurfaces[i]->surface;
181         }
182 
183         /* Create a context for this encode pipe */
184         CHECK_VA_STATUS_BOOL( vaCreateContext(admLibVA::getDisplay(), config_id,
185                                     frame_width_mbaligned, frame_height_mbaligned,
186                                     VA_PROGRESSIVE,
187                                     tmp_surfaceId, n,
188                                     &context_id));
189 
190         delete [] ttrib;
191         delete [] tmp_surfaceId;
192         tmp_surfaceId=NULL;
193 
194         int codedbuf_size = (frame_width_mbaligned * frame_height_mbaligned * 400) / (16*16);
195 
196         for (i = 0; i < SURFACE_NUM; i++)
197         {
198             vaEncodingBuffers[i]= ADM_vaEncodingBuffers::allocate(context_id,codedbuf_size);
199             if(!vaEncodingBuffers[i])
200             {
201                 ADM_warning("Cannot create encoding buffer %d\n",i);
202                 return false;;
203             }
204         }
205 
206         // Allocate VAImage
207 
208         for(int i=0;i<VA_ENC_NB_SURFACE;i++)
209         {
210             vaSurface[i]=ADM_vaSurface::allocateWithSurface(width,height);
211             if(!vaSurface[i])
212             {
213                 ADM_warning("Cannot allocate surface\n");
214                 return false;
215             }
216 
217             vaRefSurface[i]=ADM_vaSurface::allocateWithSurface(width,height);
218             if(!vaRefSurface[i])
219             {
220                 ADM_warning("Cannot allocate ref surface\n");
221                 return false;
222             }
223         }
224         tmpBuffer=new uint8_t[codedbuf_size];
225         render_sequence();
226         ADM_info("/vaH264 setup\n");
227         return true;
228 }
229 
230 
231 //-- Global Header
232 
generateExtraData(int * size,uint8_t ** data)233 bool ADM_vaEncodingContextH264Base::generateExtraData(int *size, uint8_t **data)
234 {
235     aprintf("vaH264 extraData\n");
236     vaBitstream sps,pps;
237 
238     fillSeqParam();
239     sps_rbsp(&sps);
240 
241     fillPPS(0,FRAME_IDR);
242     pps_rbsp(&pps);
243 
244     sps.stop();
245     pps.stop();
246 
247     int spsLen=sps.lengthInBytes();
248     int ppsLen=pps.lengthInBytes();
249 
250     *data =new uint8_t[spsLen+ppsLen+20];
251 
252     uint8_t *p=*data;
253     *p++=1;
254     *p++=sps.getPointer()[0];
255     *p++=sps.getPointer()[1];
256     *p++=sps.getPointer()[2];
257     *p++=0xff;
258     // SPS
259     *p++=0xe1;
260     *p++=(1+spsLen)>>8;
261     *p++=(1+spsLen)&0xff;
262     *p++=NAL_SPS; // SPS NALU
263     memcpy(p,sps.getPointer(),spsLen);
264     p+=spsLen;
265     // PPS
266     *p++=1;
267     *p++=(1+ppsLen)>>8;
268     *p++=(1+ppsLen)&0xff;
269     *p++=NAL_PPS;
270     memcpy(p,pps.getPointer(),ppsLen);
271     p+=ppsLen;
272     *size=(intptr_t)(p)-(intptr_t)*data;
273 
274     mixDump(*data,*size);
275 
276     aprintf("/vaH264 extraData\n");
277     return true;
278 }
279 /**
280  *
281  * @param in
282  * @param out
283  * @return
284  */
encode(ADMImage * in,ADMBitstream * out)285 bool ADM_vaEncodingContextH264Base::encode(ADMImage *in, ADMBitstream *out)
286 {
287     aprintf("Encoding frame %d, H264 AVC\n",current_frame_encoding);
288     vaFrameType current_frame_type;
289     if(!vaSurface[current_frame_encoding%SURFACE_NUM]->fromAdmImage(in))
290     {
291         ADM_warning("Failed to upload image to vaSurface\n");
292         return false;
293     }
294 
295     encoding2display_order(current_frame_encoding, vaH264Settings.IntraPeriod,    &current_frame_type);
296     aprintf("Encoding order = %d,  frame type=%d\n",(int)current_frame_encoding,current_frame_type);
297     int current_slot= (current_frame_encoding % SURFACE_NUM);
298 
299     CHECK_VA_STATUS_BOOL(vaBeginPicture(admLibVA::getDisplay(), context_id, vaSurface[current_slot]->surface));
300 
301 
302 
303     if (current_frame_type == FRAME_IDR)
304     {
305         out->flags = AVI_KEY_FRAME;
306     }else
307     {
308         out->flags = AVI_P_FRAME;
309     }
310     render_picture(current_frame_encoding,current_frame_type);
311     render_slice(current_frame_encoding,current_frame_type);
312     CHECK_VA_STATUS_BOOL( vaEndPicture(admLibVA::getDisplay(),context_id));
313     //--
314 
315     CHECK_VA_STATUS_BOOL( vaSyncSurface(admLibVA::getDisplay(), vaSurface[current_frame_encoding % SURFACE_NUM]->surface));
316 
317     #if 0 // Heavy convert
318 
319         int len=vaEncodingBuffers[current_frame_encoding % SURFACE_NUM]->read(tmpBuffer, out->bufferSize);
320         int l=ADM_unescapeH264(len-4,tmpBuffer+4,out->data+4);
321         out->data[0]=l>>24;
322         out->data[1]=l>>16;
323         out->data[2]=l>>8;
324         out->data[3]=l>>0;
325         out->len=l+4;
326 
327     #else            // light convert, no escape
328         out->len=vaEncodingBuffers[current_frame_encoding % SURFACE_NUM]->read(out->data, out->bufferSize);
329         ADM_assert(out->len>=0);
330 
331         // Set NAL Size (wtf ?)
332         int l=out->len-4;
333         out->data[0]=l>>24;
334         out->data[1]=l>>16;
335         out->data[2]=l>>8;
336         out->data[3]=l>>0;
337     #endif
338     /* reload a new frame data */
339 
340     update_ReferenceFrames(current_frame_type);
341     current_frame_encoding++;
342     out->pts=in->Pts;
343     out->dts=out->pts;
344     return true;
345 }
346 
347 // EOF
348