以下文章用于记录个人学习使用,如有出入请帮忙指正,不胜感激!
承接上编博文分析:https://blog.youkuaiyun.com/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)