mad_decoder可以看作是整个解码器的“核心”结构。它用c语言有限的对象机制实现了面向对象语言的部分功能。decoder封装了一次解码过程中的所有必要成分,包括用户定义的消息、6个回调函数和相应的选项。mad_decoder结构在decoder.h头文件中定义:
struct mad_decoder { enum mad_decoder_mode mode; int options; struct { long pid; int in; int out; } async; struct { struct mad_stream stream; struct mad_frame frame; struct mad_synth synth; } *sync; void *cb_data; enum mad_flow (*input_func)(void *, struct mad_stream *); enum mad_flow (*header_func)(void *, struct mad_header const *); enum mad_flow (*filter_func)(void *, struct mad_stream const *, struct mad_frame *); enum mad_flow (*output_func)(void *, struct mad_header const *, struct mad_pcm *); enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *); enum mad_flow (*message_func)(void *, void *, unsigned int *); };
其中mode成员有MAD_DECODER_MODE_SYNC和MAD_DECODER_MODE_ASYNC两个取值,分别指定同步和异步两种工作方式;options被带入到sync成员的stream中的options;async成员携带异步解码时必须的状态消息;async成员不论同步还是异步方式都要由input回调函数填充并经历整个解码过程;cb_data是用户定义的消息结构,在解码过程开始时在各处理过程(如输入、过滤、输出、错误处理等)中传递状态;下面的6个回调函数可根据需要选择填充,比如最基本的解码至少要包含input和output,其他的回调函数都是可选的。
通常使用mad_decoder_inti()函数对一个mad_decoder变量进行初始化,正如本文开头和minimad.c里做的那样(这个文件可以在压缩包里找到)。之后这个mad_decoder变量就可以使用mad_decoder_run()函数进行解码了。解码过程中output回调函数不断地被调用(每解码完成一个mp3帧),直到所有的有效帧全部被解码或遇到一个不可忽略的错误,mad_decoder_run()函数才返回。
最后使用mad_decoder_finish()函数做最后的清理工作。
mad_decoder的封装不是严格意义上的封装。其中的成员完全保持了对外的可见性,只是在处理过程中将一个解码过程看作一个整体,其中的各成分存在于mad_decoder对象中。并非严格的面向对象意义上的“is a…”和“has a…”。
最后我们又回到了原点——6个回调函数。下面的章节我们会集中讨论他们。