瑞芯微rv1126 SDK代码基础分析---Flow::StartStream()分析(1)

以下文章用于记录个人学习使用,如有出入请帮忙指正,不胜感激!

承接上编博文分析:https://blog.csdn.net/zhfabel/article/details/119177540

代码位置:$(SDK)\external\rkmedia\src\flow.cc

void Flow::StartStream() {

  source_start_cond_mtx->lock();

  waite_down_flow = false;

  source_start_cond_mtx->notify();

  source_start_cond_mtx->unlock();

}

 主要此句函数作用waite_down_flow = false;

$(SDK)\external\rkmedia\src\flow\source_stream_flow.cc

void SourceStreamFlow::ReadThreadRun() {
  prctl(PR_SET_NAME, this->tag.c_str());
  source_start_cond_mtx->lock();
  if (waite_down_flow) {
    if (down_flow_num == 0 && IsEnable()) {
      source_start_cond_mtx->wait();
    }
  }
  source_start_cond_mtx->unlock();
  while (loop) {
    if (stream->Eof()) {
      // TODO: tell that I reach eof
      SetDisable();
      break;
    }
    auto buffer = stream->Read();
    SendInput(buffer, 0);
  }
}

auto buffer = stream->Read(); stream流中取流buffer,stream再做分析

 SendInput(buffer, 0); 数据流buffer,发送给flow的input

SendInput查阅为Flow基类中申明及定义,非虚函数,SourceStreamFlow继承之

SendInput函数位置:$(SDK)\external\rkmedia\src\flow.cc

void Flow::SendInput(std::shared_ptr<MediaBuffer> &input, int in_slot_index) {
  if (in_slot_index < 0 || in_slot_index >= input_slot_num) {
    errno = EINVAL;
    LOG("ERROR: Input slot[%d] is vaild!\n", in_slot_index);
    return;
  }
  if (enable) {
    auto &in = v_input[in_slot_index];
    CALL_MEMBER_FN(in, in.send_input_behavior)(input);
  }
}

CALL_MEMBER_FN(in, in.send_input_behavior)(input);此函数导致回调,回调v_input( std::vector<Input> v_input;)类中的send_input_behavior函数指针,该函数指针在

void Flow::Input::Init(Flow *f, Model m, int mcn, InputMode im, bool f_block, std::shared_ptr<FlowCoroutine> fc)中被初始化,实际调用函数根据同步方式执行如下函数的其一:

&Input::ASyncSendInputCommonBehavior;

&Input::ASyncSendInputAtomicBehavior;

&Input::SyncSendInputBehavior;

void Flow::Input::Init(Flow *f, Model m, int mcn, InputMode im, bool f_block,
                       std::shared_ptr<FlowCoroutine> fc) {
  assert(!valid);
  valid = true;
  flow = f;
  thread_model = m;
  fetch_block = f_block;
  max_cache_num = mcn;
  mode_when_full = im;
  switch (m) {
  case Model::ASYNCCOMMON:
    send_input_behavior = &Input::ASyncSendInputCommonBehavior;
    break;
  case Model::ASYNCATOMIC:
    send_input_behavior = &Input::ASyncSendInputAtomicBehavior;
    break;
  case Model::SYNC:
    send_input_behavior = &Input::SyncSendInputBehavior;
    coroutine = fc;
    break;
  default:
    break;
  }
  switch (im) {
  case InputMode::BLOCKING:
    async_full_behavior = &Input::ASyncFullBlockingBehavior;
    break;
  case InputMode::DROPFRONT:
    async_full_behavior = &Input::ASyncFullDropFrontBehavior;
    break;
  case InputMode::DROPCURRENT:
    async_full_behavior = &Input::ASyncFullDropCurrentBehavior;
    break;
  default:
    break;
  }
}

以&Input::SyncSendInputBehavior为例继续分析:

void Flow::Input::SyncSendInputBehavior(std::shared_ptr<MediaBuffer> &input) {
  cached_buffer = input;
  coroutine->RunOnce();
  cached_buffer.reset();
}

实际执行协同程序中的coroutine->RunOnce();执行一次。

