luat_lib_multimedia.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. /*
  2. @module multimedia
  3. @summary 多媒体
  4. @version 1.0
  5. @date 2022.03.11
  6. @demo multimedia
  7. */
  8. #include "luat_base.h"
  9. #include "luat_multimedia.h"
  10. #include "luat_msgbus.h"
  11. #include "luat_zbuff.h"
  12. #define LUAT_LOG_TAG "multimedia"
  13. #include "luat_log.h"
  14. #include <stddef.h>
  15. #include "mp3_decode/minimp3.h"
  16. #define LUAT_M_CODE_TYPE "MCODER*"
  17. #define MP3_FRAME_LEN 4 * 1152
  18. typedef struct
  19. {
  20. union
  21. {
  22. mp3dec_t *mp3_decoder;
  23. uint32_t read_len;
  24. };
  25. FILE* fd;
  26. luat_zbuff_t buff;
  27. uint8_t type;
  28. uint8_t is_decoder;
  29. }luat_multimedia_codec_t;
  30. #define MAX_DEVICE_COUNT 2
  31. typedef struct luat_multimedia_cb {
  32. int function_ref;
  33. } luat_multimedia_cb_t;
  34. static luat_multimedia_cb_t multimedia_cbs[MAX_DEVICE_COUNT];
  35. int l_multimedia_raw_handler(lua_State *L, void* ptr) {
  36. rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
  37. lua_pop(L, 1);
  38. if (multimedia_cbs[msg->arg2].function_ref) {
  39. lua_geti(L, LUA_REGISTRYINDEX, multimedia_cbs[msg->arg2].function_ref);
  40. if (lua_isfunction(L, -1)) {
  41. lua_pushinteger(L, msg->arg2);
  42. lua_pushinteger(L, msg->arg1);
  43. lua_call(L, 2, 0);
  44. }
  45. }
  46. lua_pushinteger(L, 0);
  47. return 1;
  48. }
  49. /*
  50. 启动一个多媒体通道准备播放音频
  51. @api audio.start(id, audio_format, num_channels, sample_rate, bits_per_sample, is_signed)
  52. @int 多媒体播放通道号,0或者1
  53. @int 音频格式
  54. @int 声音通道数
  55. @int 采样频率
  56. @int 采样位数
  57. @boolean 是否有符号,默认true
  58. @return boolean 成功true, 失败false
  59. @usage
  60. audio.start(0, audio.PCM, 1, 16000, 16)
  61. */
  62. static int l_audio_start_raw(lua_State *L){
  63. int multimedia_id = luaL_checkinteger(L, 1);
  64. int audio_format = luaL_checkinteger(L, 2);
  65. int num_channels= luaL_checkinteger(L, 3);
  66. int sample_rate = luaL_checkinteger(L, 4);
  67. int bits_per_sample = luaL_checkinteger(L, 5);
  68. int is_signed = 1;
  69. if (lua_isboolean(L, 6))
  70. {
  71. is_signed = lua_toboolean(L, 6);
  72. }
  73. lua_pushboolean(L, !luat_audio_start_raw(multimedia_id, audio_format, num_channels, sample_rate, bits_per_sample, is_signed));
  74. return 1;
  75. }
  76. /**
  77. 往一个多媒体通道写入音频数据
  78. @api audio.write(id, data)
  79. @string or zbuff 音频数据
  80. @return boolean 成功返回true,否则返回false
  81. @usage
  82. audio.write(0, "xxxxxx")
  83. */
  84. static int l_audio_write_raw(lua_State *L) {
  85. int multimedia_id = luaL_checkinteger(L, 1);
  86. size_t len;
  87. uint8_t *buf;
  88. if(lua_isuserdata(L, 2))
  89. {
  90. luat_zbuff_t *buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE));
  91. len = buff->used;
  92. buf = (const char *)(buff->addr);
  93. }
  94. else
  95. {
  96. buf = lua_tolstring(L, 2, &len);//取出字符串数据
  97. }
  98. lua_pushboolean(L, !luat_audio_write_raw(multimedia_id, buf, len));
  99. return 1;
  100. }
  101. /**
  102. 停止指定的多媒体通道
  103. @api audio.stop(id)
  104. @int audio id,例如0
  105. @return boolean 成功返回true,否则返回false
  106. @usage
  107. audio.stop(0)
  108. */
  109. static int l_audio_stop_raw(lua_State *L) {
  110. lua_pushboolean(L, !luat_audio_stop_raw(luaL_checkinteger(L, 1)));
  111. return 1;
  112. }
  113. /**
  114. 暂停/恢复指定的多媒体通道
  115. @api audio.pause(id, pause)
  116. @int audio id,例如0
  117. @boolean onoff true 暂停,false 恢复
  118. @return boolean 成功返回true,否则返回false
  119. @usage
  120. audio.pause(0, true) --暂停通道0
  121. audio.pause(0, false) --恢复通道0
  122. */
  123. static int l_audio_pause_raw(lua_State *L) {
  124. lua_pushboolean(L, !luat_audio_pause_raw(luaL_checkinteger(L, 1), lua_toboolean(L, 2)));
  125. return 1;
  126. }
  127. /**
  128. 注册audio播放事件回调
  129. @api audio.on(id, event, func)
  130. @int audio id, audio 0写0, audio 1写1
  131. @function 回调方法
  132. @return nil 无返回值
  133. @usage
  134. audio.on(0, function(id, str)
  135. print(id, str)
  136. end)
  137. */
  138. static int l_audio_raw_on(lua_State *L) {
  139. int multimedia_id = luaL_checkinteger(L, 1);
  140. if (multimedia_cbs[multimedia_id].function_ref != 0) {
  141. luaL_unref(L, LUA_REGISTRYINDEX, multimedia_cbs[multimedia_id].function_ref);
  142. multimedia_cbs[multimedia_id].function_ref = 0;
  143. }
  144. if (lua_isfunction(L, 2)) {
  145. lua_pushvalue(L, 2);
  146. multimedia_cbs[multimedia_id].function_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  147. }
  148. return 0;
  149. }
  150. /*
  151. 播放或者停止播放一个文件,播放完成后,会回调一个audio.DONE消息,可以用pause来暂停或者恢复,其他API不可用。考虑到读SD卡速度比较慢而拖累luavm进程的速度,所以尽量使用本API
  152. @api audio.play(id, path)
  153. @int 音频通道
  154. @string 文件名,如果为空,则表示停止播放
  155. @return boolean 成功返回true,否则返回false
  156. @usage
  157. audio.play(0, "xxxxxx") --开始播放某个文件
  158. audio.play(0) --停止播放某个文件
  159. */
  160. static int l_audio_play(lua_State *L) {
  161. int multimedia_id = luaL_checkinteger(L, 1);
  162. size_t len, i;
  163. int result = 0;
  164. const uint8_t *buf;
  165. if (lua_isstring(L, 2))
  166. {
  167. buf = lua_tolstring(L, 2, &len);//取出字符串数据
  168. char *path = luat_heap_malloc(len + 1);
  169. memcpy(path, buf, len);
  170. path[len] = 0;
  171. result = luat_audio_play_file(multimedia_id, path);
  172. lua_pushboolean(L, !result);
  173. luat_heap_free(path);
  174. }
  175. else
  176. {
  177. luat_audio_play_stop(multimedia_id);
  178. lua_pushboolean(L, 1);
  179. }
  180. return 1;
  181. }
  182. /**
  183. 检查当前文件是否已经播放结束
  184. @api audio.isEnd(id, path)
  185. @int 音频通道
  186. @return boolean 成功返回true,否则返回false
  187. @usage
  188. audio.isEnd(0)
  189. */
  190. static int l_audio_play_wait_end(lua_State *L) {
  191. int multimedia_id = luaL_checkinteger(L, 1);
  192. lua_pushboolean(L, luat_audio_is_finish(multimedia_id));
  193. return 1;
  194. }
  195. /**
  196. 创建编解码用的codec
  197. @api codec.create(codec.MP3)
  198. @int 多媒体类型,目前支持decode.MP3
  199. @boolean 是否是编码器,默认true,是解码器
  200. @return userdata 成功返回一个数据结构,否则返回nil
  201. @usage
  202. -- 创建decoder
  203. local decoder = codec.create(codec.MP3)--创建一个mp3的decoder
  204. */
  205. static int l_codec_create(lua_State *L) {
  206. uint8_t type = luaL_optinteger(L, 1, MULTIMEDIA_DATA_TYPE_MP3);
  207. uint8_t is_decoder = 1;
  208. if (lua_isboolean(L, 2)) {
  209. is_decoder = lua_toboolean(L, 2);
  210. }
  211. luat_multimedia_codec_t *coder = (luat_multimedia_codec_t *)lua_newuserdata(L, sizeof(luat_multimedia_codec_t));
  212. if (coder == NULL) {
  213. lua_pushnil(L);
  214. } else {
  215. memset(coder, 0, sizeof(luat_multimedia_codec_t));
  216. coder->type = type;
  217. coder->is_decoder = is_decoder;
  218. if (is_decoder)
  219. {
  220. switch (type) {
  221. case MULTIMEDIA_DATA_TYPE_MP3:
  222. coder->mp3_decoder = luat_heap_malloc(sizeof(mp3dec_t));
  223. if (!coder->mp3_decoder) {
  224. lua_pushnil(L);
  225. return 1;
  226. }
  227. break;
  228. }
  229. }
  230. luaL_setmetatable(L, LUAT_M_CODE_TYPE);
  231. }
  232. return 1;
  233. }
  234. /**
  235. decoder从文件中解析出音频信息
  236. @api codec.info(decoder, file_path)
  237. @coder 解码用的decoder
  238. @string 文件路径
  239. @return
  240. @boolean 是否成功解析
  241. @int 音频格式
  242. @int 声音通道数
  243. @int 采样频率
  244. @int 采样位数
  245. @boolean 是否有符号
  246. @usage
  247. local result, audio_format, num_channels, sample_rate, bits_per_sample, is_signed= codec.get_audio_info(coder, "xxx")
  248. */
  249. static int l_codec_get_audio_info(lua_State *L) {
  250. luat_multimedia_codec_t *coder = (luat_multimedia_codec_t *)luaL_checkudata(L, 1, LUAT_M_CODE_TYPE);
  251. uint32_t jump, i;
  252. uint8_t temp[16];
  253. int result = 0;
  254. int audio_format;
  255. int num_channels;
  256. int sample_rate;
  257. int bits_per_sample = 16;
  258. uint32_t align;
  259. int is_signed = 1;
  260. size_t len;
  261. mp3dec_frame_info_t info;
  262. const char *file_path = luaL_checklstring(L, 2, &len);
  263. FILE *fd = luat_fs_fopen(file_path, "r");
  264. if (fd && coder)
  265. {
  266. switch(coder->type)
  267. {
  268. case MULTIMEDIA_DATA_TYPE_MP3:
  269. mp3dec_init(coder->mp3_decoder);
  270. coder->buff.addr = luat_heap_malloc(MP3_FRAME_LEN);
  271. coder->buff.len = MP3_FRAME_LEN;
  272. coder->buff.used = luat_fs_fread(temp, 10, 1, fd);
  273. if (coder->buff.used != 10)
  274. {
  275. break;
  276. }
  277. if (!memcmp(temp, "ID3", 3))
  278. {
  279. jump = 0;
  280. for(i = 0; i < 4; i++)
  281. {
  282. jump <<= 7;
  283. jump |= temp[6 + i] & 0x7f;
  284. }
  285. // LLOGD("jump head %d", jump);
  286. luat_fs_fseek(fd, jump, SEEK_SET);
  287. }
  288. coder->buff.used = luat_fs_fread(coder->buff.addr, MP3_FRAME_LEN, 1, fd);
  289. result = mp3dec_decode_frame(coder->mp3_decoder, coder->buff.addr, coder->buff.used, NULL, &info);
  290. memset(coder->mp3_decoder, 0, sizeof(mp3dec_t));
  291. audio_format = MULTIMEDIA_DATA_TYPE_PCM;
  292. num_channels = info.channels;
  293. sample_rate = info.hz;
  294. break;
  295. case MULTIMEDIA_DATA_TYPE_WAV:
  296. luat_fs_fread(temp, 12, 1, fd);
  297. if (!memcmp(temp, "RIFF", 4) || !memcmp(temp + 8, "WAVE", 4))
  298. {
  299. luat_fs_fread(temp, 8, 1, fd);
  300. if (!memcmp(temp, "fmt ", 4))
  301. {
  302. memcpy(&len, temp + 4, 4);
  303. coder->buff.addr = luat_heap_malloc(len);
  304. luat_fs_fread(coder->buff.addr, len, 1, fd);
  305. audio_format = coder->buff.addr[0];
  306. num_channels = coder->buff.addr[2];
  307. memcpy(&sample_rate, coder->buff.addr + 4, 4);
  308. align = coder->buff.addr[12];
  309. bits_per_sample = coder->buff.addr[14];
  310. coder->read_len = (align * sample_rate >> 3) & ~(3);
  311. // LLOGD("size %d", coder->read_len);
  312. luat_heap_free(coder->buff.addr);
  313. coder->buff.addr = NULL;
  314. luat_fs_fread(temp, 8, 1, fd);
  315. if (!memcmp(temp, "fact", 4))
  316. {
  317. memcpy(&len, temp + 4, 4);
  318. luat_fs_fseek(fd, len, SEEK_CUR);
  319. luat_fs_fread(temp, 8, 1, fd);
  320. }
  321. if (!memcmp(temp, "data", 4))
  322. {
  323. result = 1;
  324. }
  325. else
  326. {
  327. LLOGD("no data");
  328. result = 0;
  329. }
  330. }
  331. else
  332. {
  333. LLOGD("no fmt");
  334. }
  335. }
  336. else
  337. {
  338. LLOGD("head error");
  339. }
  340. break;
  341. default:
  342. break;
  343. }
  344. }
  345. if (!result)
  346. {
  347. luat_fs_fclose(fd);
  348. }
  349. else
  350. {
  351. coder->fd = fd;
  352. }
  353. lua_pushboolean(L, result);
  354. lua_pushinteger(L, audio_format);
  355. lua_pushinteger(L, num_channels);
  356. lua_pushinteger(L, sample_rate);
  357. lua_pushinteger(L, bits_per_sample);
  358. lua_pushboolean(L, is_signed);
  359. return 6;
  360. }
  361. /**
  362. decoder从文件数据中解析出音频数据
  363. @api codec.data(decoder, out_buff)
  364. @coder 解码用的decoder
  365. @zbuff 存放输出数据的zbuff,空间必须不少于16KB
  366. @return
  367. @boolean 是否成功解析
  368. @usage
  369. local result = codec.get_audio_data(coder, zbuff)
  370. */
  371. static int l_codec_get_audio_data(lua_State *L) {
  372. luat_multimedia_codec_t *coder = (luat_multimedia_codec_t *)luaL_checkudata(L, 1, LUAT_M_CODE_TYPE);
  373. uint32_t pos = 0;
  374. int read_len;
  375. int result;
  376. luat_zbuff_t *out_buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE));
  377. uint32_t is_not_end = 1;
  378. mp3dec_frame_info_t info;
  379. out_buff->used = 0;
  380. if (coder)
  381. {
  382. switch(coder->type)
  383. {
  384. case MULTIMEDIA_DATA_TYPE_MP3:
  385. GET_MP3_DATA:
  386. if (coder->buff.used < MINIMP3_MAX_SAMPLES_PER_FRAME)
  387. {
  388. read_len = luat_fs_fread(coder->buff.addr + coder->buff.used, MINIMP3_MAX_SAMPLES_PER_FRAME, 1, coder->fd);
  389. if (read_len > 0)
  390. {
  391. coder->buff.used += read_len;
  392. }
  393. else
  394. {
  395. is_not_end = 0;
  396. }
  397. }
  398. do
  399. {
  400. memset(&info, 0, sizeof(info));
  401. result = mp3dec_decode_frame(coder->mp3_decoder, coder->buff.addr + pos, coder->buff.used - pos, out_buff->addr + out_buff->used, &info);
  402. out_buff->used += (result * info.channels * 2);
  403. // if (!result) {
  404. // LLOGD("jump %dbyte", info.frame_bytes);
  405. // }
  406. pos += info.frame_bytes;
  407. if ((out_buff->len - out_buff->used) < (MINIMP3_MAX_SAMPLES_PER_FRAME * 2))
  408. {
  409. break;
  410. }
  411. } while ((coder->buff.used - pos) >= (MINIMP3_MAX_SAMPLES_PER_FRAME * is_not_end + 1));
  412. // LLOGD("result %u,%u,%u,%u,%u", result, out_buff->used, coder->buff.used, pos, info.frame_bytes);
  413. if (pos >= coder->buff.used)
  414. {
  415. coder->buff.used = 0;
  416. }
  417. else
  418. {
  419. memmove(coder->buff.addr, coder->buff.addr + pos, coder->buff.used - pos);
  420. coder->buff.used -= pos;
  421. }
  422. pos = 0;
  423. if (!out_buff->used)
  424. {
  425. if (is_not_end)
  426. {
  427. goto GET_MP3_DATA;
  428. }
  429. else
  430. {
  431. result = 0;
  432. }
  433. }
  434. else
  435. {
  436. if ((out_buff->used < 16384) && is_not_end)
  437. {
  438. goto GET_MP3_DATA;
  439. }
  440. result = 1;
  441. }
  442. break;
  443. case MULTIMEDIA_DATA_TYPE_WAV:
  444. read_len = luat_fs_fread(out_buff->addr + out_buff->used, coder->read_len, 1, coder->fd);
  445. if (read_len > 0)
  446. {
  447. out_buff->used += read_len;
  448. result = 1;
  449. }
  450. else
  451. {
  452. result = 0;
  453. }
  454. break;
  455. default:
  456. break;
  457. }
  458. }
  459. lua_pushboolean(L, result);
  460. return 1;
  461. }
  462. static int l_codec_gc(lua_State *L)
  463. {
  464. luat_multimedia_codec_t *coder = ((luat_multimedia_codec_t *)luaL_checkudata(L, 1, LUAT_M_CODE_TYPE));
  465. if (coder->fd) {
  466. luat_fs_fclose(coder->fd);
  467. coder->fd = NULL;
  468. }
  469. if (coder->buff.addr)
  470. {
  471. luat_heap_free(coder->buff.addr);
  472. memset(&coder->buff, 0, sizeof(luat_zbuff_t));
  473. }
  474. switch(coder->type) {
  475. case MULTIMEDIA_DATA_TYPE_MP3:
  476. if (coder->is_decoder && coder->mp3_decoder) {
  477. luat_heap_free(coder->mp3_decoder);
  478. coder->mp3_decoder = NULL;
  479. }
  480. break;
  481. }
  482. return 0;
  483. }
  484. /**
  485. 释放编解码用的coder
  486. @api codec.release(coder)
  487. @return
  488. codec.release(coder)
  489. */
  490. static int l_codec_release(lua_State *L) {
  491. return l_codec_gc(L);
  492. }
  493. #include "rotable2.h"
  494. static const rotable_Reg_t reg_audio[] =
  495. {
  496. { "start" , ROREG_FUNC(l_audio_start_raw)},
  497. { "write" , ROREG_FUNC(l_audio_write_raw)},
  498. { "pause", ROREG_FUNC(l_audio_pause_raw)},
  499. { "stop", ROREG_FUNC(l_audio_stop_raw)},
  500. { "on", ROREG_FUNC(l_audio_raw_on)},
  501. { "play", ROREG_FUNC(l_audio_play)},
  502. { "isEnd", ROREG_FUNC(l_audio_play_wait_end)},
  503. { "PCM", ROREG_INT(MULTIMEDIA_DATA_TYPE_PCM)},
  504. { "MORE_DATA", ROREG_INT(MULTIMEDIA_CB_AUDIO_NEED_DATA)},
  505. { "DONE", ROREG_INT(MULTIMEDIA_CB_AUDIO_DONE)},
  506. { NULL, {}}
  507. };
  508. static const rotable_Reg_t reg_codec[] =
  509. {
  510. { "create" , ROREG_FUNC(l_codec_create)},
  511. { "info" , ROREG_FUNC(l_codec_get_audio_info)},
  512. { "data", ROREG_FUNC(l_codec_get_audio_data)},
  513. { "release", ROREG_FUNC(l_codec_release)},
  514. { "MP3", ROREG_INT(MULTIMEDIA_DATA_TYPE_MP3)},
  515. { "WAV", ROREG_INT(MULTIMEDIA_DATA_TYPE_WAV)},
  516. { NULL, {}}
  517. };
  518. LUAMOD_API int luaopen_multimedia_audio( lua_State *L ) {
  519. luat_newlib2(L, reg_audio);
  520. return 1;
  521. }
  522. LUAMOD_API int luaopen_multimedia_codec( lua_State *L ) {
  523. luat_newlib2(L, reg_codec);
  524. luaL_newmetatable(L, LUAT_M_CODE_TYPE); /* create metatable for file handles */
  525. lua_pushcfunction(L, l_codec_gc);
  526. lua_setfield(L, -2, "__gc");
  527. lua_pop(L, 1); /* pop new metatable */
  528. return 1;
  529. }