• DOUAudioStreamer 中kqueue的应用


    DOUAudioStreamer是一个基于Core Audio的流式音频播放器,其中的DOUAudioEventLoop通过kqueue来控制音频的各种状态。

    kqueue简介(详情请看官方manual)

    kqueue的功能类似epoll,多用于后台多个socket连接时的I/O复用,注册感兴趣的event,把描述符链表交给内核,然后就等待。一旦有某个或多个事件发生,内核就把 一个只包含有发生了事件的描述符的链表通知给进程,由此避免了每次函数返回的时候都要去遍历整个链表(相较与select和poll)。尽管对于只打开了几个描述符的进程而言这点改进算不得什么,但对于那些打开了几千个文件描述符的程序来说,这种性能改进就相当显著了。

    在DOUAudioStreamer中kqueue系统调用生成一个之关联的唯一的描述符,通过kevent来注册和监听音频播放的各种状态的变化,以及变化后(即感兴趣的event发生后)的各种处理。

    typedef NS_ENUM(uint64_t, event_type) {
      event_play,        //播放
      event_pause,        //暂停
      event_stop,        //停止
      event_seek,        //手动选择播放位置
      event_streamer_changed,  //更换了streamer
      event_provider_events,    
      event_finalizing,      //dealloc中发送event,正在释放
    #if TARGET_OS_IPHONE
      event_interruption_begin,
      event_interruption_end,
      event_old_device_unavailable,  //最后一步
    #endif /* TARGET_OS_IPHONE */
    
      event_first = event_play,
    #if TARGET_OS_IPHONE
      event_last = event_old_device_unavailable,
    #else /* TARGET_OS_IPHONE */
      event_last = event_finalizing,
    #endif /* TARGET_OS_IPHONE */
    
      event_timeout
    };

    init中初始化过程中的_setupAudioSession(也可以[AVAudioSession sharedInstance] 来设置

     AudioSessionInitialize(NULL, NULL, audio_session_interruption_listener, (__bridge void *)self);   //注册被其他应用打断,或其他应用音频播放停止后的处理
    
    //设置MediaPlayback属性可在后台播放声音,也可以避免一些不插耳机时无声的问题 UInt32 audioCategory
    = kAudioSessionCategory_MediaPlayback; AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, audio_route_change_listener, (__bridge void *)self); AudioSessionSetActive(TRUE);

    kevent相关的操作

    /**
    * 官方manual中
    The EV_SET() macro is provided for ease of initializing a kevent structure.
    EVFILT_USER   
    Establishes a user event identified by ident which is not associated with any kernel mechanism but is triggered by user level code.

    *
    *
    **/

    //相当于register各种感兴趣事件
    - (void)_enableEvents { for (uint64_t event = event_first; event <= event_last; ++event) { struct kevent kev;
       /**从event_play到event_old_device_unavailable,监听 EV_ADD--添加event,EV_ENABLE--事件被触发后允许返回,EV_CLEAR--返回事件后,重新设置event的状态 **/
    EV_SET(
    &kev, event, EVFILT_USER, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, NULL); kevent(_kq, &kev, 1, NULL, 0, NULL); } } - (void)_sendEvent:(event_type)event { [self _sendEvent:event userData:NULL]; } - (void)_sendEvent:(event_type)event userData:(void *)userData { struct kevent kev;
    //EVFILT_USER通过NOTE_TRIGGER来触发 EV_SET(
    &kev, event, EVFILT_USER, 0, NOTE_TRIGGER, 0, userData); kevent(_kq, &kev, 1, NULL, 0, NULL); } - (event_type)_waitForEvent { return [self _waitForEventWithTimeout:NSUIntegerMax]; }
    //kevent监听着感兴趣的事件,一有可用事件就返回事件类型
    - (event_type)_waitForEventWithTimeout:(NSUInteger)timeout { struct timespec _ts; struct timespec *ts = NULL; if (timeout != NSUIntegerMax) { ts = &_ts; ts->tv_sec = timeout / 1000; ts->tv_nsec = (timeout % 1000) * 1000; } while (1) { struct kevent kev; int n = kevent(_kq, NULL, 0, &kev, 1, ts); if (n > 0) { if (kev.filter == EVFILT_USER && kev.ident >= event_first && kev.ident <= event_last) { _lastKQUserData = kev.udata; return kev.ident; } } else { break; } } return event_timeout; }

    最后 _waitForEventWithTimeout / _waitForEvent 被 event_loop_main线程方法中的_eventLoop不断调用,通过_waitForEvent返回的event_type来继续在_handleEvent中处理各种状态需要的操作;

    另外_handleEvent中用的是if-else,switch语句是不是性能更好一点,。

    链接

    kqueue官方manual

  • 相关阅读:
    1、三八妇女节
    16、领导休假了,如何让领导帮忙签一下考勤呢?
    30、如何获取百分号前面的数字?
    29、如何在单元格里面添加单位?
    13、笔记本中的Excel 如何锁定单元格的$符号?
    28、把鼠标放在Excel的单元格中,单元格右下角十字架不能往下拖动
    31、如何把公式和单位合并在一起?
    27、Excel高级筛选
    2、中秋节
    9、香港人在大陆的驾驶证年审过期了,可不可以延迟办理?
  • 原文地址:https://www.cnblogs.com/edisongz/p/6942253.html
Copyright © 2020-2023  润新知