找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 9504|回复: 4

【C】libFLAC的使用

[复制链接]
发表于 2014-7-14 03:37:11 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
1、什么是libFLAC?

libFLAC是用于操作FLAC文件的C开源工具。
libFLAC的官网:https://xiph.org/flac/

2、什么是FLAC?

FLAC是一种针对声音文件的无损压缩算法。压缩比略低于AAC,但是压缩和解压的速度很理想。使用FLAC压缩的无损音乐,体积将比没有经过压缩的无损音乐小很多(取决于音乐的平均音量。通常体积能减少到原文件的50%左右)。
相比较MP3有损压缩格式而言,FLAC能保留100%的音质。对于广大音乐爱好者而言,相对于MP3,FLAC是更好的选择。

libFLAC的源码请大家到libFLAC的官网下载。点此进入官方下载页面。
我这里有备用的下载地址。如果大家无法到官网下载libFLAC的源码,大家可以来我这里下载1.3.0版。强烈建议大家去官网下载最新版。
flac-1.3.0.tar.xz (1.03 MB, 下载次数: 29)










首先我们下载libFLAC的源码。然后用VS打开它的FLAC.sln这个文件。我用的是VS2012,因此出现了提示升级工程的对话框。我就将工程文件升级为VS2012的版本了。
20140714014600.png
看左边有很多工程。其中需要留意的是这几个工程:

C语言工程:
libFLAC_dynamic:编译它你能得到libFLAC.dll。这个DLL导出的函数可以帮你完成对FLAC文件的编码和解码的操作。
libFLAC_static:编译它,你能得到libFLAC.lib。当你既想操作FLAC文件,又不想让自己的程序依赖libFLAC.dll的时候,你可以使用它。
C++工程:
libFLAC++_dynamic:C++版的libFLAC.dll。功能和C语言版的一样,但是它是C++风格的,适合C++使用。
libFLAC++_static:C++版的libFLAC.lib。

这些是libFLAC的核心部分。当你展开它你会发现以下的文件:
nasm.PNG

这些文件是用NASM语法编写的汇编代码,用NASM编译,然后和C语言程序配合进行FLAC的编码和解码的。注意这是PC平台上的x86汇编。32位的。
我编译这个工程的时候NASM产生了“nasm.h:83: warning: COFF section names limited to 8 characters: truncating”这样的警告。意思是nasm.h第83行给段起名的时候超过了8个字符的长度,超出部分被截去。
这样的警告一般不会导致什么严重的错误,除非你为了编这个程序定义了各种奇怪的段,然后一不小心就冲突了。而这样的错误一般会被链接器发现,从而提示你进行修正。所以目前可以无视。

libFLAC的源码出现了NASM汇编代码,而且在很多地方出现了_asm内联汇编的语句,看似影响了移植性,其实这只是libFLAC用于优化代码运行效率的手段。
我们只需把C语言的预定义部分加上FLAC__NO_ASM,就能使其把原本用_asm内联汇编语句实现的部分用C代替了,从而保证了移植性。

本来libFLAC的源码里面附带的工程文件的配置是只有Win32平台编译的配置的。经过我的一系列处理(去掉NASM的部分使其不参与编译,然后通过调整预处理来使其不使用内联汇编)之后我编译出了64位的libFLAC.lib(静态链接库)。大家可以在这里下载。
x86: libFLAC_static_1_3_0_x86.7z (176.17 KB, 下载次数: 12, 售价: 1 个宅币)
x64: libFLAC_static_1_3_0_x64.7z (194.76 KB, 下载次数: 6, 售价: 2 个宅币)

要使用libFLAC进行C语言编程进行FLAC文件编码和解码,我们首先需要两样东西:编译时用到的头文件(.H)和链接时用到的库文件(.LIB,无论是动态DLL还是静态LIB)。
库文件请下载上面的。头文件,则在你刚下载的libFLAC的源码的include文件夹的FLAC文件夹内。
为了便于大家学习,我将头文件打包上传了。点此下载-> FLAC.7z (43.25 KB, 下载次数: 14)

头文件都是放在FLAC文件夹里面的。首先你把FLAC文件夹拷到你的工程文件夹。然后配置好你的工程,让它链接你的库文件。键人在此就不赘述了。

0x1:解码程序怎么写?

首先你需要包含一个头文件:#include"FLAC/stream_decoder.h"

FLAC的使用是围绕一个结构体进行的:FLAC__StreamDecoder *pDecoder;//FLAC解码器对象

FLAC是面向对象编程的思路。FLAC__StreamDecoder就是它所指的对象。首先我们需要调用FLAC__stream_decoder_new()来取得这个对象。

pDecoder=FLAC__stream_decoder_new();
if(!pDecoder)
{
    fprintf(stderr,
"初始化FLAC解码器失败!\n");
    return;
}


取得的pDecoder就是我们所说的FLAC解码器对象了。切记在用完了pDecoder之后要记得把它删掉,否则就会占用内存。调用FLAC__stream_decoder_delete(pDecoder);就可以把它占用的部分删掉了。

