voip---linphone 分析

http://blog.csdn.net/dxpqxb/article/details/8556982

Linphone 源码静态分析

1,Linphone初始化工作;
    入口:linphone_core_new(...) -->linphone_core_init(core,vtable,config_path, factory_config_path, userdata);
   I,首先就是与oRTP(基于RFC3550的一个实现)协议栈相关的初始化操作:如:ortp_init();
    在这个函数里面做的工作有:
     [A]av_profile_init()即负载类型的初始化。rtp最大支持128种负载类型。这里有个概念:
     RtpProfile:

     * The RTP profile is a table RTP_PROFILE_MAX_PAYLOADS entries to make the matching
     * between RTP payload type number and the PayloadType that defines the type of
     * media.
    [B]rtp全局统计信息初始化

    typedef struct rtp_stats
    {
        uint64_t packet_sent;
        uint64_t sent;        /* bytes sent */
        uint64_t recv;         /* bytes of payload received and delivered in time to the application */
        uint64_t hw_recv;        /* bytes of payload received */
        uint64_t packet_recv;    /* number of packets received */
        uint64_t outoftime;        /* number of packets that were received too late */
        uint64_t cum_packet_loss; /* cumulative number of packet lost */
        uint64_t bad;            /* packets that did not appear to be RTP */
        uint64_t discarded;        /* incoming packets discarded because the queue exceeds its max size */
        uint64_t sent_rtcp_packets;    /* sent RTCP packets counter (only packets that embed a report block are considered) */
    } rtp_stats_t;

   接下来是Linphone所用到的一些负载类型的初始话(assign),包括音频,视频的,与number相关的及无关的两类;

  II, 其次是mediastream2的一些初始化。ms_init();
        包括日志相关的设置,ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
                ortp_set_log_handler(ms_android_log_handler);
        Filter初始化/注册:
            /* register builtin MSFilter's */
            for (i=0;ms_filter_descs[i]!=NULL;i++){
                ms_filter_register(ms_filter_descs[i]);
            }
        声卡初始化,
            cm=ms_snd_card_manager_get();
            for (i=0;ms_snd_card_descs[i]!=NULL;i++){
                ms_snd_card_manager_register_desc(cm,ms_snd_card_descs[i]);
            }
        网络摄像头的初始化,
            MSWebCamManager *wm;
            wm=ms_web_cam_manager_get();
            for (i=0;ms_web_cam_descs[i]!=NULL;i++){
                ms_web_cam_manager_register_desc(wm,ms_web_cam_descs[i]);
            }
        绘制视频图像初始化(opengl):
            libmsandroidopengldisplay_init();

    还要初始化一个mediastream2的事件队列:ms_event_queue_new();
    
    III,再次就是初始化一个很重要的结构体对象:

    struct Sal{
        SalCallbacks callbacks;
        MSList *calls; /*MSList of SalOp */
        MSList *registers;/*MSList of SalOp */
        MSList *out_subscribes;/*MSList of SalOp */
        MSList *in_subscribes;/*MSList of SalOp */
        MSList *pending_auths;/*MSList of SalOp */
        MSList *other_transactions; /*MSList of SalOp */
        int running;
        int session_expires;
        int keepalive_period;
        void *up;
        bool_t one_matching_codec;
        bool_t double_reg;
        bool_t use_rports;
        bool_t use_101;
        bool_t reuse_authorization;
        char* rootCa; /* File _or_ folder containing root CA */
    };
    这个对象很重要,在全局只有一个。是sip信号处理抽象层;初始化它的同时进行exosip的初始化:eXosip_init();
    并设置协议信号处理回调函数结构体对象:linphone_sal_callbacks,用以处理各种sip消息。

   IV,最后就是初始化一些配置信息:
    sip_setup_register_all(); //linphone_sip_login 【注册上去】。
    sound_config_read(lc);
    net_config_read(lc);
    rtp_config_read(lc);
    codecs_config_read(lc);
    sip_config_read(lc); /* this will start eXosip*/
        IP,端口,协议配置。
        linphone_core_set_sip_transports(lc,&tr);监听
        sal_root_ca();认证
        代理配置信息:lc->sip_conf.proxies
        默认代理:linphone_core_set_default_proxy_index();
        授权信息:
            /* read authentication information */
            for(i=0;; i++){
                LinphoneAuthInfo *ai=linphone_auth_info_new_from_config_file(lc->config,i);
                if (ai!=NULL){
                    linphone_core_add_auth_info(lc,ai);
                    linphone_auth_info_destroy(ai);
                }else{
                    break;
                }
            }
        其它配置信息:/*for tuning or test*/等等。。。


    }
    video_config_read(lc);

   Linphone 进入“Read” 状态。。。。。。


