精华内容
下载资源
问答
  • UIA Windows事件解析器 将Windows事件解析为键/值对(Map)或POJO。 支持语言环境 zh_CN-英文 zh_TW-繁体中文(繁体中文) 支持的事件(当前:60) ID 描述 标签 * 900 -- -- * 902 -- -- * 903 -- -- *...
  • 我收集的servlet中事件、监听器机制、web.xml配置详解
  • 事件门户网站示例: : 如何安装? 链接: : 使用heroku deploy测试一个简单的博客项目: 路线图: 推动we-core,we和generator-wejs达到100%的代码覆盖率 创建以下新示例: Angular.js + We.js Ember....
  • python之pygame

    千次阅读 2019-02-16 13:48:23
    事件队列中获得一个事件事件将从事件队列中删除,如果事件队列为空,则返回event.NOEVENT while True: event = pygame.event.poll() pygame.event.clear() 从事件队列中删除事件,默认删除所有事件...

    目录

    pygame.dispaly, pygame.event, pygame.draw:


    pygame.dispaly:

    pygame有且仅有一个屏幕;左上角坐标(0,0);以像素为单位。

    #屏幕尺寸和模

    pygame.display.set_mode(r = (0,0), flag = 0)
    #r是游戏屏幕分辨率,以元组形式输入(weight, height)
    #flag用来控制显示类型,可用 | 组合使用,常用标签有:
    pygame.RESIZABLE    #窗口大小可调
    pygame.NOFRAME      #窗口没有边界显示
    pygame.FULLSCREEN   #窗口全屏显示
    (注意每种显示要配合相应的处理机制)
    
    vinfo = pygame.display.Info()
    #产生一个显示信息对象VideoInfo,表达当前屏幕参数信息
    vinfo.current_w    #当前显示模式或窗口的像素宽度
    vinfo.current_h    #当前显示模式或窗口的像素高度
    
    pygame.VIDEORESIZE
    #这是一种窗口大小更改的事件
    #事件发生后,返回event.size元组,包含新窗口的宽度和高度
    .size[0]    #宽度,也可以用event.w
    .size[1]    #高度,也可以用event.h
    #返回参数仅在事件发生时有用
    #example
    if event.type == pygame.VIDEORESIZE:
          size = width, height = event.size[0], event.size[1]
          screen = pygame.display.set_mode(size, pygame.RESIZABLE)

    #窗口标题和图标

    pygame.display.set_caption(title, icontitle = None)
    #title设置窗口的标题内容
    #icontitle设置图表化后的小标题,小标题可选,部分系统没有
    
    pygame.display.set_icon(surface)
    #设置窗口的图标效果
    #图标是一个Surface对象
    
    pygame.display.get_caption()
    #返回当前设置窗口的标题和小标题内容,(title, icontitle)

    #窗口感知和刷新

    pygame.display.get_active()
    #当窗口在系统中显示(屏幕绘制/非图标化)时返回True,否则返回False
    #可以用来判断游戏窗口是否被最小化
    
    pygame.display.flip()
    #重新绘制整个窗口
    
    pygame.display.update()
    #仅重新绘制窗口中有变化的区域,相比.flip()执行更快

     

    pygame.event:

    #键盘事件

    pygame.event.KEYDOWN    #键盘按下事件
    pygame.event.KEYUP      #键盘释放事件
    #属性
    event.key    #按键的常量名称
    event.mod    #按键修饰符的组合值
    
    event.mod = KMOD_ALT|KMOD_SHIFT    #修饰符的按位或运算

    #鼠标事件

    #鼠标移动事件
    pygame.event.MOUSEMOTION        
    #属性
    event.pos    #鼠标当前坐标值(x,y),相对于窗口左上角
    event.rel    #鼠标相对运动距离(x,y),相对于上次事件
    event.rel    #鼠标按钮状态(a,b,c),对应于鼠标的三个键(左 中 右)
    
    pygame.event.MOUSEBUTTONUP      #鼠标释放事件
    pygame.event.MOUSEBUTTONDOWN    #鼠标按下事件
    #属性
    event.pos    #鼠标当前坐标值(x,y)
    event.button #鼠标按下键编号,左 中 右对应1 2 3

    #example

    #鼠标,键盘事件处理
    import pygame, sys
    pygame.init()
    screen = pygame.display.set_mode((600, 400))
    pygame.display.set_caption("Pygame事件处理")
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.unicode == "":
                    print("[KEYDOWN]:", "#", event.key, event.mod)
                else:
                    print("[KEYDOWN]:", event.unicode, event.key, event.mod)
            elif event.type == pygame.MOUSEMOTION:
                print("[MOUSEMOTION]:", event.pos, event.rel, event.buttons)
            elif event.type == pygame.MOUSEBUTTONUP:
                print("[MOUSEBUTTONUP]:", event.pos, event.button)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                print("[MOUSEBUTTONDOWN]:", event.pos, event.button)
        pygame.display.update()
    ​

    pygame.event.get()

    从事件列表中获得事件列表

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    可以增加参数,获得某类或某些类事件:

    pygame.event.get(type)
    pygame.event.get(typelist)

    pygame.event.poll()

    从事件队列中获得一个事件,事件将从事件队列中删除,如果事件队列为空,则返回event.NOEVENT

    while True:
        event = pygame.event.poll()

    pygame.event.clear()

    从事件队列中删除事件,默认删除所有事件,可以增加参数,删除某类或某些类事件

    pygame.event.clear(type)
    pygame.event.clear(typelist)

    pygame.draw:

    官方文档

    pygame库用来绘制形状的类

    参数列表中的Surface是当前绘制屏幕的名称,color是RGB色彩模式下的颜色,例如 黑色(0, 0, 0),白色(255, 255, 255),width = 0是绘制形状的边的宽度,如果不传入的话width=0默认为填充

    pygame.draw.rect(Surface, color, Rect, width=0)

    绘制一个矩形

    参数Rect是矩形参数,格式为[x, y, width, height]

    examples for pygame.draw.rect

    pygame.draw.circle(Surface, color, pos, radius, width=0)

    以某点为圆心绘制一个圆形

    参数pos是圆心的位置,radius是半径大小

    examples for pygame.draw.circle

    pygame.draw.ellipse(Surface, color, Rect, width=0)

    绘制一个椭圆

    通过给出矩形的参数,从而绘制一个内切于矩形的椭圆

    examples fo pygame.draw.ellipse

    pygame.draw.arc(Surface, color, Rect, start_angle, stop_angle, width=1)

    绘制椭圆的一部分

    两个角度分别是起始和结束角度,最右边为0度

    examples for pygame.draw.arc

    pygame.draw.line(Surface, color, start_pos, end_pos, width=1)

    绘制一条直线

    参数两个pos分别是起始位置和结束位置

    examples for pygame.draw.line

    pygame.draw.lines(Surface, color, closed, pointlist, width=1)

    绘制多条连续直线

    参数closed为True时首尾相连构成封闭,pointlist为顶点坐标

    examples fo pygame.draw.lines

    A big example for this module

    # Import a library of functions called 'pygame'
    import pygame
    from math import pi
    
    # Initialize the game engine
    pygame.init()
    
    # Define the colors we will use in RGB format
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    BLUE = (0, 0, 255)
    GREEN = (0, 255, 0)
    RED = (255, 0, 0)
    
    # Set the height and width of the screen
    size = [400, 300]
    screen = pygame.display.set_mode(size)
    
    pygame.display.set_caption("Example code for the draw module")
    
    # Loop until the user clicks the close button.
    done = False
    clock = pygame.time.Clock()
    
    while not done:
    
        # This limits the while loop to a max of 10 times per second.
        # Leave this out and we will use all CPU we can.
        clock.tick(10)
    
        for event in pygame.event.get():  # User did something
            if event.type == pygame.QUIT:  # If user clicked close
                done = True  # Flag that we are done so we exit this loop
    
        # All drawing code happens after the for loop and but
        # inside the main while done==False loop.
    
        # Clear the screen and set the screen background
        screen.fill(WHITE)
    
        # Draw on the screen a GREEN line from (0,0) to (50.75) 
        # 5 pixels wide.
        pygame.draw.line(screen, GREEN, [0, 0], [50, 30], 5)
    
        # Draw on the screen a GREEN line from (0,0) to (50.75) 
        # 5 pixels wide.
        pygame.draw.lines(screen, BLACK, False, [[0, 80], [50, 90], [200, 80], [220, 30]], 5)
    
        # Draw on the screen a GREEN line from (0,0) to (50.75) 
        # 5 pixels wide.
        pygame.draw.aaline(screen, GREEN, [0, 50], [50, 80], True)
    
        # Draw a rectangle outline
        pygame.draw.rect(screen, BLACK, [75, 10, 50, 20], 2)
    
        # Draw a solid rectangle
        pygame.draw.rect(screen, BLACK, [150, 10, 50, 20])
    
        # Draw an ellipse outline, using a rectangle as the outside boundaries
        pygame.draw.ellipse(screen, RED, [225, 10, 50, 20], 2)
    
        # Draw an solid ellipse, using a rectangle as the outside boundaries
        pygame.draw.ellipse(screen, RED, [300, 10, 50, 20])
    
        # This draws a triangle using the polygon command
        pygame.draw.polygon(screen, BLACK, [[100, 100], [0, 200], [200, 200]], 5)
    
        # Draw an arc as part of an ellipse. 
        # Use radians to determine what angle to draw.
        pygame.draw.arc(screen, BLACK, [210, 75, 150, 125], 0, pi / 2, 2)
        pygame.draw.arc(screen, GREEN, [210, 75, 150, 125], pi / 2, pi, 2)
        pygame.draw.arc(screen, BLUE, [210, 75, 150, 125], pi, 3 * pi / 2, 2)
        pygame.draw.arc(screen, RED, [210, 75, 150, 125], 3 * pi / 2, 2 * pi, 2)
    
        # Draw a circle
        pygame.draw.circle(screen, BLUE, [60, 250], 40)
    
        # Go ahead and update the screen with what we've drawn.
        # This MUST happen after all the other drawing commands.
        pygame.display.flip()
    
    # Be IDLE friendly
    pygame.quit()

     

    展开全文
  • msm_we-源码

    2021-03-27 01:56:13
    msm_we 加权整体轨迹的马尔可夫状态建模代码。 免费软件:MIT许可证 文档: : 。 特征 去做 行话 单词 定义 经 当粒子从吸收态回收到源态时 分割 待办事项 segindList不包含从1到n_segs的连续整数列表? 是什么...
  • Hbuilder We're sorry

    千次阅读 2019-10-05 19:45:05
    错误页面中监听事件 在定义的error.html页面中可以通过监听"error"事件获取更多错误信息: 复制代码 // 获取错误信息 document .addEventListener( "error" , function ( e ) { var url = e.url; // ...

    当Webview窗口加载错误地址(如本地页面不存在)或者访问网络资源失败(如无法访问网络)时会自动显示默认错误页面:


    可以通过以下方法自定义Webview的404等错误页面。

    设置应用全局默认错误页面

    • 5+App和wap2app
      在应用的manifest.json文件的"plus"->"error"节点的url属性可配置自定义错误页面替换默认的错误页面。
      打开manifest.json文件,切换到代码视图,添加以下数据:

      复制代码"plus": {  
      "error": {  
          "url": "error.html"  
      },  
      //...  
      },  
      //...

      其中url地址推荐使用本地地址,相对于应用根目录。
      设置为“none”则关闭跳转到错误页面功能,此时页面显示Webview默认的错误页面内容。

    • uni-app使用注意

      1. 上面error节点数据放到manifest.json的"app-plus"节点下
      2. error.html需要放到根目录下的hybrid/html目录下,否则不会被编译进去

    单独设置Webview窗口的错误页面

    如果需要单独自定义某个Webview窗口的错误页面,则需要在创建时通过WebviewStyle对象的errorPage属性控制:

    复制代码var styles = {errorPage:"error.html"}; // 设置为“none”则关闭此Webview窗口的跳转到错误页面功能  
    var webview = plus.webview.create( "url", "id", styles );     
    webview.show();

    错误页面中监听事件

    在定义的error.html页面中可以通过监听"error"事件获取更多错误信息:

    复制代码// 获取错误信息  
    document.addEventListener("error",function(e){  
        var url = e.url;  // 错误页面的url地址  
        var href = e.href; // 错误页面的完整路径(包括完整的协议头)  
    },false);

    注意
    Android平台使用iframe时如果无法加载页面在不同版本系统上存在差异:
    5.0及以上版本:Webview窗口对象不会加载错误页面,仅iframe节点显示无法加载页面;
    5.0以下版本:Webview窗口对象会加载错误页面。

    补充

    今天发现ios出现这种问题还有可能就是用户在使用APP时,系统提示禁用网络之类的用户点了确认。
    在设置里面检查APP的网络权限,看是否关闭了。。。

    展开全文
  • 【UCOSIII】UCOSIII的事件标志组

    万次阅读 2018-07-05 19:10:09
    UCOSIII事件标志组 前面讲述了UCOSIII的信号量、互斥信号量,它们都可以完成任务的同步。但是有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之间有两种同步机制: “或”...

    UCOSIII事件标志组

    前面讲述了UCOSIII的信号量、互斥信号量,它们都可以完成任务的同步。但是有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之间有两种同步机制:

    • “或”同步:等待多个事件时,任何一个事件发生 ,任务都被同步,这个就称为“或”同步;
    • “与”同步:当所有的事件都发生时任务才被同步,这种同步机制被称为“与”同步。

    在UCOSIII中事件标志组为OS_FLAG_GRP,如果需要使用事件标志组的时候需要将宏OS_CFG_FLAG_EN置1。

    这两种同步机制如下图所示:

    1. 在UCOSIII中事件标志组是OS_FLAG_GRP,在os.h文件中有定义,事件标志组中也包含了一串任务,这些任务都在等待着事件标志组中的部分(或全部)事件标志被置1或被清零,在使用之前,必须创建事件标志组;
    2. 任务和ISR(中断服务程序)都可以发布事件标志,但是,只有任务可以创建、删除事件标志组以及取消其他任务对事件标志组的等待
    3. 任务可以通过调用函数OSFlagPend()等待事件标志组中的任意个事件标志,调用函数OSFlagPend()的时候可以设置一个超时时间,如果过了超时时间请求的事件还没有被发布,那么任务就会重新进入就绪态;
    4. 我们可以设置同步机制为“或”同步还是“与”同步。

     

    UCOSIII事件标志组API函数

    UCOSIII事件标志组API函数
    函数说明
    OSFlagCreate()创建事件标志组
    OSFlagDel()删除事件标志组
    OSFlagPend()等待事件标志组
    OSFlagPendAbort()取消等待事件标志组
    OSFlagPendGetFlagsRdy()获取使任务就绪的事件标志
    OSFlagPost()向事件标志组发布标志

    创建事件标志组

    在使用事件标志组之前,需要调用函数OSFlagCreate()创建一个事件标志组,OSFlagCreate()函数原型如下:

    void  OSFlagCreate (OS_FLAG_GRP  *p_grp,                        //指向事件标志组
                        CPU_CHAR     *p_name,                        //事件标志组的名字
                        OS_FLAGS      flags,                        //定义事件标志组的初始值
                        OS_ERR       *p_err)
    {
        CPU_SR_ALLOC();
    
        OS_CRITICAL_ENTER();
        p_grp->Type    = OS_OBJ_TYPE_FLAG;                      /* Set to event flag group type                           */
        p_grp->NamePtr = p_name;
        p_grp->Flags   = flags;                                 /* Set to desired initial value                           */
        p_grp->TS      = (CPU_TS)0;
        OS_PendListInit(&p_grp->PendList);
    
        OSFlagQty++;
    
        OS_CRITICAL_EXIT_NO_SCHED();
       *p_err = OS_ERR_NONE;
    }

    我们可以先看看事件标志组的结构体OS_FLAG_GRP:

    struct  os_flag_grp {                                       /* Event Flag Group                                       */
                                                                /* ------------------ GENERIC  MEMBERS ------------------ */
        OS_OBJ_TYPE          Type;                              /* Should be set to OS_OBJ_TYPE_FLAG                      */
        CPU_CHAR            *NamePtr;                           /* 事件标志组的名称    */
        OS_PEND_LIST         PendList;                          /* 等待事件标志组的任务组    */
    #if OS_CFG_DBG_EN > 0u
        OS_FLAG_GRP         *DbgPrevPtr;
        OS_FLAG_GRP         *DbgNextPtr;
        CPU_CHAR            *DbgNamePtr;
    #endif
                                                                /* ------------------ SPECIFIC MEMBERS ------------------ */
        OS_FLAGS             Flags;                             /* 8, 16 or 32 bit flags                                  */
        CPU_TS               TS;                                /* Timestamp of when last post occurred                   */
    };

    事件标志组的结构体和之前的信号量、互斥信号量、消息队列比较类似,关键的一个成员变量是Flags:

    typedef   CPU_INT32U      OS_FLAGS;                    /* Event flags,                                      8/16/<32> */

    我们可以看到定义,Flags是一个32位无符号的整型。我们在OSFlagCreate()函数中的flags参数的值就是赋值给它的,那么它代表的含义是什么呢?

    Flags是32位,它的每一位都代表着一个任务的状态,每个任务有1和0两种状态。也就是说,我们可以同时最多完成一个任务和32个任务的任务同步!我们可以设置:Falgs的第0、1两个任务为1的时候,完成任务同步,也就是说,Flags变成0x03的时候,完成同步。当然,OSFlagCreate()函数中的flags参数只是确定一个初始值。

    等待事件标志组

    等待一个事件标志组需要调用函数OSFlagPend(),函数原型如下:

    OS_FLAGS  OSFlagPend (OS_FLAG_GRP  *p_grp,                            //指向事件标志组
                          OS_FLAGS      flags,                            //bit序列
                          OS_TICK       timeout,                        //指定等待事件标志组的超时时间(时钟节拍数)
                          OS_OPT        opt,                            //决定任务等待的条件
                          CPU_TS       *p_ts,                            //指向一个时间戳
                          OS_ERR       *p_err)
    {
        CPU_BOOLEAN   consume;
        OS_FLAGS      flags_rdy;
        OS_OPT        mode;
        OS_PEND_DATA  pend_data;
        CPU_SR_ALLOC();
    
        if ((opt & OS_OPT_PEND_FLAG_CONSUME) != (OS_OPT)0) {    /* See if we need to consume the flags                    */
            consume = DEF_TRUE;
        } else {
            consume = DEF_FALSE;
        }
    
        if (p_ts != (CPU_TS *)0) {
           *p_ts = (CPU_TS)0;                                   /* Initialize the returned timestamp                      */
        }
    
        mode = opt & OS_OPT_PEND_FLAG_MASK;
        CPU_CRITICAL_ENTER();
        switch (mode) {
            case OS_OPT_PEND_FLAG_SET_ALL:                      /* See if all required flags are set                      */
                 flags_rdy = (OS_FLAGS)(p_grp->Flags & flags);  /* Extract only the bits we want                          */
                 if (flags_rdy == flags) {                      /* Must match ALL the bits that we want                   */
                     if (consume == DEF_TRUE) {                 /* See if we need to consume the flags                    */
                         p_grp->Flags &= ~flags_rdy;            /* Clear ONLY the flags that we wanted                    */
                     }
                     OSTCBCurPtr->FlagsRdy = flags_rdy;         /* Save flags that were ready                             */
                     if (p_ts != (CPU_TS *)0) {
                        *p_ts  = p_grp->TS;
                     }
                     CPU_CRITICAL_EXIT();                       /* Yes, condition met, return to caller                   */
                    *p_err = OS_ERR_NONE;
                     return (flags_rdy);
                 } else {                                       /* Block task until events occur or timeout               */
                     if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {
                         CPU_CRITICAL_EXIT();
                        *p_err = OS_ERR_PEND_WOULD_BLOCK;       /* Specified non-blocking so task would block             */
                         return ((OS_FLAGS)0);
                     } else {                                   /* Specified blocking so check is scheduler is locked     */
                         if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* See if called with scheduler locked ...      */
                             CPU_CRITICAL_EXIT();
                            *p_err = OS_ERR_SCHED_LOCKED;                 /* ... can't PEND when locked                   */
                             return ((OS_FLAGS)0);
                         }
                     }
                                                                
                     OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();     /* Lock the scheduler/re-enable interrupts                */
                     OS_FlagBlock(&pend_data,
                                  p_grp,
                                  flags,
                                  opt,
                                  timeout);
                     OS_CRITICAL_EXIT_NO_SCHED();
                 }
                 break;
    
            case OS_OPT_PEND_FLAG_SET_ANY:
                 flags_rdy = (OS_FLAGS)(p_grp->Flags & flags);  /* Extract only the bits we want                          */
                 if (flags_rdy != (OS_FLAGS)0) {                /* See if any flag set                                    */
                     if (consume == DEF_TRUE) {                 /* See if we need to consume the flags                    */
                         p_grp->Flags &= ~flags_rdy;            /* Clear ONLY the flags that we got                       */
                     }
                     OSTCBCurPtr->FlagsRdy = flags_rdy;         /* Save flags that were ready                             */
                     if (p_ts != (CPU_TS *)0) {
                        *p_ts  = p_grp->TS;
                     }
                     CPU_CRITICAL_EXIT();                       /* Yes, condition met, return to caller                   */
                    *p_err = OS_ERR_NONE;
                     return (flags_rdy);
                 } else {                                       /* Block task until events occur or timeout               */
                     if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {
                         CPU_CRITICAL_EXIT();
                        *p_err = OS_ERR_PEND_WOULD_BLOCK;       /* Specified non-blocking so task would block             */
                         return ((OS_FLAGS)0);
                     } else {                                   /* Specified blocking so check is scheduler is locked     */
                         if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* See if called with scheduler locked ...      */
                             CPU_CRITICAL_EXIT();
                            *p_err = OS_ERR_SCHED_LOCKED;                 /* ... can't PEND when locked                   */
                             return ((OS_FLAGS)0);
                         }
                     }
                                                                
                     OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();     /* Lock the scheduler/re-enable interrupts                */
                     OS_FlagBlock(&pend_data,
                                  p_grp,
                                  flags,
                                  opt,
                                  timeout);
                     OS_CRITICAL_EXIT_NO_SCHED();
                 }
                 break;
    
            default:
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_OPT_INVALID;
                 return ((OS_FLAGS)0);
        }
    
        OSSched();                                              /* Find next HPT ready to run                             */
    
        CPU_CRITICAL_ENTER();
        switch (OSTCBCurPtr->PendStatus) {
            case OS_STATUS_PEND_OK:                             /* We got the vent flags                                  */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = OSTCBCurPtr->TS;
                 }
                *p_err = OS_ERR_NONE;
                 break;
    
            case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = OSTCBCurPtr->TS;
                 }
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_PEND_ABORT;
                 return ((OS_FLAGS)0);
    
            case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get semaphore within timeout   */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = (CPU_TS  )0;
                 }
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_TIMEOUT;
                 return ((OS_FLAGS)0);
    
            case OS_STATUS_PEND_DEL:                            /* Indicate that object pended on has been deleted        */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = OSTCBCurPtr->TS;
                 }
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_OBJ_DEL;
                 return ((OS_FLAGS)0);
    
            default:
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_STATUS_INVALID;
                 return ((OS_FLAGS)0);
        }
    
        flags_rdy = OSTCBCurPtr->FlagsRdy;
        if (consume == DEF_TRUE) {                              /* See if we need to consume the flags                    */
            switch (mode) {
                case OS_OPT_PEND_FLAG_SET_ALL:
                case OS_OPT_PEND_FLAG_SET_ANY:                  /* Clear ONLY the flags we got                            */
                     p_grp->Flags &= ~flags_rdy;
                     break;
    
                default:
                     CPU_CRITICAL_EXIT();
                    *p_err = OS_ERR_OPT_INVALID;
                     return ((OS_FLAGS)0);
            }
        }
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_NONE;                                    /* Event(s) must have occurred                            */
        return (flags_rdy);
    }

    flags:bit序列,任务需要等待事件标志组的哪个位就把这个序列对应的位置1,根据设置这个序列可以是8bit、16bit或者32bit。比如任务需要等待时间标志组的bit0和bit1时(无论是等待置位还是清零),flag是的值就为0X03。

    opt:决定任务等待的条件是所有标志置位、所有标志清零、任意一个标志置位还是任意一个标志清零,具体的定义如下。

    OS_OPT_PEND_FLAG_CLR_ALL:等待事件标志组所有的位清零;
    OS_OPT_PEND_FLAG_CLR_ANY:等待事件标志组中任意一个标志清零;
    OS_OPT_PEND_FLAG_SET_ALL:等待事件标志组中所有的位置位;
    OS_OPT_PEND_FLAG_SET_ANY:等待事件标志组中任意一个标志置位。

    调用上面四个选项的时候还可以搭配下面三个选项:

    OS_OPT_PEND_FLAG_CONSUME:用来设置是否继续保留该事件标志的状态;
    OS_OPT_PEND_NON_BLOCKING:标志组不满足条件时不挂起任务;
    OS_OPT_PEND_BLOCKING:标志组不满足条件时挂起任务。

    这里应该注意选项OS_OPT_PEND_FLAG_CONSUME的使用方法,如果我们希望任务等待事件标志组的任意一个标志置位,并在满足条件后将对应的标志清零那么就可以搭配使用选项OS_OPT_PEND_FLAG_CONSUME。

    OSFlagPend()允许将事件标志组里事件标志的“与或”组合状态设置成任务的等待条件。任务等待的条件可以是标志组里任意一个标志置位或清零,也可以是所有事件标志都置位或清零。如果任务等待的事件标志组不满足设置的条件,那么该任务被置位挂起状态,直到等待的事件标志组满足条件、指定的超时时间到、事件标志被删除或另一个任务终止了该任务的挂起状态。

    向事件标志组发布标志

    调用函数OSFlagPost()可以对事件标志组进行置位或清零,函数原型如下:

    OS_FLAGS  OSFlagPost (OS_FLAG_GRP  *p_grp,                    //指向事件标志组
                          OS_FLAGS      flags,                    //决定对哪些位清零和置位
                          OS_OPT        opt,                    //决定对标志位的操作
                          OS_ERR       *p_err)
    {
        OS_FLAGS  flags_cur;
        CPU_TS    ts;
    
        ts = OS_TS_GET();                                       /* Get timestamp                                          */
    
        flags_cur = OS_FlagPost(p_grp,
                                flags,
                                opt,
                                ts,
                                p_err);
    
        return (flags_cur);
    }

    flags:决定对哪些位清零和置位,当opt参数为OS_OPT_POST_FLAG_SET的时,参数flags中置位的位就会在事件标志组中对应的位也将被置位;当opt为OS_OPT_POST_FLAG_CLR的时候参数flags中置位的位在事件标志组中对应的位将被清零。

    opt:决定对flags选定的标志位的操作,有两种选项可供选择。OS_OPT_POST_FLAG_SET:对标志位进行置位操作;OS_OPT_POST_FLAG_CLR:对标志位进行清零操作。

    这个函数的返回值时当前的flags值,通过该返回值,可以查到此时本任务在flags中的哪一个位有没有被置位,或者其他还有哪些任务在flags中的标志。

    一般情况下,需要进行置位或者清零的标志由一个掩码确定(参数flags)。OSFlagPost()修改完事件标志后,将检查并使那些等待条件已经满足的任务进入就绪态。该函数可以对已经置位或清零的标志进行重复置位和清零操作。

     

    UCOSIII实际例程

    时间标志组实验

    例程要求:设计一个程序,只有按下KEY0和KEY1(不需要同时按下)时任务flagsprocess_task任务才能执行。

    例子:

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    #include "lcd.h"
    #include "key.h"
    #include "malloc.h"
    #include "sram.h"
    #include "beep.h"
    #include "includes.h"
    
    //UCOSIII中以下优先级用户程序不能使用,ALIENTEK
    //将这些优先级分配给了UCOSIII的5个系统内部任务
    //优先级0:中断服务服务管理任务 OS_IntQTask()
    //优先级1:时钟节拍任务 OS_TickTask()
    //优先级2:定时任务 OS_TmrTask()
    //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
    //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()
    
    //任务优先级
    #define START_TASK_PRIO		3
    //任务堆栈大小	
    #define START_STK_SIZE 		128
    //任务控制块
    OS_TCB StartTaskTCB;
    //任务堆栈	
    CPU_STK START_TASK_STK[START_STK_SIZE];
    //任务函数
    void start_task(void *p_arg);
    
    //任务优先级
    #define MAIN_TASK_PRIO		4
    //任务堆栈大小	
    #define MAIN_STK_SIZE 		128
    //任务控制块
    OS_TCB Main_TaskTCB;
    //任务堆栈	
    CPU_STK MAIN_TASK_STK[MAIN_STK_SIZE];
    void main_task(void *p_arg);
    
    //任务优先级
    #define FLAGSPROCESS_TASK_PRIO	5
    //任务堆栈大小	
    #define FLAGSPROCESS_STK_SIZE 	128
    //任务控制块
    OS_TCB Flagsprocess_TaskTCB;
    //任务堆栈	
    CPU_STK FLAGSPROCESS_TASK_STK[FLAGSPROCESS_STK_SIZE];
    //任务函数
    void flagsprocess_task(void *p_arg);
    
    //LCD刷屏时使用的颜色
    int lcd_discolor[14]={	WHITE, BLACK, BLUE,  BRED,      
    						GRED,  GBLUE, RED,   MAGENTA,       	 
    						GREEN, CYAN,  YELLOW,BROWN, 			
    						BRRED, GRAY };
    
    事件标志组//
    #define KEY0_FLAG		0x01
    #define KEY1_FLAG		0x02
    #define KEYFLAGS_VALUE	0X00						
    OS_FLAG_GRP	EventFlags;		//定义一个事件标志组
    												
    //加载主界面
    void ucos_load_main_ui(void)
    {
    	POINT_COLOR = RED;
    	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
    	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 12-1");
    	LCD_ShowString(30,50,200,16,16,"Event Flags");
    	LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
    	LCD_ShowString(30,90,200,16,16,"2015/3/19");
    	POINT_COLOR = BLACK;
    	LCD_DrawRectangle(5,130,234,314);	//画矩形
    	POINT_COLOR = BLUE;
    	LCD_ShowString(30,110,220,16,16,"Event Flags Value:0");
    }
    
    int main(void)                                //主函数
    {
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	
    	delay_init();  //时钟初始化
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置
    	uart_init(115200);   //串口初始化
    	LED_Init();         //LED初始化	
    	LCD_Init();			//LCD初始化	
    	KEY_Init();			//按键初始化
    	BEEP_Init();		//初始化蜂鸣器
    	FSMC_SRAM_Init();	//初始化SRAM
    	my_mem_init(SRAMIN);//初始化内部RAM
    	ucos_load_main_ui();//加载主UI
    	
    	OSInit(&err);		    //初始化UCOSIII
    	OS_CRITICAL_ENTER();	//进入临界区			 
    	//创建开始任务
    	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任务控制块
    				 (CPU_CHAR	* )"start task", 		//任务名字
                     (OS_TASK_PTR )start_task, 			//任务函数
                     (void		* )0,					//传递给任务函数的参数
                     (OS_PRIO	  )START_TASK_PRIO,     //任务优先级
                     (CPU_STK   * )&START_TASK_STK[0],	//任务堆栈基地址
                     (CPU_STK_SIZE)START_STK_SIZE/10,	//任务堆栈深度限位
                     (CPU_STK_SIZE)START_STK_SIZE,		//任务堆栈大小
                     (OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                     (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                     (void   	* )0,					//用户补充的存储区
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                     (OS_ERR 	* )&err);				//存放该函数错误时的返回值
    	OS_CRITICAL_EXIT();	//退出临界区	 
    	OSStart(&err);      //开启UCOSIII
    }
    
    void start_task(void *p_arg)                            //开始任务函数
    {
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	p_arg = p_arg;
    	
    	CPU_Init();
    #if OS_CFG_STAT_TASK_EN > 0u
       OSStatTaskCPUUsageInit(&err);  	//统计任务                
    #endif
    	
    #ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了测量中断关闭时间
        CPU_IntDisMeasMaxCurReset();	
    #endif
    	
    #if	OS_CFG_SCHED_ROUND_ROBIN_EN  //当使用时间片轮转的时候
    	 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
    	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
    #endif	
    		
    	OS_CRITICAL_ENTER();	//进入临界区
    	//创建一个事件标志组
    	OSFlagCreate((OS_FLAG_GRP*)&EventFlags,		//指向事件标志组
                     (CPU_CHAR*	  )"Event Flags",	//名字
                     (OS_FLAGS	  )KEYFLAGS_VALUE,	//事件标志组初始值
                     (OS_ERR*  	  )&err);			//错误码
    
    	OSTaskCreate((OS_TCB*     )&Main_TaskTCB,	                	//创建主任务	
    				 (CPU_CHAR*   )"Main task", 		
                     (OS_TASK_PTR )main_task, 			
                     (void*       )0,					
                     (OS_PRIO	  )MAIN_TASK_PRIO,     
                     (CPU_STK*    )&MAIN_TASK_STK[0],	
                     (CPU_STK_SIZE)MAIN_STK_SIZE/10,	
                     (CPU_STK_SIZE)MAIN_STK_SIZE,		
                     (OS_MSG_QTY  )0,					
                     (OS_TICK	  )0,  					
                     (void*       )0,					
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                     (OS_ERR*     )&err);						
    
    	OSTaskCreate((OS_TCB*     )&Flagsprocess_TaskTCB,			//创建MSGDIS任务
    				 (CPU_CHAR*   )"Flagsprocess task", 		
                     (OS_TASK_PTR )flagsprocess_task, 			
                     (void* 	  )0,					
                     (OS_PRIO	  )FLAGSPROCESS_TASK_PRIO,     
                     (CPU_STK* 	  )&FLAGSPROCESS_TASK_STK[0],	
                     (CPU_STK_SIZE)FLAGSPROCESS_STK_SIZE/10,	
                     (CPU_STK_SIZE)FLAGSPROCESS_STK_SIZE,		
                     (OS_MSG_QTY  )0,					
                     (OS_TICK	  )0,  					
                     (void* 	  )0,					
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                     (OS_ERR* 	  )&err);	
    	OS_CRITICAL_EXIT();	//退出临界区
    	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
    }
    
    void main_task(void *p_arg)                        //主任务的任务函数
    {    
    	u8 key,num;
    	OS_FLAGS flags_num;
    	OS_ERR err;
    	while(1)
    	{
    		key = KEY_Scan(0);  //扫描按键
    		if(key == KEY0_PRES)
    		{
    			//向事件标志组EventFlags发送标志
    			flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,
    								 (OS_FLAGS	  )KEY0_FLAG,
    								 (OS_OPT	  )OS_OPT_POST_FLAG_SET,
    					             (OS_ERR*	  )&err);
    			LCD_ShowxNum(174,110,flags_num,1,16,0);
    			printf("事件标志组EventFlags的值:%d\r\n",flags_num);
    		}
    		else if(key == KEY1_PRES)
    		{
    			//向事件标志组EventFlags发送标志
    			flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,
    								 (OS_FLAGS	  )KEY1_FLAG,
    								 (OS_OPT	  )OS_OPT_POST_FLAG_SET,
    								 (OS_ERR*     )&err);
    			LCD_ShowxNum(174,110,flags_num,1,16,0);
    			printf("事件标志组EventFlags的值:%d\r\n",flags_num);
    		}
    		num++;
    		if(num==50)
    		{
    			num=0;
    			LED0 = ~LED0;
    		}
    		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
    	}
    }
    
    void flagsprocess_task(void *p_arg)                        //事件标志组处理任务
    {
    	u8 num;
    	OS_ERR err; 
    	while(1)
    	{
    		//等待事件标志组
    		OSFlagPend((OS_FLAG_GRP*)&EventFlags,
    				   (OS_FLAGS	)KEY0_FLAG+KEY1_FLAG,
    		     	   (OS_TICK     )0,
    				   (OS_OPT	    )OS_OPT_PEND_FLAG_SET_ALL+OS_OPT_PEND_FLAG_CONSUME,
    				   (CPU_TS*     )0,
    				   (OS_ERR*	    )&err);
    		num++;
    		LED1 = ~LED1;
    		LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
    		printf("事件标志组EventFlags的值:%d\r\n",EventFlags.Flags);
    		LCD_ShowxNum(174,110,EventFlags.Flags,1,16,0);
    	}
    }
    	//创建主任务	
    				 (CPU_CHAR*   )"Main task", 		
                     (OS_TASK_PTR )main_task, 			
                     (void*       )0,					
                     (OS_PRIO	  )MAIN_TASK_PRIO,     
                     (CPU_STK*    )&MAIN_TASK_STK[0],	
                     (CPU_STK_SIZE)MAIN_STK_SIZE/10,	
                     (CPU_STK_SIZE)MAIN_STK_SIZE,		
                     (OS_MSG_QTY  )0,					
                     (OS_TICK	  )0,  					
                     (void*       )0,					
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                     (OS_ERR*     )&err);						
    
    	OSTaskCreate((OS_TCB*     )&Flagsprocess_TaskTCB,			//创建MSGDIS任务
    				 (CPU_CHAR*   )"Flagsprocess task", 		
                     (OS_TASK_PTR )flagsprocess_task, 			
                     (void* 	  )0,					
                     (OS_PRIO	  )FLAGSPROCESS_TASK_PRIO,     
                     (CPU_STK* 	  )&FLAGSPROCESS_TASK_STK[0],	
                     (CPU_STK_SIZE)FLAGSPROCESS_STK_SIZE/10,	
                     (CPU_STK_SIZE)FLAGSPROCESS_STK_SIZE,		
                     (OS_MSG_QTY  )0,					
                     (OS_TICK	  )0,  					
                     (void* 	  )0,					
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                     (OS_ERR* 	  )&err);	
    	OS_CRITICAL_EXIT();	//退出临界区
    	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
    }
    
    void main_task(void *p_arg)                        //主任务的任务函数
    {    
    	u8 key,num;
    	OS_FLAGS flags_num;
    	OS_ERR err;
    	while(1)
    	{
    		key = KEY_Scan(0);  //扫描按键
    		if(key == KEY0_PRES)
    		{
    			//向事件标志组EventFlags发送标志
    			flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,
    								 (OS_FLAGS	  )KEY0_FLAG,
    								 (OS_OPT	  )OS_OPT_POST_FLAG_SET,
    					             (OS_ERR*	  )&err);
    			LCD_ShowxNum(174,110,flags_num,1,16,0);
    			printf("事件标志组EventFlags的值:%d\r\n",flags_num);
    		}
    		else if(key == KEY1_PRES)
    		{
    			//向事件标志组EventFlags发送标志
    			flags_num=OSFlagPost((OS_FLAG_GRP*)&EventFlags,
    								 (OS_FLAGS	  )KEY1_FLAG,
    								 (OS_OPT	  )OS_OPT_POST_FLAG_SET,
    								 (OS_ERR*     )&err);
    			LCD_ShowxNum(174,110,flags_num,1,16,0);
    			printf("事件标志组EventFlags的值:%d\r\n",flags_num);
    		}
    		num++;
    		if(num==50)
    		{
    			num=0;
    			LED0 = ~LED0;
    		}
    		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
    	}
    }
    
    void flagsprocess_task(void *p_arg)                        //事件标志组处理任务
    {
    	u8 num;
    	OS_ERR err; 
    	while(1)
    	{
    		//等待事件标志组
    		OSFlagPend((OS_FLAG_GRP*)&EventFlags,
    				   (OS_FLAGS	)KEY0_FLAG+KEY1_FLAG,
    		     	   (OS_TICK     )0,
    				   (OS_OPT	    )OS_OPT_PEND_FLAG_SET_ALL+OS_OPT_PEND_FLAG_CONSUME,
    				   (CPU_TS*     )0,
    				   (OS_ERR*	    )&err);
    		num++;
    		LED1 = ~LED1;
    		LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
    		printf("事件标志组EventFlags的值:%d\r\n",EventFlags.Flags);
    		LCD_ShowxNum(174,110,EventFlags.Flags,1,16,0);
    	}
    }

     

    展开全文
  • webrtc QOS方法四(Sender Side BWE

    千次阅读 多人点赞 2018-10-09 14:34:54
    BWE(Bandwidth Estimation)可能是WebRTC视频引擎中最关键的模块了。BWE模块决定视频通讯中可以发送多大码率视频不会使网络拥塞,防止视频通讯质量下降。 早期的带宽评估算法比较简单,大多是基于丢包来估计,基本...

    背景介绍

    BWE(Bandwidth Estimation)可能是WebRTC视频引擎中最关键的模块了。BWE模块决定视频通讯中可以发送多大码率视频不会使网络拥塞,防止视频通讯质量下降。

    早期的带宽评估算法比较简单,大多是基于丢包来估计,基本的策略是逐步增加发送的数据量,直到检测到丢包为止。为了让发送端获悉网络上的丢包信息,可以使用标准的RTCP的RR来发送周期性的报告。

    现代的带宽评估算法则可以在网络链路发生丢包以前就监测到网络拥塞,它可以通过侦测数据包接收的时延来预测未来可能的拥塞。它是基于链路上的路由器都有一定的缓存,在数据包开始被丢弃之前,先发生数据在缓存里堆积的事件,所以时延相比于丢包,对拥塞的反应更加灵敏。

    最新版本webrtc使用trendline算法实现网络拥塞预估,早期版本使用KalmanFilter算法。两种算法都是基于接收端的网络延迟进行码率估计。早期的KalmanFilter算法是在接收端根据网络延时,计算出合适的带宽,通过REMB RTCP报文反馈给发送端,让发送端按照该码率发送视频数据。trendline算法对此进行了优化,在发送端根据网络延时,计算合适的带宽。

    实现原理

    发送端码控模块结构,如上图所示。

    一、remb bitrate

    仅做向下兼容使用。兼容老版本的KalmanFilter算法。

    二、基于丢包拥塞控制

    发送端基于丢包率控制发送码率,其基本思想是:丢包率反映网络拥塞状况。如果丢包率很小或者为0,说明网络状况良好,在不超过预设最大码率的情况下,可以增大发送端码率;反之如果丢包率变大,说明网络状况变差,此时应减少发送端码率。在其它情况下,发送端码率保持不变。

    webrtc中发送端收到RTCP RR报文并解析得到丢包率后,根据下图公式计算发送端码率:当丢包率大于0.1时,说明网络发生拥塞,此时降低发送端码率;当丢包率小于0.02时,说明网络状况良好,此时增大发送端码率;其他情况下,发送端码率保持不变。

    三、基于延时拥塞控制

    基于延迟的拥塞控制是通过每组包的到达时间的延迟差(delta delay)的增长趋势来判断网络是否过载,如果过载进行码率下调,如果处于平衡范围维持当前码率,如果是网络承载不饱满进行码率上调。这里有几个关键技术:包组延迟评估(InterArrival)、滤波器趋势判断(TrendlineEstimator)、过载检测(OveruseDetector)和码率调节(AimdRateControl)。

    1、组包延时

    音频报文发送的时间间隔是按照一帧的打包时长发送的,但是视频却不能按照实际帧率发送,因为一帧视频有可能分别封装在几个RTP报文,若这个视频帧的RTP报文一起发送到网络上,必然会导致网络瞬间拥塞。以25fps为例,若这帧视频的RTP报文,能够在40ms之内发送给接收端,接收端既可以正常工作,也缓冲了网络拥塞的压力。所以计算视频的包延时,不能用单包计算,需要按照一组包(一帧视频)计算。Trendline滤波器需要的三个参数:发送时刻差值(delta_timestamp)、到达时刻差值(delta_arrival)和包组数据大小差值(delta_size)。从上图可以得出: 

    2、Trendline滤波趋势判断

    如果平稳网速下传输数据的延迟时间就是数据大小除以速度,如果这数据块很大,超过恒定网速下延迟上限,这意味着要它要占用其他后续数据块的传输时间,那么如此往复,网络就产生了延迟和拥塞。Trendline filter通过到达时间差、发送时间差和数据大小来得到一个趋势增长值,如果这个值越大说明网络延迟越来越严重,如果这个值越小,说明延迟逐步下降。计算公式如下:

    先计算单个包组传输增长的延迟,可以记作:

    然后做每个包组的叠加延迟,可以记作:

    在通过累积延迟计算一个均衡平滑延迟值,alpha=0.9可以记作:

    然后统一对累计延迟和均衡平滑延迟再求平均,分别记作:

    我们将第i个包组的传输持续时间记作:

    趋势斜率分子值为:

    趋势斜率分母值为:

    最终的趋势值为:

    3、过载检测

    在计算得到trendline值后WebRTC通过动态阈值gamma_1进行判断拥塞程度,trendline乘以周期包组个数就是m_i,以下是判断拥塞程度的伪代码:

    通过以上伪代码就可以判断出当前网络负载状态是否发生了过载,如果发生过载,WebRTC是通过一个有限状态机来进行网络状态迁徙,关于状态机细节可以参看下图:

    从上图可以看出,网络状态机的状态迁徙是由于网络过载状态发生了变化,所以状态迁徙作为了aimd带宽调节的触发事件,aimd根据当前所处的网络状态进行带宽调节,其过程是处于Hold状态表示维持当前码率,处于Decr状态表示需要进行码率递减,处于Incr状态需要进行码率递增。那他们是怎么递增和递减的呢?WebRTC引入了aimd算法解决这个问题。

    4、AIMD码率调节

    aimd的全称是Additive Increase Multiplicative Decrease,意思是:和式增加,积式减少。aimd controller是TCP底层的码率调节概念,但是WebRTC并没有完全照搬TCP的机制,而是设计了套自己的算法,用公式表示为:

    如果处于Incr状态,增加码率的方式分为两种:一种是通信会话刚刚开始,相当于TCP慢启动,它会进行一个倍数增加,当前使用的码率乘以系数,系数是1.08;如果是持续在通信状态,其增加的码率值是当前码率在一个RTT时间周期所能传输的数据速率。
    如果处于Decrease状态,递减原则是:过去500ms时间窗内的最大acked bitrate乘上系数0.85,acked bitrate通过feedback反馈过来的报文序号查找本地发送列表就可以得到。
    aimd根据上面的规则最终计算到的码率就是基于延迟拥塞评估到的bwe bitrate码率。

    webrtc具体实现

    一、基于延时拥塞控制

    BaseChannel::ProcessPacket
    ->WebRtcVideoChannel::OnRtcpReceived
    ->Call::DeliverPacket
    ->Call::DeliverRtcp
    ->VideoSendStream::DeliverRtcp
    ->VideoSendStreamImpl::DeliverRtcp
    ->ModuleRtpRtcpImpl::IncomingRtcpPacket
    ->RTCPReceiver::IncomingPacket
    ->RTCPReceiver::TriggerCallbacksFromRtcpPacket
    ->SendSideCongestionController::OnTransportFeedback

    result = delay_based_bwe_->IncomingPacketFeedbackVector更新接收端时延信息到DelayBasedBwe类。
    该类使用InterArrival、TrendlineEstimator、OveruseDetector、AimdRateControl等类计算BWE值。详细实现,请参见:

    Delay_based_bwe.cc
    inter_arrival.cc
    trendline_estimator.cc
    overuse_detector.cc
    Aimd_rate_control.cc

    bitrate_controller_->OnDelayBasedBweResult生效rendline计算BWE值,到SendSideBandwidthEstimation。函数调用栈如下:

    BitrateControllerImpl::OnDelayBasedBweResult
    ->SendSideBandwidthEstimation::UpdateDelayBasedEstimate
    ->SendSideBandwidthEstimation::CapBitrateToThresholds

    二、基于丢包拥塞控制

    BaseChannel::ProcessPacket
    ->WebRtcVideoChannel::OnRtcpReceived
    ->Call::DeliverPacket
    ->Call::DeliverRtcp
    ->VideoSendStream::DeliverRtcp     
    ->VideoSendStreamImpl::DeliverRtcp
    ->ModuleRtpRtcpImpl::IncomingRtcpPacket
    ->RTCPReceiver::IncomingPacket
    ->RTCPReceiver::TriggerCallbacksFromRtcpPacket
    ->BitrateControllerImpl::RtcpBandwidthObserverImpl::OnReceivedRtcpReceiverReport
    ->BitrateControllerImpl::OnReceivedRtcpReceiverReport
    ->SendSideBandwidthEstimation::UpdateReceiverBlock
    ->SendSideBandwidthEstimation::UpdateEstimate

    具体调整函数如下:

    low_loss_threshold_ =  kDefaultLowLossThreshold = 0.02f;
    high_loss_threshold_ =  kDefaultHighLossThreshold = 0.1f;

    void SendSideBandwidthEstimation::UpdateEstimate(int64_t now_ms) {
      uint32_t new_bitrate = current_bitrate_bps_;
      // We trust the REMB and/or delay-based estimate during the first 2 seconds if
      // we haven't had any packet loss reported, to allow startup bitrate probing.
      if (last_fraction_loss_ == 0 && IsInStartPhase(now_ms)) {
        new_bitrate = std::max(bwe_incoming_, new_bitrate);
        new_bitrate = std::max(delay_based_bitrate_bps_, new_bitrate);
    
        if (new_bitrate != current_bitrate_bps_) {
          min_bitrate_history_.clear();
          min_bitrate_history_.push_back(
              std::make_pair(now_ms, current_bitrate_bps_));
          CapBitrateToThresholds(now_ms, new_bitrate);
          return;
        }
      }
      UpdateMinHistory(now_ms);
      if (last_packet_report_ms_ == -1) {
        // No feedback received.
        CapBitrateToThresholds(now_ms, current_bitrate_bps_);
        return;
      }
      int64_t time_since_packet_report_ms = now_ms - last_packet_report_ms_;
      int64_t time_since_feedback_ms = now_ms - last_feedback_ms_;
      if (time_since_packet_report_ms < 1.2 * kFeedbackIntervalMs) {
        // We only care about loss above a given bitrate threshold.
        float loss = last_fraction_loss_ / 256.0f;
        // We only make decisions based on loss when the bitrate is above a
        // threshold. This is a crude way of handling loss which is uncorrelated
        // to congestion.
        if (current_bitrate_bps_ < bitrate_threshold_bps_ ||
            loss <= low_loss_threshold_) {
          // Loss < 2%: Increase rate by 8% of the min bitrate in the last
          // kBweIncreaseIntervalMs.
          // Note that by remembering the bitrate over the last second one can
          // rampup up one second faster than if only allowed to start ramping
          // at 8% per second rate now. E.g.:
          //   If sending a constant 100kbps it can rampup immediatly to 108kbps
          //   whenever a receiver report is received with lower packet loss.
          //   If instead one would do: current_bitrate_bps_ *= 1.08^(delta time),
          //   it would take over one second since the lower packet loss to achieve
          //   108kbps.
          new_bitrate = static_cast<uint32_t>(
              min_bitrate_history_.front().second * 1.08 + 0.5);
    
          // Add 1 kbps extra, just to make sure that we do not get stuck
          // (gives a little extra increase at low rates, negligible at higher
          // rates).
          new_bitrate += 1000;
        } else if (current_bitrate_bps_ > bitrate_threshold_bps_) {
          if (loss <= high_loss_threshold_) {
            // Loss between 2% - 10%: Do nothing.
          } else {
            // Loss > 10%: Limit the rate decreases to once a kBweDecreaseIntervalMs
            // + rtt.
            if (!has_decreased_since_last_fraction_loss_ &&
                (now_ms - time_last_decrease_ms_) >=
                    (kBweDecreaseIntervalMs + last_round_trip_time_ms_)) {
              time_last_decrease_ms_ = now_ms;
    
              // Reduce rate:
              //   newRate = rate * (1 - 0.5*lossRate);
              //   where packetLoss = 256*lossRate;
              new_bitrate = static_cast<uint32_t>(
                  (current_bitrate_bps_ *
                   static_cast<double>(512 - last_fraction_loss_)) /
                  512.0);
              has_decreased_since_last_fraction_loss_ = true;
            }
          }
        }
      } else if (time_since_feedback_ms >
                     kFeedbackTimeoutIntervals * kFeedbackIntervalMs &&
                 (last_timeout_ms_ == -1 ||
                  now_ms - last_timeout_ms_ > kTimeoutIntervalMs)) {
        if (in_timeout_experiment_) {
          LOG(LS_WARNING) << "Feedback timed out (" << time_since_feedback_ms
                          << " ms), reducing bitrate.";
          new_bitrate *= 0.8;
          // Reset accumulators since we've already acted on missing feedback and
          // shouldn't to act again on these old lost packets.
          lost_packets_since_last_loss_update_Q8_ = 0;
          expected_packets_since_last_loss_update_ = 0;
          last_timeout_ms_ = now_ms;
        }
      }
    
      CapBitrateToThresholds(now_ms, new_bitrate);
    }

    三、最终确定Send Side BWE值

    void SendSideBandwidthEstimation::CapBitrateToThresholds(int64_t now_ms,
                                                             uint32_t bitrate_bps) {
      if (bwe_incoming_ > 0 && bitrate_bps > bwe_incoming_) {
        bitrate_bps = bwe_incoming_;
      }
      if (delay_based_bitrate_bps_ > 0 && bitrate_bps > delay_based_bitrate_bps_) {
        bitrate_bps = delay_based_bitrate_bps_;
      }
      if (bitrate_bps > max_bitrate_configured_) {
        bitrate_bps = max_bitrate_configured_;
      }
      if (bitrate_bps < min_bitrate_configured_) {
        if (last_low_bitrate_log_ms_ == -1 ||
            now_ms - last_low_bitrate_log_ms_ > kLowBitrateLogPeriodMs) {
          LOG(LS_WARNING) << "Estimated available bandwidth " << bitrate_bps / 1000
                          << " kbps is below configured min bitrate "
                          << min_bitrate_configured_ / 1000 << " kbps.";
          last_low_bitrate_log_ms_ = now_ms;
        }
        bitrate_bps = min_bitrate_configured_;
      }
    
      if (bitrate_bps != current_bitrate_bps_ ||
          last_fraction_loss_ != last_logged_fraction_loss_ ||
          now_ms - last_rtc_event_log_ms_ > kRtcEventLogPeriodMs) {
        event_log_->LogLossBasedBweUpdate(bitrate_bps, last_fraction_loss_,
                                          expected_packets_since_last_loss_update_);
        last_logged_fraction_loss_ = last_fraction_loss_;
        last_rtc_event_log_ms_ = now_ms;
      }
      current_bitrate_bps_ = bitrate_bps;
    }

    四、更新码率值到pacer、fec、encode模块

    void SendSideCongestionController::MaybeTriggerOnNetworkChanged() {
      uint32_t bitrate_bps;
      uint8_t fraction_loss;
      int64_t rtt;
      bool estimate_changed = bitrate_controller_->GetNetworkParameters(
          &bitrate_bps, &fraction_loss, &rtt);
      if (estimate_changed) {
        pacer_->SetEstimatedBitrate(bitrate_bps);
        probe_controller_->SetEstimatedBitrate(bitrate_bps);
        retransmission_rate_limiter_->SetMaxRate(bitrate_bps);
      }
    
      bitrate_bps = IsNetworkDown() || IsSendQueueFull() ? 0 : bitrate_bps;
    
      if (HasNetworkParametersToReportChanged(bitrate_bps, fraction_loss, rtt)) {
        int64_t probing_interval_ms;
        {
          rtc::CritScope cs(&bwe_lock_);
          probing_interval_ms = delay_based_bwe_->GetExpectedBwePeriodMs();
        }
        {
          rtc::CritScope cs(&observer_lock_);
          if (observer_) {
            observer_->OnNetworkChanged(bitrate_bps, fraction_loss, rtt,
                                        probing_interval_ms);
          }
        }
      }
    }

     

    函数详细调用关系,可以参考《webrtc QOS方法四.1(Sender Side BWE函数实现调用关系图)

    附录

    一、概念说明

    由于webrtc里面包含GCC、Sendside-BWE两种拥塞控制算法。M55之前用的是GCC,M55之后用的是Sendside-BWE。里面有很多概念梳理如下,以免混淆,不方便理解代码。

     GCCSendside-BWE
    码控计算模块接收端发送端
    RTP头部扩展AbsSendTimeTransportSequenceNumber
    接收端关键对象RemoteBitrateEstimatorAbsSendTimeRemoteEstimatorProxy
    网络延时滤波器Kalman FilterTrendline Filter
    接收端反馈RTCP报文REMBTransportCC

    二、BWE三个典型的算法

    Google Congest Control(https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02)、

    爱立信的SCEAM(https://github.com/EricssonResearch/scream)

    MIT的SPROUT(http://aim.nms.lcs.mit.edu/papers/nsdi13-sprout.pdf)。

    Mozilla的这篇文章讲述了拥塞控制算法演变的历史(https://blog.mozilla.org/webrtc/what-is-rmcat-congestion-control/)

    参考

    http://www.freehacker.cn/media/tcc-vs-gcc/?utm_source=tuicool&utm_medium=referral

    展开全文
  • 源码已开源到Github,详细代码可以查看:《React Native 触摸事件代码实践》。 在基础篇,对RN中的触摸事件做了详细的介绍。相信大家对于触摸事件流程机制有了更为清晰的认识。没有浏览的可以先看看基础篇:《 ...
  • 小程序图片裁剪we-cropper的使用

    千次阅读 2019-05-27 16:05:06
    // 插件通过touchStart、touchMove、touchEnd方法来接收事件对象。 touchStart(e) { this.cropper.touchStart(e) }, touchMove(e) { this.cropper.touchMove(e) }, touchEnd(e) { this.cropper.touchEnd(e)...
  • Android ViewGroup事件分发机制

    万次阅读 多人点赞 2014-09-09 09:38:03
    上一篇已经完整的解析了Android View的事件分发机制,今天给大家代码ViewGroup事件分发的源码解析~~凡是自定义ViewGroup实现各种滑动效果的,不可避免的会出现很多事件的冲突,对ViewGroup事件分发机制的了解,也...
  • 支持的事件回调很单一 从文档上看只是能支持横向滑动 拓展性不强we-swiper插件: 丰富的事件回调 丰富的属性 支持横、纵向滑动 强拓展(可在原插件基础上二次开发) ScreenShots横向滚动 (此图片来源于网络,如有侵权...
  • we-swiper 微信小程序触摸内容滑动解决方案,API设计细节及命名参考于. 为什么要开发这款插件 官方swiper组件: 支持的事件回调很单一 从文档上看只是能支持横向滑动(官方swiper组件实际是支持纵向滚动的,设置属性...
  • 文章目录引言事件事件数据结构事件控制块与事件相关的宏定义创建事件销毁事件 引言 大家在裸机编程中很可能经常用到flag这种变量,用来标志一下某个事件的发生,然后在循环中判断这些标志是否发生,如果是等待多个...
  • Android Touch事件分发过程

    万次阅读 多人点赞 2014-08-31 13:38:48
    Android Touch 事件分发过程深度分析
  • 事件分发机制是android中的核心知识点和难点。相信很多人也和我一样对于这点感到非常困惑。我看了很多篇博客和书面资料。今天我们就聊聊事件的分发机制。 一、点击事件的传递规则 1、什么是点击事件(MotionEvent) ...
  • Android TouchEvent事件传递机制初识 二. android点击事件传递源码讲解(ViewGroup) 三.android点击事件传递源码讲解(View) 1. 前言android点击 事件一直以来都是很多安卓程序员的心病,之前通过demo模拟总结...
  • 小程序中提供了上传文件的api wx....我个可以从 github 上找到一个插件 we-cropper 还有一个插件叫image-cropper(这个有兴趣可以研究一下,本人没有用过) 地址:https://github.com/we-plugin/we-cropper ...
  • 其实我一直准备写一篇关于Android事件分发机制的文章,从我的第一篇博客开始,就零零散散在好多地方使用到了Android事件分发的知识。也有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何...
  • Android事件分发机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 Android事件分发机制的简介 Android事件分发机制的...
  • Android事件分发机制

    千次阅读 2017-07-07 11:18:46
    在android开发中会经常遇到滑动冲突(比如ScrollView或是SliddingMenu与ListView的嵌套)的问题,需要我们深入的了解android事件响应机制才能解决,事件响应机制已经是android开发者必不可少的知识。面试找工作的...
  • 一、前言android的事件分发,大多数人都是似懂非懂,很多时候就卡在事件冲突这一步。比如在按钮上不能滑动出侧边栏,比如说ViewPager和banner冲突。我之前也是这样,然后狠下心去看了一遍源码,并且看了很多大神的...
  • tp6事件的用法

    千次阅读 2019-09-26 17:05:48
    新版的事件系统可以看成是5.1版本行为系统的升级版,事件系统相比行为系统强大的地方在于事件本身可以是一个类,并且可以更好的支持事件订阅者。 事件相比较中间件的优势是事件比中间件更加精准定位(或者说粒度更...
  • Android ViewGroup拦截触摸事件详解

    万次阅读 2014-10-01 21:29:13
    在自定义ViewGroup中,有时候需要实现触摸事件拦截,比如ListView下拉刷新就是典型的触摸事件拦截的例子。触摸事件拦截就是在触摸事件被parent view拦截,而不会分发给其child,即使触摸发生在该child身上。被拦截的...
  • WeOnlyDo.Server服务器介绍

    千次阅读 2015-10-10 10:42:38
    WeOnlyDo.Server服务器相当于自己写的Web服务器,不过和IIS、Tomcat比起来会简单很多,但用来实现自己的功能也会非常方便,笔者现在手头有C#和VB实现的两个版本。 WeOnlyDo.Server服务器的核心类是:WeOnlyDo....
  • 基于 LeanCloud 的事件流系统。AVObject、AVUser、AVFile、AVStatus、Follower、Followee、Pointer…… Backend 用公共账号登录 ,账号/密码:/Public123 ,选择应用 WeShare 即可查看表结构。 Document
  • 分析完事件的读取后,东忙西忙,不知不觉已过去了快五个月了…也不是说没有事件把这部分源码分析完,而是实在是分析不下去,因此转战到其他地方去了。然而这一块始终是心头那不舍的惦记,经过一段时间的沉淀,有参考...
  • 1.创建了一个InputQueue,它表示一个输入事件队列并设置事件回调。 2.创建一个WindowInputEventReceiver的实例负责读取事件。 因此,目前的关键就是WindowInputEventReceiver到底是怎么读取事件的。 下面我们...
  • Android View 事件分发机制 源码解析 (上)

    万次阅读 多人点赞 2014-09-02 09:32:38
    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~首先我们先写个简单的例子来测试View的事件转发的流程~1、案例为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,297
精华内容 46,918
关键字:

we事件