然后就是它的初始化工作了。有以下几种初始化的函数可供选择:
FLAC__stream_decoder_init_stream:由用户自行将FLAC文件的内容输入,然后取得解码得到的波形数据。
FLAC__stream_decoder_init_ogg_stream:由用户自行将ogg FLAC文件的内容输入,然后取得解码得到的波形数据。
FLAC__stream_decoder_init_FILE:用户提供打开了FLAC文件的FILE*,然后FLAC解码器会自动从文件流读入数据,再解码。
FLAC__stream_decoder_init_ogg_FILE:用户提供打开了ogg FLAC文件的FILE*,然后FLAC解码器会自动从文件流读入数据,再解码。
FLAC__stream_decoder_init_file:用户只需提供FLAC文件的路径,FLAC解码器自动打开文件进行解码。
FLAC__stream_decoder_init_ogg_file:用户只需提供ogg FLAC文件的路径,FLAC解码器自动打开文件进行解码。
(PS.什么是ogg FLAC?)

这些函数都是以回调函数的方式运作的。用户只需提供回调函数,然后让它解码,最后等它调用你的回调函数就能取得解码的数据了。
我这里把这几个函数的原型贴出来。
  1. /** Initialize the decoder instance to decode native FLAC streams.
  2. *
  3. *  This flavor of initialization sets up the decoder to decode from a
  4. *  native FLAC stream. I/O is performed via callbacks to the client.
  5. *  For decoding from a plain file via filename or open FILE*,
  6. *  FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE()
  7. *  provide a simpler interface.
  8. *
  9. *  This function should be called after FLAC__stream_decoder_new() and
  10. *  FLAC__stream_decoder_set_*() but before any of the
  11. *  FLAC__stream_decoder_process_*() functions.  Will set and return the
  12. *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
  13. *  if initialization succeeded.
  14. *
  15. * \param  decoder            An uninitialized decoder instance.
  16. * \param  read_callback      See FLAC__StreamDecoderReadCallback.  This
  17. *                            pointer must not be \c NULL.
  18. * \param  seek_callback      See FLAC__StreamDecoderSeekCallback.  This
  19. *                            pointer may be \c NULL if seeking is not
  20. *                            supported.  If \a seek_callback is not \c NULL then a
  21. *                            \a tell_callback, \a length_callback, and \a eof_callback must also be supplied.
  22. *                            Alternatively, a dummy seek callback that just
  23. *                            returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED
  24. *                            may also be supplied, all though this is slightly
  25. *                            less efficient for the decoder.
  26. * \param  tell_callback      See FLAC__StreamDecoderTellCallback.  This
  27. *                            pointer may be \c NULL if not supported by the client.  If
  28. *                            \a seek_callback is not \c NULL then a
  29. *                            \a tell_callback must also be supplied.
  30. *                            Alternatively, a dummy tell callback that just
  31. *                            returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED
  32. *                            may also be supplied, all though this is slightly
  33. *                            less efficient for the decoder.
  34. * \param  length_callback    See FLAC__StreamDecoderLengthCallback.  This
  35. *                            pointer may be \c NULL if not supported by the client.  If
  36. *                            \a seek_callback is not \c NULL then a
  37. *                            \a length_callback must also be supplied.
  38. *                            Alternatively, a dummy length callback that just
  39. *                            returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED
  40. *                            may also be supplied, all though this is slightly
  41. *                            less efficient for the decoder.
  42. * \param  eof_callback       See FLAC__StreamDecoderEofCallback.  This
  43. *                            pointer may be \c NULL if not supported by the client.  If
  44. *                            \a seek_callback is not \c NULL then a
  45. *                            \a eof_callback must also be supplied.
  46. *                            Alternatively, a dummy length callback that just
  47. *                            returns \c false
  48. *                            may also be supplied, all though this is slightly
  49. *                            less efficient for the decoder.
  50. * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
  51. *                            pointer must not be \c NULL.
  52. * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
  53. *                            pointer may be \c NULL if the callback is not
  54. *                            desired.
  55. * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
  56. *                            pointer must not be \c NULL.
  57. * \param  client_data        This value will be supplied to callbacks in their
  58. *                            \a client_data argument.
  59. * \assert
  60. *    \code decoder != NULL \endcode
  61. * \retval FLAC__StreamDecoderInitStatus
  62. *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
  63. *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
  64. */
  65. FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream(
  66.         FLAC__StreamDecoder *decoder,
  67.         FLAC__StreamDecoderReadCallback read_callback,
  68.         FLAC__StreamDecoderSeekCallback seek_callback,
  69.         FLAC__StreamDecoderTellCallback tell_callback,
  70.         FLAC__StreamDecoderLengthCallback length_callback,
  71.         FLAC__StreamDecoderEofCallback eof_callback,
  72.         FLAC__StreamDecoderWriteCallback write_callback,
  73.         FLAC__StreamDecoderMetadataCallback metadata_callback,
  74.         FLAC__StreamDecoderErrorCallback error_callback,
  75.         void *client_data
  76. );

  77. /** Initialize the decoder instance to decode Ogg FLAC streams.
  78. *
  79. *  This flavor of initialization sets up the decoder to decode from a
  80. *  FLAC stream in an Ogg container. I/O is performed via callbacks to the
  81. *  client.  For decoding from a plain file via filename or open FILE*,
  82. *  FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE()
  83. *  provide a simpler interface.
  84. *
  85. *  This function should be called after FLAC__stream_decoder_new() and
  86. *  FLAC__stream_decoder_set_*() but before any of the
  87. *  FLAC__stream_decoder_process_*() functions.  Will set and return the
  88. *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
  89. *  if initialization succeeded.
  90. *
  91. *  \note Support for Ogg FLAC in the library is optional.  If this
  92. *  library has been built without support for Ogg FLAC, this function
  93. *  will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER.
  94. *
  95. * \param  decoder            An uninitialized decoder instance.
  96. * \param  read_callback      See FLAC__StreamDecoderReadCallback.  This
  97. *                            pointer must not be \c NULL.
  98. * \param  seek_callback      See FLAC__StreamDecoderSeekCallback.  This
  99. *                            pointer may be \c NULL if seeking is not
  100. *                            supported.  If \a seek_callback is not \c NULL then a
  101. *                            \a tell_callback, \a length_callback, and \a eof_callback must also be supplied.
  102. *                            Alternatively, a dummy seek callback that just
  103. *                            returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED
  104. *                            may also be supplied, all though this is slightly
  105. *                            less efficient for the decoder.
  106. * \param  tell_callback      See FLAC__StreamDecoderTellCallback.  This
  107. *                            pointer may be \c NULL if not supported by the client.  If
  108. *                            \a seek_callback is not \c NULL then a
  109. *                            \a tell_callback must also be supplied.
  110. *                            Alternatively, a dummy tell callback that just
  111. *                            returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED
  112. *                            may also be supplied, all though this is slightly
  113. *                            less efficient for the decoder.
  114. * \param  length_callback    See FLAC__StreamDecoderLengthCallback.  This
  115. *                            pointer may be \c NULL if not supported by the client.  If
  116. *                            \a seek_callback is not \c NULL then a
  117. *                            \a length_callback must also be supplied.
  118. *                            Alternatively, a dummy length callback that just
  119. *                            returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED
  120. *                            may also be supplied, all though this is slightly
  121. *                            less efficient for the decoder.
  122. * \param  eof_callback       See FLAC__StreamDecoderEofCallback.  This
  123. *                            pointer may be \c NULL if not supported by the client.  If
  124. *                            \a seek_callback is not \c NULL then a
  125. *                            \a eof_callback must also be supplied.
  126. *                            Alternatively, a dummy length callback that just
  127. *                            returns \c false
  128. *                            may also be supplied, all though this is slightly
  129. *                            less efficient for the decoder.
  130. * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
  131. *                            pointer must not be \c NULL.
  132. * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
  133. *                            pointer may be \c NULL if the callback is not
  134. *                            desired.
  135. * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
  136. *                            pointer must not be \c NULL.
  137. * \param  client_data        This value will be supplied to callbacks in their
  138. *                            \a client_data argument.
  139. * \assert
  140. *    \code decoder != NULL \endcode
  141. * \retval FLAC__StreamDecoderInitStatus
  142. *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
  143. *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
  144. */
  145. FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream(
  146.         FLAC__StreamDecoder *decoder,
  147.         FLAC__StreamDecoderReadCallback read_callback,
  148.         FLAC__StreamDecoderSeekCallback seek_callback,
  149.         FLAC__StreamDecoderTellCallback tell_callback,
  150.         FLAC__StreamDecoderLengthCallback length_callback,
  151.         FLAC__StreamDecoderEofCallback eof_callback,
  152.         FLAC__StreamDecoderWriteCallback write_callback,
  153.         FLAC__StreamDecoderMetadataCallback metadata_callback,
  154.         FLAC__StreamDecoderErrorCallback error_callback,
  155.         void *client_data
  156. );

  157. /** Initialize the decoder instance to decode native FLAC files.
  158. *
  159. *  This flavor of initialization sets up the decoder to decode from a
  160. *  plain native FLAC file.  For non-stdio streams, you must use
  161. *  FLAC__stream_decoder_init_stream() and provide callbacks for the I/O.
  162. *
  163. *  This function should be called after FLAC__stream_decoder_new() and
  164. *  FLAC__stream_decoder_set_*() but before any of the
  165. *  FLAC__stream_decoder_process_*() functions.  Will set and return the
  166. *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
  167. *  if initialization succeeded.
  168. *
  169. * \param  decoder            An uninitialized decoder instance.
  170. * \param  file               An open FLAC file.  The file should have been
  171. *                            opened with mode \c "rb" and rewound.  The file
  172. *                            becomes owned by the decoder and should not be
  173. *                            manipulated by the client while decoding.
  174. *                            Unless \a file is \c stdin, it will be closed
  175. *                            when FLAC__stream_decoder_finish() is called.
  176. *                            Note however that seeking will not work when
  177. *                            decoding from \c stdout since it is not seekable.
  178. * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
  179. *                            pointer must not be \c NULL.
  180. * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
  181. *                            pointer may be \c NULL if the callback is not
  182. *                            desired.
  183. * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
  184. *                            pointer must not be \c NULL.
  185. * \param  client_data        This value will be supplied to callbacks in their
  186. *                            \a client_data argument.
  187. * \assert
  188. *    \code decoder != NULL \endcode
  189. *    \code file != NULL \endcode
  190. * \retval FLAC__StreamDecoderInitStatus
  191. *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
  192. *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
  193. */
  194. FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE(
  195.         FLAC__StreamDecoder *decoder,
  196.         FILE *file,
  197.         FLAC__StreamDecoderWriteCallback write_callback,
  198.         FLAC__StreamDecoderMetadataCallback metadata_callback,
  199.         FLAC__StreamDecoderErrorCallback error_callback,
  200.         void *client_data
  201. );

  202. /** Initialize the decoder instance to decode Ogg FLAC files.
  203. *
  204. *  This flavor of initialization sets up the decoder to decode from a
  205. *  plain Ogg FLAC file.  For non-stdio streams, you must use
  206. *  FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O.
  207. *
  208. *  This function should be called after FLAC__stream_decoder_new() and
  209. *  FLAC__stream_decoder_set_*() but before any of the
  210. *  FLAC__stream_decoder_process_*() functions.  Will set and return the
  211. *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
  212. *  if initialization succeeded.
  213. *
  214. *  \note Support for Ogg FLAC in the library is optional.  If this
  215. *  library has been built without support for Ogg FLAC, this function
  216. *  will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER.
  217. *
  218. * \param  decoder            An uninitialized decoder instance.
  219. * \param  file               An open FLAC file.  The file should have been
  220. *                            opened with mode \c "rb" and rewound.  The file
  221. *                            becomes owned by the decoder and should not be
  222. *                            manipulated by the client while decoding.
  223. *                            Unless \a file is \c stdin, it will be closed
  224. *                            when FLAC__stream_decoder_finish() is called.
  225. *                            Note however that seeking will not work when
  226. *                            decoding from \c stdout since it is not seekable.
  227. * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
  228. *                            pointer must not be \c NULL.
  229. * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
  230. *                            pointer may be \c NULL if the callback is not
  231. *                            desired.
  232. * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
  233. *                            pointer must not be \c NULL.
  234. * \param  client_data        This value will be supplied to callbacks in their
  235. *                            \a client_data argument.
  236. * \assert
  237. *    \code decoder != NULL \endcode
  238. *    \code file != NULL \endcode
  239. * \retval FLAC__StreamDecoderInitStatus
  240. *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
  241. *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
  242. */
  243. FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE(
  244.         FLAC__StreamDecoder *decoder,
  245.         FILE *file,
  246.         FLAC__StreamDecoderWriteCallback write_callback,
  247.         FLAC__StreamDecoderMetadataCallback metadata_callback,
  248.         FLAC__StreamDecoderErrorCallback error_callback,
  249.         void *client_data
  250. );

  251. /** Initialize the decoder instance to decode native FLAC files.
  252. *
  253. *  This flavor of initialization sets up the decoder to decode from a plain
  254. *  native FLAC file.  If POSIX fopen() semantics are not sufficient, (for
  255. *  example, with Unicode filenames on Windows), you must use
  256. *  FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream()
  257. *  and provide callbacks for the I/O.
  258. *
  259. *  This function should be called after FLAC__stream_decoder_new() and
  260. *  FLAC__stream_decoder_set_*() but before any of the
  261. *  FLAC__stream_decoder_process_*() functions.  Will set and return the
  262. *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
  263. *  if initialization succeeded.
  264. *
  265. * \param  decoder            An uninitialized decoder instance.
  266. * \param  filename           The name of the file to decode from.  The file will
  267. *                            be opened with fopen().  Use \c NULL to decode from
  268. *                            \c stdin.  Note that \c stdin is not seekable.
  269. * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
  270. *                            pointer must not be \c NULL.
  271. * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
  272. *                            pointer may be \c NULL if the callback is not
  273. *                            desired.
  274. * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
  275. *                            pointer must not be \c NULL.
  276. * \param  client_data        This value will be supplied to callbacks in their
  277. *                            \a client_data argument.
  278. * \assert
  279. *    \code decoder != NULL \endcode
  280. * \retval FLAC__StreamDecoderInitStatus
  281. *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
  282. *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
  283. */
  284. FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file(
  285.         FLAC__StreamDecoder *decoder,
  286.         const char *filename,
  287.         FLAC__StreamDecoderWriteCallback write_callback,
  288.         FLAC__StreamDecoderMetadataCallback metadata_callback,
  289.         FLAC__StreamDecoderErrorCallback error_callback,
  290.         void *client_data
  291. );

  292. /** Initialize the decoder instance to decode Ogg FLAC files.
  293. *
  294. *  This flavor of initialization sets up the decoder to decode from a plain
  295. *  Ogg FLAC file.  If POSIX fopen() semantics are not sufficient, (for
  296. *  example, with Unicode filenames on Windows), you must use
  297. *  FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream()
  298. *  and provide callbacks for the I/O.
  299. *
  300. *  This function should be called after FLAC__stream_decoder_new() and
  301. *  FLAC__stream_decoder_set_*() but before any of the
  302. *  FLAC__stream_decoder_process_*() functions.  Will set and return the
  303. *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
  304. *  if initialization succeeded.
  305. *
  306. *  \note Support for Ogg FLAC in the library is optional.  If this
  307. *  library has been built without support for Ogg FLAC, this function
  308. *  will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER.
  309. *
  310. * \param  decoder            An uninitialized decoder instance.
  311. * \param  filename           The name of the file to decode from.  The file will
  312. *                            be opened with fopen().  Use \c NULL to decode from
  313. *                            \c stdin.  Note that \c stdin is not seekable.
  314. * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
  315. *                            pointer must not be \c NULL.
  316. * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
  317. *                            pointer may be \c NULL if the callback is not
  318. *                            desired.
  319. * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
  320. *                            pointer must not be \c NULL.
  321. * \param  client_data        This value will be supplied to callbacks in their
  322. *                            \a client_data argument.
  323. * \assert
  324. *    \code decoder != NULL \endcode
  325. * \retval FLAC__StreamDecoderInitStatus
  326. *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
  327. *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
  328. */
  329. FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file(
  330.         FLAC__StreamDecoder *decoder,
  331.         const char *filename,
  332.         FLAC__StreamDecoderWriteCallback write_callback,
  333.         FLAC__StreamDecoderMetadataCallback metadata_callback,
  334.         FLAC__StreamDecoderErrorCallback error_callback,
  335.         void *client_data
  336. );
