我刚开始使用合并了ffmpeg-mt的最新版本的ffmpeg。
但是,由于我的应用程序使用TBB (Intel线程构建块),因此使用新线程创建和同步的ffmpeg-mt实现不太适合,因为它可能会阻止我的TBB任务执行解码函数。此外,它还会对缓存进行不必要的回收。
我在pthread.c中查看了一下,它似乎实现了ffmpeg用来启用多线程的接口。
我的问题是,是否有可能创建一个tbb.c,它实现相同的功能,但使用tbb任务而不是显式线程?
我没有使用C语言的经验,但我猜将tbb (它是C++)编译成ffmpeg是不可能的。因此,也许在运行时以某种方式覆盖ffmpeg函数指针将是可行的方法?
我非常感谢任何关于将TBB实现为ffmpeg线程api的建议或评论。
发布于 2011-05-19 21:35:36
所以我通过阅读ffmpeg代码找到了如何做到这一点。
基本上,您所要做的就是包含以下代码,并使用tbb_avcodec_open/tbb_avcodec_close而不是ffmpegs的avcodec_open/avcodec_close。
这将使用TBB任务并行执行解码。
// Author Robert Nagy
#include "tbb_avcodec.h"
#include <tbb/task.h>
#include <tbb/atomic.h>
extern "C"
{
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <libavformat/avformat.h>
}
int task_execute(AVCodecContext* s, std::function<int(void* arg, int arg_size, int jobnr, int threadnr)>&& func, void* arg, int* ret, int count, int size)
{
tbb::atomic<int> counter;
counter = 0;
// Execute s->thread_count number of tasks in parallel.
tbb::parallel_for(0, s->thread_count, 1, [&](int threadnr)
{
while(true)
{
int jobnr = counter++;
if(jobnr >= count)
break;
int r = func(arg, size, jobnr, threadnr);
if (ret)
ret[jobnr] = r;
}
});
return 0;
}
int thread_execute(AVCodecContext* s, int (*func)(AVCodecContext *c2, void *arg2), void* arg, int* ret, int count, int size)
{
return task_execute(s, [&](void* arg, int arg_size, int jobnr, int threadnr) -> int
{
return func(s, reinterpret_cast<uint8_t*>(arg) + jobnr*size);
}, arg, ret, count, size);
}
int thread_execute2(AVCodecContext* s, int (*func)(AVCodecContext* c2, void* arg2, int, int), void* arg, int* ret, int count)
{
return task_execute(s, [&](void* arg, int arg_size, int jobnr, int threadnr) -> int
{
return func(s, arg, jobnr, threadnr);
}, arg, ret, count, 0);
}
void thread_init(AVCodecContext* s)
{
static const size_t MAX_THREADS = 16; // See mpegvideo.h
static int dummy_opaque;
s->active_thread_type = FF_THREAD_SLICE;
s->thread_opaque = &dummy_opaque;
s->execute = thread_execute;
s->execute2 = thread_execute2;
s->thread_count = MAX_THREADS; // We are using a task-scheduler, so use as many "threads/tasks" as possible.
}
void thread_free(AVCodecContext* s)
{
s->thread_opaque = nullptr;
}
int tbb_avcodec_open(AVCodecContext* avctx, AVCodec* codec)
{
avctx->thread_count = 1;
if((codec->capabilities & CODEC_CAP_SLICE_THREADS) && (avctx->thread_type & FF_THREAD_SLICE))
thread_init(avctx);
// ff_thread_init will not be executed since thread_opaque != nullptr || thread_count == 1.
return avcodec_open(avctx, codec);
}
int tbb_avcodec_close(AVCodecContext* avctx)
{
thread_free(avctx);
// ff_thread_free will not be executed since thread_opaque == nullptr.
return avcodec_close(avctx);
}发布于 2011-05-24 19:28:49
在这里转载我对你的回复,at the TBB forum,为了任何可能感兴趣的人。
在我看来,上面答案中的代码很不错;这是在考虑到本机线程的上下文中使用TBB的一种聪明方式。我想知道是否可以让它更TBBish,可以这么说。我有一些想法,如果你有时间和愿望,你可以尝试一下。
如果希望/需要控制线程的数量,则以下两项可能是有意义的。
tbb::task_scheduler_init (TSI)对象,并使用所需数量的线程对其进行初始化(不需要MAX_THREADS)。如果可能/允许,则将此对象的地址保留在s->thread_opaque中;如果不允许,则可能的解决方案是全局映射,将AVCodecContext*映射到thread_free中相应的TSI的地址,获取并删除该TSI对象。与上面的内容无关,另一个潜在的变化是如何调用tbb::parallel_for。与其仅仅用它来创建足够的线程,不如直接使用它,就像下面这样?
int task_execute(AVCodecContext* s,
std::function<int(void*, int, int, int)>&& f,
void* arg, int* ret, int count, int size)
{
tbb::atomic<int> counter;
counter = 0;
// Execute 'count' number of tasks in parallel.
tbb::parallel_for(tbb::blocked_range<int>(0, count, 2),
[&](const tbb::blocked_range<int> &r)
{
int threadnr = counter++;
for(int jobnr=r.begin(); jobnr!=r.end(); ++jobnr)
{
int r = func(arg, size, jobnr, threadnr);
if (ret)
ret[jobnr] = r;
}
--counter;
});
return 0;
}如果count比thread_count大得多,这可以更好地执行,因为a)更多的并行松弛意味着TBB工作更有效(您显然知道这一点),以及b)集中式原子计数器的开销分布在更多的迭代中。请注意,我为blocked_range选择的粒度为2;这是因为计数器在循环体中既递增又递减,因此每个任务(以及相应的count>=2*thread_count)至少需要两次迭代才能“匹配”您的变量。
https://stackoverflow.com/questions/6049798
复制相似问题