2,注册到服务器的过程。
    
    在linphone_core_iterate()中     proxy_update(lc); 就是注册的触发函数,依次循环每个代理配置,判断commit=true && reg_sendregistry = true;
    I,linphone_proxy_config_activate_sip_setup:激活一个sipsetup环境
    II,linphone_proxy_config_register(LinphoneProxyConfig *cfg);生成一个用于注册的SalOp,并设置其contact和
    user_pointer 如:
        sal_op_set_contact(obj->op,contact);
        ms_free(contact);
        sal_op_set_user_pointer(obj->op,obj);
    然后发出注册消息:sal_register(obj->op,obj->reg_proxy,obj->reg_identity,obj->expires),开始了注册过程...
        首次注册没有授权信息,所以会失败,收到sip消息: EXOSIP_REGISTRATION_FAILURE:
    case 401:case 407:process_authentication(sal,ev);就会提出授权要求(有sip信号处理回调函数来处理):auth_requested,并添加到:op->pending_auth=ev;
    如果其它原因的错误就需要其它的处理了。比如case 423:case 606,当用户收到授权请求时,就会判断当前的授权信息是否满足,
    LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);然后给注册操作授权:sal_op_authenticate(h,&sai);
    如果当前没有满足的授权信息,则可能需要用户输入授权信息。。。。
    
    如果注册成功:则需要首先提示授权工作了authentication_ok(sal,ev)。再确定回应里面的请求-是否需要重新注册新的contact.(register_again_with_updated_contact),
    如果需要,就重新注册,update_contact_from_response(op,last_answer);contact do not match, need to update the register ??。。。。
    不需要的话,就可以提示注册成功的消息了。至此,注册完毕;