复制代码
大家可以通过看注释来了解它们的使用方式。

刚才只是初始化的设定而已。设置好初始化的操作以后,你要做的就是让libFLAC帮你解码。
有以下几种方式可以启动它的解码过程:
FLAC__stream_decoder_process_single:解码一个块,得到一个块的数据。
FLAC__stream_decoder_process_until_end_of_metadata:一直解码,直到元数据(metadata)的结尾。
FLAC__stream_decoder_process_until_end_of_stream:一直解码,直到流的结尾(整个文件解码完)
当你调用上面三个函数中的一个的时候,libFLAC就会开始帮你解码,调用你提供的回调函数,把解码得到的数据提供给你。

其中可以用于辅助解码的函数如下:
FLAC__stream_decoder_skip_single_frame:跳过一个块。
FLAC__stream_decoder_seek_absolute:定位到某一个样本处。

详细的来自头文件的信息:
  1. /** Decode one metadata block or audio frame.
  2. *  This version instructs the decoder to decode a either a single metadata
  3. *  block or a single frame and stop, unless the callbacks return a fatal
  4. *  error or the read callback returns
  5. *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM.
  6. *
  7. *  As the decoder needs more input it will call the read callback.
  8. *  Depending on what was decoded, the metadata or write callback will be
  9. *  called with the decoded metadata block or audio frame.
  10. *
  11. *  Unless there is a fatal read error or end of stream, this function
  12. *  will return once one whole frame is decoded.  In other words, if the
  13. *  stream is not synchronized or points to a corrupt frame header, the
  14. *  decoder will continue to try and resync until it gets to a valid
  15. *  frame, then decode one frame, then return.  If the decoder points to
  16. *  a frame whose frame CRC in the frame footer does not match the
  17. *  computed frame CRC, this function will issue a
  18. *  FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the
  19. *  error callback, and return, having decoded one complete, although
  20. *  corrupt, frame.  (Such corrupted frames are sent as silence of the
  21. *  correct length to the write callback.)
  22. *
  23. * \param  decoder  An initialized decoder instance.
  24. * \assert
  25. *    \code decoder != NULL \endcode
  26. * \retval FLAC__bool
  27. *    \c false if any fatal read, write, or memory allocation error
  28. *    occurred (meaning decoding must stop), else \c true; for more
  29. *    information about the decoder, check the decoder state with
  30. *    FLAC__stream_decoder_get_state().
  31. */
  32. FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder);

  33. /** Decode until the end of the metadata.
  34. *  This version instructs the decoder to decode from the current position
  35. *  and continue until all the metadata has been read, or until the
  36. *  callbacks return a fatal error or the read callback returns
  37. *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM.
  38. *
  39. *  As the decoder needs more input it will call the read callback.
  40. *  As each metadata block is decoded, the metadata callback will be called
  41. *  with the decoded metadata.
  42. *
  43. * \param  decoder  An initialized decoder instance.
  44. * \assert
  45. *    \code decoder != NULL \endcode
  46. * \retval FLAC__bool
  47. *    \c false if any fatal read, write, or memory allocation error
  48. *    occurred (meaning decoding must stop), else \c true; for more
  49. *    information about the decoder, check the decoder state with
  50. *    FLAC__stream_decoder_get_state().
  51. */
  52. FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder);

  53. /** Decode until the end of the stream.
  54. *  This version instructs the decoder to decode from the current position
  55. *  and continue until the end of stream (the read callback returns
  56. *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the
  57. *  callbacks return a fatal error.
  58. *
  59. *  As the decoder needs more input it will call the read callback.
  60. *  As each metadata block and frame is decoded, the metadata or write
  61. *  callback will be called with the decoded metadata or frame.
  62. *
  63. * \param  decoder  An initialized decoder instance.
  64. * \assert
  65. *    \code decoder != NULL \endcode
  66. * \retval FLAC__bool
  67. *    \c false if any fatal read, write, or memory allocation error
  68. *    occurred (meaning decoding must stop), else \c true; for more
  69. *    information about the decoder, check the decoder state with
  70. *    FLAC__stream_decoder_get_state().
  71. */
  72. FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder);

  73. /** Skip one audio frame.
  74. *  This version instructs the decoder to 'skip' a single frame and stop,
  75. *  unless the callbacks return a fatal error or the read callback returns
  76. *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM.
  77. *
  78. *  The decoding flow is the same as what occurs when
  79. *  FLAC__stream_decoder_process_single() is called to process an audio
  80. *  frame, except that this function does not decode the parsed data into
  81. *  PCM or call the write callback.  The integrity of the frame is still
  82. *  checked the same way as in the other process functions.
  83. *
  84. *  This function will return once one whole frame is skipped, in the
  85. *  same way that FLAC__stream_decoder_process_single() will return once
  86. *  one whole frame is decoded.
  87. *
  88. *  This function can be used in more quickly determining FLAC frame
  89. *  boundaries when decoding of the actual data is not needed, for
  90. *  example when an application is separating a FLAC stream into frames
  91. *  for editing or storing in a container.  To do this, the application
  92. *  can use FLAC__stream_decoder_skip_single_frame() to quickly advance
  93. *  to the next frame, then use
  94. *  FLAC__stream_decoder_get_decode_position() to find the new frame
  95. *  boundary.
  96. *
  97. *  This function should only be called when the stream has advanced
  98. *  past all the metadata, otherwise it will return \c false.
  99. *
  100. * \param  decoder  An initialized decoder instance not in a metadata
  101. *                  state.
  102. * \assert
  103. *    \code decoder != NULL \endcode
  104. * \retval FLAC__bool
  105. *    \c false if any fatal read, write, or memory allocation error
  106. *    occurred (meaning decoding must stop), or if the decoder
  107. *    is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or
  108. *    FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more
  109. *    information about the decoder, check the decoder state with
  110. *    FLAC__stream_decoder_get_state().
  111. */
  112. FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder);

  113. /** Flush the input and seek to an absolute sample.
  114. *  Decoding will resume at the given sample.  Note that because of
  115. *  this, the next write callback may contain a partial block.  The
  116. *  client must support seeking the input or this function will fail
  117. *  and return \c false.  Furthermore, if the decoder state is
  118. *  \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed
  119. *  with FLAC__stream_decoder_flush() or reset with
  120. *  FLAC__stream_decoder_reset() before decoding can continue.
  121. *
  122. * \param  decoder  A decoder instance.
  123. * \param  sample   The target sample number to seek to.
  124. * \assert
  125. *    \code decoder != NULL \endcode
  126. * \retval FLAC__bool
  127. *    \c true if successful, else \c false.
  128. */
  129. FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample);
