source: mediastreamer2/plugins/msx264/src/msx264.c @ 1415:cb57f4622319

Last change on this file since 1415:cb57f4622319 was 1415:cb57f4622319, checked in by laurent <laurent@…>, 21 months ago

update msx264

File size: 11.1 KB
Line 
1/*
2 mediastreamer2 x264 plugin
3 Copyright (C) 2006-2010 Belledonne Communications SARL (simon.morlat@linphone.org)
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20#include "mediastreamer2/msfilter.h"
21#include "mediastreamer2/msticker.h"
22#include "mediastreamer2/msvideo.h"
23#include "mediastreamer2/rfc3984.h"
24
25#ifdef _MSC_VER
26#include <stdint.h>
27#endif
28
29#include <x264.h>
30
31#ifndef VERSION
32#define VERSION "1.4.1"
33#endif
34
35
36#define RC_MARGIN 10000 /*bits per sec*/
37
38/*
39 * WARNING!
40 *
41 * The following definition is aimed to build a forced high-resolution
42 * profile with fast encoding parameters for a single-core Android smartphone
43 * in order to obtain a reasonable framerate.
44 *
45 * You SHOULD NOT use it unless you know what you are doing.
46 *
47 * Maximal observed framerates: 12 frames/s with CIF, 3 frames/s with VGA,
48 * on a Samsung GalaxyS I9000.
49 *
50 * Since this is a rude, hard-coded modification, it MAY induce unstable
51 * behaviours: caution is advised.
52 *
53 * To enable this special build, uncomment the following definition of the
54 * SPECIAL_HIGHRES_BUILD pre-processor variable and set it to a
55 * MediaStreamer2-compliant video size (e.g. MS_VIDEO_SIZE_CIF).
56 *
57 * You MUST keep the definition of the SPECIAL_HIGHRES_BUILD_CRF variable.
58 * You MAY change its value which SHOULD remain between 22 and 28.
59 *
60 *
61 * In order to ensure JNI compatibility:
62 * You MUST have the org.linphone.BandwithManager.currentProfile set to
63 * HIGH_RESOLUTION in the constructor.
64 * You MUST have the HIGH_RESOLUTION case of
65 * org.linphone.BandwithManager.maximumVideoSize(int, boolean) returning the
66 * MediaStreamer2-compliant video size you want; you MAY need to define the
67 * size in org.linphone.core.VideoSize if it does not exist.
68 * You SHOULD have org.linphone.BandwithManager.bandwidthes[0] set to
69 * {1024, 1024} to tell Linphone to use maximal bandwidth.
70 */
71#define SPECIAL_HIGHRES_BUILD MS_VIDEO_SIZE_QVGA
72#define SPECIAL_HIGHRES_BUILD_CRF 28
73
74/* the goal of this small object is to tell when to send I frames at startup:
75 at 2 and 4 seconds*/
76typedef struct VideoStarter{
77        uint64_t next_time;
78        int i_frame_count;
79}VideoStarter;
80
81static void video_starter_init(VideoStarter *vs){
82        vs->next_time=0;
83        vs->i_frame_count=0;
84}
85
86static void video_starter_first_frame(VideoStarter *vs, uint64_t curtime){
87        vs->next_time=curtime+2000;
88}
89
90static bool_t video_starter_need_i_frame(VideoStarter *vs, uint64_t curtime){
91        if (vs->next_time==0) return FALSE;
92        if (curtime>=vs->next_time){
93                vs->i_frame_count++;
94                if (vs->i_frame_count==1){
95                        vs->next_time+=2000;
96                }else{
97                        vs->next_time=0;
98                }
99                return TRUE;
100        }
101        return FALSE;
102}
103
104typedef struct _EncData{
105        x264_t *enc;
106        MSVideoSize vsize;
107        int bitrate;
108        float fps;
109        int mode;
110        uint64_t framenum;
111        Rfc3984Context *packer;
112        int keyframe_int;
113        VideoStarter starter;
114        bool_t generate_keyframe;
115}EncData;
116
117
118static void enc_init(MSFilter *f){
119        EncData *d=ms_new(EncData,1);
120        d->enc=NULL;
121    MS_VIDEO_SIZE_ASSIGN(d->vsize,CIF);
122        d->bitrate=384000;
123        d->fps=30;
124        d->keyframe_int=10; /*10 seconds */
125        d->mode=0;
126        d->framenum=0;
127        d->generate_keyframe=FALSE;
128        d->packer=NULL;
129        f->data=d;
130}
131
132static void enc_uninit(MSFilter *f){
133        EncData *d=(EncData*)f->data;
134        ms_free(d);
135}
136
137static void enc_preprocess(MSFilter *f){
138        EncData *d=(EncData*)f->data;
139        x264_param_t params;
140        float bitrate;
141       
142        d->packer=rfc3984_new();
143        rfc3984_set_mode(d->packer,d->mode);
144        rfc3984_enable_stap_a(d->packer,FALSE);
145       
146        x264_param_default(&params);
147   
148        params.i_threads=X264_THREADS_AUTO;
149        params.i_sync_lookahead=0;
150        params.i_width=d->vsize.width;
151        params.i_height=d->vsize.height;
152        params.i_fps_num=(int)d->fps;
153        params.i_fps_den=1;
154        params.i_slice_max_size=ms_get_payload_max_size()-100; //-100 security margin
155        params.i_keyint_max=250;
156        params.i_keyint_min=25;
157        params.i_scenecut_threshold=40;
158        params.b_deblocking_filter=1;
159        params.i_bframe_adaptive=X264_B_ADAPT_FAST;
160        params.analyse.intra=X264_ANALYSE_I4x4 & X264_ANALYSE_I8x8 & X264_ANALYSE_PSUB8x8;
161        params.analyse.inter=X264_ANALYSE_I4x4 & X264_ANALYSE_I8x8 & X264_ANALYSE_PSUB8x8;
162        params.analyse.b_transform_8x8=1;
163        params.analyse.b_fast_pskip=1;
164        params.analyse.i_me_method=X264_ME_HEX;
165        params.analyse.i_me_range=16;
166        params.analyse.i_direct_mv_pred=1;
167        params.analyse.b_chroma_me=1;
168   
169        bitrate=(float)d->bitrate*0.92;
170        if (bitrate>RC_MARGIN)
171                bitrate-=RC_MARGIN;
172   
173        params.rc.i_rc_method = X264_RC_CRF;
174        params.rc.f_rf_constant=SPECIAL_HIGHRES_BUILD_CRF;
175        params.rc.i_qp_min=10;
176        params.rc.i_qp_max=51;
177        params.rc.i_qp_step=10;
178        params.rc.f_qcompress=0.6;
179   
180        params.rc.i_lookahead=0;
181        /*enable this by config ?*/
182        /*
183     params.i_keyint_max = (int)d->fps*d->keyframe_int;
184     params.i_keyint_min = (int)d->fps;
185     */
186        params.b_repeat_headers=1;
187        params.b_annexb=0;
188   
189        //these parameters must be set so that our stream is baseline
190        params.analyse.b_transform_8x8 = 0;
191        params.b_cabac = 0;
192        params.i_cqm_preset = X264_CQM_FLAT; // X264_CQM_JVT; ?
193        params.i_bframe = 0;
194        params.analyse.i_weighted_pred = X264_WEIGHTP_NONE;
195   
196    // tune --no-latency
197    params.i_bframe = 0;
198       
199        d->enc=x264_encoder_open(&params);
200        if (d->enc==NULL) ms_error("Fail to create x264 encoder.");
201        d->framenum=0;
202        video_starter_init(&d->starter);
203}
204
205static void x264_nals_to_msgb(x264_nal_t *xnals, int num_nals, MSQueue * nalus){
206        int i;
207        mblk_t *m;
208        /*int bytes;*/
209        for (i=0;i<num_nals;++i){
210                m=allocb(xnals[i].i_payload+10,0);
211               
212                memcpy(m->b_wptr,xnals[i].p_payload+4,xnals[i].i_payload-4);
213                m->b_wptr+=xnals[i].i_payload-4;
214                if (xnals[i].i_type==7) {
215                        ms_message("A SPS is being sent.");
216                }else if (xnals[i].i_type==8) {
217                        ms_message("A PPS is being sent.");
218                }
219                ms_queue_put(nalus,m);
220        }
221}
222
223static void enc_process(MSFilter *f){
224        EncData *d=(EncData*)f->data;
225        uint32_t ts=f->ticker->time*90LL;
226        mblk_t *im;
227        MSPicture pic;
228        MSQueue nalus;
229        ms_queue_init(&nalus);
230        while((im=ms_queue_get(f->inputs[0]))!=NULL){
231                if (ms_yuv_buf_init_from_mblk(&pic,im)==0){
232                        x264_picture_t xpic;
233                        x264_picture_t oxpic;
234                        x264_nal_t *xnals=NULL;
235                        int num_nals=0;
236           
237                        memset(&xpic, 0, sizeof(xpic));
238                        memset(&oxpic, 0, sizeof(oxpic));
239           
240                        /*send I frame 2 seconds and 4 seconds after the beginning */
241                        if (video_starter_need_i_frame(&d->starter,f->ticker->time))
242                                d->generate_keyframe=TRUE;
243           
244                        if (d->generate_keyframe){
245                                xpic.i_type=X264_TYPE_IDR;
246                                d->generate_keyframe=FALSE;
247                        }else xpic.i_type=X264_TYPE_AUTO;
248                        xpic.i_qpplus1=0;
249                        xpic.i_pts=d->framenum;
250                        xpic.param=NULL;
251                        xpic.img.i_csp=X264_CSP_I420;
252                        xpic.img.i_plane=3;
253                        xpic.img.i_stride[0]=pic.strides[0];
254                        xpic.img.i_stride[1]=pic.strides[1];
255                        xpic.img.i_stride[2]=pic.strides[2];
256                        xpic.img.i_stride[3]=0;
257                        xpic.img.plane[0]=pic.planes[0];
258                        xpic.img.plane[1]=pic.planes[1];
259                        xpic.img.plane[2]=pic.planes[2];
260                        xpic.img.plane[3]=0;
261           
262                        if (x264_encoder_encode(d->enc,&xnals,&num_nals,&xpic,&oxpic)>=0){
263                                x264_nals_to_msgb(xnals,num_nals,&nalus);
264                if (num_nals == 0)
265                    ms_message("Delayed frames info: current=%d max=%d\n", 
266                               x264_encoder_delayed_frames(d->enc),
267                               x264_encoder_maximum_delayed_frames(d->enc));
268                                rfc3984_pack(d->packer,&nalus,f->outputs[0],ts);
269                                d->framenum++;
270                                if (d->framenum==0)
271                                        video_starter_first_frame(&d->starter,f->ticker->time);
272                        }else{
273                                ms_error("x264_encoder_encode() error.");
274                        }
275                }
276                freemsg(im);
277        }
278}
279
280static void enc_postprocess(MSFilter *f){
281        EncData *d=(EncData*)f->data;
282        rfc3984_destroy(d->packer);
283        d->packer=NULL;
284        if (d->enc!=NULL){
285                x264_encoder_close(d->enc);
286                d->enc=NULL;
287        }
288}
289
290static int enc_set_br(MSFilter *f, void *arg){
291        EncData *d=(EncData*)f->data;
292        d->bitrate=*(int*)arg;
293   
294#ifndef ANDROID | defined(TARGET_OS_IPHONE)
295        if (d->bitrate>=1024000){
296                MS_VIDEO_SIZE_ASSIGN(d->vsize,VGA);
297                d->fps=25;
298        }else if (d->bitrate>=512000){
299                MS_VIDEO_SIZE_ASSIGN(d->vsize,VGA);
300                d->fps=25;
301        }else if (d->bitrate>=384000){
302                MS_VIDEO_SIZE_ASSIGN(d->vsize,CIF);
303                d->fps=25;
304        }else if (d->bitrate>=256000){
305                MS_VIDEO_SIZE_ASSIGN(d->vsize,CIF);
306                d->fps=15;
307        }else if (d->bitrate>=128000){
308                MS_VIDEO_SIZE_ASSIGN(d->vsize,CIF);
309                d->fps=15;
310        }else if (d->bitrate>=64000){
311                MS_VIDEO_SIZE_ASSIGN(d->vsize,CIF);
312                d->fps=10;
313        }else if (d->bitrate>=32000){
314                MS_VIDEO_SIZE_ASSIGN(d->vsize,QCIF);
315                d->fps=10;
316        }else{
317                MS_VIDEO_SIZE_ASSIGN(d->vsize,QCIF);
318                d->fps=5;
319        }
320#endif
321   
322#ifdef ANDROID
323        /* we have to limit size and fps on android due to limited CPU */
324        d->vsize=MS_VIDEO_SIZE_QCIF;
325        if (d->fps>7) d->fps=7;
326#endif
327   
328        ms_message("bitrate set to %i",d->bitrate);
329        return 0;
330}
331
332static int enc_set_fps(MSFilter *f, void *arg){
333        EncData *d=(EncData*)f->data;
334        d->fps=*(float*)arg;
335        return 0;
336}
337
338static int enc_get_fps(MSFilter *f, void *arg){
339        EncData *d=(EncData*)f->data;
340        *(float*)arg=d->fps;
341        return 0;
342}
343
344static int enc_get_vsize(MSFilter *f, void *arg){
345        EncData *d=(EncData*)f->data;
346        *(MSVideoSize*)arg=d->vsize;
347        return 0;
348}
349
350static int enc_set_vsize(MSFilter *f, void *arg){
351        EncData *d=(EncData*)f->data;
352   
353        d->vsize=*(MSVideoSize*)arg;
354   
355        return 0;
356}
357
358static int enc_add_fmtp(MSFilter *f, void *arg){
359        EncData *d=(EncData*)f->data;
360        const char *fmtp=(const char *)arg;
361        char value[12];
362        if (fmtp_get_value(fmtp,"packetization-mode",value,sizeof(value))){
363                d->mode=atoi(value);
364                ms_message("packetization-mode set to %i",d->mode);
365        }
366        return 0;
367}
368
369static int enc_req_vfu(MSFilter *f, void *arg){
370        EncData *d=(EncData*)f->data;
371        d->generate_keyframe=TRUE;
372        return 0;
373}
374
375
376static MSFilterMethod enc_methods[]={
377        {       MS_FILTER_SET_FPS       ,       enc_set_fps     },
378        {       MS_FILTER_SET_BITRATE   ,       enc_set_br      },
379        {       MS_FILTER_GET_FPS       ,       enc_get_fps     },
380        {       MS_FILTER_GET_VIDEO_SIZE,       enc_get_vsize   },
381        {       MS_FILTER_SET_VIDEO_SIZE,       enc_set_vsize   },
382        {       MS_FILTER_ADD_FMTP      ,       enc_add_fmtp    },
383        {       MS_FILTER_REQ_VFU       ,       enc_req_vfu     },
384        {       0       ,                       NULL            }
385};
386
387#ifndef _MSC_VER
388
389static MSFilterDesc x264_enc_desc={
390        .id=MS_FILTER_PLUGIN_ID,
391        .name="MSX264Enc",
392        .text="A H264 encoder based on x264 project",
393        .category=MS_FILTER_ENCODER,
394        .enc_fmt="H264",
395        .ninputs=1,
396        .noutputs=1,
397        .init=enc_init,
398        .preprocess=enc_preprocess,
399        .process=enc_process,
400        .postprocess=enc_postprocess,
401        .uninit=enc_uninit,
402        .methods=enc_methods
403};
404
405#else
406
407static MSFilterDesc x264_enc_desc={
408        MS_FILTER_PLUGIN_ID,
409        "MSX264Enc",
410        "A H264 encoder based on x264 project",
411        MS_FILTER_ENCODER,
412        "H264",
413        1,
414        1,
415        enc_init,
416        enc_preprocess,
417        enc_process,
418        enc_postprocess,
419        enc_uninit,
420        enc_methods
421};
422
423#endif
424
425MS2_PUBLIC void libmsx264_init(void){
426        ms_filter_register(&x264_enc_desc);
427        ms_message("ms264-" VERSION " plugin registered.");
428}
429
Note: See TracBrowser for help on using the repository browser.