luat_lib_multimedia.c 15 KB

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