复制代码
回调函数的原型详见libFLAC的头文件。里面有非常详细的介绍。你只需在VS里把光标放到这些函数类型上然后按下F12就能看到所有的信息。
不过为了便于理解,我把这些回调函数的原型也复制了过来。这样大家就不用亲自去按F12看信息了。直接看我的帖子就行。
  1. /** Signature for the read callback.
  2. *
  3. *  A function pointer matching this signature must be passed to
  4. *  FLAC__stream_decoder_init*_stream(). The supplied function will be
  5. *  called when the decoder needs more input data.  The address of the
  6. *  buffer to be filled is supplied, along with the number of bytes the
  7. *  buffer can hold.  The callback may choose to supply less data and
  8. *  modify the byte count but must be careful not to overflow the buffer.
  9. *  The callback then returns a status code chosen from
  10. *  FLAC__StreamDecoderReadStatus.
  11. *
  12. * Here is an example of a read callback for stdio streams:
  13. * \code
  14. * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
  15. * {
  16. *   FILE *file = ((MyClientData*)client_data)->file;
  17. *   if(*bytes > 0) {
  18. *     *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file);
  19. *     if(ferror(file))
  20. *       return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
  21. *     else if(*bytes == 0)
  22. *       return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
  23. *     else
  24. *       return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
  25. *   }
  26. *   else
  27. *     return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
  28. * }
  29. * \endcode
  30. *
  31. * \note In general, FLAC__StreamDecoder functions which change the
  32. * state should not be called on the \a decoder while in the callback.
  33. *
  34. * \param  decoder  The decoder instance calling the callback.
  35. * \param  buffer   A pointer to a location for the callee to store
  36. *                  data to be decoded.
  37. * \param  bytes    A pointer to the size of the buffer.  On entry
  38. *                  to the callback, it contains the maximum number
  39. *                  of bytes that may be stored in \a buffer.  The
  40. *                  callee must set it to the actual number of bytes
  41. *                  stored (0 in case of error or end-of-stream) before
  42. *                  returning.
  43. * \param  client_data  The callee's client data set through
  44. *                      FLAC__stream_decoder_init_*().
  45. * \retval FLAC__StreamDecoderReadStatus
  46. *    The callee's return status.  Note that the callback should return
  47. *    \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if
  48. *    zero bytes were read and there is no more data to be read.
  49. */
  50. typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);

  51. /** Signature for the seek callback.
  52. *
  53. *  A function pointer matching this signature may be passed to
  54. *  FLAC__stream_decoder_init*_stream().  The supplied function will be
  55. *  called when the decoder needs to seek the input stream.  The decoder
  56. *  will pass the absolute byte offset to seek to, 0 meaning the
  57. *  beginning of the stream.
  58. *
  59. * Here is an example of a seek callback for stdio streams:
  60. * \code
  61. * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
  62. * {
  63. *   FILE *file = ((MyClientData*)client_data)->file;
  64. *   if(file == stdin)
  65. *     return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
  66. *   else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0)
  67. *     return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
  68. *   else
  69. *     return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
  70. * }
  71. * \endcode
  72. *
  73. * \note In general, FLAC__StreamDecoder functions which change the
  74. * state should not be called on the \a decoder while in the callback.
  75. *
  76. * \param  decoder  The decoder instance calling the callback.
  77. * \param  absolute_byte_offset  The offset from the beginning of the stream
  78. *                               to seek to.
  79. * \param  client_data  The callee's client data set through
  80. *                      FLAC__stream_decoder_init_*().
  81. * \retval FLAC__StreamDecoderSeekStatus
  82. *    The callee's return status.
  83. */
  84. typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);

  85. /** Signature for the tell callback.
  86. *
  87. *  A function pointer matching this signature may be passed to
  88. *  FLAC__stream_decoder_init*_stream().  The supplied function will be
  89. *  called when the decoder wants to know the current position of the
  90. *  stream.  The callback should return the byte offset from the
  91. *  beginning of the stream.
  92. *
  93. * Here is an example of a tell callback for stdio streams:
  94. * \code
  95. * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
  96. * {
  97. *   FILE *file = ((MyClientData*)client_data)->file;
  98. *   off_t pos;
  99. *   if(file == stdin)
  100. *     return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
  101. *   else if((pos = ftello(file)) < 0)
  102. *     return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
  103. *   else {
  104. *     *absolute_byte_offset = (FLAC__uint64)pos;
  105. *     return FLAC__STREAM_DECODER_TELL_STATUS_OK;
  106. *   }
  107. * }
  108. * \endcode
  109. *
  110. * \note In general, FLAC__StreamDecoder functions which change the
  111. * state should not be called on the \a decoder while in the callback.
  112. *
  113. * \param  decoder  The decoder instance calling the callback.
  114. * \param  absolute_byte_offset  A pointer to storage for the current offset
  115. *                               from the beginning of the stream.
  116. * \param  client_data  The callee's client data set through
  117. *                      FLAC__stream_decoder_init_*().
  118. * \retval FLAC__StreamDecoderTellStatus
  119. *    The callee's return status.
  120. */
  121. typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);

  122. /** Signature for the length callback.
  123. *
  124. *  A function pointer matching this signature may be passed to
  125. *  FLAC__stream_decoder_init*_stream().  The supplied function will be
  126. *  called when the decoder wants to know the total length of the stream
  127. *  in bytes.
  128. *
  129. * Here is an example of a length callback for stdio streams:
  130. * \code
  131. * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
  132. * {
  133. *   FILE *file = ((MyClientData*)client_data)->file;
  134. *   struct stat filestats;
  135. *
  136. *   if(file == stdin)
  137. *     return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
  138. *   else if(fstat(fileno(file), &filestats) != 0)
  139. *     return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
  140. *   else {
  141. *     *stream_length = (FLAC__uint64)filestats.st_size;
  142. *     return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
  143. *   }
  144. * }
  145. * \endcode
  146. *
  147. * \note In general, FLAC__StreamDecoder functions which change the
  148. * state should not be called on the \a decoder while in the callback.
  149. *
  150. * \param  decoder  The decoder instance calling the callback.
  151. * \param  stream_length  A pointer to storage for the length of the stream
  152. *                        in bytes.
  153. * \param  client_data  The callee's client data set through
  154. *                      FLAC__stream_decoder_init_*().
  155. * \retval FLAC__StreamDecoderLengthStatus
  156. *    The callee's return status.
  157. */
  158. typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);

  159. /** Signature for the EOF callback.
  160. *
  161. *  A function pointer matching this signature may be passed to
  162. *  FLAC__stream_decoder_init*_stream().  The supplied function will be
  163. *  called when the decoder needs to know if the end of the stream has
  164. *  been reached.
  165. *
  166. * Here is an example of a EOF callback for stdio streams:
  167. * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data)
  168. * \code
  169. * {
  170. *   FILE *file = ((MyClientData*)client_data)->file;
  171. *   return feof(file)? true : false;
  172. * }
  173. * \endcode
  174. *
  175. * \note In general, FLAC__StreamDecoder functions which change the
  176. * state should not be called on the \a decoder while in the callback.
  177. *
  178. * \param  decoder  The decoder instance calling the callback.
  179. * \param  client_data  The callee's client data set through
  180. *                      FLAC__stream_decoder_init_*().
  181. * \retval FLAC__bool
  182. *    \c true if the currently at the end of the stream, else \c false.
  183. */
  184. typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data);

  185. /** Signature for the write callback.
  186. *
  187. *  A function pointer matching this signature must be passed to one of
  188. *  the FLAC__stream_decoder_init_*() functions.
  189. *  The supplied function will be called when the decoder has decoded a
  190. *  single audio frame.  The decoder will pass the frame metadata as well
  191. *  as an array of pointers (one for each channel) pointing to the
  192. *  decoded audio.
  193. *
  194. * \note In general, FLAC__StreamDecoder functions which change the
  195. * state should not be called on the \a decoder while in the callback.
  196. *
  197. * \param  decoder  The decoder instance calling the callback.
  198. * \param  frame    The description of the decoded frame.  See
  199. *                  FLAC__Frame.
  200. * \param  buffer   An array of pointers to decoded channels of data.
  201. *                  Each pointer will point to an array of signed
  202. *                  samples of length \a frame->header.blocksize.
  203. *                  Channels will be ordered according to the FLAC
  204. *                  specification; see the documentation for the
  205. *                  <A HREF="../format.html#frame_header">frame header</A>.
  206. * \param  client_data  The callee's client data set through
  207. *                      FLAC__stream_decoder_init_*().
  208. * \retval FLAC__StreamDecoderWriteStatus
  209. *    The callee's return status.
  210. */
  211. typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);

  212. /** Signature for the metadata callback.
  213. *
  214. *  A function pointer matching this signature must be passed to one of
  215. *  the FLAC__stream_decoder_init_*() functions.
  216. *  The supplied function will be called when the decoder has decoded a
  217. *  metadata block.  In a valid FLAC file there will always be one
  218. *  \c STREAMINFO block, followed by zero or more other metadata blocks.
  219. *  These will be supplied by the decoder in the same order as they
  220. *  appear in the stream and always before the first audio frame (i.e.
  221. *  write callback).  The metadata block that is passed in must not be
  222. *  modified, and it doesn't live beyond the callback, so you should make
  223. *  a copy of it with FLAC__metadata_object_clone() if you will need it
  224. *  elsewhere.  Since metadata blocks can potentially be large, by
  225. *  default the decoder only calls the metadata callback for the
  226. *  \c STREAMINFO block; you can instruct the decoder to pass or filter
  227. *  other blocks with FLAC__stream_decoder_set_metadata_*() calls.
  228. *
  229. * \note In general, FLAC__StreamDecoder functions which change the
  230. * state should not be called on the \a decoder while in the callback.
  231. *
  232. * \param  decoder  The decoder instance calling the callback.
  233. * \param  metadata The decoded metadata block.
  234. * \param  client_data  The callee's client data set through
  235. *                      FLAC__stream_decoder_init_*().
  236. */
  237. typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);

  238. /** Signature for the error callback.
  239. *
  240. *  A function pointer matching this signature must be passed to one of
  241. *  the FLAC__stream_decoder_init_*() functions.
  242. *  The supplied function will be called whenever an error occurs during
  243. *  decoding.
  244. *
  245. * \note In general, FLAC__StreamDecoder functions which change the
  246. * state should not be called on the \a decoder while in the callback.
  247. *
  248. * \param  decoder  The decoder instance calling the callback.
  249. * \param  status   The error encountered by the decoder.
  250. * \param  client_data  The callee's client data set through
  251. *                      FLAC__stream_decoder_init_*().
  252. */
  253. typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