3,一次呼叫建立的过程(重点,内容很多呀...);

    LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url) :
    LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url);
    LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *addr);
    LinphoneCall * linphone_core_invite_with_params(LinphoneCore *lc, const char *url, const LinphoneCallParams *params);
    LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const LinphoneAddress *addr, const LinphoneCallParams *params);
    四个出发函数。最终要依赖linphone_core_invite_address_with_params,中间可能多一些地址转换,呼叫参数(如是否支持视频等)的初始化工作。
    进入到呼叫函数linphone_core_invite_address_with_params后,首先判断当前是否有呼叫,以及是否达到呼叫数目的最大限。
    linphone_core_in_call(lc);
    linphone_core_can_we_add_call(lc);
    接这对默认代理,和呼叫地址中的代理进行匹配,如果不一样,则进行重写默认的代理,以呼叫地址中的代理为准。生成一个from字符串。
    如果都为空则from=linphone_core_get_primary_contact(lc);/* if no proxy or no identity defined for this proxy, default to primary contact*/
    生成URL:parsed_url2=linphone_address_new(from);创建一个新的Call:
    call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),params);里面包括一些设置:
        
        linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
        linphone_call_init_common(call,from,to);//在这里计数:refcnt=1,设置引用基数.
        call->params=*params;
        call->localdesc=create_local_media_description (lc,call);   //本地媒体类型描述
        call->camera_active=params->has_video;
        if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun)
            linphone_core_run_stun_tests(call->core,call);  //防火墙策略
        discover_mtu(lc,linphone_address_get_domain (to));
        if (params->referer){
            sal_call_set_referer (call->op,params->referer->op); //是呼叫转移吗
        }
    
    设置route:sal_op_set_route(call->op,route);添加到LinphoneCore:linphone_core_add_call(lc,call),然后lc->current_call=call;
    接下来,如果需要ping
       就可以直接进行呼叫了:linphone_core_start_invite(lc,call,dest_proxy);不然就接着进行ping操作:
    if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){
        linphone_core_start_invite(lc,call,dest_proxy);
    }else{
        /*defer the start of the call after the OPTIONS ping*/
        call->ping_op=sal_op_new(lc->sal);
        sal_ping(call->ping_op,from,real_url); ///ping操作,,,eXosip_options_build_request -->...--> eXosip_options_send_request(options);
        sal_op_set_user_pointer(call->ping_op,call);
        call->start_time=time(NULL);
    }
    
    linphone_core_iterator里面如果curtime-call->start_time>=2,则会不等ping回来,就呼叫linphone_core_start_invite(lc,call,NULL);
    
    如果需要ping操作,就需要处理Ping_op的回应,消息处理函数是:other_request_reply(sal,ev);
    -->sal->callbacks.ping_reply(op);在ping_reply里面:linphone_core_start_invite(call->core,call,NULL);
    至此,终于可以进行呼叫了。当对初始化了的call进行,真正呼叫时要什么呢?
    I,为呼叫操作设置contact,sal_op_set_contact(call->op, contact);而这里的contact是从get_fixed_contact(lc,call,dest_proxy),即dest_proxy而来的。
    II,linphone_call_init_media_streams(call);初始化音频,视频的媒体流,
        第一部分:初始化 audio_stream_new ,统计信息初始化:ms_filter_enable_statistics(TRUE);ms_filter_reset_statistics();
            创建配置session:stream->session=create_duplex_rtpsession(locport,ipv6);//RTP_SESSION_SENDRECV 模式的
            rtpr=rtp_session_new(RTP_SESSION_SENDRECV);[[[===
                    rtp_session_set_recv_buf_size(rtpr,MAX_RTP_SIZE);
                    rtp_session_set_scheduling_mode(rtpr,0);
                    rtp_session_set_blocking_mode(rtpr,0);
                    rtp_session_enable_adaptive_jitter_compensation(rtpr,TRUE);
                    rtp_session_set_symmetric_rtp(rtpr,TRUE);
                    rtp_session_set_local_addr(rtpr,ipv6 ? "::" : "0.0.0.0",locport);
                    rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)rtp_session_resync,(long)NULL);
                    rtp_session_signal_connect(rtpr,"ssrc_changed",(RtpCallback)rtp_session_resync,(long)NULL);
                    rtp_session_set_ssrc_changed_threshold(rtpr,0);
                    rtp_session_set_rtcp_report_interval(rtpr,2500); /*at the beginning of the session send more reports*/
                    disable_checksums(rtp_session_get_rtp_socket(rtpr));
            添加发送的filter:stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
            添加回声消除的filter:stream->ec=ms_filter_new_from_desc(ec_desc);
            生成并初始化,为本stream注册rtp事件队列:stream->evq=ortp_ev_queue_new();rtp_session_register_event_queue(stream->session,stream->evq);
            其它初始化:stream->play_dtmfs=TRUE;
                stream->use_gc=FALSE;
                stream->use_agc=FALSE;
                stream->use_ng=FALSE;
            ===]]]

            接着,如果支持回声限制,则根据配置信息设置一些相关参数,如:audio_stream_enable_echo_limiter(audiostream,ELControlFull);
            接着,如果支持回声消除,则根据配置信息设置一些相关参数,如:audio_stream_set_echo_canceller_params;
            接着,是否支持获取控制,以便不获取噪声,有个噪声的gateway要设置:
            int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
            audio_stream_enable_noise_gate(audiostream,enabled);

            在就是为session设置if (lc->a_rtp) rtp_session_set_transports(audiostream->session,lc->a_rtp,lc->a_rtcp);
            给Call也注册一个ort事件队列:call->audiostream_app_evq = ortp_ev_queue_new();
                        rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq);
        第二部分:如果支持视频,则需要call->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc));//初始化视频流
            具体:VideoStream *stream = (VideoStream *)ms_new0 (VideoStream, 1);
                stream->session=create_duplex_rtpsession(locport,use_ipv6);
                stream->evq=ortp_ev_queue_new();
                stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
                rtp_session_register_event_queue(stream->session,stream->evq);
                stream->sent_vsize.width=MS_VIDEO_SIZE_CIF_W;
                stream->sent_vsize.height=MS_VIDEO_SIZE_CIF_H;
                stream->dir=VideoStreamSendRecv;
                choose_display_name(stream);
              
            接下来:设置display_filter_name,设置video_stream_set_event_callback,设置rtp_session_set_transports,
            注册rtp事件队列:rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
            
    III,sal_call_set_local_media_description ,设置本地媒体格式描述。
    解析地址from ,url,等,然后开始真正呼叫。。err=sal_call(call->op,from,real_url);具体展开。。。:
        
            int sal_call(SalOp *h, const char *from, const char *to){
            int err;
            osip_message_t *invite=NULL;
            sal_op_set_from(h,from);
            sal_op_set_to(h,to);
            sal_exosip_fix_route(h);
            err=eXosip_call_build_initial_invite(&invite,to,from,sal_op_get_route(h),"Phone call");
            if (err!=0){
                ms_error("Could not create call.");
                return -1;
            }
            osip_message_set_allow(invite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
            if (h->base.contact){
                _osip_list_set_empty(&invite->contacts,(void (*)(void*))osip_contact_free);
                osip_message_set_contact(invite,h->base.contact);
            }
            if (h->base.root->session_expires!=0){
                osip_message_set_header(invite, "Session-expires", "200");
                osip_message_set_supported(invite, "timer");
            }
            if (h->base.local_media){
                h->sdp_offering=TRUE;
                set_sdp_from_desc(invite,h->base.local_media);
            }else h->sdp_offering=FALSE;
            if (h->replaces){
                osip_message_set_header(invite,"Replaces",h->replaces);
                if (h->referred_by)
                    osip_message_set_header(invite,"Referred-By",h->referred_by);
            }
    
            eXosip_lock();
            err=eXosip_call_send_initial_invite(invite);
            eXosip_unlock();
            h->cid=err;
            if (err<0){
                ms_error("Fail to send invite !");
                return -1;
            }else{
                sal_add_call(h->base.root,h);//把操作添加到sal中....
            }
            return 0;
    }

最后设置状态:Contacting。。。

        barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
        if (lc->vtable.display_status!=NULL)
            lc->vtable.display_status(lc,barmsg);

如此,便开始了等待代理服务器返回消息的状态了。。。。

接下来分析,当接受到对放来的call-request的时候,怎么处理。。。。

        case EXOSIP_CALL_INVITE:表示收到了一个呼叫的消息。执行:inc_new_call(Sal *sal, eXosip_event_t *ev);
    首先为这个消息生成一个SalOp操作。得到sdp信息:eXosip_get_sdp_info(ev->request);接着从request里面获取一些参数如:origin
ua,replaces,from ,to ,sdp,call_info,tid,cid, did,等等,然后进入sip消息回调里面的call_received回调进行处理,进入这个函数后就是
和主动呼叫(outgoingcall)的模式很相像了,判断收处于呼叫状态,是否达到最大呼叫数目linphone_core_can_we_add_call,有传入的salop得到from
和to,进而判断是否是重复呼叫is_duplicate_call?如果都满足上述条件,就可以惊醒呼叫的创建了:call=linphone_call_new_incoming(lc,from_addr,to_addr,h);
里面包括判决是否发ping指令,本地medaidesc,linphone_call_init_common,create_local_media_description,以及
linphone_core_get_firewall_policy等等。。。,随后开始进行sdp,媒体类型协商:sal_call_get_final_media_description-->sdp_process,
最后添加call,linphone_core_add_call,提示电话来了的消息: lc->vtable.display_status(lc,barmesg); //XX is contacting you?

与此同时,开始ring_start(),播放ringback(/* play the ring if this is the only call*/),
同时发180消息sal_call_notify_ringing(h,propose_early_media || ringback_tone!=NULL);如果自动应答模式,还需接受call.linphone_core_accept_call(lc,call);
具体如下:
【【【---
    sal_call_notify_ringing(h,propose_early_media || ringback_tone!=NULL);//180消息

    if (propose_early_media || ringback_tone!=NULL){
        linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media");
        linphone_core_update_streams(lc,call,md);
    }
    if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){
        linphone_core_accept_call(lc,call);
    }
