playercontroller.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. #include "playercontroller.h"
  2. #include "common.h"
  3. #include <QApplication>
  4. #include <QElapsedTimer>
  5. #include <QStatusBar>
  6. #include <cassert>
  7. #include <memory>
  8. #include "ffmpeg_init.h"
  9. #include "start_play_thread.h"
  10. #include "video_state.h"
  11. #include "audio_decode_thread.h"
  12. #include "audio_effect_helper.h"
  13. #include "audio_play_thread.h"
  14. #include "play_control_window.h"
  15. #include "read_thread.h"
  16. #include "start_play_thread.h"
  17. #include "stopplay_waiting_thread.h"
  18. #include "subtitle_decode_thread.h"
  19. #include "video_decode_thread.h"
  20. #include "video_play_thread.h"
  21. #include "video_state.h"
  22. PlayerController::PlayerController(QWidget* parent)
  23. : QObject(parent)
  24. {
  25. ffmpeg_init();
  26. }
  27. PlayerController::~PlayerController()
  28. {
  29. // 等待初始化线程安全退出
  30. if (m_initThread.joinable()) {
  31. {
  32. std::lock_guard<std::mutex> lock(m_initMutex);
  33. m_initInProgress = false;
  34. }
  35. m_initCv.notify_all();
  36. m_initThread.join();
  37. }
  38. stopPlay();
  39. }
  40. // 播放控制接口
  41. void PlayerController::startToPlay(const QString& file)
  42. {
  43. if (isPlaying()) {
  44. if (m_currentFile == file)
  45. return;
  46. waitStopPlay(file);
  47. return;
  48. }
  49. m_currentFile = file;
  50. if (m_initInProgress) return; // 正在初始化,直接返回
  51. m_initInProgress = true;
  52. if (m_initThread.joinable()) m_initThread.join();
  53. m_initThread = std::thread(&PlayerController::asyncInit, this, file);
  54. }
  55. void PlayerController::asyncInit(const QString& file)
  56. {
  57. bool success = false;
  58. // startPlay 的主体迁移到这里
  59. QElapsedTimer timer;
  60. timer.start();
  61. if (file.isEmpty()) {
  62. qWarning("Filename is invalid. Please select a valid media file.");
  63. success = false;
  64. } else {
  65. qInfo("Starting playback: %s", qUtf8Printable(toNativePath(file)));
  66. if (!createReadThread()) {
  67. qWarning("Packet read thread creation failed");
  68. success = false;
  69. } else if (!createVideoState(file)) {
  70. qWarning("Video state creation failed");
  71. readPacketStopped();
  72. success = false;
  73. } else if (!m_videoState) {
  74. qWarning("Video state initialization error");
  75. success = false;
  76. } else {
  77. m_packetReadThread->set_video_state(m_videoState->get_state());
  78. const bool hasVideo = playingHasVideo();
  79. const bool hasAudio = playingHasAudio();
  80. const bool hasSubtitle = playingHasSubtitle();
  81. if (hasVideo) {
  82. if (!createDecodeVideoThread() || !createVideoPlayThread()) {
  83. qWarning("Video processing setup failed");
  84. success = false;
  85. }
  86. }
  87. if (hasAudio) {
  88. if (!createDecodeAudioThread() || !createAudioPlayThread()) {
  89. qWarning("Audio processing setup failed");
  90. success = false;
  91. }
  92. }
  93. if (hasSubtitle && !createDecodeSubtitleThread()) {
  94. qWarning("Subtitle processing setup failed");
  95. success = false;
  96. }
  97. // 开始播放
  98. if (hasAudio) {
  99. // 音频设备初始化在独立线程中完成
  100. startPlayThread();
  101. } else {
  102. playStarted();
  103. }
  104. success = true;
  105. }
  106. }
  107. {
  108. std::lock_guard<std::mutex> lock(m_initMutex);
  109. m_initSuccess = success;
  110. m_initInProgress = false;
  111. }
  112. m_initCv.notify_all();
  113. onInitFinished(success);
  114. qDebug("Playback initialized in %lld ms", timer.elapsed());
  115. }
  116. void PlayerController::onInitFinished(bool success)
  117. {
  118. // 这里可以发 Qt 信号或回调
  119. if (!success) {
  120. playFailed(m_currentFile);
  121. return;
  122. }
  123. emit startToPlaySignal();
  124. }
  125. void PlayerController::stopPlay()
  126. {
  127. deleteVideoState();
  128. m_packetReadThread.reset();
  129. m_decodeVideoThread.reset();
  130. m_decodeAudioThread.reset();
  131. m_decodeSubtitleThread.reset();
  132. m_videoPlayThread.reset();
  133. m_audioPlayThread.reset();
  134. }
  135. void PlayerController::pausePlay()
  136. {
  137. if (!m_videoState)
  138. return;
  139. if (auto state = m_videoState->get_state())
  140. toggle_pause(state, !state->paused);
  141. emit updatePlayControlStatus();
  142. }
  143. void PlayerController::playMute(bool mute)
  144. {
  145. if (!m_videoState)
  146. return;
  147. if (auto state = m_videoState->get_state())
  148. toggle_mute(state, mute);
  149. }
  150. void PlayerController::playStartSeek()
  151. {
  152. emit playSeek();
  153. pausePlay();
  154. }
  155. void PlayerController::playSeekPre()
  156. {
  157. videoSeekInc(-2);
  158. }
  159. void PlayerController::playSeekNext()
  160. {
  161. videoSeekInc(2);
  162. }
  163. void PlayerController::setVolume(int volume, int maxValue)
  164. {
  165. if (!m_audioPlayThread)
  166. return;
  167. const float vol = static_cast<float>(volume) / maxValue;
  168. m_audioPlayThread->set_device_volume(vol);
  169. }
  170. void PlayerController::setPlaySpeed(double speed)
  171. {
  172. if (m_videoState) {
  173. if (auto state = m_videoState->get_state()) {
  174. #if USE_AVFILTER_AUDIO
  175. set_audio_playspeed(state, speed);
  176. #endif
  177. }
  178. }
  179. }
  180. // 状态访问接口
  181. QString PlayerController::playingFile() const
  182. {
  183. return isPlaying() ? m_currentFile : QString();
  184. }
  185. bool PlayerController::isPlaying() const
  186. {
  187. return m_videoState || m_packetReadThread || m_decodeVideoThread || m_decodeAudioThread
  188. || m_audioPlayThread || m_videoPlayThread || m_decodeSubtitleThread;
  189. }
  190. bool PlayerController::playingHasVideo()
  191. {
  192. return m_videoState ? m_videoState->has_video() : false;
  193. }
  194. bool PlayerController::playingHasAudio()
  195. {
  196. return m_videoState ? m_videoState->has_audio() : false;
  197. }
  198. bool PlayerController::playingHasSubtitle()
  199. {
  200. return m_videoState ? m_videoState->has_subtitle() : false;
  201. }
  202. VideoState* PlayerController::state()
  203. {
  204. return m_videoState ? m_videoState->get_state() : nullptr;
  205. }
  206. float PlayerController::deviceVolume() const
  207. {
  208. return m_audioPlayThread ? m_audioPlayThread->get_device_volume() : 0.0f;
  209. }
  210. void PlayerController::setDeviceVolume(float volume)
  211. {
  212. if (m_audioPlayThread)
  213. m_audioPlayThread->set_device_volume(volume);
  214. }
  215. // 播放状态回调槽函数
  216. void PlayerController::playStarted(bool success)
  217. {
  218. if (!success) {
  219. qWarning("Audio device initialization failed!");
  220. return;
  221. }
  222. allThreadStart();
  223. setThreads();
  224. }
  225. void PlayerController::playFailed(const QString& file)
  226. {
  227. emit showMessage(QString("Playback failed: %1").arg(toNativePath(file)), "Warning", "");
  228. }
  229. // 线程生命周期管理槽函数
  230. void PlayerController::readPacketStopped()
  231. {
  232. if (m_packetReadThread) {
  233. m_packetReadThread.reset();
  234. qDebug("************* Read packets thread stopped.");
  235. }
  236. stopPlay();
  237. }
  238. void PlayerController::decodeVideoStopped()
  239. {
  240. m_decodeVideoThread.reset();
  241. qDebug("************* Video decode thread stopped.");
  242. }
  243. void PlayerController::decodeAudioStopped()
  244. {
  245. m_decodeAudioThread.reset();
  246. qDebug("************* Audio decode thread stopped.");
  247. }
  248. void PlayerController::decodeSubtitleStopped()
  249. {
  250. m_decodeSubtitleThread.reset();
  251. qDebug("************* Subtitle decode thread stopped.");
  252. }
  253. void PlayerController::audioPlayStopped()
  254. {
  255. m_audioPlayThread.reset();
  256. qDebug("************* Audio play thread stopped.");
  257. emit audioStopped(); // 音频结束
  258. }
  259. void PlayerController::videoPlayStopped()
  260. {
  261. m_videoPlayThread.reset();
  262. qDebug("************* Video play thread stopped.");
  263. emit videoStopped(); // 视频结束
  264. }
  265. // 线程管理槽函数
  266. void PlayerController::setThreads()
  267. {
  268. if (!m_videoState)
  269. return;
  270. Threads threads;
  271. threads.read_tid = m_packetReadThread.get();
  272. threads.video_decode_tid = m_decodeVideoThread.get();
  273. threads.audio_decode_tid = m_decodeAudioThread.get();
  274. threads.video_play_tid = m_videoPlayThread.get();
  275. threads.audio_play_tid = m_audioPlayThread.get();
  276. threads.subtitle_decode_tid = m_decodeSubtitleThread.get();
  277. m_videoState->threads_setting(m_videoState->get_state(), threads);
  278. }
  279. void PlayerController::startSendData(bool send)
  280. {
  281. if (m_audioPlayThread)
  282. m_audioPlayThread->send_visual_open(send);
  283. }
  284. void PlayerController::videoSeek(double position, double increment)
  285. {
  286. if (!m_videoState)
  287. return;
  288. auto state = m_videoState->get_state();
  289. if (!state)
  290. return;
  291. if (state->ic->start_time != AV_NOPTS_VALUE
  292. && position < state->ic->start_time / static_cast<double>(AV_TIME_BASE)) {
  293. position = state->ic->start_time / static_cast<double>(AV_TIME_BASE);
  294. }
  295. stream_seek(state,
  296. static_cast<int64_t>(position * AV_TIME_BASE),
  297. static_cast<int64_t>(increment * AV_TIME_BASE),
  298. 0);
  299. }
  300. // 核心私有实现
  301. bool PlayerController::startPlay()
  302. {
  303. QElapsedTimer timer;
  304. timer.start();
  305. if (m_currentFile.isEmpty()) {
  306. qWarning("Filename is invalid. Please select a valid media file.");
  307. return false;
  308. }
  309. qInfo("Starting playback: %s", qUtf8Printable(toNativePath(m_currentFile)));
  310. // 创建数据包读取线程
  311. if (!createReadThread()) {
  312. qWarning("Packet read thread creation failed");
  313. return false;
  314. }
  315. // 创建视频状态对象
  316. if (!createVideoState(m_currentFile)) {
  317. qWarning("Video state creation failed");
  318. readPacketStopped();
  319. return false;
  320. }
  321. // 检查状态有效性
  322. assert(m_videoState);
  323. if (!m_videoState) {
  324. qWarning("Video state initialization error");
  325. return false;
  326. }
  327. m_packetReadThread->set_video_state(m_videoState->get_state());
  328. const bool hasVideo = playingHasVideo();
  329. const bool hasAudio = playingHasAudio();
  330. const bool hasSubtitle = playingHasSubtitle();
  331. // 创建视频相关线程
  332. if (hasVideo) {
  333. if (!createDecodeVideoThread() || !createVideoPlayThread()) {
  334. qWarning("Video processing setup failed");
  335. return false;
  336. }
  337. }
  338. // 创建音频相关线程
  339. if (hasAudio) {
  340. if (!createDecodeAudioThread() || !createAudioPlayThread()) {
  341. qWarning("Audio processing setup failed");
  342. return false;
  343. }
  344. }
  345. // 创建字幕线程
  346. if (hasSubtitle && !createDecodeSubtitleThread()) {
  347. qWarning("Subtitle processing setup failed");
  348. return false;
  349. }
  350. // 开始播放
  351. if (hasAudio) {
  352. startPlayThread(); // 异步启动(处理音频设备初始化)
  353. } else {
  354. playStarted(); // 同步启动(无音频流)
  355. }
  356. qDebug("Playback initialized in %lld ms", timer.elapsed());
  357. return true;
  358. }
  359. void PlayerController::waitStopPlay(const QString& file)
  360. {
  361. m_stopPlayWaitingThread = std::make_unique<StopWaitingThread>(this, file);
  362. connect(m_stopPlayWaitingThread.get(),
  363. &StopWaitingThread::stopPlay,
  364. this,
  365. &PlayerController::stopPlay);
  366. connect(m_stopPlayWaitingThread.get(),
  367. &StopWaitingThread::startPlay,
  368. this,
  369. &PlayerController::startToPlay);
  370. m_stopPlayWaitingThread->start();
  371. qDebug("++++++++++ StopPlay waiting thread started");
  372. }
  373. void PlayerController::allThreadStart()
  374. {
  375. // 启动所有创建的线程
  376. if (m_packetReadThread) {
  377. m_packetReadThread->start();
  378. qDebug("++++++++++ Read packets thread started");
  379. }
  380. if (m_decodeVideoThread) {
  381. m_decodeVideoThread->start();
  382. qDebug("++++++++++ Video decode thread started");
  383. }
  384. if (m_decodeAudioThread) {
  385. m_decodeAudioThread->start(QThread::Priority::HighPriority);
  386. qDebug("++++++++++ Audio decode thread started");
  387. }
  388. if (m_decodeSubtitleThread) {
  389. m_decodeSubtitleThread->start();
  390. qDebug("++++++++++ Subtitle decode thread started");
  391. }
  392. if (m_videoPlayThread) {
  393. m_videoPlayThread->start();
  394. qDebug("++++++++++ Video play thread started");
  395. }
  396. if (m_audioPlayThread) {
  397. m_audioPlayThread->start();
  398. qDebug("++++++++++ Audio play thread started");
  399. }
  400. // 通知UI更新
  401. emit setPlayControlWnd(true);
  402. emit updatePlayControlVolume();
  403. emit updatePlayControlStatus();
  404. }
  405. // 辅助函数
  406. void PlayerController::videoSeekInc(double increment)
  407. {
  408. if (!m_videoState)
  409. return;
  410. auto state = m_videoState->get_state();
  411. if (!state)
  412. return;
  413. double position = get_master_clock(state);
  414. if (std::isnan(position)) {
  415. position = static_cast<double>(state->seek_pos) / AV_TIME_BASE;
  416. }
  417. position += increment;
  418. videoSeek(position, increment);
  419. }
  420. // 线程创建方法
  421. bool PlayerController::createVideoState(const QString& file)
  422. {
  423. const bool useHardware = false; // 待实现:来自UI设置
  424. const bool loop = false; // 待实现:来自UI设置
  425. if (m_videoState)
  426. return false;
  427. m_videoState = std::make_unique<VideoStateData>(useHardware, loop);
  428. const int ret = m_videoState->create_video_state(file.toUtf8().constData());
  429. if (ret < 0) {
  430. m_videoState.reset();
  431. qWarning("Video state creation failed (error: %d)", ret);
  432. return false;
  433. }
  434. return true;
  435. }
  436. void PlayerController::deleteVideoState()
  437. {
  438. m_videoState.reset();
  439. }
  440. bool PlayerController::createReadThread()
  441. {
  442. if (m_packetReadThread)
  443. return false;
  444. m_packetReadThread = std::make_unique<ReadThread>(this);
  445. connect(m_packetReadThread.get(),
  446. &ReadThread::finished,
  447. this,
  448. &PlayerController::readPacketStopped);
  449. return true;
  450. }
  451. bool PlayerController::createDecodeVideoThread()
  452. {
  453. if (!m_videoState || m_decodeVideoThread)
  454. return false;
  455. auto state = m_videoState->get_state();
  456. if (!state)
  457. return false;
  458. m_decodeVideoThread = std::make_unique<VideoDecodeThread>(this, state);
  459. connect(m_decodeVideoThread.get(),
  460. &VideoDecodeThread::finished,
  461. this,
  462. &PlayerController::decodeVideoStopped);
  463. auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_VIDEO);
  464. // 初始化视频解码器
  465. int ret = decoder_init(&state->viddec,
  466. codecContext,
  467. &state->videoq,
  468. state->continue_read_thread);
  469. if (ret < 0) {
  470. qWarning("Video decoder initialization failed (error: %d)", ret);
  471. return false;
  472. }
  473. ret = decoder_start(&state->viddec, m_decodeVideoThread.get(), "video_decoder");
  474. if (ret < 0) {
  475. qWarning("Video decoder start failed (error: %d)", ret);
  476. return false;
  477. }
  478. state->queue_attachments_req = 1;
  479. return true;
  480. }
  481. bool PlayerController::createDecodeAudioThread()
  482. {
  483. if (!m_videoState || m_decodeAudioThread)
  484. return false;
  485. auto state = m_videoState->get_state();
  486. if (!state)
  487. return false;
  488. m_decodeAudioThread = std::make_unique<AudioDecodeThread>(this, state);
  489. connect(m_decodeAudioThread.get(),
  490. &AudioDecodeThread::finished,
  491. this,
  492. &PlayerController::decodeAudioStopped);
  493. auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_AUDIO);
  494. // 初始化音频解码器
  495. int ret = decoder_init(&state->auddec,
  496. codecContext,
  497. &state->audioq,
  498. state->continue_read_thread);
  499. if (ret < 0) {
  500. qWarning("Audio decoder initialization failed (error: %d)", ret);
  501. return false;
  502. }
  503. ret = decoder_start(&state->auddec, m_decodeAudioThread.get(), "audio_decoder");
  504. if (ret < 0) {
  505. qWarning("Audio decoder start failed (error: %d)", ret);
  506. return false;
  507. }
  508. return true;
  509. }
  510. bool PlayerController::createDecodeSubtitleThread()
  511. {
  512. if (!m_videoState || m_decodeSubtitleThread)
  513. return false;
  514. auto state = m_videoState->get_state();
  515. if (!state)
  516. return false;
  517. m_decodeSubtitleThread = std::make_unique<SubtitleDecodeThread>(this, state);
  518. connect(m_decodeSubtitleThread.get(),
  519. &SubtitleDecodeThread::finished,
  520. this,
  521. &PlayerController::decodeSubtitleStopped);
  522. auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_SUBTITLE);
  523. // 初始化字幕解码器
  524. int ret = decoder_init(&state->subdec,
  525. codecContext,
  526. &state->subtitleq,
  527. state->continue_read_thread);
  528. if (ret < 0) {
  529. qWarning("Subtitle decoder initialization failed (error: %d)", ret);
  530. return false;
  531. }
  532. ret = decoder_start(&state->subdec, m_decodeSubtitleThread.get(), "subtitle_decoder");
  533. if (ret < 0) {
  534. qWarning("Subtitle decoder start failed (error: %d)", ret);
  535. return false;
  536. }
  537. return true;
  538. }
  539. bool PlayerController::createVideoPlayThread()
  540. {
  541. if (!m_videoState || m_videoPlayThread)
  542. return false;
  543. auto state = m_videoState->get_state();
  544. if (!state)
  545. return false;
  546. m_videoPlayThread = std::make_unique<VideoPlayThread>(this, state);
  547. // 连接信号
  548. connect(m_videoPlayThread.get(),
  549. &VideoPlayThread::finished,
  550. this,
  551. &PlayerController::videoPlayStopped);
  552. connect(m_videoPlayThread.get(),
  553. &VideoPlayThread::frameReady,
  554. this,
  555. &PlayerController::frameReady);
  556. connect(m_videoPlayThread.get(),
  557. &VideoPlayThread::subtitle_ready,
  558. this,
  559. &PlayerController::subtitleReady);
  560. connect(this,
  561. &PlayerController::stopVideoPlayThread,
  562. m_videoPlayThread.get(),
  563. &VideoPlayThread::stop_thread);
  564. // 初始化参数
  565. auto videoContext = m_videoState->get_contex(AVMEDIA_TYPE_VIDEO);
  566. const bool useHardware = m_videoState->is_hardware_decode();
  567. if (!m_videoPlayThread->init_resample_param(videoContext, useHardware)) {
  568. qWarning("Video resample parameters initialization failed");
  569. return false;
  570. }
  571. return true;
  572. }
  573. bool PlayerController::createAudioPlayThread()
  574. {
  575. if (!m_videoState || m_audioPlayThread)
  576. return false;
  577. auto state = m_videoState->get_state();
  578. if (!state)
  579. return false;
  580. m_audioPlayThread = std::make_unique<AudioPlayThread>(this, state);
  581. // 连接信号
  582. connect(m_audioPlayThread.get(),
  583. &AudioPlayThread::finished,
  584. this,
  585. &PlayerController::audioPlayStopped);
  586. connect(this,
  587. &PlayerController::stopAudioPlayThread,
  588. m_audioPlayThread.get(),
  589. &AudioPlayThread::stop_thread);
  590. connect(m_audioPlayThread.get(),
  591. &AudioPlayThread::update_play_time,
  592. this,
  593. &PlayerController::updatePlayTime);
  594. connect(m_audioPlayThread.get(),
  595. &AudioPlayThread::data_visual_ready,
  596. this,
  597. &PlayerController::audioData);
  598. // 音频设备初始化在独立线程中完成
  599. return true;
  600. }
  601. bool PlayerController::startPlayThread()
  602. {
  603. if (m_beforePlayThread)
  604. return false;
  605. m_beforePlayThread = std::make_unique<StartPlayThread>(this, this);
  606. connect(m_beforePlayThread.get(),
  607. &StartPlayThread::audio_device_init,
  608. this,
  609. &PlayerController::playStarted);
  610. m_beforePlayThread->start();
  611. qDebug("++++++++++ StartPlay thread (audio init) started");
  612. return true;
  613. }
  614. // 调试辅助函数
  615. void PlayerController::printDecodeContext(const AVCodecContext* codecCtx, bool isVideo) const
  616. {
  617. if (!codecCtx)
  618. return;
  619. qInfo("%s codec: %s", isVideo ? "Video" : "Audio", codecCtx->codec->name);
  620. qInfo(" Type: %d, ID: %d, Tag: %d",
  621. codecCtx->codec_type,
  622. codecCtx->codec_id,
  623. codecCtx->codec_tag);
  624. if (isVideo) {
  625. qInfo(" Dimensions: %dx%d", codecCtx->width, codecCtx->height);
  626. } else {
  627. qInfo(" Sample rate: %d Hz, Channels: %d, Format: %d",
  628. codecCtx->sample_rate,
  629. codecCtx->ch_layout.nb_channels,
  630. codecCtx->sample_fmt);
  631. qInfo(" Frame size: %d, Block align: %d", codecCtx->frame_size, codecCtx->block_align);
  632. }
  633. }