复制代码
那么到最后,我来总结一下解码器的写法:

1、取得解码器对象。FLAC__StreamDecoder *pDecoder=FLAC__stream_decoder_new();
2、初始化解码器。用以下的几个函数中的一个来完成。其实就是告诉libFLAC,要解码的数据的来源,以及解码得到的数据的去向。
FLAC__stream_decoder_init_stream
FLAC__stream_decoder_init_ogg_stream
FLAC__stream_decoder_init_FILE
FLAC__stream_decoder_init_ogg_FILE
FLAC__stream_decoder_init_file
FLAC__stream_decoder_init_ogg_file

3、开始解码过程。调用以下的函数来实现。
FLAC__stream_decoder_process_single:解码一个块。
FLAC__stream_decoder_process_until_end_of_metadata:解码全部metadata块。
FLAC__stream_decoder_process_until_end_of_stream:解码全部块。

4、完成解码后清理用过的内存。
FLAC__stream_decoder_delete(pDecoder);

就这些。

本帖被以下淘专辑推荐:

回复

使用道具 举报

发表于 2015-4-13 18:18:34 | 显示全部楼层
求大神编译一个1.3.1版本的,这玩意编译真是太蛋疼了
回复 赞! 靠!

使用道具 举报

发表于 2015-4-13 18:26:43 | 显示全部楼层
我试了下,不知为什么链接错误一大堆,我在国外网站找的也一样
QQ截图20150413182604.jpg
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2015-4-13 19:20:37 | 显示全部楼层
界限突破 发表于 2015-4-13 18:26
我试了下,不知为什么链接错误一大堆,我在国外网站找的也一样

你用的是libFLAC的C艹库。你需要自己构建这个子类,然后完成那些接口,否则无法链接。
回复 赞! 靠!

使用道具 举报

发表于 2015-4-13 22:46:42 | 显示全部楼层
0xAA55 发表于 2015-4-13 19:20
你用的是libFLAC的C艹库。你需要自己构建这个子类,然后完成那些接口,否则无法链接。 ...

soga,多谢指点
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-25 08:23 , Processed in 0.040398 second(s), 29 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表