---】】】



在换回来说吧,当主叫一方收到180Ring的消息后(case EXOSIP_CALL_RINGING),进入到函数:call_ringing(Sal *sal, eXosip_event_t *ev)
首先:call_proceeding(Sal *sal, eXosip_event_t *ev) ---> 
    /* update contact if received and rport are set by the server
     note: will only be used by remote for next INVITE, if any...*/
    update_contact_from_response(op,ev->response);
然后得到sdp:sdp=eXosip_get_sdp_info(ev->response);生成本地的mediadesc,然后协商出一个result md,if (op->base.local_media) sdp_process(op);
回调到sip消息回调处理函数里面sal->callbacks.call_ringing(op);,通知界面,开始early medai,ringing了。。。linphone_core_update_streams,即update一下。

又回去,当被叫决定答应呼叫是,他会调用int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call);
里面会做:I,/* check if this call is supposed to replace an already running one*/replaced=sal_call_get_replaces(call->op);
II,/*try to be best-effort in giving real local or routable contact address*/ --->sal_op_set_contact(call->op,contact);
III,/*stop ringing */-->ring_stop(lc->ringstream);
IV,if (call->audiostream==NULL)
        linphone_call_init_media_streams(call); //这个之前已经做过了,这里只是检查做没做,谨慎期间。

