ps:最近在做ffmpeg+rtmp的视频直播,这算是里面用到的一个小技术点。实际上这个技术很常用很常用,重要的事情多说一遍。

什么是call back?

  • call back的定义

    callback,又叫回调函数,是指用函数指针的形式将函数作为另一个函数的参数,当这个指针所指向的函数被调用时,这个函数就叫回调函数。

听起来是不是挺简单的,但是本文介绍的重点不在于如何定义一个函数指针,也不在于如何通过函数指针调用一个函数,而是介绍回调函数机制,重点在机制的分析,也就是通过回调函数的技术实现某些特定的功能。


emmmm, 好吧,还是先介绍一下如何定义一个函数指针:

void (*function_callback)(AVFrame *pframe);

这种定义方法当然没错,但是将这么长的一个式子放进另一个函数中作为参数,代码就有点不好看

  • 我们可以这么做:
typedef void (*function_callback)(AVFrame *pframe);
function_callback updatecallback;

注意,下面这两行代码里面的function_callback不是一个具体的函数指针了,这是通过typedef定义的一个函数指针类型,通过这个函数指针类型定义了一个函数指针updatecallback
也许你会问,typedef的形式不是应该像下面这样吗?

typedef char* pchar;

原因是,在定义函数声明时,会将函数指针放到前面,使其看起来像一个完整的函数形式,如果把函数指针拿到后面,不就和这种形式一样了麽?

对于回调函数,还有一种说法:

用户调用windows api叫做call, 就是调用,
windows api调用用户的函数叫call back

有这种说法的存在是因为windows的内核中确实存在大量的回调函数需要用户去实现,但实际上这是回调函数的一种应用场景,call back还有其他的很多应用场景,在下面介绍。

callback的应用场景

线程同步,底层代码与GUI交互,一个接口多个实现等

线程同步有很多种方式,其中 qt中有信号与槽的方式,在线程A中发送信号,线程B接受到信号执行槽函数,在linux中有信号量等,,,

  • 使用callback的实现方法如下:

  • 模块A在完成某一操作后需要调用模块B的操作

    如ffmpeg在视频解码完毕之后需要调用GUI层的函数来渲染图像,但是不同的平台的GUI又一样或者说GUI与ffmpeg不在一个线程中,在写ffmpeg的时候不知道调用哪个,此时,就可以用回调函数。
    不管这个是什么函数,先用函数指针的形式定义出来,再把它放在应该放的位置调用,最后提供一个设置callback的接口setcallback。
    别的模块按照这个函数指针的格式实现一个具体的函数,再调用setcallback的接口设置这个callback

  • 一个接口多个实现
    看例子:

void PrintMsg1(){
   printf("This is the message 1!\n");
}
 
void PrintMsg2(){
   printf("This is the message 2!\n");
}
 
void PrintMsg3(){
   printf("This is the message 3!\n");
}
 
void ShowMessage(void (* p)()){
	p;
}

int main(){
   ShowMessage(PrintMsg1);
   ShowMessage(PrintMsg2);
   ShowMessage(PrintMsg3);
}

callback的优点:灵活,跨平台

实现线程同步的代码示例

  • ffmpeg解码线程:
  • 声明:
typedef void (*function_callback)(AVFrame *pframe);

function_callback updatecallback;
  • 定义setupcallback:
void setupcallback(function_callback callback)
{
	updatecallback = callback;
}
  • callback使用:
void start_decode()
{
	...
	while(1)
	{
		...
		avcodec_decode_video2(pOutCodecCtx,&pframe,&dec_got_frame,&pkt);// 解码
		updatecallback(&pframe);// 刷新GUI
		...
	}
	...
}
  • qt gui线程

  • 实现callback:

static void onUpdateDecodedVideoData(AVFrame *pframe)
{
...
}
  • 调用setupcallback把上面的函数替换掉ffmpeg线程里的函数
setupcallback(onUpdateDecodedVideoData);

这里需要注意以下几点:
C++类成员函数的callback函数一般是静态的,原因在于C++的普通成员函数的参数会有一个默认的this,会导致callback函数的参数不匹配,编译器报错。
静态的成员函数无法调用非静态的成员变量,解决这个问题有个简单的方法就是在类内部定义一个静态的对象指针,把this指针赋值给这个静态的对象指针,在静态成员函数中使用这个静态对象代替this调用类内部的成员变量。


技术是在不断的讨论中进步的,如所述有不妥之处,希望能联系 devtty@qq.com 进行指正,万分感谢!


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Linux应用程序调试方法 上一篇
热拔插驱动 下一篇