ffmpeg.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. #include "ffmpeg.h"
  2. #ifdef _WIN32
  3. // FFmpeg DLL句柄定义 (仅Windows)
  4. HMODULE h_avutil = NULL;
  5. HMODULE h_avcodec = NULL;
  6. HMODULE h_avformat = NULL;
  7. HMODULE h_swresample = NULL;
  8. // 函数指针变量定义
  9. av_find_best_stream_t p_av_find_best_stream = NULL;
  10. avformat_open_input_t p_avformat_open_input = NULL;
  11. avformat_find_stream_info_t p_avformat_find_stream_info = NULL;
  12. avformat_close_input_t p_avformat_close_input = NULL;
  13. av_read_frame_t p_av_read_frame = NULL;
  14. avcodec_find_decoder_t p_avcodec_find_decoder = NULL;
  15. avcodec_alloc_context3_t p_avcodec_alloc_context3 = NULL;
  16. avcodec_free_context_t p_avcodec_free_context = NULL;
  17. avcodec_parameters_to_context_t p_avcodec_parameters_to_context = NULL;
  18. avcodec_open2_t p_avcodec_open2 = NULL;
  19. avcodec_send_packet_t p_avcodec_send_packet = NULL;
  20. avcodec_receive_frame_t p_avcodec_receive_frame = NULL;
  21. av_get_media_type_string_t p_av_get_media_type_string = NULL;
  22. av_get_default_channel_layout_t p_av_get_default_channel_layout = NULL;
  23. av_frame_alloc_t p_av_frame_alloc = NULL;
  24. av_frame_free_t p_av_frame_free = NULL;
  25. av_packet_alloc_t p_av_packet_alloc = NULL;
  26. av_packet_free_t p_av_packet_free = NULL;
  27. av_packet_unref_t p_av_packet_unref = NULL;
  28. av_samples_alloc_t p_av_samples_alloc = NULL;
  29. av_freep_t p_av_freep = NULL;
  30. av_rescale_rnd_t p_av_rescale_rnd = NULL;
  31. av_get_bytes_per_sample_t p_av_get_bytes_per_sample = NULL;
  32. av_log_set_level_t p_av_log_set_level = NULL;
  33. swr_alloc_set_opts_t p_swr_alloc_set_opts = NULL;
  34. swr_init_t p_swr_init = NULL;
  35. swr_free_t p_swr_free = NULL;
  36. swr_convert_t p_swr_convert = NULL;
  37. swr_get_delay_t p_swr_get_delay = NULL;
  38. #endif // _WIN32
  39. static int luat_ffmpeg_debug = 0;
  40. #ifdef _WIN32
  41. // 显式加载FFmpeg DLL (仅Windows)
  42. int luat_load_ffmpeg_dlls(void) {
  43. char exe_path[MAX_PATH];
  44. char dll_dir[MAX_PATH];
  45. char dll_path[MAX_PATH];
  46. // 获取exe路径和目录
  47. if (GetModuleFileNameA(NULL, exe_path, MAX_PATH) == 0) {
  48. return -1;
  49. }
  50. char* last_slash = strrchr(exe_path, '\\');
  51. if (last_slash) *last_slash = '\0';
  52. // plan1: 从..\..\release加载
  53. snprintf(dll_dir, sizeof(dll_dir), "%s\\..\\..\\release", exe_path);
  54. snprintf(dll_path, sizeof(dll_path), "%s\\avutil-56.dll", dll_dir);
  55. h_avutil = LoadLibraryA(dll_path);
  56. // plan2: 从exe同目录加载
  57. if (!h_avutil) {
  58. strcpy(dll_dir, exe_path);
  59. snprintf(dll_path, sizeof(dll_path), "%s\\avutil-56.dll", dll_dir);
  60. h_avutil = LoadLibraryA(dll_path);
  61. }
  62. if (!h_avutil) {
  63. return -1; // 两个位置都没找到
  64. }
  65. snprintf(dll_path, sizeof(dll_path), "%s\\swresample-3.dll", dll_dir);
  66. h_swresample = LoadLibraryA(dll_path);
  67. if (!h_swresample) {
  68. return -1;
  69. }
  70. snprintf(dll_path, sizeof(dll_path), "%s\\avcodec-58.dll", dll_dir);
  71. h_avcodec = LoadLibraryA(dll_path);
  72. if (!h_avcodec) {
  73. return -1;
  74. }
  75. snprintf(dll_path, sizeof(dll_path), "%s\\avformat-58.dll", dll_dir);
  76. h_avformat = LoadLibraryA(dll_path);
  77. if (!h_avformat) {
  78. return -1;
  79. }
  80. #define LOAD_FUNC(module, name) \
  81. p_##name = (name##_t)GetProcAddress(module, #name); \
  82. if (!p_##name) return -1;
  83. LOAD_FUNC(h_avformat, av_find_best_stream);
  84. LOAD_FUNC(h_avformat, avformat_open_input);
  85. LOAD_FUNC(h_avformat, avformat_find_stream_info);
  86. LOAD_FUNC(h_avformat, avformat_close_input);
  87. LOAD_FUNC(h_avformat, av_read_frame);
  88. LOAD_FUNC(h_avcodec, avcodec_find_decoder);
  89. LOAD_FUNC(h_avcodec, avcodec_alloc_context3);
  90. LOAD_FUNC(h_avcodec, avcodec_free_context);
  91. LOAD_FUNC(h_avcodec, avcodec_parameters_to_context);
  92. LOAD_FUNC(h_avcodec, avcodec_open2);
  93. LOAD_FUNC(h_avcodec, avcodec_send_packet);
  94. LOAD_FUNC(h_avcodec, avcodec_receive_frame);
  95. LOAD_FUNC(h_avutil, av_get_media_type_string);
  96. LOAD_FUNC(h_avutil, av_get_default_channel_layout);
  97. LOAD_FUNC(h_avutil, av_frame_alloc);
  98. LOAD_FUNC(h_avutil, av_frame_free);
  99. LOAD_FUNC(h_avcodec, av_packet_alloc);
  100. LOAD_FUNC(h_avcodec, av_packet_free);
  101. LOAD_FUNC(h_avcodec, av_packet_unref);
  102. LOAD_FUNC(h_avutil, av_samples_alloc);
  103. LOAD_FUNC(h_avutil, av_freep);
  104. LOAD_FUNC(h_avutil, av_rescale_rnd);
  105. LOAD_FUNC(h_avutil, av_get_bytes_per_sample);
  106. LOAD_FUNC(h_avutil, av_log_set_level);
  107. LOAD_FUNC(h_swresample, swr_alloc_set_opts);
  108. LOAD_FUNC(h_swresample, swr_init);
  109. LOAD_FUNC(h_swresample, swr_free);
  110. LOAD_FUNC(h_swresample, swr_convert);
  111. LOAD_FUNC(h_swresample, swr_get_delay);
  112. av_log_set_level(16); // AV_LOG_ERROR 只显示错误
  113. return 0;
  114. }
  115. void luat_unload_ffmpeg_dlls(void) {
  116. if (h_avformat) FreeLibrary(h_avformat);
  117. if (h_avcodec) FreeLibrary(h_avcodec);
  118. if (h_swresample) FreeLibrary(h_swresample);
  119. if (h_avutil) FreeLibrary(h_avutil);
  120. luat_ffmpeg_debug = 0;
  121. }
  122. #else
  123. int luat_load_ffmpeg_dlls(void) {
  124. av_log_set_level(16);
  125. return 0;
  126. }
  127. void luat_unload_ffmpeg_dlls(void) {
  128. // Linux下无需卸载
  129. }
  130. #endif // _WIN32
  131. int luat_ffmpeg_play_file(const char *path) {
  132. AVFormatContext *fmt_ctx = NULL;
  133. // 打开输入文件
  134. if (avformat_open_input(&fmt_ctx, path, NULL, NULL) < 0) {
  135. return -1;
  136. }
  137. // 获取流信息
  138. if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
  139. avformat_close_input(&fmt_ctx);
  140. return -1;
  141. }
  142. // 打印流信息
  143. for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
  144. AVStream *stream = fmt_ctx->streams[i];
  145. AVCodecParameters *codecpar = stream->codecpar;
  146. if(luat_ffmpeg_debug) {
  147. printf("Stream %u: type=%s, codec_id=%d\n",
  148. i,
  149. av_get_media_type_string(codecpar->codec_type),
  150. codecpar->codec_id);
  151. }
  152. }
  153. // 查找最佳音频流
  154. int audio_stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
  155. if (audio_stream_idx < 0) {
  156. avformat_close_input(&fmt_ctx);
  157. return -1;
  158. }
  159. // 获取编码参数和解码器
  160. AVCodecParameters *codec_par = fmt_ctx->streams[audio_stream_idx]->codecpar;
  161. const AVCodec *codec = avcodec_find_decoder(codec_par->codec_id);
  162. if (!codec) {
  163. avformat_close_input(&fmt_ctx);
  164. return -1;
  165. }
  166. // 分配解码器上下文
  167. AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
  168. if (!codec_ctx) {
  169. avformat_close_input(&fmt_ctx);
  170. return -1;
  171. }
  172. // 复制编码参数到解码器上下文
  173. if (avcodec_parameters_to_context(codec_ctx, codec_par) < 0) {
  174. avcodec_free_context(&codec_ctx);
  175. avformat_close_input(&fmt_ctx);
  176. return -1;
  177. }
  178. // 确保 channel_layout 已设置
  179. if (!codec_ctx->channel_layout) {
  180. codec_ctx->channel_layout = av_get_default_channel_layout(codec_ctx->channels);
  181. }
  182. // 打开解码器
  183. if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
  184. avcodec_free_context(&codec_ctx);
  185. avformat_close_input(&fmt_ctx);
  186. return -1;
  187. }
  188. SwrContext *swr_ctx = NULL;
  189. uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
  190. enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
  191. int out_sample_rate = 44100;
  192. swr_ctx = swr_alloc_set_opts(NULL,
  193. out_ch_layout, out_sample_fmt, out_sample_rate,
  194. codec_ctx->channel_layout, codec_ctx->sample_fmt, codec_ctx->sample_rate,
  195. 0, NULL);
  196. if (!swr_ctx) {
  197. avcodec_free_context(&codec_ctx);
  198. avformat_close_input(&fmt_ctx);
  199. return -1;
  200. }
  201. // 初始化重采样器
  202. if (swr_init(swr_ctx) < 0) {
  203. swr_free(&swr_ctx);
  204. avcodec_free_context(&codec_ctx);
  205. avformat_close_input(&fmt_ctx);
  206. return -1;
  207. }
  208. // 分配帧和包
  209. AVPacket *pkt = av_packet_alloc();
  210. AVFrame *frame = av_frame_alloc();
  211. if (!pkt || !frame) {
  212. av_frame_free(&frame);
  213. av_packet_free(&pkt);
  214. swr_free(&swr_ctx);
  215. avcodec_free_context(&codec_ctx);
  216. avformat_close_input(&fmt_ctx);
  217. return -1;
  218. }
  219. #ifdef LUAT_USE_GUI
  220. // 初始化SDL音频子系统
  221. if (SDL_Init(SDL_INIT_AUDIO) < 0) {
  222. av_frame_free(&frame);
  223. av_packet_free(&pkt);
  224. swr_free(&swr_ctx);
  225. avcodec_free_context(&codec_ctx);
  226. avformat_close_input(&fmt_ctx);
  227. return -1;
  228. }
  229. // 设置SDL音频规格
  230. SDL_AudioSpec desired_spec, obtained_spec;
  231. desired_spec.freq = out_sample_rate;
  232. desired_spec.format = AUDIO_S16SYS; // 16位有符号整数,系统字节序
  233. desired_spec.channels = 2; // 立体声
  234. desired_spec.samples = 1024; // 缓冲区大小
  235. desired_spec.callback = NULL; // 使用队列方式
  236. desired_spec.userdata = NULL;
  237. // 打开音频设备
  238. SDL_AudioDeviceID audio_device = SDL_OpenAudioDevice(NULL, 0, &desired_spec, &obtained_spec, 0);
  239. if (audio_device == 0) {
  240. SDL_Quit();
  241. av_frame_free(&frame);
  242. av_packet_free(&pkt);
  243. swr_free(&swr_ctx);
  244. avcodec_free_context(&codec_ctx);
  245. avformat_close_input(&fmt_ctx);
  246. return -1;
  247. }
  248. // 开始播放音频
  249. SDL_PauseAudioDevice(audio_device, 0);
  250. #endif
  251. // 读取并处理帧
  252. while (av_read_frame(fmt_ctx, pkt) >= 0) {
  253. if (pkt->stream_index == audio_stream_idx) {
  254. // 发送包到解码器
  255. if (avcodec_send_packet(codec_ctx, pkt) < 0) {
  256. break;
  257. }
  258. // 接收解码后的帧
  259. while (avcodec_receive_frame(codec_ctx, frame) == 0) {
  260. // 分配重采样缓冲区
  261. uint8_t *resampled_data[AV_NUM_DATA_POINTERS];
  262. int resampled_linesize;
  263. int resampled_nb_samples = av_rescale_rnd(
  264. swr_get_delay(swr_ctx, frame->sample_rate) + frame->nb_samples,
  265. out_sample_rate,
  266. frame->sample_rate,
  267. AV_ROUND_UP
  268. );
  269. if (av_samples_alloc(
  270. resampled_data, &resampled_linesize,
  271. 2, // 立体声通道数
  272. resampled_nb_samples,
  273. out_sample_fmt,
  274. 0
  275. ) < 0) {
  276. break;
  277. }
  278. // 执行重采样
  279. int out_nb_samples = swr_convert(
  280. swr_ctx,
  281. resampled_data, resampled_nb_samples,
  282. (const uint8_t **)frame->data, frame->nb_samples
  283. );
  284. if (out_nb_samples < 0) {
  285. av_freep(&resampled_data[0]);
  286. break;
  287. }
  288. // 计算PCM数据大小(字节数)
  289. int pcm_size = out_nb_samples * 2 * av_get_bytes_per_sample(out_sample_fmt); // 2 = 立体声通道数
  290. #ifdef LUAT_USE_GUI
  291. // 将PCM数据加入SDL音频队列进行播放
  292. if (SDL_QueueAudio(audio_device, resampled_data[0], pcm_size) < 0) {
  293. fprintf(stderr, "Failed to queue audio: %s\n", SDL_GetError());
  294. }
  295. if(luat_ffmpeg_debug) {
  296. printf("Queued %d bytes of audio data\n", pcm_size);
  297. }
  298. #endif
  299. // 释放重采样缓冲区
  300. av_freep(&resampled_data[0]);
  301. }
  302. }
  303. av_packet_unref(pkt);
  304. }
  305. #ifdef LUAT_USE_GUI
  306. // 等待音频播放完成
  307. Uint32 queued = SDL_GetQueuedAudioSize(audio_device);
  308. while (queued > 0) {
  309. SDL_Delay(100); // 等待100毫秒
  310. queued = SDL_GetQueuedAudioSize(audio_device);
  311. }
  312. // 清理资源
  313. SDL_CloseAudioDevice(audio_device);
  314. SDL_Quit();
  315. #endif
  316. av_frame_free(&frame);
  317. av_packet_free(&pkt);
  318. swr_free(&swr_ctx);
  319. avcodec_free_context(&codec_ctx);
  320. avformat_close_input(&fmt_ctx);
  321. return 0;
  322. }
  323. void luat_ffmpeg_set_debug(int level) {
  324. /**
  325. * ffmepg 日志等级设置
  326. * AV_LOG_QUIET -8 关闭所有日志输出(最 “安静”)
  327. * AV_LOG_PANIC 0 仅输出导致程序崩溃的致命错误(如内存分配失败)
  328. * AV_LOG_FATAL 8 输出严重错误(如无法打开文件),程序可能无法继续运行
  329. * AV_LOG_ERROR 16 输出普通错误(如处理帧失败),程序可能仍能部分运行
  330. * AV_LOG_WARNING 24 输出警告信息(如不推荐的用法、格式不兼容),不影响核心功能
  331. * AV_LOG_INFO 32 输出信息性消息(如处理进度、参数信息),用于说明程序正常运行状态
  332. * AV_LOG_VERBOSE 40 输出详细信息(比INFO更细致),通常用于跟踪程序流程
  333. * AV_LOG_DEBUG 48 输出调试信息(如函数调用细节、中间变量值),用于开发调试
  334. * AV_LOG_TRACE 56 输出最详细的跟踪信息(如每一步操作的细节),仅用于深度调试
  335. */
  336. // av_log_set_level(AV_LOG_QUIET);
  337. if (level) {
  338. luat_ffmpeg_debug = 1;
  339. } else {
  340. luat_ffmpeg_debug = 0;
  341. }
  342. }