IIV,sal_call_accept(call->op);//发送200OK消息
IIIV,new_md=sal_call_get_final_media_description(call->op);
    linphone_core_update_streams(lc, call, new_md);      //更新媒体流。。。
VI,通知:ms_message("call answered.");


4,
到这里,一个呼叫的整个常规流程也就完事儿了。而整个linphone的主要功能框架也基本完成。但中间一些细节,如如何添加每个节点的filter,如何维持更新媒体流,借助与mediastream2,怎
么实现流媒体的过程,还需进一步分析。(2011-11-10 宣继托)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先接到这一个项目,说是要用mediastreamer2做一个网络电话。之前也是从来没有接触过。于是首先开始在百度中搜索一下需要哪些东西,以及那些步骤。最后大致了解了一下,做这个项目最终要的就是需要移植好多的库,每一个库都需要配置,最后在交叉编译好动态库,然后在执行mediastreamer2的时候去调用这些动态库和头文件就OK了。 1、首先meidastream2是基于ortp库的,那么首先就是下载源码,交叉编译。 交叉编译ortp 下载源码:http://savannah.c3sl.ufpr.br/linphone/ortp/sources/?C=S;O=A 我使用0.18.0版本 ortp-0.18.0.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf ortp-0.18.0.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure CC=arm-linux-gcc --host=arm-linux --target=arm-linux --prefix=/home/protocol_stack/install/ make make install 然后用chmod 777 **.sh 执行脚本./**.sh 这样子就完成了配置,编译,安装。(安装目录为/home/protocol_stack/install/,也就是最后生成的头文件,可执行文件,库文件都会在这个目录下) 2、因为项目是要用到SIP协议的,所以我们还需要移植sip的库 osip2和eXosip2协议,这两个协议对应两个库,osip是简单的osip协议,但是因为API少等一系列原因,增加了eXosip2对osip2的补充。 交叉编译osip2 下载源码:http://ftp.gnu.org/gnu/osip/ 我使用的版本是3.6.0 libosip2-3.6.0.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf libosip2-3.6.0.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure --host=arm-linux --target=arm-linux --prefix=/home/protocol_stack/install/ make make install 然后用chmod 777 **.sh 执行脚本./**.sh 交叉编译eXosip2 下载源码:http://ftp.gnu.org/gnu/osip/ 我使用的版本是3.6.0 libeXosip2-3.6.0.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf libeXosip2-3.6.0.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure --host=arm-linux --target=arm-linux --prefix=/home/protocol_stack/install/ PKG_CONFIG_PATH=/home/protocol_stack/install/lib/pkgconfig make make install 然后用chmod 777 **.sh 执行脚本./**.sh 接下来可以编译mediastreamer2了,不过ms2,依赖好多库:ogg、speex、pulseaudio。而pulseaudio又依赖许多库:alsa、json、libtool。 3、交叉编译ogg 下载源码:http://xiph.org/downloads/ 我使用1.3.1版本 libogg-1.3.3.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf libogg-1.3.3.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure CC=arm-linux-gcc --prefix=/home/protocol_stack/install/ --host=arm-linux make make install 然后用chmod 777 **.sh 执行脚本./**.sh 4、交叉编译speex 下载源码:http://www.speex.org/downloads/ 我使用1.2rc1版本 speex-1.2rc1.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf speex-1.2rc1.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure CC=arm-linux-gcc --prefix=/home/protocol_stack/install/ --with-ogg=/home/protocol_stack/install/ --enable-fixed-point --disable-float-api \ --host=arm-linux make make install 然后用chmod 777 **.sh 执行脚本./**.sh 5、交叉编译pulseaudio 下载源码:http://freedesktop.org/software/pulseaudio/releases/ 我使用1.0版本 pulseaudio-1.0.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf pulseaudio-1.0.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure CC=arm-linux-gcc CXX=arm-linux-g++ --prefix=/home/protocol_stack/install --host=arm-linux --disable-rpath --disable-nls --disable-dbus --disable-bluez --disable-samplerate --disable-solaris --disable-gconf --disable-avahi --disable-jack --disable-lirc --disable-glib2 --disable-gtk2 --disable-openssl --disable-ipv6 --disable-asyncns --disable-per-user-esound-socket --disable-oss-output --disable-oss-wrapper --disable-x11 --enable-neon-opt=no --with-database=simple PKG_CONFIG_PATH=/home/protocol_stack/install/lib/pkgconfig CPPFLAGS=-I/home/protocol_stack/install/include LDFLAGS=-L/home/protocol_stack/install/lib CFLAGS=-I/home/protocol_stack/install/include make make install 然后用chmod 777 **.sh 执行脚本./**.sh 错误1: checking for ltdl.h... no configure: error: Unable to find libltdl version 2. Makes sure you have libtool 2.4 or later installed. make: *** No targets specified and no makefile found. Stop. 分析;找不到libltdl。确保你有libtool 2.4及以上的版本。 下载libtool 2.4.2版本 这时需要交叉编译libtool 下载源码:ftp://ftp.gnu.org/gnu/libtool/ 我使用2.4.2版本 libtool-2.4.2.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf libtool-2.4.2.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure --host=arm-linux --prefix =/home/protocol_stack/install/ make make install 然后用chmod 777 **.sh 执行脚本./**.sh 交叉编译alsa: http://www.alsa-project.org/main/index.php/Main_Page 这个库的版本需要根据你嵌入式Linux内核中alsa的版本而定,可以使用命令查看内核中alsa的版本: # cat /proc/asound/version Advanced Linux Sound Architecture Driver Version 1.0.24. 可以到内核中alsa驱动版本是1.0.24,所以我选1.0.24版本 alsa-lib-1.0.24.1.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf speex-1.2rc1.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure --host=arm-linux --prefix =/home/protocol_stack/install/ make make install 然后用chmod 777 **.sh 执行脚本./**.sh 错误:configure: error: Package requirements ( sndfile >= 1.0.20 ) were not met: No package 'sndfile' found 分析:缺少库 libsndfile库,那么接下来再进行交叉编译libsndfile libsndfile-1.0.25.tar.gz http://www.linuxfromscratch.org/blfs/view/svn/multimedia/libsndfile.html 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf libsndfile-1.0.25.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure --host=arm-linux --prefix =/home/protocol_stack/install/ make make install 然后用chmod 777 **.sh 执行脚本./**.sh 7、最后编译mediastreamer2 下载源码:http://ftp.twaren.net/Unix/NonGNU//linphone/mediastreamer/ 我使用2.8版本 mediastreamer-2.8.0.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf mediastreamer-2.8.0.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 把下边这三行写成一个脚本 vim **.sh ./configure CC=arm-linux-gcc --prefix=/home/protocol_stack/install/ PKG_CONFIG_PATH=/home/protocol_stack/install/lib/pkgconfig --disable-gsm --enable-video=no --enable-macsnd=no --disable-static --disable-sdl --disable-x11 --disable-ffmpeg --host=arm-linux --target=arm-linux make make install 然后用chmod 777 **.sh 执行脚本./**.sh 上面的configure选项没有屏蔽v4l1和v4l2,所以还得交叉编译v4l 编译v4l libv4l-0.6.4.tar.gz 下载源码:http://pkgs.fedoraproject.org/repo/pkgs/libv4l/ 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf libv4l-0.6.4.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 我使用0.6.4版本 libv4l-0.6.4.tar.gz make clean make CC=arm-linux-gcc make install PREFIX=/home/protocol_stack/install 编译mediastreamer2出错:(1)checking for LIBCHECK... no checking for LIBJSON... no configure: error: Package requirements ( json >= 0.9 ) were not met: No package 'json' found 解决方法就是交叉编译json 下载源码:http://ftp.debian.org/debian/pool/main/j/json-c/ 分析:缺少json库,那么我们继续交叉编译json库 json-c_0.12.1.orig.tar.gz 然后通过winshare(Windows和Linux的通信)把下载好的库文件拷贝到Linux下, 然后解压 tar zxvf mediastreamer-2.8.0.tar.gz 注意这个时候可能会发生错误,是没有权限的问题,那么就在命令行前边加上sudo 然后配置 ./configure --host=linux-arm \ --prefix =/home/protocol_stack/install/ make && make install 好了,json库已经编译完成了。接下来我们继续编译mediastreamer2 。。。。。 但是还是有问题,怎么办呢?还是哪个问题还是找不到json库。 分析:在json的论坛中,找到了解决方案:把编译生成的/lib/pkgconfig/这个目录下生成了一个json-c.pc。最后mediastreamer2在调用的时候找的是json.pc。那么我们就把这个文件名改为json.pc #mv json-c.pc json.pc OK,这次这个是可以编译的过去了。接下来继续编译 。。。 又出现问题了 /home/protocol_stack/install/lib/libjson.so: undefined reference to `rpl_malloc' /home/protocol_stack/install/lib/libjson.so: undefined reference to `rpl_realloc' 问题分析: 这个错误的原因是因为没有定义 rpl_malloc 和 rpl_realloc 这两个变量。 那么我们应该怎么办么? 那么就在这个目录下进行查这两个变量是在哪里定义的? 于是:#grep "rpl_malloc" -nR * ....... 找到了,原来这两个变量是在这个config的文件中的。是一个宏开关 那么就好办了,我们就直接把这两个宏进行注释。 嗯嗯,继续。。。我们重新编译json库。。。嗯嗯编译好了,接下来继续来编译mediastreamer2 。。。。 又出错了,还是这个原因 /home/protocol_stack/install/lib/libjson.so: undefined reference to `rpl_malloc' /home/protocol_stack/install/lib/libjson.so: undefined reference to `rpl_realloc 嗯嗯,还是这个原因?究竟是为什么呢。再次来到json的目录下,再次看有没有把那两个宏开关给关闭? 嗯哼? 竟然没有关闭? 分析?明白了。原来是我把配置和编译同时执行了。这个宏开关是./configure ...生成的。 那么就只好,这样。把./configure。。。生成的config文件,再进行关闭宏开关。最后直接make && make install -j8 直接编译,安装,是不能再次进行配置的。因为以配置config文件就会再次生成,那么宏开关就又开了。 OK,安装好了,下来继续进行编译mediastreamer2.。。。。。。。。。。。 。。。。。。。。。。。。。。 又出现了问题? error: /user/include/python2.7/pyconfig.h:15:52: fatal error: arm-linux-gnueabi/python2.7/pyconfig.h: No such file or directory compilation terminated. 分析::找不到arm-linux-gnueabi/python2.7/pyconfig.h这文件。那就继续交叉编译python 好吧,继续下载python,然后再进行交叉编译,但是编译Python的时候出来一系列的问题。根本没有办法解决。 那么该怎么办呢?时候一个小时又一个小时的过去? 最后有一个大胆的想法,既然python都编译不下去。那就不要了。 于是,在mediastreamer2的./configure 中添上一项 --without-python 。 。。。再次配置编译。。。。。。。。。。。 error: /user/include/python2.7/pyconfig.h:15:52: fatal error: arm-linux-gnueabi/python2.7/pyconfig.h: No such file or directory compilation termiated. 嗯哼?还是一样的错误。怎么办呢? 于是乎就又在论坛上进行找灵感。。。。。 还是找不到。。。 又一结合前边几个库的配置编译,发现不使能一个模块还可以用另外一个--disable-python 。。。 于是乎 就把--without-python改为了--disable-python 继续编译。。。。 。。。。。。。。。。。。。。。。。。。。。 到了这个节骨眼上了,编译每跳一下,我的心就跟着逗一下。。。。心酸 。。。。。。 。。。。。。 。。。。。。 竟然编译成功了。。。。 哈哈。。。。。。。。。 于是,马上就把编译好的库,拷贝到了开发板。。。 嗯嗯,本来还想把编译好的库目录树拷贝下的,但是太多了,放不下。。。算了吧。。。。 找到编译好的库 在库中的/bin中找到arm-linux-mediastream 然后执行./arm-linux-mediastream 。。。。报错了 问题: error : while loading shared libraries: libmediastreamer.so.1: cannot open shared object file: No such file 答案:分析: 遇到这个问题就是,libmediastreamer.so.1这个动态库,在可执行文件armlinuxmediastreamer执行的时候,会调用这个动态库,但是环境变量中找不到这个动态库。那么我们就是要把我们编译好的动态链接库的目录加到环境变量中 LD_LIBRARY_PATH=$LD_LIBRARY_PATH://arm/lib/这个目录下就是放着我们编译好的所有的动态链接库(包括libmediastreamer.so.1) 执行步骤:LD_LIBRARY_PATH=$LD_LIBRARY_PATH://arm/lib export LD_LIBRARY_PATH ./arm-linux-mediastream mediastream --local --remote --payload [ --fmtp ] [ --jitter ] [ --width ] [ --height ] [ --bitrate ] [ --ec (enable echo canceller) ] [ --ec-tail ] [ --ec-delay ] [ --ec-framesize ] [ --agc (enable automatic gain control) ] [ --ng (enable noise gate)] [ --ng-threshold (noise gate threshold) ] [ --ng-floorgain (gain applied to the signal when its energy is below the threshold.) ] [ --capture-card ] [ --playback-card ] [ --infile <input wav file> specify a wav file to be used for input, instead of soundcard ] [ --outfile specify a wav file to write audio into, instead of soundcard ] [ --camera ] [ --el (enable echo limiter) ] [ --el-speed (gain changes are smoothed with a coefficent) ] [ --el-thres (Threshold above which the system becomes active) ] [ --el-force (The proportional coefficient controlling the mic attenuation) ] [ --el-sustain (Time in milliseconds for which the attenuation is kept unchanged after) ] [ --el-transmit-thres (TO BE DOCUMENTED) ] [ --rc (enable adaptive rate control) ] [ --zrtp (enable zrtp) ] [ --verbose (most verbose messages) ] [ --video-windows-id <video surface:preview surface>] [ --srtp (enable srtp, master key is generated if absent from comand line) [ --netsim-bandwidth (simulates a network download bandwidth limit) 于是按照第一种方式进行 参数添加 ./arm-linux-mediastream --local 8888 --remote 127.0.0.1:88 88 OK运行正常了 下面是运行信息。。。 ortp-message-audio_stream_process_rtcp: interarrival jitter=119 , lost packets percentage since last report=0.000000, round trip time=0.000000 seconds ortp-message-oRTP-stats: RTP stats : ortp-message- number of rtp packet sent=150 ortp-message- number of rtp bytes sent=25800 bytes ortp-message- number of rtp packet received=150 ortp-message- number of rtp bytes received=25800 bytes ortp-message- number of incoming rtp bytes successfully delivered to the application=25284 ortp-message- number of rtp packet lost=0 ortp-message- number of rtp packets received too late=0 ortp-message- number of bad formatted rtp packets=0 ortp-message- number of packet discarded because of queue overflow=0 ortp-message-Bandwidth usage: download=81.290281 kbits/sec, upload=81.288664 kbits/sec ortp-message-Receiving RTCP SR ortp-message-Receiving RTCP SDES ortp-message-Found CNAME=unknown@unknown ortp-message-Found TOOL=oRTP-0.18.0 ortp-message-Found NOTE=This is free sofware (LGPL) ! ortp-message-Quality indicator : 4.888437 运行正常了。。。。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值