void FlowCoroutine::RunOnce() {
  bool ret = true;
  (this->*fetch_input_func)(in_vector);

  if (flow->GetRunTimesRemaining()) {
#ifndef NDEBUG
    {
      AutoDuration ad;
#endif
      is_processing = true;
      ret = (*th_run)(flow, in_vector);
      is_processing = false;
#ifndef NDEBUG
      if (expect_process_time > 0)
        check_consume_time(name.c_str(), expect_process_time,
                           (int)(ad.Get() / 1000));
    }
#endif // DEBUG
  }

  for (int idx : out_slots) {
    auto &fm = flow->downflowmap[idx];
    std::list<Flow::FlowInputMap> flows;
    fm.list_mtx.read_lock();
    flows = fm.flows;
    fm.list_mtx.unlock();
    (this->*send_down_func)(fm, in_vector, flows, ret);
  }
  for (auto &buffer : in_vector)
    buffer.reset();

  pthread_yield();
}

其中存在2个关键回调函数:

(this->*fetch_input_func)(in_vector);此函数最终调用,flow协同类中的方法:

根据同步方式选择执行对应分支回调函数,以&FlowCoroutine::SyncFetchInput分析

bool FlowCoroutine::Start() {
  bool need_thread = false;
  auto func = &FlowCoroutine::WhileRun;
  switch (model) {
  case Model::ASYNCCOMMON:
    need_thread = true;
    fetch_input_func = &FlowCoroutine::ASyncFetchInputCommon;
    send_down_func = &FlowCoroutine::SendBufferDownFromDeque;
    break;
  case Model::ASYNCATOMIC:
    need_thread = true;
    fetch_input_func = &FlowCoroutine::ASyncFetchInputAtomic;
    send_down_func = &FlowCoroutine::SendBufferDown;
    func = &FlowCoroutine::WhileRunSleep;
    break;
  case Model::SYNC:
    fetch_input_func = &FlowCoroutine::SyncFetchInput;
    send_down_func = &FlowCoroutine::SendBufferDown;
    break;
  default:
    LOG("invalid model %d\n", (int)model);
    return false;
  }
  in_vector.resize(in_slots.size());
  if (need_thread) {
    th = new std::thread(func, this);
    if (!th) {
      errno = ENOMEM;
      return false;
    }
  }
  return true;
}

SyncFetchInput作用:遍历flow->input的cache buffer传给MediaBufferVector 向量,并做cache buffer的清空动作

void FlowCoroutine::SyncFetchInput(MediaBufferVector &in) {
  int i = 0;
  for (int idx : in_slots) {
    auto &buffer = flow->v_input[idx].cached_buffer;
    in[i++] = buffer;
    buffer.reset();
  }
}

ret = (*th_run)(flow, in_vector);其中th_run为FunctionProcess类型的函数指针由其构造函数传入

flow.cc中的InstallSlotMap下的函数

auto c = std::make_shared<FlowCoroutine>(this, map.thread_model, map.process, map.interval);

SlotMap下的process是什么函数,有什么作用?先看下SlotMap类定义作用。

SlotMap类代码位置:$(SDK)\external\rkmedia\include\easymedia\flow.h

class _API SlotMap {
public:
  SlotMap()
      : thread_model(Model::SYNC), mode_when_full(InputMode::DROPFRONT),
        process(nullptr), interval(16.66f) {}
  std::vector<int> input_slots;
  Model thread_model;
  InputMode mode_when_full;
  std::vector<bool> fetch_block; // if ASYNCCOMMON
  std::vector<int> input_maxcachenum;
  std::vector<int> output_slots;
  // std::vector<DataSetModel> output_ds_model;
  std::vector<HoldInputMode> hold_input;
  FunctionProcess process;
  float interval;
};

个人理解为:视频流缓存池的管理map,里面定义同步方式,满缓池的处理方式,功能函数(待分析),间隔16.67(用途待分析);

etch_block定义MediaBufferVector 缓存池中缓存块对应对应向量的获取数据阻塞or非阻塞;input_maxcachenum:定义缓存池中缓存块节点的最大数量(vector不能无限增长下去);

output_slots:缓存池出列数量

hold_input:对应缓冲池中缓存块的是否插入模式

所以从定义来看,InstallSlotMap作用就是插入缓存池的管理类,用于管理缓存池

InstallSlotMap函数又是什么时机被调用,谁调用?传入的SlotMap对象是被初始化了什么参数?

查找了下发现很多地方调用,该函数很关键,是所有flow对象的运行开始函数。

以下函数代码段做分析:

调用InstallSlotMap函数段位置:${SDK}\external\rkmedia\src\flow.cc

代码段如下:

bool Flow::SetAsSource(const std::vector<int> &output_slots, FunctionProcess f,
                       const std::string &mark) {
  source_start_cond_mtx = std::make_shared<ConditionLockMutex>();
  if (!source_start_cond_mtx)
    return false;
  SlotMap sm;
  sm.input_slots = std::vector<int>{0};
  sm.output_slots = output_slots;
  sm.process = f;
  sm.thread_model = Model::SYNC;
  sm.mode_when_full = InputMode::DROPFRONT;
  if (!InstallSlotMap(sm, mark, 0)) {
    LOG("Fail to InstallSlotMap, read %s\n", mark.c_str());
    return false;
  }
  return true;
}

该函数从字面理解应该是将某个flow设置为source,作为下级flow的sink,类同于Linux v4l2的media框架概论。

以SetAsSource调用者进一步分析,代码段如下:

$(SDK\external\rkmedia\src\flow\source_stream_flow.cc

SourceStreamFlow::SourceStreamFlow(const char *param)
    : loop(false), read_thread(nullptr) {
  std::list<std::string> separate_list;
  std::map<std::string, std::string> params;
  if (!ParseWrapFlowParams(param, params, separate_list)) {
    SetError(-EINVAL);
    return;
  }
  std::string &name = params[KEY_NAME];
  const char *stream_name = name.c_str();
  const std::string &stream_param = separate_list.back();
  stream = REFLECTOR(Stream)::Create<Stream>(stream_name, stream_param.c_str());
  if (!stream) {
    LOG("Create stream %s failed\n", stream_name);
    SetError(-EINVAL);
    return;
  }
  tag = "SourceFlow:";
  tag.append(name);
  if (!SetAsSource(std::vector<int>({0}), void_transaction00, tag)) {
    SetError(-EINVAL);
    return;
  }
  loop = true;
  read_thread = new std::thread(&SourceStreamFlow::ReadThreadRun, this);
  if (!read_thread) {
    loop = false;
    SetError(-EINVAL);
    return;
  }
  SetFlowTag(tag);
}

其他代码暂不做分析,以SetAsSource(std::vector<int>({0}), void_transaction00, tag)传参分析,看传入的参数:

1. std::vector<int>({0}),int 容器初始化为0,重点void_transaction00函数,tag是标签 tag = "SourceFlow:" 表示此为源flow,如前面分析。

先看下其定义:  static const FunctionProcess void_transaction00;

位置:$(SDK)\external\rkmedia\include\easymedia\flow.h的flow定义的静态成员函数,初始化在flow.cc 中开始位置const FunctionProcess Flow::void_transaction00 = void_transaction<0, 0>;

所以实际调用者为:void_transaction<0, 0>;此函数原型为flow.h中定义如下:

bool void_transaction(Flow *f, MediaBufferVector &input_vector);从void_transaction<0, 0>调用形式看应该是一个模板函数,继续查实际flow中的友联函数:

  template <int in_index, int out_index>
  friend bool void_transaction(Flow *f, MediaBufferVector &input_vector) {
    return f->SetOutput(input_vector[in_index], out_index);
  }

转了一圈又调回flow类中的SetOutput函数,传入参数为有in_index,MediaBufferVector的缓存池块,索引值为in_index,out_index不变,转为故重点分析下SetOutput函数:

代码段如下:$(SDK)\external\rkmedia\src\flow.cc

bool Flow::SetOutput(const std::shared_ptr<MediaBuffer> &output,
                     int out_slot_index) {
  if (out_slot_index < 0 || out_slot_index >= out_slot_num) {
    errno = EINVAL;
    LOG("ERROR: Output slot[%d] is vaild!\n", out_slot_index);
    return false;
  }

  if (out_callback_ && output)
    out_callback_(out_handler_, output);

  if (enable) {
    auto &out = downflowmap[out_slot_index];
    CALL_MEMBER_FN(out, out.set_output_behavior)(output);
    return true;
  }
  return false;
}

out_callback_(out_handler_, output);作用分析:out_callback_函数指针是由如下函数传入:

$(SDK)\external\rkmedia\include\easymedia\flow.h

  void SetOutputCallBack(CallBackHandler handler, OutputCallBack callback) {
    out_handler_ = handler;
    out_callback_ = callback;
  }

SetOutputCallBack函数又是何时机被调用?传入实际又是哪种函数?

以${SDK}\external\rkmedia\examples\uintTest\flow\video_encoder_flow_test.cc的函数分析,先看下video_encoder_flow的定义,此定义方式上一篇博客有做分析,也就是实例化flow类的继承类video_encoder_flow = easymedia::REFLECTOR(Flow)::Create<easymedia::Flow>,所以从整体来看flow的回调,虚函数等都是继承类中实例化,具体做业务flow的功能,其中video_encoder_flow_test.cc的main函数中做了很多事情,其中我们需要分析的是如下函数段:

  easymedia::video_encoder_enable_statistics(video_encoder_flow, 1);

  if (output_cb_enable) {
    LOG("Regest callback handle:%p\n", cb_str);
    video_encoder_flow->SetOutputCallBack((void*)cb_str, encoder_output_cb);
  } else {
    video_encoder_flow->AddDownFlow(video_save_flow, 0, 0);
  }
  video_read_flow->AddDownFlow(video_encoder_flow, 0, 0);

encoder_output_cb就是被传入的函数指针,其干了什么事,进一步分下:

void encoder_output_cb(void *handle, std::shared_ptr<easymedia::MediaBuffer> mb) {
  printf("==Encoder Output CallBack recived: %p(%s), %p, %d, %dBytes\n",
    handle, (char *)handle, mb->GetPtr(), mb->GetFD(),
    mb->GetValidSize());

  if (save_file)
    fwrite(mb->GetPtr(), 1, mb->GetValidSize(), save_file);
}

哈哈,到这里就会看到我们缓存池块MediaBuffer的用途了,就能理解前面所分析的MediaBufferVector 的作用,printf清晰可见其查看MediaBuffer的调用flow即传入的handle参数,MediaBuffer的大小,指针及句柄,当前flow的MediaBuffer的内部使用用途。

回到flow类中的SetOutput函数,调用者SetAsSource函数,SetOutput的作用就是定义的flow继承对象对MediaBuffer的实际操作方法。




接下回到void FlowCoroutine::RunOnce()函数,继续分析,以上2个回调函数主要是初始化MediaBuffer和当前flow读取到的MediaBuffer的用途,接下来函数经分析是用于下级sink flow的输入,代码段如下:

  for (int idx : out_slots) {
    auto &fm = flow->downflowmap[idx];
    std::list<Flow::FlowInputMap> flows;
    fm.list_mtx.read_lock();
    flows = fm.flows;
    fm.list_mtx.unlock();
    (this->*send_down_func)(fm, in_vector, flows, ret);
  }

 搜索downflowmap也就是,找到下级flowmap中的FlowInputMap(从之前博客有分析FlowInputMap为Flow类的子类,由FlowMap管理),从这里也可以看出flow上下级联是通过当前flow的std::vector<FlowMap> downflowmap成员变量管理,然后FlowMap中又有std::list<FlowInputMap> flows成员变量,管理着1个或则多个FlowInputMap的对象。FlowInputMap类比较简单就是flow指针和索引,用于保存flow实际对象的指针和索引,放于list,便于检索管理。

作为send_down_func的回调函数的参数输入

下面看下send_down_func回调函数哪里初始化,具体用途;

初始化为位置:$(SDK)/external/rkmedia/src/flow.cc的bool FlowCoroutine::Start()函数内

  switch (model) {
  case Model::ASYNCCOMMON:
    need_thread = true;
    fetch_input_func = &FlowCoroutine::ASyncFetchInputCommon;
    send_down_func = &FlowCoroutine::SendBufferDownFromDeque;
    break;
  case Model::ASYNCATOMIC:
    need_thread = true;
    fetch_input_func = &FlowCoroutine::ASyncFetchInputAtomic;
    send_down_func = &FlowCoroutine::SendBufferDown;
    func = &FlowCoroutine::WhileRunSleep;
    break;
  case Model::SYNC:
    fetch_input_func = &FlowCoroutine::SyncFetchInput;
    send_down_func = &FlowCoroutine::SendBufferDown;
    break;
  default:
    LOG("invalid model %d\n", (int)model);
    return false;
  }

根据同步模式选择相应的回调函数,此处以&FlowCoroutine::SendBufferDown做进一步分析,此函数很短,贴出如下:

void FlowCoroutine::SendBufferDown(Flow::FlowMap &fm,
                                   const MediaBufferVector &in,
                                   std::list<Flow::FlowInputMap> &flows,
                                   bool process_ret) {
  if (!process_ret) {
    SendNullBufferDown(fm, in, flows);
    return;
  }
  for (auto &f : flows) {
    OutputHoldRelated(fm, fm.cached_buffer, in);
    f.flow->SendInput(fm.cached_buffer, f.index_of_in);
  }
  fm.cached_buffer.reset();
}

SendNullBufferDown(fm, in, flows);空缓存的而对应处理

因为一个Flow可能级联多个下级flow,所有用FlowInputMap中的lfows list连接,递归遍历下级flow 

OutputHoldRelated(fm, fm.cached_buffer, in);

从函数作用:取出上级flow中的buffer缓存指针,放入下级flow中的指针向量中

 f.flow->SendInput(fm.cached_buffer, f.index_of_in);

当前flow的下级所有flow调用SendInput函数处理上级flow中的数据。此SendInput函数又回到了本章开头分析的部分,由具体flow处理来自上级flow的缓存数据,至此就会形成一个pipe的数据流链路。




回归SetAsSource->InstallSlotMap->SlotMap::process-> (*th_run)(flow, in_vector)回调-> FlowCoroutine::RunOnce()-> Flow::Input::SyncSendInputBehavior-> Flow::SendInput::CALL_MEMBER_FN(in, in.send_input_behavior)(input)->SourceStreamFlow::ReadThreadRun()->Flow::StartStream(),以上为分析倒推流程,其中SourceStreamFlow::ReadThreadRun()的线程为重点,开始分析切入点,回归到该线程继续分析。

见下篇博客:瑞芯微rv1126 SDK代码基础分析---Flow::StartStream()分析(2)

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Live555是一款开源的多媒体流服务器,支持实时流媒体的传输。下面是部分Live555源代码分析: 1. MediaSession类 MediaSession类是Live555中最核心的类之一,它表示了一个媒体会话,包括了媒体流的传输协议、媒体编码格式、传输方式等信息。MediaSession类中的成员变量包括: - fServerMediaSession:代表了一个媒体服务器会话,负责提供媒体流的传输。 - fSubsessions:代表了一个或多个媒体流的传输会话,可以是RTP/RTCP、HTTP/RTSP等协议。 - fSdpLines:代表了SDP协议中的信息,可以是媒体流的编码格式、传输方式等信息。 MediaSession类中的核心方法包括: - createNew:用于创建一个新的媒体会话。 - addSubsession:用于添加一个媒体流的传输会话。 - generateSdpDescription:用于生成SDP协议描述信息。 - startStream:用于开始媒体流的传输。 - pauseStream:用于暂停媒体流的传输。 - seekStream:用于跳转媒体流的传输。 2. MediaSubsession类 MediaSubsession类表示了一个媒体流的传输会话,包括了媒体流的传输协议、媒体编码格式、传输方式等信息。MediaSubsession类中的成员变量包括: - fParentSession:代表了父级MediaSession类的实例。 - fRTPSink:代表了RTP数据的发送器。 - fRTCPInstance:代表了RTCP数据的发送器。 - fTrackNumber:代表了媒体流的轨道编号。 - fCodecName:代表了媒体流的编码格式。 - fMediumName:代表了媒体流的传输方式。 MediaSubsession类中的核心方法包括: - initiate:用于初始化媒体流的传输。 - startStream:用于开始媒体流的传输。 - pauseStream:用于暂停媒体流的传输。 - seekStream:用于跳转媒体流的传输。 3. RTSPServer类 RTSPServer类是Live555中实现RTSP协议的服务器类。RTSPServer类中的成员变量包括: - fServerMediaSession:代表了一个媒体服务器会话,负责提供媒体流的传输。 - fHTTPServerPort:代表了HTTP服务器的端口号。 - fRTSPServerPort:代表了RTSP服务器的端口号。 RTSPServer类中的核心方法包括: - createNew:用于创建一个新的RTSP服务器。 - start:用于启动RTSP服务器。 - stop:用于停止RTSP服务器。 - incomingConnectionHandler:用于处理RTSP客户端的连接请求。 - handleCmd_DESCRIBE:用于处理DESCRIBE命令。 - handleCmd_SETUP:用于处理SETUP命令。 - handleCmd_PLAY:用于处理PLAY命令。 - handleCmd_PAUSE:用于处理PAUSE命令。 - handleCmd_TEARDOWN:用于处理TEARDOWN命令。 以上是Live555源代码的部分分析,希望对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值