From 6938844d1c6ae0f7449656b0539fda43cb12f094 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 1 Feb 2022 20:31:25 +0800 Subject: [PATCH 01/41] =?UTF-8?q?=E6=96=B0=E5=A2=9Effmpeg=E6=A0=B8?= =?UTF-8?q?=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 + MusicPlayer2/CommonData.h | 2 + MusicPlayer2/FfmpegCore.cpp | 235 +++++++++++++ MusicPlayer2/FfmpegCore.h | 74 +++++ MusicPlayer2/IPlayerCore.h | 3 +- MusicPlayer2/MusicPlayer2.rc | Bin 550332 -> 550910 bytes MusicPlayer2/MusicPlayer2.vcxproj | 2 + MusicPlayer2/MusicPlayer2.vcxproj.filters | 9 + MusicPlayer2/MusicPlayerDlg.cpp | 4 + MusicPlayer2/PlaySettingsDlg.cpp | 12 + MusicPlayer2/PlaySettingsDlg.h | 2 + MusicPlayer2/Player.cpp | 7 + MusicPlayer2/Player.h | 1 + MusicPlayer2/resource.h | 1 + ffmpeg_core/.gitignore | 2 + ffmpeg_core/CMakeLists.txt | 43 +++ ffmpeg_core/cmake/FindAVCODEC.cmake | 29 ++ ffmpeg_core/cmake/FindAVFORMAT.cmake | 29 ++ ffmpeg_core/cmake/FindAVUTIL.cmake | 29 ++ ffmpeg_core/cmake/FindSDL2.cmake | 388 ++++++++++++++++++++++ ffmpeg_core/cmake/FindSWRESAMPLE.cmake | 30 ++ ffmpeg_core/ffmpeg_core.h | 82 +++++ ffmpeg_core/src/core.cpp | 153 +++++++++ ffmpeg_core/src/core.h | 73 ++++ ffmpeg_core/src/decode.c | 123 +++++++ ffmpeg_core/src/decode.h | 19 ++ ffmpeg_core/src/loop.c | 87 +++++ ffmpeg_core/src/loop.h | 12 + ffmpeg_core/src/open.c | 29 ++ ffmpeg_core/src/open.h | 13 + ffmpeg_core/src/output.c | 104 ++++++ ffmpeg_core/src/output.h | 14 + ffmpeg_core/utils | 1 + 33 files changed, 1614 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100644 MusicPlayer2/FfmpegCore.cpp create mode 100644 MusicPlayer2/FfmpegCore.h create mode 100644 ffmpeg_core/.gitignore create mode 100644 ffmpeg_core/CMakeLists.txt create mode 100644 ffmpeg_core/cmake/FindAVCODEC.cmake create mode 100644 ffmpeg_core/cmake/FindAVFORMAT.cmake create mode 100644 ffmpeg_core/cmake/FindAVUTIL.cmake create mode 100644 ffmpeg_core/cmake/FindSDL2.cmake create mode 100644 ffmpeg_core/cmake/FindSWRESAMPLE.cmake create mode 100644 ffmpeg_core/ffmpeg_core.h create mode 100644 ffmpeg_core/src/core.cpp create mode 100644 ffmpeg_core/src/core.h create mode 100644 ffmpeg_core/src/decode.c create mode 100644 ffmpeg_core/src/decode.h create mode 100644 ffmpeg_core/src/loop.c create mode 100644 ffmpeg_core/src/loop.h create mode 100644 ffmpeg_core/src/open.c create mode 100644 ffmpeg_core/src/open.h create mode 100644 ffmpeg_core/src/output.c create mode 100644 ffmpeg_core/src/output.h create mode 160000 ffmpeg_core/utils diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..2ff362cb6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ffmpeg_core/utils"] + path = ffmpeg_core/utils + url = https://github.com/lifegpc/c-utils diff --git a/MusicPlayer2/CommonData.h b/MusicPlayer2/CommonData.h index 6d42d8dce..f4d8dab7d 100644 --- a/MusicPlayer2/CommonData.h +++ b/MusicPlayer2/CommonData.h @@ -298,6 +298,8 @@ struct PlaySettingData int fade_time{ 500 }; //淡入淡出时间(毫秒) bool use_mci{ false }; //是否使用MCI内核 + /// 是否使用ffmpeg内核 + bool use_ffmpeg{ false }; }; struct GlobalHotKeySettingData diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp new file mode 100644 index 000000000..f51e61786 --- /dev/null +++ b/MusicPlayer2/FfmpegCore.cpp @@ -0,0 +1,235 @@ +#include "stdafx.h" +#include "FfmpegCore.h" + +#include "AudioCommon.h" +#include "MusicPlayer2.h" + + +CFfmpegCore::CFfmpegCore() { + handle = nullptr; + err = 0; + Init(L"ffmpeg_core.dll"); + if (!IsSucceed()) { + } +} + +CFfmpegCore::~CFfmpegCore() { + if (handle) { + Close(); + } + CDllLib::UnInit(); +} + +void CFfmpegCore::InitCore() { + if (IsSucceed()) { + CAudioCommon::m_surpported_format.clear(); + SupportedFormat format; + format.description = L"FFMPEG"; + format.extensions.push_back(L"mp3"); + format.extensions.push_back(L"wma"); + format.extensions.push_back(L"wav"); + format.extensions.push_back(L"m4a"); + format.extensions.push_back(L"flac"); + format.extensions.push_back(L"ape"); + format.extensions.push_back(L"mp4"); + format.extensions.push_back(L"mkv"); + format.extensions.push_back(L"m2ts"); + format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.flac;*.ape;*.mp4;*.mkv;*.m2ts"; + CAudioCommon::m_surpported_format.push_back(format); + CAudioCommon::m_all_surpported_extensions = format.extensions; + } +} + +void CFfmpegCore::UnInitCore() { +} + +unsigned int CFfmpegCore::GetHandle() { + return 0; +} + +std::wstring CFfmpegCore::GetAudioType() { + return L""; +} + +int CFfmpegCore::GetChannels() { + if (ffmpeg_core_get_channels && handle) { + return ffmpeg_core_get_channels(handle); + } else return 0; +} + +int CFfmpegCore::GetFReq() { + if (ffmpeg_core_get_freq && handle) { + return ffmpeg_core_get_freq(handle); + } else return 0; +} + +std::wstring CFfmpegCore::GetSoundFontName() { + return L""; +} + +void CFfmpegCore::Open(const wchar_t* file_path) { + if (ffmpeg_core_open) { + int re = ffmpeg_core_open(file_path, &handle); + if (re) { + err = re; + } + } +} + +void CFfmpegCore::Close() { + if (free_music_handle && handle) { + free_music_handle(handle); + handle = nullptr; + } +} + +void CFfmpegCore::Play() { + if (ffmpeg_core_play && handle) { + ffmpeg_core_play(handle); + } +} + +void CFfmpegCore::Pause() { + if (ffmpeg_core_pause && handle) { + ffmpeg_core_pause(handle); + } +} + +void CFfmpegCore::Stop() { + // Stop +} + +void CFfmpegCore::SetVolume(int volume) { +} + +void CFfmpegCore::SetSpeed(float speed) { +} + +bool CFfmpegCore::SongIsOver() { + if (ffmpeg_core_song_is_over && handle) { + return ffmpeg_core_song_is_over(handle); + } else return false; +} + +int CFfmpegCore::GetCurPosition() { + if (ffmpeg_core_get_cur_position && handle) { + return ffmpeg_core_get_cur_position(handle) / 1000; + } else return 0; +} + +int CFfmpegCore::GetSongLength() { + if (ffmpeg_core_get_song_length && handle) { + return ffmpeg_core_get_song_length(handle) / 1000; + } else return 0; +} + +void CFfmpegCore::SetCurPosition(int position) { + if (ffmpeg_core_seek && handle) { + int re = ffmpeg_core_seek(handle, (int64_t)position * 1000); + if (re) { + err = re; + } + } +} + +void CFfmpegCore::GetAudioInfo(SongInfo& song_info, int flag) { + if (!handle) return; + if (flag & AF_LENGTH) song_info.lengh = GetSongLength(); + if (flag & AF_CHANNEL_INFO) { + song_info.freq = ffmpeg_core_get_freq(handle); + song_info.bits = ffmpeg_core_get_bits(handle); + song_info.channels = ffmpeg_core_get_channels(handle); + } +} + +void CFfmpegCore::GetAudioInfo(const wchar_t* file_path, SongInfo& song_info, int flag) { +} + +bool CFfmpegCore::IsMidi() { + return false; +} + +bool CFfmpegCore::IsMidiConnotPlay() { + return false; +} + +MidiInfo CFfmpegCore::GetMidiInfo() { + return MidiInfo(); +} + +std::wstring CFfmpegCore::GetMidiInnerLyric() { + return L""; +} + +bool CFfmpegCore::MidiNoLyric() { + return true; +} + +PlayingState CFfmpegCore::GetPlayingState() { + if (ffmpeg_core_is_playing && handle) { + return ffmpeg_core_is_playing(handle) ? PlayingState::PS_PLAYING : PlayingState::PS_PAUSED; + } else return PlayingState::PS_STOPED; +} + +void CFfmpegCore::ApplyEqualizer(int channel, int gain) { +} + +void CFfmpegCore::SetReverb(int mix, int time) { +} + +void CFfmpegCore::ClearReverb() { +} + +void CFfmpegCore::GetFFTData(float fft_data[FFT_SAMPLE]) { +} + +int CFfmpegCore::GetErrorCode() { + return err; +} + +std::wstring CFfmpegCore::GetErrorInfo(int error_code) { + return L""; +} + +std::wstring CFfmpegCore::GetErrorInfo() { + return L""; +} + +PlayerCoreType CFfmpegCore::GetCoreType() { + return PlayerCoreType::PT_FFMPEG; +} + +int CFfmpegCore::GetDeviceCount() { + return 0; +} + +bool CFfmpegCore::GetFunction() { + bool rtn = true; + //ȡ + free_music_handle = (_free_music_handle)::GetProcAddress(m_dll_module, "free_music_handle"); + ffmpeg_core_open = (_ffmpeg_core_open)::GetProcAddress(m_dll_module, "ffmpeg_core_open"); + ffmpeg_core_play = (_ffmpeg_core_play)::GetProcAddress(m_dll_module, "ffmpeg_core_play"); + ffmpeg_core_pause = (_ffmpeg_core_pause)::GetProcAddress(m_dll_module, "ffmpeg_core_pause"); + ffmpeg_core_seek = (_ffmpeg_core_seek)::GetProcAddress(m_dll_module, "ffmpeg_core_seek"); + ffmpeg_core_get_cur_position = (_ffmpeg_core_get_cur_position)::GetProcAddress(m_dll_module, "ffmpeg_core_get_cur_position"); + ffmpeg_core_song_is_over = (_ffmpeg_core_song_is_over)::GetProcAddress(m_dll_module, "ffmpeg_core_song_is_over"); + ffmpeg_core_get_song_length = (_ffmpeg_core_get_song_length)::GetProcAddress(m_dll_module, "ffmpeg_core_get_song_length"); + ffmpeg_core_get_channels = (_ffmpeg_core_get_channels)::GetProcAddress(m_dll_module, "ffmpeg_core_get_channels"); + ffmpeg_core_get_freq = (_ffmpeg_core_get_freq)::GetProcAddress(m_dll_module, "ffmpeg_core_get_freq"); + ffmpeg_core_is_playing = (_ffmpeg_core_is_playing)::GetProcAddress(m_dll_module, "ffmpeg_core_is_playing"); + ffmpeg_core_get_bits = (_ffmpeg_core_get_bits)::GetProcAddress(m_dll_module, "ffmpeg_core_get_bits"); + //жǷɹ + rtn &= (free_music_handle != NULL); + rtn &= (ffmpeg_core_open != NULL); + rtn &= (ffmpeg_core_play != NULL); + rtn &= (ffmpeg_core_pause != NULL); + rtn &= (ffmpeg_core_seek != NULL); + rtn &= (ffmpeg_core_get_cur_position != NULL); + rtn &= (ffmpeg_core_song_is_over != NULL); + rtn &= (ffmpeg_core_get_song_length != NULL); + rtn &= (ffmpeg_core_get_channels != NULL); + rtn &= (ffmpeg_core_get_freq != NULL); + rtn &= (ffmpeg_core_is_playing != NULL); + rtn &= (ffmpeg_core_get_bits != NULL); + return rtn; +} diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h new file mode 100644 index 000000000..d60c3a363 --- /dev/null +++ b/MusicPlayer2/FfmpegCore.h @@ -0,0 +1,74 @@ +#pragma once +#include "IPlayerCore.h" +#include "DllLib.h" + +typedef struct MusicHandle MusicHandle; +typedef void(*_free_music_handle)(MusicHandle*); +typedef int(*_ffmpeg_core_open)(const wchar_t*, MusicHandle**); +typedef int(*_ffmpeg_core_play)(MusicHandle*); +typedef int(*_ffmpeg_core_pause)(MusicHandle*); +typedef int(*_ffmpeg_core_seek)(MusicHandle*, int64_t); +typedef int64_t(*_ffmpeg_core_get_cur_position)(MusicHandle*); +typedef int(*_ffmpeg_core_song_is_over)(MusicHandle*); +typedef int64_t(*_ffmpeg_core_get_song_length)(MusicHandle*); +typedef int(*_ffmpeg_core_get_channels)(MusicHandle*); +typedef int(*_ffmpeg_core_get_freq)(MusicHandle*); +typedef int(*_ffmpeg_core_is_playing)(MusicHandle*); +typedef int(*_ffmpeg_core_get_bits)(MusicHandle*); + +class CFfmpegCore : public IPlayerCore, public CDllLib { +public: + CFfmpegCore(); + ~CFfmpegCore(); + virtual void InitCore() override; + virtual void UnInitCore() override; + virtual unsigned int GetHandle() override; + virtual std::wstring GetAudioType() override; + virtual int GetChannels() override; + virtual int GetFReq() override; + virtual std::wstring GetSoundFontName() override; + virtual void Open(const wchar_t* file_path) override; + virtual void Close() override; + virtual void Play() override; + virtual void Pause() override; + virtual void Stop() override; + virtual void SetVolume(int volume) override; + virtual void SetSpeed(float speed) override; + virtual bool SongIsOver() override; + virtual int GetCurPosition() override; + virtual int GetSongLength() override; + virtual void SetCurPosition(int position) override; + virtual void GetAudioInfo(SongInfo& song_info, int flag = AF_LENGTH | AF_BITRATE | AF_TAG_INFO) override; + virtual void GetAudioInfo(const wchar_t* file_path, SongInfo& song_info, int flag = AF_LENGTH | AF_BITRATE | AF_TAG_INFO) override; + virtual bool IsMidi() override; + virtual bool IsMidiConnotPlay() override; + virtual MidiInfo GetMidiInfo() override; + virtual std::wstring GetMidiInnerLyric() override; + virtual bool MidiNoLyric() override; + virtual PlayingState GetPlayingState() override; + virtual void ApplyEqualizer(int channel, int gain) override; + virtual void SetReverb(int mix, int time) override; + virtual void ClearReverb() override; + virtual void GetFFTData(float fft_data[FFT_SAMPLE]) override; + virtual int GetErrorCode() override; + virtual std::wstring GetErrorInfo(int error_code) override; + virtual std::wstring GetErrorInfo() override; + virtual PlayerCoreType GetCoreType() override; + virtual int GetDeviceCount() override; +private: + virtual bool GetFunction() override; + _free_music_handle free_music_handle; + _ffmpeg_core_open ffmpeg_core_open; + _ffmpeg_core_play ffmpeg_core_play; + _ffmpeg_core_pause ffmpeg_core_pause; + _ffmpeg_core_seek ffmpeg_core_seek; + _ffmpeg_core_get_cur_position ffmpeg_core_get_cur_position; + _ffmpeg_core_song_is_over ffmpeg_core_song_is_over; + _ffmpeg_core_get_song_length ffmpeg_core_get_song_length; + _ffmpeg_core_get_channels ffmpeg_core_get_channels; + _ffmpeg_core_get_freq ffmpeg_core_get_freq; + _ffmpeg_core_is_playing ffmpeg_core_is_playing; + _ffmpeg_core_get_bits ffmpeg_core_get_bits; + MusicHandle* handle; + int err; +}; diff --git a/MusicPlayer2/IPlayerCore.h b/MusicPlayer2/IPlayerCore.h index 19c576e52..76121ece0 100644 --- a/MusicPlayer2/IPlayerCore.h +++ b/MusicPlayer2/IPlayerCore.h @@ -23,7 +23,8 @@ enum AudioInfoFlag enum PlayerCoreType { PT_BASS, - PT_MCI + PT_MCI, + PT_FFMPEG, }; enum PlayingState //正在播放标志 diff --git a/MusicPlayer2/MusicPlayer2.rc b/MusicPlayer2/MusicPlayer2.rc index 6d4ecbae22aca19a30873ca01c62fe160f9f0947..84834312409a80688fc0cfffeef7a73283d34b37 100644 GIT binary patch delta 704 zcmb8s&1(};5CHI*-S;*d4K%%!TA`$|lpsO&wMo;c1>2a`O4|gh;GwPtl4__*Fg=K1 z@T1U^AS`jfQw1p^J>(!f3#EDxFCHvBWV{@4*`>Y7Pwu}yHY-8tAZ^+;< zb#F*)CUJRDL$veL!Eu_f7a8vqN|_D-W6qlJqevKLweX>nTvAO1Nzf zfYL9<<%nSA+h^5Zyu*_+k4J(`m4j(G%|AH@lMrk@(VckzLk*<;Js?3hui-E)uLp#F qJ|#~)qx^U5tGia1+)m3`atwbP2F@q6<4ukpR#!BJI4@y*~JH#-uP51L)6`RiI&dM`6pqGu^lEDI~ z#BjR225au*g5OHhWl|aSm@OGBxBI6uvNAFoGMH=+Ph-?&W;B`}c!+`*;@dDlmL(dfTnR&{q%h z#N;U~dD|UaS?c~WTQHbyKj_5zk{RdBrnzt(XlMOsDsUv$#(8@L)Z_Y{X!&o!gVuk%iF+=yAjCKnF)KPG7f?QER%u ob5@q^i~LzVwoETjV=`zDU||Dd_U!>I95-UQ(cQEi=-w%;09C$wtN;K2 diff --git a/MusicPlayer2/MusicPlayer2.vcxproj b/MusicPlayer2/MusicPlayer2.vcxproj index 15bc1e5f2..1e98f4a28 100644 --- a/MusicPlayer2/MusicPlayer2.vcxproj +++ b/MusicPlayer2/MusicPlayer2.vcxproj @@ -245,6 +245,7 @@ + @@ -397,6 +398,7 @@ + diff --git a/MusicPlayer2/MusicPlayer2.vcxproj.filters b/MusicPlayer2/MusicPlayer2.vcxproj.filters index 9ab8cafc7..3de90bebc 100644 --- a/MusicPlayer2/MusicPlayer2.vcxproj.filters +++ b/MusicPlayer2/MusicPlayer2.vcxproj.filters @@ -363,6 +363,9 @@ {bfba0398-39b7-4eb5-97b5-6b1fa8e31ad7} + + {1374b4aa-def4-4b40-8f36-1caf49de536a} + @@ -824,6 +827,9 @@ 公共的类\tinyxml2 + + 功能逻辑类\PlayerCore\FFMPEG + @@ -1249,6 +1255,9 @@ 公共的类\tinyxml2 + + 功能逻辑类\PlayerCore\FFMPEG + diff --git a/MusicPlayer2/MusicPlayerDlg.cpp b/MusicPlayer2/MusicPlayerDlg.cpp index a3cc76762..53f6da985 100644 --- a/MusicPlayer2/MusicPlayerDlg.cpp +++ b/MusicPlayer2/MusicPlayerDlg.cpp @@ -444,6 +444,7 @@ void CMusicPlayerDlg::SaveConfig() ini.WriteInt(L"config", L"fade_time", theApp.m_play_setting_data.fade_time); ini.WriteString(L"config", L"output_device", theApp.m_play_setting_data.output_device); ini.WriteBool(L"config", L"use_mci", theApp.m_play_setting_data.use_mci); + ini.WriteBool(L"config", L"use_ffmpeg", theApp.m_play_setting_data.use_ffmpeg); ini.WriteInt(L"config", L"UI_selected", GetUiSelected()); //保存热键设置 @@ -616,6 +617,7 @@ void CMusicPlayerDlg::LoadConfig() theApp.m_play_setting_data.fade_time = 2000; theApp.m_play_setting_data.output_device = ini.GetString(L"config", L"output_device", L""); theApp.m_play_setting_data.use_mci = ini.GetBool(L"config", L"use_mci", false); + theApp.m_play_setting_data.use_ffmpeg = ini.GetBool(L"config", L"use_ffmpeg", false); int ui_selected = ini.GetInt(L"config", L"UI_selected", 1); SelectUi(ui_selected); @@ -3713,6 +3715,8 @@ afx_msg LRESULT CMusicPlayerDlg::OnSetTitle(WPARAM wParam, LPARAM lParam) if (CPlayer::GetInstance().IsMciCore()) title_suffix += _T(" (MCI)"); + else if (CPlayer::GetInstance().IsFfmpegCore()) + title_suffix += _T(" (FFMPEG)"); #ifdef _DEBUG title_suffix += _T(' '); diff --git a/MusicPlayer2/PlaySettingsDlg.cpp b/MusicPlayer2/PlaySettingsDlg.cpp index f65e66840..f8010565f 100644 --- a/MusicPlayer2/PlaySettingsDlg.cpp +++ b/MusicPlayer2/PlaySettingsDlg.cpp @@ -34,6 +34,7 @@ void CPlaySettingsDlg::DoDataExchange(CDataExchange* pDX) DDX_Control(pDX, IDC_CONTINUE_WHEN_SWITCH_PLAYLIST_CHECK, m_continue_when_switch_playlist_check); DDX_Control(pDX, IDC_BASS_RADIO, m_bass_radio); DDX_Control(pDX, IDC_MCI_RADIO, m_mci_radio); + DDX_Control(pDX, IDC_FFMPEG_RADIO, m_ffmpeg_radio); } void CPlaySettingsDlg::ShowDeviceInfo() @@ -107,6 +108,7 @@ BEGIN_MESSAGE_MAP(CPlaySettingsDlg, CTabDlg) ON_BN_CLICKED(IDC_CONTINUE_WHEN_SWITCH_PLAYLIST_CHECK, &CPlaySettingsDlg::OnBnClickedContinueWhenSwitchPlaylistCheck) ON_BN_CLICKED(IDC_BASS_RADIO, &CPlaySettingsDlg::OnBnClickedBassRadio) ON_BN_CLICKED(IDC_MCI_RADIO, &CPlaySettingsDlg::OnBnClickedMciRadio) + ON_BN_CLICKED(IDC_FFMPEG_RADIO, &CPlaySettingsDlg::OnBnClickedFfmpegRadio) END_MESSAGE_MAP() @@ -142,6 +144,8 @@ BOOL CPlaySettingsDlg::OnInitDialog() if (m_data.use_mci) m_mci_radio.SetCheck(TRUE); + else if (m_data.use_ffmpeg) + m_ffmpeg_radio.SetCheck(TRUE); else m_bass_radio.SetCheck(TRUE); @@ -230,6 +234,7 @@ void CPlaySettingsDlg::OnBnClickedBassRadio() { // TODO: 在此添加控件通知处理程序代码 m_data.use_mci = false; + m_data.use_ffmpeg = false; } @@ -237,6 +242,13 @@ void CPlaySettingsDlg::OnBnClickedMciRadio() { // TODO: 在此添加控件通知处理程序代码 m_data.use_mci = true; + m_data.use_ffmpeg = false; +} + + +void CPlaySettingsDlg::OnBnClickedFfmpegRadio() { + m_data.use_mci = false; + m_data.use_ffmpeg = true; } diff --git a/MusicPlayer2/PlaySettingsDlg.h b/MusicPlayer2/PlaySettingsDlg.h index 9614da69d..2ded5505c 100644 --- a/MusicPlayer2/PlaySettingsDlg.h +++ b/MusicPlayer2/PlaySettingsDlg.h @@ -31,6 +31,7 @@ class CPlaySettingsDlg : public CTabDlg CListCtrlEx m_device_info_list; CButton m_bass_radio; CButton m_mci_radio; + CButton m_ffmpeg_radio; CToolTipCtrl m_toolTip; protected: @@ -51,5 +52,6 @@ class CPlaySettingsDlg : public CTabDlg afx_msg void OnBnClickedContinueWhenSwitchPlaylistCheck(); afx_msg void OnBnClickedBassRadio(); afx_msg void OnBnClickedMciRadio(); + afx_msg void OnBnClickedFfmpegRadio(); virtual BOOL PreTranslateMessage(MSG* pMsg); }; diff --git a/MusicPlayer2/Player.cpp b/MusicPlayer2/Player.cpp index 437af0a8d..4ee343a35 100644 --- a/MusicPlayer2/Player.cpp +++ b/MusicPlayer2/Player.cpp @@ -5,6 +5,7 @@ #include "Playlist.h" #include "BassCore.h" #include "MciCore.h" +#include "FfmpegCore.h" #include "MusicPlayerCmdHelper.h" #include "SongDataManager.h" #include "SongInfoHelper.h" @@ -32,6 +33,8 @@ void CPlayer::IniPlayerCore() { if (theApp.m_play_setting_data.use_mci) m_pCore = new CMciCore(); + else if (theApp.m_play_setting_data.use_ffmpeg) + m_pCore = new CFfmpegCore(); else m_pCore = new CBassCore(); @@ -2679,6 +2682,10 @@ bool CPlayer::IsMciCore() const return m_pCore->GetCoreType() == PT_MCI; } +bool CPlayer::IsFfmpegCore() const { + return m_pCore->GetCoreType() == PT_FFMPEG; +} + void CPlayer::SetContainSubFolder(bool contain_sub_folder) { if (m_contain_sub_folder != contain_sub_folder) diff --git a/MusicPlayer2/Player.h b/MusicPlayer2/Player.h index 836d04227..66642f9eb 100644 --- a/MusicPlayer2/Player.h +++ b/MusicPlayer2/Player.h @@ -361,6 +361,7 @@ class CPlayer wstring GetPlaylistPath() const; IPlayerCore* GetPlayerCore() { return m_pCore; } bool IsMciCore() const; + bool IsFfmpegCore() const; bool IsFileOpened() const { return m_file_opend; } bool IsContainSubFolder() const { return m_contain_sub_folder; } void SetContainSubFolder(bool contain_sub_folder); diff --git a/MusicPlayer2/resource.h b/MusicPlayer2/resource.h index 39bccd118..173c645a9 100644 --- a/MusicPlayer2/resource.h +++ b/MusicPlayer2/resource.h @@ -1083,6 +1083,7 @@ #define IDC_ORIGINAL_STATIC 1187 #define IDC_SAVE_TO_APPDATA_RADIO 1188 #define IDC_SAVE_TO_PROGRAM_DIR_RADIO 1189 +#define IDC_FFMPEG_RADIO 1190 #define ID_32771 32771 #define ID_32772 32772 #define ID_OPEN 32773 diff --git a/ffmpeg_core/.gitignore b/ffmpeg_core/.gitignore new file mode 100644 index 000000000..dc84959d1 --- /dev/null +++ b/ffmpeg_core/.gitignore @@ -0,0 +1,2 @@ +build/ + diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt new file mode 100644 index 000000000..5bcf4e01b --- /dev/null +++ b/ffmpeg_core/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.0) + +project(MusicPlayer2_ffmpeg_core) + +if (MSVC) + add_compile_options(/utf-8) # 让编译器使用UTF-8编码 +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +find_package(AVFORMAT 58 REQUIRED) +find_package(AVCODEC 58 REQUIRED) +find_package(AVUTIL 56 REQUIRED) +# find_package(AVFILTER 7) +find_package(SWRESAMPLE 4 REQUIRED) +find_package(SDL2 REQUIRED) + +set(ENABLE_ICONV OFF CACHE BOOL "Libiconv is not needed.") +add_subdirectory(utils) +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/utils") + +set(CORE_FILES +ffmpeg_core.h +src/core.h +src/core.cpp +src/decode.h +src/decode.c +src/open.h +src/open.c +"src/output.h" +"src/output.c" +src/loop.h +src/loop.c +) + +add_library(ffmpeg_core SHARED "${CORE_FILES}") +target_compile_definitions(ffmpeg_core PRIVATE BUILD_FFMPEG_CORE) +target_link_libraries(ffmpeg_core AVFORMAT::AVFORMAT) +target_link_libraries(ffmpeg_core AVCODEC::AVCODEC) +target_link_libraries(ffmpeg_core AVUTIL::AVUTIL) +target_link_libraries(ffmpeg_core SWRESAMPLE::SWRESAMPLE) +target_link_libraries(ffmpeg_core SDL2::Core) +target_link_libraries(ffmpeg_core SDL2::Main) +target_link_libraries(ffmpeg_core utils) diff --git a/ffmpeg_core/cmake/FindAVCODEC.cmake b/ffmpeg_core/cmake/FindAVCODEC.cmake new file mode 100644 index 000000000..bdcb29539 --- /dev/null +++ b/ffmpeg_core/cmake/FindAVCODEC.cmake @@ -0,0 +1,29 @@ +find_package(PkgConfig) +if (PkgConfig_FOUND) + pkg_check_modules(PC_AVCODEC QUIET IMPORTED_TARGET GLOBAL libavcodec) +endif() + +if (PC_AVCODEC_FOUND) + set(AVCODEC_FOUND TRUE) + set(AVCODEC_VERSION ${PC_AVCODEC_VERSION}) + set(AVCODEC_VERSION_STRING ${PC_AVCODEC_STRING}) + set(AVCODEC_LIBRARYS ${PC_AVCODEC_LIBRARIES}) + if (USE_STATIC_LIBS) + set(AVCODEC_INCLUDE_DIRS ${PC_AVCODEC_STATIC_INCLUDE_DIRS}) + else() + set(AVCODEC_INCLUDE_DIRS ${PC_AVCODEC_INCLUDE_DIRS}) + endif() + if (NOT TARGET AVCODEC::AVCODEC) + add_library(AVCODEC::AVCODEC ALIAS PkgConfig::PC_AVCODEC) + endif() +else() + message(FATAL_ERROR "failed.") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AVCODEC + FOUND_VAR AVCODEC_FOUND + REQUIRED_VARS + AVCODEC_LIBRARYS + VERSION_VAR AVCODEC_VERSION +) diff --git a/ffmpeg_core/cmake/FindAVFORMAT.cmake b/ffmpeg_core/cmake/FindAVFORMAT.cmake new file mode 100644 index 000000000..a17bf9499 --- /dev/null +++ b/ffmpeg_core/cmake/FindAVFORMAT.cmake @@ -0,0 +1,29 @@ +find_package(PkgConfig) +if (PkgConfig_FOUND) + pkg_check_modules(PC_AVFORMAT QUIET IMPORTED_TARGET GLOBAL libavformat) +endif() + +if (PC_AVFORMAT_FOUND) + set(AVFORMAT_FOUND TRUE) + set(AVFORMAT_VERSION ${PC_AVFORMAT_VERSION}) + set(AVFORMAT_VERSION_STRING ${PC_AVFORMAT_STRING}) + set(AVFORMAT_LIBRARYS ${PC_AVFORMAT_LIBRARIES}) + if (USE_STATIC_LIBS) + set(AVFORMAT_INCLUDE_DIRS ${PC_AVFORMAT_STATIC_INCLUDE_DIRS}) + else() + set(AVFORMAT_INCLUDE_DIRS ${PC_AVFORMAT_INCLUDE_DIRS}) + endif() + if (NOT TARGET AVFORMAT::AVFORMAT) + add_library(AVFORMAT::AVFORMAT ALIAS PkgConfig::PC_AVFORMAT) + endif() +else() + message(FATAL_ERROR "failed.") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AVFORMAT + FOUND_VAR AVFORMAT_FOUND + REQUIRED_VARS + AVFORMAT_LIBRARYS + VERSION_VAR AVFORMAT_VERSION +) diff --git a/ffmpeg_core/cmake/FindAVUTIL.cmake b/ffmpeg_core/cmake/FindAVUTIL.cmake new file mode 100644 index 000000000..0a0e7bf19 --- /dev/null +++ b/ffmpeg_core/cmake/FindAVUTIL.cmake @@ -0,0 +1,29 @@ +find_package(PkgConfig) +if (PkgConfig_FOUND) + pkg_check_modules(PC_AVUTIL QUIET IMPORTED_TARGET GLOBAL libavutil) +endif() + +if (PC_AVUTIL_FOUND) + set(AVUTIL_FOUND TRUE) + set(AVUTIL_VERSION ${PC_AVUTIL_VERSION}) + set(AVUTIL_VERSION_STRING ${PC_AVUTIL_STRING}) + set(AVUTIL_LIBRARYS ${PC_AVUTIL_LIBRARIES}) + if (USE_STATIC_LIBS) + set(AVUTIL_INCLUDE_DIRS ${PC_AVUTIL_STATIC_INCLUDE_DIRS}) + else() + set(AVUTIL_INCLUDE_DIRS ${PC_AVUTIL_INCLUDE_DIRS}) + endif() + if (NOT TARGET AVUTIL::AVUTIL) + add_library(AVUTIL::AVUTIL ALIAS PkgConfig::PC_AVUTIL) + endif() +else() + message(FATAL_ERROR "failed.") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AVUTIL + FOUND_VAR AVUTIL_FOUND + REQUIRED_VARS + AVUTIL_LIBRARYS + VERSION_VAR AVUTIL_VERSION +) diff --git a/ffmpeg_core/cmake/FindSDL2.cmake b/ffmpeg_core/cmake/FindSDL2.cmake new file mode 100644 index 000000000..2445d36ec --- /dev/null +++ b/ffmpeg_core/cmake/FindSDL2.cmake @@ -0,0 +1,388 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Copyright 2019 Amine Ben Hassouna +# Copyright 2000-2019 Kitware, Inc. and Contributors +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# * Neither the name of Kitware, Inc. nor the names of Contributors +# may be used to endorse or promote products derived from this +# software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[=======================================================================[.rst: +FindSDL2 +-------- + +Locate SDL2 library + +This module defines the following 'IMPORTED' targets: + +:: + + SDL2::Core + The SDL2 library, if found. + Libraries should link to SDL2::Core + + SDL2::Main + The SDL2main library, if found. + Applications should link to SDL2::Main instead of SDL2::Core + + + +This module will set the following variables in your project: + +:: + + SDL2_LIBRARIES, the name of the library to link against + SDL2_INCLUDE_DIRS, where to find SDL.h + SDL2_FOUND, if false, do not try to link to SDL2 + SDL2MAIN_FOUND, if false, do not try to link to SDL2main + SDL2_VERSION_STRING, human-readable string containing the version of SDL2 + + + +This module responds to the following cache variables: + +:: + + SDL2_PATH + Set a custom SDL2 Library path (default: empty) + + SDL2_NO_DEFAULT_PATH + Disable search SDL2 Library in default path. + If SDL2_PATH (default: ON) + Else (default: OFF) + + SDL2_INCLUDE_DIR + SDL2 headers path. + + SDL2_LIBRARY + SDL2 Library (.dll, .so, .a, etc) path. + + SDL2MAIN_LIBRAY + SDL2main Library (.a) path. + + SDL2_BUILDING_LIBRARY + This flag is useful only when linking to SDL2_LIBRARIES insead of + SDL2::Main. It is required only when building a library that links to + SDL2_LIBRARIES, because only applications need main() (No need to also + link to SDL2main). + If this flag is defined, then no SDL2main will be added to SDL2_LIBRARIES + and no SDL2::Main target will be created. + + +Don't forget to include SDLmain.h and SDLmain.m in your project for the +OS X framework based version. (Other versions link to -lSDL2main which +this module will try to find on your behalf.) Also for OS X, this +module will automatically add the -framework Cocoa on your behalf. + + +Additional Note: If you see an empty SDL2_LIBRARY in your project +configuration, it means CMake did not find your SDL2 library +(SDL2.dll, libsdl2.so, SDL2.framework, etc). Set SDL2_LIBRARY to point +to your SDL2 library, and configure again. Similarly, if you see an +empty SDL2MAIN_LIBRARY, you should set this value as appropriate. These +values are used to generate the final SDL2_LIBRARIES variable and the +SDL2::Core and SDL2::Main targets, but when these values are unset, +SDL2_LIBRARIES, SDL2::Core and SDL2::Main does not get created. + + +$SDL2DIR is an environment variable that would correspond to the +./configure --prefix=$SDL2DIR used in building SDL2. l.e.galup 9-20-02 + + + +Created by Amine Ben Hassouna: + Adapt FindSDL.cmake to SDL2 (FindSDL2.cmake). + Add cache variables for more flexibility: + SDL2_PATH, SDL2_NO_DEFAULT_PATH (for details, see doc above). + Mark 'Threads' as a required dependency for non-OSX systems. + Modernize the FindSDL2.cmake module by creating specific targets: + SDL2::Core and SDL2::Main (for details, see doc above). + + +Original FindSDL.cmake module: + Modified by Eric Wing. Added code to assist with automated building + by using environmental variables and providing a more + controlled/consistent search behavior. Added new modifications to + recognize OS X frameworks and additional Unix paths (FreeBSD, etc). + Also corrected the header search path to follow "proper" SDL + guidelines. Added a search for SDLmain which is needed by some + platforms. Added a search for threads which is needed by some + platforms. Added needed compile switches for MinGW. + +On OSX, this will prefer the Framework version (if found) over others. +People will have to manually change the cache value of SDL2_LIBRARY to +override this selection or set the SDL2_PATH variable or the CMake +environment CMAKE_INCLUDE_PATH to modify the search paths. + +Note that the header path has changed from SDL/SDL.h to just SDL.h +This needed to change because "proper" SDL convention is #include +"SDL.h", not . This is done for portability reasons +because not all systems place things in SDL/ (see FreeBSD). +#]=======================================================================] + +# Define options for searching SDL2 Library in a custom path + +set(SDL2_PATH "" CACHE STRING "Custom SDL2 Library path") + +set(_SDL2_NO_DEFAULT_PATH OFF) +if(SDL2_PATH) + set(_SDL2_NO_DEFAULT_PATH ON) +endif() + +set(SDL2_NO_DEFAULT_PATH ${_SDL2_NO_DEFAULT_PATH} + CACHE BOOL "Disable search SDL2 Library in default path") +unset(_SDL2_NO_DEFAULT_PATH) + +set(SDL2_NO_DEFAULT_PATH_CMD) +if(SDL2_NO_DEFAULT_PATH) + set(SDL2_NO_DEFAULT_PATH_CMD NO_DEFAULT_PATH) +endif() + +# Search for the SDL2 include directory +find_path(SDL2_INCLUDE_DIR SDL.h + HINTS + ENV SDL2DIR + ${SDL2_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES SDL2 + # path suffixes to search inside ENV{SDL2DIR} + include/SDL2 include + PATHS ${SDL2_PATH} + DOC "Where the SDL2 headers can be found" +) + +set(SDL2_INCLUDE_DIRS "${SDL2_INCLUDE_DIR}") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else() + set(VC_LIB_PATH_SUFFIX lib/x86) +endif() + +# SDL-2.0 is the name used by FreeBSD ports... +# don't confuse it for the version number. +find_library(SDL2_LIBRARY + NAMES SDL2 SDL-2.0 + HINTS + ENV SDL2DIR + ${SDL2_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2_PATH} + DOC "Where the SDL2 Library can be found" +) + +set(SDL2_LIBRARIES "${SDL2_LIBRARY}") + +if(NOT SDL2_BUILDING_LIBRARY) + if(NOT SDL2_INCLUDE_DIR MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + + if(SDL2_PATH) + set(SDL2MAIN_LIBRARY_PATHS "${SDL2_PATH}") + endif() + + if(NOT SDL2_NO_DEFAULT_PATH) + set(SDL2MAIN_LIBRARY_PATHS + /sw + /opt/local + /opt/csw + /opt + "${SDL2MAIN_LIBRARY_PATHS}" + ) + endif() + + find_library(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + ENV SDL2DIR + ${SDL2_NO_DEFAULT_PATH_CMD} + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2MAIN_LIBRARY_PATHS} + DOC "Where the SDL2main library can be found" + ) + unset(SDL2MAIN_LIBRARY_PATHS) + endif() +endif() + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +if(NOT APPLE) + find_package(Threads QUIET) + if(NOT Threads_FOUND) + set(SDL2_THREADS_NOT_FOUND "Could NOT find Threads (Threads is required by SDL2).") + if(SDL2_FIND_REQUIRED) + message(FATAL_ERROR ${SDL2_THREADS_NOT_FOUND}) + else() + if(NOT SDL2_FIND_QUIETLY) + message(STATUS ${SDL2_THREADS_NOT_FOUND}) + endif() + return() + endif() + unset(SDL2_THREADS_NOT_FOUND) + endif() +endif() + +# MinGW needs an additional link flag, -mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows +if(MINGW) + set(MINGW32_LIBRARY mingw32 "-mwindows" CACHE STRING "link flags for MinGW") +endif() + +if(SDL2_LIBRARY) + # For SDL2main + if(SDL2MAIN_LIBRARY AND NOT SDL2_BUILDING_LIBRARY) + list(FIND SDL2_LIBRARIES "${SDL2MAIN_LIBRARY}" _SDL2_MAIN_INDEX) + if(_SDL2_MAIN_INDEX EQUAL -1) + set(SDL2_LIBRARIES "${SDL2MAIN_LIBRARY}" ${SDL2_LIBRARIES}) + endif() + unset(_SDL2_MAIN_INDEX) + endif() + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + if(APPLE) + set(SDL2_LIBRARIES ${SDL2_LIBRARIES} -framework Cocoa) + endif() + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + if(NOT APPLE) + set(SDL2_LIBRARIES ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + endif() + + # For MinGW library + if(MINGW) + set(SDL2_LIBRARIES ${MINGW32_LIBRARY} ${SDL2_LIBRARIES}) + endif() + +endif() + +# Read SDL2 version +if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL_version.h") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") + set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}) + unset(SDL2_VERSION_MAJOR_LINE) + unset(SDL2_VERSION_MINOR_LINE) + unset(SDL2_VERSION_PATCH_LINE) + unset(SDL2_VERSION_MAJOR) + unset(SDL2_VERSION_MINOR) + unset(SDL2_VERSION_PATCH) +endif() + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 + REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR + VERSION_VAR SDL2_VERSION_STRING) + +if(SDL2MAIN_LIBRARY) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main + REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR + VERSION_VAR SDL2_VERSION_STRING) +endif() + + +mark_as_advanced(SDL2_PATH + SDL2_NO_DEFAULT_PATH + SDL2_LIBRARY + SDL2MAIN_LIBRARY + SDL2_INCLUDE_DIR + SDL2_BUILDING_LIBRARY) + + +# SDL2:: targets (SDL2::Core and SDL2::Main) +if(SDL2_FOUND) + + # SDL2::Core target + if(SDL2_LIBRARY AND NOT TARGET SDL2::Core) + add_library(SDL2::Core UNKNOWN IMPORTED) + set_target_properties(SDL2::Core PROPERTIES + IMPORTED_LOCATION "${SDL2_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}") + + if(APPLE) + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # For more details, please see above. + set_property(TARGET SDL2::Core APPEND PROPERTY + INTERFACE_LINK_OPTIONS -framework Cocoa) + else() + # For threads, as mentioned Apple doesn't need this. + # For more details, please see above. + set_property(TARGET SDL2::Core APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() + endif() + + # SDL2::Main target + # Applications should link to SDL2::Main instead of SDL2::Core + # For more details, please see above. + if(NOT SDL2_BUILDING_LIBRARY AND NOT TARGET SDL2::Main) + + if(SDL2_INCLUDE_DIR MATCHES ".framework" OR NOT SDL2MAIN_LIBRARY) + add_library(SDL2::Main INTERFACE IMPORTED) + set_property(TARGET SDL2::Main PROPERTY + INTERFACE_LINK_LIBRARIES SDL2::Core) + elseif(SDL2MAIN_LIBRARY) + # MinGW requires that the mingw32 library is specified before the + # libSDL2main.a static library when linking. + # The SDL2::MainInternal target is used internally to make sure that + # CMake respects this condition. + add_library(SDL2::MainInternal UNKNOWN IMPORTED) + set_property(TARGET SDL2::MainInternal PROPERTY + IMPORTED_LOCATION "${SDL2MAIN_LIBRARY}") + set_property(TARGET SDL2::MainInternal PROPERTY + INTERFACE_LINK_LIBRARIES SDL2::Core) + + add_library(SDL2::Main INTERFACE IMPORTED) + + if(MINGW) + # MinGW needs an additional link flag '-mwindows' and link to mingw32 + set_property(TARGET SDL2::Main PROPERTY + INTERFACE_LINK_LIBRARIES "mingw32" "-mwindows") + endif() + + set_property(TARGET SDL2::Main APPEND PROPERTY + INTERFACE_LINK_LIBRARIES SDL2::MainInternal) + endif() + + endif() +endif() diff --git a/ffmpeg_core/cmake/FindSWRESAMPLE.cmake b/ffmpeg_core/cmake/FindSWRESAMPLE.cmake new file mode 100644 index 000000000..cc6c44e0d --- /dev/null +++ b/ffmpeg_core/cmake/FindSWRESAMPLE.cmake @@ -0,0 +1,30 @@ +find_package(PkgConfig) +if (PkgConfig_FOUND) + pkg_check_modules(PC_SWRESAMPLE QUIET IMPORTED_TARGET GLOBAL libswresample) +endif() + +if (PC_SWRESAMPLE_FOUND) + set(SWRESAMPLE_FOUND TRUE) + set(SWRESAMPLE_VERSION ${PC_SWRESAMPLE_VERSION}) + set(SWRESAMPLE_VERSION_STRING ${PC_SWRESAMPLE_STRING}) + set(SWRESAMPLE_LIBRARYS ${PC_SWRESAMPLE_LIBRARIES}) + if (USE_STATIC_LIBS) + set(SWRESAMPLE_INCLUDE_DIRS ${PC_SWRESAMPLE_STATIC_INCLUDE_DIRS}) + else() + set(SWRESAMPLE_INCLUDE_DIRS ${PC_SWRESAMPLE_INCLUDE_DIRS}) + endif() + if (NOT TARGET SWRESAMPLE::SWRESAMPLE) + add_library(SWRESAMPLE::SWRESAMPLE ALIAS PkgConfig::PC_SWRESAMPLE) + endif() +else() + message(FATAL_ERROR "failed.") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SWRESAMPLE + FOUND_VAR SWRESAMPLE_FOUND + REQUIRED_VARS + SWRESAMPLE_LIBRARYS + SWRESAMPLE_INCLUDE_DIRS + VERSION_VAR SWRESAMPLE_VERSION +) diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h new file mode 100644 index 000000000..48f383a7b --- /dev/null +++ b/ffmpeg_core/ffmpeg_core.h @@ -0,0 +1,82 @@ +#ifndef _MUSICPLAYER2_FFMPEG_CORE_H +#define _MUSICPLAYER2_FFMPEG_CORE_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#if BUILD_FFMPEG_CORE +#define FFMPEG_CORE_API __declspec(dllexport) +#else +#define FFMPEG_CORE_API __declspec(dllimport) +#endif +typedef struct MusicHandle MusicHandle; +// 负数即为来自ffmpeg的错误 + +#define FFMPEG_CORE_ERR_OK 0 +#define FFMPEG_CORE_ERR_NULLPTR 1 +#define FFMPEG_CORE_ERR_INVAILD_NAME 2 +#define FFMPEG_CORE_ERR_OOM 3 +#define FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER 4 +#define FFMPEG_CORE_ERR_UNKNOWN_SAMPLE_FMT 5 +#define FFMPEG_CORE_ERR_SDL 6 +#define FFMPEG_CORE_ERR_FAILED_CREATE_THREAD 7 +#define FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX 8 +#define FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED 9 +FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); +FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); +FFMPEG_CORE_API int ffmpeg_core_play(MusicHandle* handle); +FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); +FFMPEG_CORE_API int ffmpeg_core_seek(MusicHandle* handle, int64_t time); +/** + * @brief 获取当前播放位置 + * @param handle Handle + * @return 如果Handle为NULL,返回-1,反之返回以AV_TIME_BASE为基准的时间(1相当于1/1000000s) +*/ +FFMPEG_CORE_API int64_t ffmpeg_core_get_cur_position(MusicHandle* handle); +/** + * @brief 是否已经播放完 + * @param handle Handle + * @return 如果Handle为NULL或未播放完,返回0,反之返回1 +*/ +FFMPEG_CORE_API int ffmpeg_core_song_is_over(MusicHandle* handle); +/** + * @brief 获取长度(由demuxer回报,可能不准或者不存在) + * @param handle Handle + * @return 如果Handle为NULL,返回-1,反之返回以AV_TIME_BASE为基准的时间(1相当于1/1000000s) +*/ +FFMPEG_CORE_API int64_t ffmpeg_core_get_song_length(MusicHandle* handle); +/** + * @brief 返回音频文件声道数(SDL可能会改如果音频设备不支持) + * @param handle Handle + * @return 如果Handle为NULL,返回-1,反之返回声道数 +*/ +FFMPEG_CORE_API int ffmpeg_core_get_channels(MusicHandle* handle); +/** + * @brief 返回音频文件采样频率(SDL可能会修改) + * @param handle Handle + * @return 如果Handle为NULL,返回-1,反之返回采样频率 +*/ +FFMPEG_CORE_API int ffmpeg_core_get_freq(MusicHandle* handle); +/** + * @brief 返回当前状态 + * @param handle Handle + * @return 如果Handle为NULL,返回-1,正在播放返回1,未播放返回0 +*/ +FFMPEG_CORE_API int ffmpeg_core_is_playing(MusicHandle* handle); +/** + * @brief 返回位数 + * @param handle Handle + * @return 如果Handle为NULL,返回-1 +*/ +FFMPEG_CORE_API int ffmpeg_core_get_bits(MusicHandle* handle); +/** + * @brief 返回比特率 + * @param handle Handle + * @return 如果Handle为NULL,返回-1 +*/ +FFMPEG_CORE_API int ffmpeg_core_get_bitrate(MusicHandle* handle); +#ifdef __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp new file mode 100644 index 000000000..5337c4235 --- /dev/null +++ b/ffmpeg_core/src/core.cpp @@ -0,0 +1,153 @@ +#include "core.h" + +#include +#include +#include "wchar_util.h" +#include "open.h" +#include "output.h" +#include "loop.h" +#include "decode.h" + +void free_music_handle(MusicHandle* handle) { + if (!handle) return; + if (handle->device_id) SDL_CloseAudioDevice(handle->device_id); + if (handle->thread) { + DWORD status; + while (GetExitCodeThread(handle->thread, &status)) { + if (status == STILL_ACTIVE) { + status = 0; + handle->stoping = 1; + Sleep(10); + } else { + break; + } + } + } + if (handle->buffer) av_audio_fifo_free(handle->buffer); + if (handle->swrac) swr_free(&handle->swrac); + if (handle->decoder) avcodec_free_context(&handle->decoder); + if (handle->fmt) avformat_close_input(&handle->fmt); + if (handle->sdl_initialized) { + SDL_QuitSubSystem(SDL_INIT_AUDIO); + } + free(handle); +} + +int ffmpeg_core_open(const wchar_t* url, MusicHandle** h) { + if (!url || !h) return FFMPEG_CORE_ERR_NULLPTR; + std::string u; + // 将文件名转为UTF-8,ffmpeg API处理的都是UTF-8文件名 + if (!wchar_util::wstr_to_str(u, url, CP_UTF8)) { + return FFMPEG_CORE_ERR_INVAILD_NAME; + } + // 设置ffmpeg日志级别为Error + av_log_set_level(AV_LOG_ERROR); + MusicHandle* handle = (MusicHandle*)malloc(sizeof(MusicHandle)); + int re = FFMPEG_CORE_ERR_OK; + if (!handle) { + return FFMPEG_CORE_ERR_OOM; + } + memset(handle, 0, sizeof(MusicHandle)); + handle->first_pts = INT64_MIN; + if ((re = open_input(handle, u.c_str()))) { + goto end; + } + if ((re = find_audio_stream(handle))) { + goto end; + } + if ((re = open_decoder(handle))) { + goto end; + } + if ((re = init_output(handle))) { + goto end; + } + handle->mutex = CreateMutexW(nullptr, FALSE, nullptr); + if (!handle->mutex) { + re = FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX; + goto end; + } + handle->thread = CreateThread(nullptr, 0, event_loop, handle, 0, &handle->thread_id); + if (!handle->thread) { + re = FFMPEG_CORE_ERR_FAILED_CREATE_THREAD; + goto end; + } + *h = handle; + return re; +end: + free_music_handle(handle); + return re; +} + +int ffmpeg_core_play(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + SDL_PauseAudioDevice(handle->device_id, 0); + handle->is_playing = 1; + return FFMPEG_CORE_ERR_OK; +} + +int ffmpeg_core_pause(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + SDL_PauseAudioDevice(handle->device_id, 1); + handle->is_playing = 0; + return FFMPEG_CORE_ERR_OK; +} + +int64_t ffmpeg_core_get_cur_position(MusicHandle* handle) { + if (!handle) return -1; + // 忽略 SDL 可能长达 0.01s 的 buffer + return handle->pts; +} + +int ffmpeg_core_song_is_over(MusicHandle* handle) { + if (!handle || !handle->buffer) return 0; + if (handle->is_eof && av_audio_fifo_size(handle->buffer) == 0) return 1; + else return 0; +} + +int64_t ffmpeg_core_get_song_length(MusicHandle* handle) { + if (!handle || !handle->fmt) return -1; + return handle->fmt->duration; +} + +int ffmpeg_core_get_channels(MusicHandle* handle) { + if (!handle || !handle->decoder) return -1; + return handle->decoder->channels; +} + +int ffmpeg_core_get_freq(MusicHandle* handle) { + if (!handle || !handle->decoder) return -1; + return handle->decoder->sample_rate; +} + +int ffmpeg_core_seek(MusicHandle* handle, int64_t time) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + DWORD re = WaitForSingleObject(handle->mutex, INFINITE); + if (re == WAIT_OBJECT_0) { + handle->is_seek = 1; + handle->seek_pos = time; + } else { + return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + } + handle->have_err = 0; + ReleaseMutex(handle->mutex); + while (1) { + if (!handle->is_seek) break; + Sleep(10); + } + return handle->have_err ? handle->err : FFMPEG_CORE_ERR_OK; +} + +int ffmpeg_core_is_playing(MusicHandle* handle) { + if (!handle) return -1; + return handle->is_playing; +} + +int ffmpeg_core_get_bits(MusicHandle* handle) { + if (!handle || !handle->decoder) return -1; + return handle->decoder->bits_per_coded_sample; +} + +int ffmpeg_core_get_bitrate(MusicHandle* handle) { + if (!handle || !handle->decoder) return -1; + return handle->decoder->bit_rate; +} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h new file mode 100644 index 000000000..17ff5179a --- /dev/null +++ b/ffmpeg_core/src/core.h @@ -0,0 +1,73 @@ +#ifndef _MUSICPLAYER2_CORE_H +#define _MUSICPLAYER2_CORE_H +#if __cplusplus +extern "C" { +#endif +#include "../ffmpeg_core.h" +#include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" +#include "libavutil/avutil.h" +#include "libavutil/audio_fifo.h" +#include "libavutil/rational.h" +#include "libswresample/swresample.h" +#include "SDL2/SDL.h" +#include + +#ifndef min +#define min(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +typedef struct MusicHandle { +/// Demux 用 +AVFormatContext* fmt; +/// 要解码的流 +AVStream* is; +/// 解码器类型 +const AVCodec* codec; +/// 解码器 +AVCodecContext* decoder; +/// 用于转换音频格式 +struct SwrContext* swrac; +/// 指定的SDL输出格式 +SDL_AudioSpec sdl_spec; +/// 事件处理线程 +HANDLE thread; +/// 事件处理线程线程ID +DWORD thread_id; +/// 音频缓冲区 +AVAudioFifo* buffer; +/// 输出格式 +enum AVSampleFormat target_format; +/// 每样本的字节数 +int target_format_pbytes; +/// SDL音频设备ID +SDL_AudioDeviceID device_id; +/// 错误信息(ffmpeg错误或Core错误 +int err; +/// Mutex对象,作为线程锁(用于保护缓冲区和时间) +HANDLE mutex; +/// 缓冲区开始时间 +int64_t pts; +/// 缓冲区结束时间 +int64_t end_pts; +/// 第一个sample的pts +int64_t first_pts; +int64_t seek_pos; +/// SDL是否被初始化 +unsigned char sdl_initialized : 1; +/// 让事件处理线程退出标志位 +unsigned char stoping : 1; +/// 是否已读到文件尾部 +unsigned char is_eof : 1; +/// 是否有错误 +unsigned char have_err : 1; +/// 是否需要Seek +unsigned char is_seek : 1; +/// 是否需要设置新的缓冲区时间 +unsigned char set_new_pts : 1; +unsigned char is_playing : 1; +} MusicHandle; +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c new file mode 100644 index 000000000..c76dd0afb --- /dev/null +++ b/ffmpeg_core/src/decode.c @@ -0,0 +1,123 @@ +#include "decode.h" + +#include "libavutil/timestamp.h" + +int open_decoder(MusicHandle* handle) { + if (!handle || !handle->fmt || !handle->is) return FFMPEG_CORE_ERR_NULLPTR; + handle->codec = avcodec_find_decoder(handle->is->codecpar->codec_id); + if (!handle->codec) return FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER; + handle->decoder = avcodec_alloc_context3(handle->codec); + if (!handle->decoder) return FFMPEG_CORE_ERR_OOM; + int re = 0; + // 从输入流复制参数 + if ((re = avcodec_parameters_to_context(handle->decoder, handle->is->codecpar)) < 0) { + return re; + } + // 打开解码器 + if ((re = avcodec_open2(handle->decoder, handle->codec, NULL)) < 0) { + return re; + } + return FFMPEG_CORE_ERR_OK; +} + +int decode_audio(MusicHandle* handle, char* writed) { + if (!handle | !writed) return FFMPEG_CORE_ERR_NULLPTR; + AVPacket pkt; + AVFrame* frame = av_frame_alloc(); + *writed = 0; + if (!frame) { + return FFMPEG_CORE_ERR_OOM; + } + int re = FFMPEG_CORE_ERR_OK; + while (1) { + if ((re = av_read_frame(handle->fmt, &pkt)) < 0) { + if (re == AVERROR_EOF) { + handle->is_eof = 1; + re = FFMPEG_CORE_ERR_OK; + goto end; + } + goto end; + } + if (pkt.stream_index != handle->is->index) { + // 其他流,跳过并释放引用 + av_packet_unref(&pkt); + continue; + } + if ((re = avcodec_send_packet(handle->decoder, &pkt)) < 0) { + av_packet_unref(&pkt); + goto end; + } + av_packet_unref(&pkt); + re = avcodec_receive_frame(handle->decoder, frame); + if (re >= 0) { + if (handle->first_pts == INT64_MIN) { + handle->first_pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + printf("first_pts: %s\n", av_ts2timestr(handle->first_pts, &AV_TIME_BASE_Q)); + } + if (handle->set_new_pts) { + printf("pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); + handle->pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX) - handle->first_pts; + handle->end_pts = handle->pts; + handle->set_new_pts = 0; + } + re = convert_samples_and_add_to_fifo(handle, frame, writed); + goto end; + } else if (re == AVERROR(EAGAIN)) { + // 数据不够,继续读取 + re = FFMPEG_CORE_ERR_OK; + continue; + } else if (re == AVERROR_EOF) { + handle->is_eof = 1; + re = FFMPEG_CORE_ERR_OK; + goto end; + } + } +end: + if (frame) av_frame_free(&frame); + return re; +} + +int convert_samples_and_add_to_fifo(MusicHandle* handle, AVFrame* frame, char* writed) { + if (!handle || !frame || !writed) return FFMPEG_CORE_ERR_NULLPTR; + uint8_t** converted_input_samples = NULL; + int re = FFMPEG_CORE_ERR_OK; + AVRational base = { 1, handle->decoder->sample_rate }, target = { 1, handle->sdl_spec.freq }; + /// 输出的样本数 + int64_t frames = av_rescale_q_rnd(frame->nb_samples, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + /// 实际输出样本数 + int converted_samples = 0; + DWORD res = 0; + if (!(converted_input_samples = malloc(sizeof(void*) * handle->sdl_spec.channels))) { + re = FFMPEG_CORE_ERR_OOM; + goto end; + } + memset(converted_input_samples, 0, sizeof(void*) * handle->sdl_spec.channels); + if ((re = av_samples_alloc(converted_input_samples, NULL, handle->sdl_spec.channels, frames, handle->target_format, 0)) < 0) { + re = FFMPEG_CORE_ERR_OOM; + goto end; + } + re = 0; + if ((converted_samples = swr_convert(handle->swrac, converted_input_samples, frames, (const uint8_t*)frame->extended_data, frame->nb_samples)) < 0) { + re = converted_samples; + goto end; + } + res = WaitForSingleObject(handle->mutex, INFINITE); + if (res != WAIT_OBJECT_0) { + re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + goto end; + } + if ((converted_samples = av_audio_fifo_write(handle->buffer, (void**)converted_input_samples, converted_samples)) < 0) { + ReleaseMutex(handle->mutex); + re = converted_samples; + goto end; + } + handle->end_pts += av_rescale_q_rnd(converted_samples, target, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + *writed = 1; + ReleaseMutex(handle->mutex); +end: + if (converted_input_samples) { + av_freep(&converted_input_samples[0]); + free(converted_input_samples); + } + return re; +} diff --git a/ffmpeg_core/src/decode.h b/ffmpeg_core/src/decode.h new file mode 100644 index 000000000..23f629fc9 --- /dev/null +++ b/ffmpeg_core/src/decode.h @@ -0,0 +1,19 @@ +#ifndef _MUSICPLAYER2_DECODE_H +#define _MUSICPLAYER2_DECODE_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +int open_decoder(MusicHandle* handle); +/** + * @brief 解码 + * @param handle Handle + * @param writed 是否成功往缓冲区添加数据 + * @return 非0如果发生错误 +*/ +int decode_audio(MusicHandle* handle, char* writed); +int convert_samples_and_add_to_fifo(MusicHandle* handle, AVFrame* frame, char* writed); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c new file mode 100644 index 000000000..67d35fa07 --- /dev/null +++ b/ffmpeg_core/src/loop.c @@ -0,0 +1,87 @@ +#include "loop.h" + +#include "decode.h" + +int seek_to_pos(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + int re = FFMPEG_CORE_ERR_OK; + DWORD r = WaitForSingleObject(handle->mutex, INFINITE); + AVRational base = { 1, handle->sdl_spec.freq }; + if (r != WAIT_OBJECT_0) { + re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + goto end; + } + if (handle->seek_pos >= handle->pts && handle->seek_pos <= handle->end_pts) { + // 已经在缓冲区,直接从缓冲区移除不需要的数据 + int64_t samples = min(av_rescale_q_rnd(handle->seek_pos - handle->pts, AV_TIME_BASE_Q, base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX), av_audio_fifo_size(handle->buffer)); + if ((re = av_audio_fifo_drain(handle->buffer, samples)) < 0) { + ReleaseMutex(handle->mutex); + goto end; + } + // 增大当前时间 + handle->pts += av_rescale_q_rnd(samples, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + } else { + // 不在缓冲区,调用av_seek_frame并清空缓冲区 + int flags = 0; + if (handle->seek_pos < handle->end_pts) { + flags |= AVSEEK_FLAG_BACKWARD; + } + if ((re = av_seek_frame(handle->fmt, handle->is->index, av_rescale_q_rnd(handle->seek_pos + handle->first_pts, AV_TIME_BASE_Q, handle->is->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX), flags)) < 0) { + ReleaseMutex(handle->mutex); + goto end; + } + re = 0; + av_audio_fifo_reset(handle->buffer); + handle->set_new_pts = 1; + handle->is_eof = 0; + } + ReleaseMutex(handle->mutex); +end: + handle->is_seek = 0; + return re; +} + +DWORD WINAPI event_loop(LPVOID handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + MusicHandle* h = (MusicHandle*)handle; + int samples = h->decoder->sample_rate * 15; + /// 本次循环是否有做事 + char doing = 0; + /// 是否往缓冲区加了数据 + char writed = 0; + int buffered_size = h->sdl_spec.freq * 15; + while (1) { + doing = 0; + if (h->stoping) break; + if (h->is_seek && h->first_pts != INT64_MIN) { + int re = seek_to_pos(h); + if (re) { + h->have_err = 1; + h->err = re; + } + doing = 1; + goto end; + } + if (!h->is_eof) { + if (av_audio_fifo_size(h->buffer) < buffered_size) { + int re = decode_audio(handle, &writed); + if (re) { + h->have_err = 1; + h->err = re; + } + doing = 1; + } + } else { + // 播放完毕,自动停止播放 + if (av_audio_fifo_size(h->buffer) == 0) { + SDL_PauseAudioDevice(h->device_id, 1); + h->is_playing = 0; + } + } +end: + if (!doing) { + Sleep(10); + } + } + return FFMPEG_CORE_ERR_OK; +} diff --git a/ffmpeg_core/src/loop.h b/ffmpeg_core/src/loop.h new file mode 100644 index 000000000..884c56b66 --- /dev/null +++ b/ffmpeg_core/src/loop.h @@ -0,0 +1,12 @@ +#ifndef _MUSICPLAYER2_LOOP_H +#define _MUSICPLAYER2_LOOP_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +int seek_to_pos(MusicHandle* handle); +DWORD WINAPI event_loop(LPVOID handle); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/open.c b/ffmpeg_core/src/open.c new file mode 100644 index 000000000..909cf3b70 --- /dev/null +++ b/ffmpeg_core/src/open.c @@ -0,0 +1,29 @@ +#include "open.h" + +int open_input(MusicHandle* handle, const char* url) { + if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; + int re = 0; + if ((re = avformat_open_input(&handle->fmt, url, NULL, NULL)) < 0) { + return re; + } + if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { + return re; + } + return FFMPEG_CORE_ERR_OK; +} + +int find_audio_stream(MusicHandle* handle) { + if (!handle || !handle->fmt) return FFMPEG_CORE_ERR_NULLPTR; + for (unsigned int i = 0; i < handle->fmt->nb_streams; i++) { + AVStream* is = handle->fmt->streams[i]; + if (is->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + // 确保有对应的解码器 + if (!avcodec_find_decoder(is->codecpar->codec_id)) { + continue; + } + handle->is = is; + return FFMPEG_CORE_ERR_OK; + } + } + return FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER; +} diff --git a/ffmpeg_core/src/open.h b/ffmpeg_core/src/open.h new file mode 100644 index 000000000..36e4b381b --- /dev/null +++ b/ffmpeg_core/src/open.h @@ -0,0 +1,13 @@ +#ifndef _MUSICPLAYER2_OPEN_H +#define _MUSICPLAYER2_OPEN_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +/// 打开文件 +int open_input(MusicHandle* handle, const char* url); +int find_audio_stream(MusicHandle* handle); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c new file mode 100644 index 000000000..fd02cc15e --- /dev/null +++ b/ffmpeg_core/src/output.c @@ -0,0 +1,104 @@ +#include "output.h" + +int init_output(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + if (!handle->sdl_initialized) { + SDL_InitSubSystem(SDL_INIT_AUDIO); + handle->sdl_initialized = 1; + } + SDL_AudioSpec sdl_spec; + sdl_spec.freq = handle->decoder->sample_rate; + sdl_spec.format = convert_to_sdl_format(handle->decoder->sample_fmt); + if (!sdl_spec.format) { + return FFMPEG_CORE_ERR_UNKNOWN_SAMPLE_FMT; + } + sdl_spec.channels = handle->decoder->channels; + sdl_spec.samples = handle->decoder->sample_rate / 100; + sdl_spec.callback = SDL_callback; + sdl_spec.userdata = handle; + memcpy(&handle->sdl_spec, &sdl_spec, sizeof(SDL_AudioSpec)); + handle->device_id = SDL_OpenAudioDevice(NULL, 0, &sdl_spec, &handle->sdl_spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + if (!handle->device_id) { + return FFMPEG_CORE_ERR_SDL; + } + enum AVSampleFormat target_format = convert_to_sdl_supported_format(handle->decoder->sample_fmt); + handle->swrac = swr_alloc_set_opts(NULL, handle->decoder->channel_layout, target_format, handle->sdl_spec.freq, handle->decoder->channel_layout, handle->decoder->sample_fmt, handle->decoder->sample_rate, 0, NULL); + if (!handle->swrac) { + return FFMPEG_CORE_ERR_OOM; + } + int re = 0; + if ((re = swr_init(handle->swrac)) < 0) { + return re; + } + if (!(handle->buffer = av_audio_fifo_alloc(target_format, handle->decoder->channels, 1))) { + return FFMPEG_CORE_ERR_OOM; + } + handle->target_format = target_format; + handle->target_format_pbytes = av_get_bytes_per_sample(target_format); + return FFMPEG_CORE_ERR_OK; +} + +enum AVSampleFormat convert_to_sdl_supported_format(enum AVSampleFormat fmt) { + switch (fmt) { + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_FLTP: + case AV_SAMPLE_FMT_DBLP: + return AV_SAMPLE_FMT_FLT; + case AV_SAMPLE_FMT_U8P: + return AV_SAMPLE_FMT_U8; + case AV_SAMPLE_FMT_S16P: + return AV_SAMPLE_FMT_S16; + case AV_SAMPLE_FMT_S32P: + case AV_SAMPLE_FMT_S64: + case AV_SAMPLE_FMT_S64P: + return AV_SAMPLE_FMT_S32; + default: + return fmt; + } +} + +SDL_AudioFormat convert_to_sdl_format(enum AVSampleFormat fmt) { + fmt = convert_to_sdl_supported_format(fmt); + switch (fmt) { + case AV_SAMPLE_FMT_U8: + return AUDIO_U8; + case AV_SAMPLE_FMT_S16: + return AUDIO_S16SYS; + case AV_SAMPLE_FMT_S32: + return AUDIO_S32SYS; + case AV_SAMPLE_FMT_FLT: + return AUDIO_F32SYS; + default: + return 0; + } +} + +void SDL_callback(void* userdata, uint8_t* stream, int len) { + MusicHandle* handle = (MusicHandle*)userdata; + DWORD re = WaitForSingleObject(handle->mutex, 10); + if (re != WAIT_OBJECT_0) { + // 无法获取Mutex所有权,填充空白数据 + memset(stream, 0, sizeof(len)); + return; + } + int samples_need = len / handle->target_format_pbytes / handle->sdl_spec.channels; + if (av_audio_fifo_size(handle->buffer) == 0) { + // 缓冲区没有数据,填充空白数据 + memset(stream, 0, sizeof(len)); + } else { + int writed = av_audio_fifo_read(handle->buffer, (void**)&stream, samples_need); + if (writed > 0) { + // 增大缓冲区开始时间 + AVRational base = { 1, handle->sdl_spec.freq }; + handle->pts += av_rescale_q_rnd(writed, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + } + if (writed < 0) { + // 读取发生错误,填充空白数据 + memset(stream, 0, sizeof(len)); + } else if (writed < samples_need) { + // 不足的区域用空白数据填充 + memset(stream + (size_t)writed * handle->target_format_pbytes, 0, (((size_t)samples_need - writed) * handle->target_format_pbytes)); + } + } + ReleaseMutex(handle->mutex); +} diff --git a/ffmpeg_core/src/output.h b/ffmpeg_core/src/output.h new file mode 100644 index 000000000..efdc32323 --- /dev/null +++ b/ffmpeg_core/src/output.h @@ -0,0 +1,14 @@ +#ifndef _MUSICPLAYER2_OUTPUT_H +#define _MUSICPLAYER2_OUTPUT_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +int init_output(MusicHandle* handle); +enum AVSampleFormat convert_to_sdl_supported_format(enum AVSampleFormat fmt); +void SDL_callback(void* userdata, uint8_t* stream, int len); +SDL_AudioFormat convert_to_sdl_format(enum AVSampleFormat fmt); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/utils b/ffmpeg_core/utils new file mode 160000 index 000000000..0c8dba0a0 --- /dev/null +++ b/ffmpeg_core/utils @@ -0,0 +1 @@ +Subproject commit 0c8dba0a0acc0fd4fb525539cee3d92dd798030a From 0824b155751741412a2d5e141edc65f17a648d3f Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 2 Feb 2022 09:14:04 +0800 Subject: [PATCH 02/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86=20MK?= =?UTF-8?q?V=20=E6=96=87=E4=BB=B6=E9=9F=B3=E9=A2=91=E4=B8=BA=E8=AE=BE?= =?UTF-8?q?=E7=BD=AEchannel=5Flayout=E5=AF=BC=E8=87=B4=E7=9A=84BUG=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86=20MKV=20=E5=A4=A7=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=AE=9A=E4=BD=8D=E7=BC=93=E6=85=A2=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/decode.c | 4 ++++ ffmpeg_core/src/loop.c | 2 +- ffmpeg_core/src/open.c | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index c76dd0afb..0e8afc8f4 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -13,6 +13,10 @@ int open_decoder(MusicHandle* handle) { if ((re = avcodec_parameters_to_context(handle->decoder, handle->is->codecpar)) < 0) { return re; } + if (handle->decoder->channel_layout == 0) { + // 如果未设置,设置为默认值 + handle->decoder->channel_layout = av_get_default_channel_layout(handle->decoder->channels); + } // 打开解码器 if ((re = avcodec_open2(handle->decoder, handle->codec, NULL)) < 0) { return re; diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index 67d35fa07..65182c839 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -26,7 +26,7 @@ int seek_to_pos(MusicHandle* handle) { if (handle->seek_pos < handle->end_pts) { flags |= AVSEEK_FLAG_BACKWARD; } - if ((re = av_seek_frame(handle->fmt, handle->is->index, av_rescale_q_rnd(handle->seek_pos + handle->first_pts, AV_TIME_BASE_Q, handle->is->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX), flags)) < 0) { + if ((re = av_seek_frame(handle->fmt, -1, handle->seek_pos + handle->first_pts, flags)) < 0) { ReleaseMutex(handle->mutex); goto end; } diff --git a/ffmpeg_core/src/open.c b/ffmpeg_core/src/open.c index 909cf3b70..70fc982bd 100644 --- a/ffmpeg_core/src/open.c +++ b/ffmpeg_core/src/open.c @@ -9,6 +9,7 @@ int open_input(MusicHandle* handle, const char* url) { if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { return re; } + // handle->fmt->flags |= AVFMT_FLAG_FAST_SEEK; // 允许快速定位 return FFMPEG_CORE_ERR_OK; } From cbebd1877a5e1f4d4de73ae0fd6cdf273eaa44c1 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 2 Feb 2022 09:50:22 +0800 Subject: [PATCH 03/41] =?UTF-8?q?=E8=BE=93=E5=87=BA=E4=B8=8ESDL=E5=A3=B0?= =?UTF-8?q?=E9=81=93=E5=B8=83=E5=B1=80=E4=B8=80=E8=87=B4=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/ffmpeg_core.h | 2 +- ffmpeg_core/src/core.cpp | 2 +- ffmpeg_core/src/output.c | 23 ++++++++++++++++++++++- ffmpeg_core/src/output.h | 6 ++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 48f383a7b..b19f2ffd4 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -75,7 +75,7 @@ FFMPEG_CORE_API int ffmpeg_core_get_bits(MusicHandle* handle); * @param handle Handle * @return 如果Handle为NULL,返回-1 */ -FFMPEG_CORE_API int ffmpeg_core_get_bitrate(MusicHandle* handle); +FFMPEG_CORE_API int64_t ffmpeg_core_get_bitrate(MusicHandle* handle); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 5337c4235..b92b9b62d 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -147,7 +147,7 @@ int ffmpeg_core_get_bits(MusicHandle* handle) { return handle->decoder->bits_per_coded_sample; } -int ffmpeg_core_get_bitrate(MusicHandle* handle) { +int64_t ffmpeg_core_get_bitrate(MusicHandle* handle) { if (!handle || !handle->decoder) return -1; return handle->decoder->bit_rate; } diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index fd02cc15e..df6bcfdee 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -22,7 +22,7 @@ int init_output(MusicHandle* handle) { return FFMPEG_CORE_ERR_SDL; } enum AVSampleFormat target_format = convert_to_sdl_supported_format(handle->decoder->sample_fmt); - handle->swrac = swr_alloc_set_opts(NULL, handle->decoder->channel_layout, target_format, handle->sdl_spec.freq, handle->decoder->channel_layout, handle->decoder->sample_fmt, handle->decoder->sample_rate, 0, NULL); + handle->swrac = swr_alloc_set_opts(NULL, get_sdl_channel_layout(handle->decoder->channels), target_format, handle->sdl_spec.freq, handle->decoder->channel_layout, handle->decoder->sample_fmt, handle->decoder->sample_rate, 0, NULL); if (!handle->swrac) { return FFMPEG_CORE_ERR_OOM; } @@ -102,3 +102,24 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { } ReleaseMutex(handle->mutex); } + +uint64_t get_sdl_channel_layout(int channels) { + switch (channels) { + case 2: + return av_get_channel_layout("FL+FR"); + case 3: + return av_get_channel_layout("FL+FR+LFE"); + case 4: + return av_get_channel_layout("FL+FR+BL+BR"); + case 5: + return av_get_channel_layout("FL+FR+FC+BL+BR"); + case 6: + return av_get_channel_layout("FL+FR+FC+LFE+SL+SR"); + case 7: + return av_get_channel_layout("FL+FR+FC+LFE+BC+SL+SR"); + case 8: + return av_get_channel_layout("FL+FR+FC+LFE+BL+BR+SL+SR"); + default: + return av_get_default_channel_layout(channels); + } +} diff --git a/ffmpeg_core/src/output.h b/ffmpeg_core/src/output.h index efdc32323..d69630ef3 100644 --- a/ffmpeg_core/src/output.h +++ b/ffmpeg_core/src/output.h @@ -8,6 +8,12 @@ int init_output(MusicHandle* handle); enum AVSampleFormat convert_to_sdl_supported_format(enum AVSampleFormat fmt); void SDL_callback(void* userdata, uint8_t* stream, int len); SDL_AudioFormat convert_to_sdl_format(enum AVSampleFormat fmt); +/** + * @brief 获取与SDL输出匹配的输出布局 + * @param channels 声道数 + * @return 布局 +*/ +uint64_t get_sdl_channel_layout(int channels); #if __cplusplus } #endif From 196fff10f57ba5a8c3f5e991d1de556210164541 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 2 Feb 2022 10:03:10 +0800 Subject: [PATCH 04/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8DMingW=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=97=B6=E9=94=99=E8=AF=AF=E5=92=8C=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/CMakeLists.txt | 1 + ffmpeg_core/src/core.h | 4 +++- ffmpeg_core/src/decode.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index 5bcf4e01b..3ab844952 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -33,6 +33,7 @@ src/loop.c ) add_library(ffmpeg_core SHARED "${CORE_FILES}") +set_target_properties(ffmpeg_core PROPERTIES PREFIX "") target_compile_definitions(ffmpeg_core PRIVATE BUILD_FFMPEG_CORE) target_link_libraries(ffmpeg_core AVFORMAT::AVFORMAT) target_link_libraries(ffmpeg_core AVCODEC::AVCODEC) diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 17ff5179a..ee652c7a1 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -13,8 +13,10 @@ extern "C" { #include "SDL2/SDL.h" #include +#ifndef __cplusplus #ifndef min -#define min(x, y) (((x) < (y)) ? (x) : (y)) +#define min(x,y) (((x) < (y)) ? (x) : (y)) +#endif #endif typedef struct MusicHandle { diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 0e8afc8f4..692b68321 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -101,7 +101,7 @@ int convert_samples_and_add_to_fifo(MusicHandle* handle, AVFrame* frame, char* w goto end; } re = 0; - if ((converted_samples = swr_convert(handle->swrac, converted_input_samples, frames, (const uint8_t*)frame->extended_data, frame->nb_samples)) < 0) { + if ((converted_samples = swr_convert(handle->swrac, converted_input_samples, frames, (const uint8_t**)frame->extended_data, frame->nb_samples)) < 0) { re = converted_samples; goto end; } From b8f6aed586e5946ba83f95ca51e06c1edb0467e0 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 2 Feb 2022 14:22:48 +0800 Subject: [PATCH 05/41] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=B8=B8=E7=94=A8=E7=9A=84=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 125 +++++++++++++++++++++++++++++++++++- MusicPlayer2/FfmpegCore.h | 56 ++++++++++++---- ffmpeg_core/ffmpeg_core.h | 17 +++++ ffmpeg_core/src/core.cpp | 122 +++++++++++++++++++++++++++++++++++ ffmpeg_core/src/core.h | 6 ++ ffmpeg_core/src/open.c | 24 +++++++ ffmpeg_core/src/open.h | 2 + 7 files changed, 339 insertions(+), 13 deletions(-) diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index f51e61786..1c9e24fe2 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -68,7 +68,11 @@ std::wstring CFfmpegCore::GetSoundFontName() { } void CFfmpegCore::Open(const wchar_t* file_path) { + if (handle) { + Close(); + } if (ffmpeg_core_open) { + if (file_path) recent_file = file_path; int re = ffmpeg_core_open(file_path, &handle); if (re) { err = re; @@ -84,6 +88,9 @@ void CFfmpegCore::Close() { } void CFfmpegCore::Play() { + if (!handle && !recent_file.empty()) { + Open(recent_file.c_str()); + } if (ffmpeg_core_play && handle) { ffmpeg_core_play(handle); } @@ -96,7 +103,7 @@ void CFfmpegCore::Pause() { } void CFfmpegCore::Stop() { - // Stop + Close(); } void CFfmpegCore::SetVolume(int volume) { @@ -140,9 +147,43 @@ void CFfmpegCore::GetAudioInfo(SongInfo& song_info, int flag) { song_info.bits = ffmpeg_core_get_bits(handle); song_info.channels = ffmpeg_core_get_channels(handle); } + if (flag & AF_BITRATE) { + song_info.bitrate = ffmpeg_core_get_bitrate(handle) / 1000; + } + if (flag & AF_TAG_INFO) { + song_info.title = GetTitle(); + song_info.artist = GetArtist(); + song_info.album = GetAlbum(); + song_info.comment = GetComment(); + song_info.genre = GetGenre(); + song_info.year = GetYear(); + song_info.track = GetTrackNum(); + } } void CFfmpegCore::GetAudioInfo(const wchar_t* file_path, SongInfo& song_info, int flag) { + MusicInfoHandle* h = nullptr; + int re = ffmpeg_core_info_open(file_path, &h); + if (re || !h) return; + if (flag & AF_LENGTH) song_info.lengh = ffmpeg_core_info_get_song_length(h) / 1000; + if (flag & AF_CHANNEL_INFO) { + song_info.freq = ffmpeg_core_info_get_freq(h); + song_info.bits = ffmpeg_core_info_get_bits(h); + song_info.channels = ffmpeg_core_info_get_channels(h); + } + if (flag & AF_BITRATE) { + song_info.bitrate = ffmpeg_core_info_get_bitrate(h) / 1000; + } + if (flag & AF_TAG_INFO) { + song_info.title = GetTitle(h); + song_info.artist = GetArtist(h); + song_info.album = GetAlbum(h); + song_info.comment = GetComment(h); + song_info.genre = GetGenre(h); + song_info.year = GetYear(h); + song_info.track = GetTrackNum(h); + } + free_music_info_handle(h); } bool CFfmpegCore::IsMidi() { @@ -207,29 +248,111 @@ bool CFfmpegCore::GetFunction() { bool rtn = true; //ȡ free_music_handle = (_free_music_handle)::GetProcAddress(m_dll_module, "free_music_handle"); + free_music_info_handle = (_free_music_info_handle)::GetProcAddress(m_dll_module, "free_music_info_handle"); ffmpeg_core_open = (_ffmpeg_core_open)::GetProcAddress(m_dll_module, "ffmpeg_core_open"); + ffmpeg_core_info_open = (_ffmpeg_core_info_open)::GetProcAddress(m_dll_module, "ffmpeg_core_info_open"); ffmpeg_core_play = (_ffmpeg_core_play)::GetProcAddress(m_dll_module, "ffmpeg_core_play"); ffmpeg_core_pause = (_ffmpeg_core_pause)::GetProcAddress(m_dll_module, "ffmpeg_core_pause"); ffmpeg_core_seek = (_ffmpeg_core_seek)::GetProcAddress(m_dll_module, "ffmpeg_core_seek"); ffmpeg_core_get_cur_position = (_ffmpeg_core_get_cur_position)::GetProcAddress(m_dll_module, "ffmpeg_core_get_cur_position"); ffmpeg_core_song_is_over = (_ffmpeg_core_song_is_over)::GetProcAddress(m_dll_module, "ffmpeg_core_song_is_over"); ffmpeg_core_get_song_length = (_ffmpeg_core_get_song_length)::GetProcAddress(m_dll_module, "ffmpeg_core_get_song_length"); + ffmpeg_core_info_get_song_length = (_ffmpeg_core_info_get_song_length)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_song_length"); ffmpeg_core_get_channels = (_ffmpeg_core_get_channels)::GetProcAddress(m_dll_module, "ffmpeg_core_get_channels"); + ffmpeg_core_info_get_channels = (_ffmpeg_core_info_get_channels)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_channels"); ffmpeg_core_get_freq = (_ffmpeg_core_get_freq)::GetProcAddress(m_dll_module, "ffmpeg_core_get_freq"); + ffmpeg_core_info_get_freq = (_ffmpeg_core_info_get_freq)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_freq"); ffmpeg_core_is_playing = (_ffmpeg_core_is_playing)::GetProcAddress(m_dll_module, "ffmpeg_core_is_playing"); ffmpeg_core_get_bits = (_ffmpeg_core_get_bits)::GetProcAddress(m_dll_module, "ffmpeg_core_get_bits"); + ffmpeg_core_info_get_bits = (_ffmpeg_core_info_get_bits)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_bits"); + ffmpeg_core_get_bitrate = (_ffmpeg_core_get_bitrate)::GetProcAddress(m_dll_module, "ffmpeg_core_get_bitrate"); + ffmpeg_core_info_get_bitrate = (_ffmpeg_core_info_get_bitrate)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_bitrate"); + ffmpeg_core_get_metadata = (_ffmpeg_core_get_metadata)::GetProcAddress(m_dll_module, "ffmpeg_core_get_metadata"); + ffmpeg_core_info_get_metadata = (_ffmpeg_core_info_get_metadata)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_metadata"); //жǷɹ rtn &= (free_music_handle != NULL); + rtn &= (free_music_info_handle != NULL); rtn &= (ffmpeg_core_open != NULL); + rtn &= (ffmpeg_core_info_open != NULL); rtn &= (ffmpeg_core_play != NULL); rtn &= (ffmpeg_core_pause != NULL); rtn &= (ffmpeg_core_seek != NULL); rtn &= (ffmpeg_core_get_cur_position != NULL); rtn &= (ffmpeg_core_song_is_over != NULL); rtn &= (ffmpeg_core_get_song_length != NULL); + rtn &= (ffmpeg_core_info_get_song_length != NULL); rtn &= (ffmpeg_core_get_channels != NULL); + rtn &= (ffmpeg_core_info_get_channels != NULL); rtn &= (ffmpeg_core_get_freq != NULL); + rtn &= (ffmpeg_core_info_get_freq != NULL); rtn &= (ffmpeg_core_is_playing != NULL); rtn &= (ffmpeg_core_get_bits != NULL); + rtn &= (ffmpeg_core_info_get_bits != NULL); + rtn &= (ffmpeg_core_get_bitrate != NULL); + rtn &= (ffmpeg_core_info_get_bitrate != NULL); + rtn &= (ffmpeg_core_get_metadata != NULL); + rtn &= (ffmpeg_core_info_get_metadata != NULL); return rtn; } + +std::wstring CFfmpegCore::GetMetadata(std::string key, MusicInfoHandle* h) { + if (h) { + auto r = ffmpeg_core_info_get_metadata(h, key.c_str()); + if (!r) return L""; + std::wstring re(r); + free(r); + return re; + } + if (!handle) return L""; + auto r = ffmpeg_core_get_metadata(handle, key.c_str()); + if (!r) return L""; + std::wstring re(r); + free(r); + return re; +} + +std::wstring CFfmpegCore::GetTitle(MusicInfoHandle* h) { + return GetMetadata("title", h); +} + +std::wstring CFfmpegCore::GetArtist(MusicInfoHandle* h) { + return GetMetadata("artist", h); +} + +std::wstring CFfmpegCore::GetAlbum(MusicInfoHandle* h) { + return GetMetadata("album", h); +} + +std::wstring CFfmpegCore::GetComment(MusicInfoHandle* h) { + auto re = GetMetadata("comment", h); + if (!re.empty()) return re; + return GetMetadata("description", h); +} + +std::wstring CFfmpegCore::GetGenre(MusicInfoHandle* h) { + return GetMetadata("genre", h); +} + +std::wstring CFfmpegCore::GetDate(MusicInfoHandle* h) { + return GetMetadata("date", h); +} + +unsigned short CFfmpegCore::GetYear(MusicInfoHandle* h) { + auto r = GetDate(h); + if (r.empty()) return 0; + unsigned short year = 0; + if (swscanf_s(r.c_str(), L"%hu", &year) == 1) return year; + return 0; +} + +std::wstring CFfmpegCore::GetTrack(MusicInfoHandle* h) { + return GetMetadata("track", h); +} + +int CFfmpegCore::GetTrackNum(MusicInfoHandle* h) { + auto r = GetTrack(h); + if (r.empty()) return 0; + int track; + if (swscanf_s(r.c_str(), L"%i", &track) == 1) return track; + return 0; +} diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index d60c3a363..7fbc38734 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -3,18 +3,29 @@ #include "DllLib.h" typedef struct MusicHandle MusicHandle; +typedef struct MusicInfoHandle MusicInfoHandle; typedef void(*_free_music_handle)(MusicHandle*); +typedef void(*_free_music_info_handle)(MusicInfoHandle*); typedef int(*_ffmpeg_core_open)(const wchar_t*, MusicHandle**); +typedef int(*_ffmpeg_core_info_open)(const wchar_t*, MusicInfoHandle**); typedef int(*_ffmpeg_core_play)(MusicHandle*); typedef int(*_ffmpeg_core_pause)(MusicHandle*); typedef int(*_ffmpeg_core_seek)(MusicHandle*, int64_t); typedef int64_t(*_ffmpeg_core_get_cur_position)(MusicHandle*); typedef int(*_ffmpeg_core_song_is_over)(MusicHandle*); typedef int64_t(*_ffmpeg_core_get_song_length)(MusicHandle*); +typedef int64_t(*_ffmpeg_core_info_get_song_length)(MusicInfoHandle*); typedef int(*_ffmpeg_core_get_channels)(MusicHandle*); +typedef int(*_ffmpeg_core_info_get_channels)(MusicInfoHandle*); typedef int(*_ffmpeg_core_get_freq)(MusicHandle*); +typedef int(*_ffmpeg_core_info_get_freq)(MusicInfoHandle*); typedef int(*_ffmpeg_core_is_playing)(MusicHandle*); typedef int(*_ffmpeg_core_get_bits)(MusicHandle*); +typedef int(*_ffmpeg_core_info_get_bits)(MusicInfoHandle*); +typedef int64_t(*_ffmpeg_core_get_bitrate)(MusicHandle*); +typedef int64_t(*_ffmpeg_core_info_get_bitrate)(MusicInfoHandle*); +typedef wchar_t*(*_ffmpeg_core_get_metadata)(MusicHandle*, const char* key); +typedef wchar_t*(*_ffmpeg_core_info_get_metadata)(MusicInfoHandle*, const char* key); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -55,20 +66,41 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { virtual std::wstring GetErrorInfo() override; virtual PlayerCoreType GetCoreType() override; virtual int GetDeviceCount() override; + std::wstring GetTitle(MusicInfoHandle* h = nullptr); + std::wstring GetArtist(MusicInfoHandle* h = nullptr); + std::wstring GetAlbum(MusicInfoHandle* h = nullptr); + std::wstring GetComment(MusicInfoHandle* h = nullptr); + std::wstring GetGenre(MusicInfoHandle* h = nullptr); + std::wstring GetDate(MusicInfoHandle* h = nullptr); + unsigned short GetYear(MusicInfoHandle* h = nullptr); + std::wstring GetTrack(MusicInfoHandle* h = nullptr); + int GetTrackNum(MusicInfoHandle* h = nullptr); private: + std::wstring GetMetadata(std::string key, MusicInfoHandle* h = nullptr); virtual bool GetFunction() override; - _free_music_handle free_music_handle; - _ffmpeg_core_open ffmpeg_core_open; - _ffmpeg_core_play ffmpeg_core_play; - _ffmpeg_core_pause ffmpeg_core_pause; - _ffmpeg_core_seek ffmpeg_core_seek; - _ffmpeg_core_get_cur_position ffmpeg_core_get_cur_position; - _ffmpeg_core_song_is_over ffmpeg_core_song_is_over; - _ffmpeg_core_get_song_length ffmpeg_core_get_song_length; - _ffmpeg_core_get_channels ffmpeg_core_get_channels; - _ffmpeg_core_get_freq ffmpeg_core_get_freq; - _ffmpeg_core_is_playing ffmpeg_core_is_playing; - _ffmpeg_core_get_bits ffmpeg_core_get_bits; + _free_music_handle free_music_handle = nullptr; + _free_music_info_handle free_music_info_handle = nullptr; + _ffmpeg_core_open ffmpeg_core_open = nullptr; + _ffmpeg_core_info_open ffmpeg_core_info_open = nullptr; + _ffmpeg_core_play ffmpeg_core_play = nullptr; + _ffmpeg_core_pause ffmpeg_core_pause = nullptr; + _ffmpeg_core_seek ffmpeg_core_seek = nullptr; + _ffmpeg_core_get_cur_position ffmpeg_core_get_cur_position = nullptr; + _ffmpeg_core_song_is_over ffmpeg_core_song_is_over = nullptr; + _ffmpeg_core_get_song_length ffmpeg_core_get_song_length = nullptr; + _ffmpeg_core_info_get_song_length ffmpeg_core_info_get_song_length = nullptr; + _ffmpeg_core_get_channels ffmpeg_core_get_channels = nullptr; + _ffmpeg_core_info_get_channels ffmpeg_core_info_get_channels = nullptr; + _ffmpeg_core_get_freq ffmpeg_core_get_freq = nullptr; + _ffmpeg_core_info_get_freq ffmpeg_core_info_get_freq = nullptr; + _ffmpeg_core_is_playing ffmpeg_core_is_playing = nullptr; + _ffmpeg_core_get_bits ffmpeg_core_get_bits = nullptr; + _ffmpeg_core_info_get_bits ffmpeg_core_info_get_bits = nullptr; + _ffmpeg_core_get_bitrate ffmpeg_core_get_bitrate = nullptr; + _ffmpeg_core_info_get_bitrate ffmpeg_core_info_get_bitrate = nullptr; + _ffmpeg_core_get_metadata ffmpeg_core_get_metadata = nullptr; + _ffmpeg_core_info_get_metadata ffmpeg_core_info_get_metadata = nullptr; MusicHandle* handle; + std::wstring recent_file; int err; }; diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index b19f2ffd4..8ef6bfea5 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -11,6 +11,7 @@ extern "C" { #define FFMPEG_CORE_API __declspec(dllimport) #endif typedef struct MusicHandle MusicHandle; +typedef struct MusicInfoHandle MusicInfoHandle; // 负数即为来自ffmpeg的错误 #define FFMPEG_CORE_ERR_OK 0 @@ -23,8 +24,11 @@ typedef struct MusicHandle MusicHandle; #define FFMPEG_CORE_ERR_FAILED_CREATE_THREAD 7 #define FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX 8 #define FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED 9 +#define FFMPEG_CORE_ERR_NO_AUDIO 10 FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); +FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); +FFMPEG_CORE_API int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle); FFMPEG_CORE_API int ffmpeg_core_play(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_seek(MusicHandle* handle, int64_t time); @@ -46,18 +50,21 @@ FFMPEG_CORE_API int ffmpeg_core_song_is_over(MusicHandle* handle); * @return 如果Handle为NULL,返回-1,反之返回以AV_TIME_BASE为基准的时间(1相当于1/1000000s) */ FFMPEG_CORE_API int64_t ffmpeg_core_get_song_length(MusicHandle* handle); +FFMPEG_CORE_API int64_t ffmpeg_core_info_get_song_length(MusicInfoHandle* handle); /** * @brief 返回音频文件声道数(SDL可能会改如果音频设备不支持) * @param handle Handle * @return 如果Handle为NULL,返回-1,反之返回声道数 */ FFMPEG_CORE_API int ffmpeg_core_get_channels(MusicHandle* handle); +FFMPEG_CORE_API int ffmpeg_core_info_get_channels(MusicInfoHandle* handle); /** * @brief 返回音频文件采样频率(SDL可能会修改) * @param handle Handle * @return 如果Handle为NULL,返回-1,反之返回采样频率 */ FFMPEG_CORE_API int ffmpeg_core_get_freq(MusicHandle* handle); +FFMPEG_CORE_API int ffmpeg_core_info_get_freq(MusicInfoHandle* handle); /** * @brief 返回当前状态 * @param handle Handle @@ -70,12 +77,22 @@ FFMPEG_CORE_API int ffmpeg_core_is_playing(MusicHandle* handle); * @return 如果Handle为NULL,返回-1 */ FFMPEG_CORE_API int ffmpeg_core_get_bits(MusicHandle* handle); +FFMPEG_CORE_API int ffmpeg_core_info_get_bits(MusicInfoHandle* handle); /** * @brief 返回比特率 * @param handle Handle * @return 如果Handle为NULL,返回-1 */ FFMPEG_CORE_API int64_t ffmpeg_core_get_bitrate(MusicHandle* handle); +FFMPEG_CORE_API int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle); +/** + * @brief 获取元数据 + * @param handle Handle + * @param key 元数据Key + * @return 结果,需要手动调用free释放内存 +*/ +FFMPEG_CORE_API wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key); +FFMPEG_CORE_API wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index b92b9b62d..f7304abcf 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -2,12 +2,15 @@ #include #include +#include "cpp2c.h" #include "wchar_util.h" #include "open.h" #include "output.h" #include "loop.h" #include "decode.h" +#define CODEPAGE_SIZE 3 + void free_music_handle(MusicHandle* handle) { if (!handle) return; if (handle->device_id) SDL_CloseAudioDevice(handle->device_id); @@ -33,6 +36,12 @@ void free_music_handle(MusicHandle* handle) { free(handle); } +void free_music_info_handle(MusicInfoHandle* handle) { + if (!handle) return; + if (handle->fmt) avformat_close_input(&handle->fmt); + free(handle); +} + int ffmpeg_core_open(const wchar_t* url, MusicHandle** h) { if (!url || !h) return FFMPEG_CORE_ERR_NULLPTR; std::string u; @@ -78,6 +87,34 @@ int ffmpeg_core_open(const wchar_t* url, MusicHandle** h) { return re; } +int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle) { + if (!url || !handle) return FFMPEG_CORE_ERR_NULLPTR; + std::string u; + // 将文件名转为UTF-8,ffmpeg API处理的都是UTF-8文件名 + if (!wchar_util::wstr_to_str(u, url, CP_UTF8)) { + return FFMPEG_CORE_ERR_INVAILD_NAME; + } + // 设置ffmpeg日志级别为Error + av_log_set_level(AV_LOG_ERROR); + MusicInfoHandle* h = (MusicInfoHandle*)malloc(sizeof(MusicInfoHandle)); + int re = FFMPEG_CORE_ERR_OK; + if (!h) { + return FFMPEG_CORE_ERR_OOM; + } + memset(h, 0, sizeof(MusicInfoHandle)); + if ((re = open_input2(h, u.c_str()))) { + goto end; + } + if ((re = find_audio_stream2(h))) { + goto end; + } + *handle = h; + return FFMPEG_CORE_ERR_OK; +end: + free_music_info_handle(h); + return re; +} + int ffmpeg_core_play(MusicHandle* handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; SDL_PauseAudioDevice(handle->device_id, 0); @@ -109,16 +146,31 @@ int64_t ffmpeg_core_get_song_length(MusicHandle* handle) { return handle->fmt->duration; } +int64_t ffmpeg_core_info_get_song_length(MusicInfoHandle* handle) { + if (!handle || !handle->fmt) return -1; + return handle->fmt->duration; +} + int ffmpeg_core_get_channels(MusicHandle* handle) { if (!handle || !handle->decoder) return -1; return handle->decoder->channels; } +int ffmpeg_core_info_get_channels(MusicInfoHandle* handle) { + if (!handle || !handle->is) return -1; + return handle->is->codecpar->channels; +} + int ffmpeg_core_get_freq(MusicHandle* handle) { if (!handle || !handle->decoder) return -1; return handle->decoder->sample_rate; } +int ffmpeg_core_info_get_freq(MusicInfoHandle* handle) { + if (!handle || !handle->is) return -1; + return handle->is->codecpar->sample_rate; +} + int ffmpeg_core_seek(MusicHandle* handle, int64_t time) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; DWORD re = WaitForSingleObject(handle->mutex, INFINITE); @@ -147,7 +199,77 @@ int ffmpeg_core_get_bits(MusicHandle* handle) { return handle->decoder->bits_per_coded_sample; } +int ffmpeg_core_info_get_bits(MusicInfoHandle* handle) { + if (!handle || !handle->is) return -1; + return handle->is->codecpar->bits_per_coded_sample; +} + int64_t ffmpeg_core_get_bitrate(MusicHandle* handle) { if (!handle || !handle->decoder) return -1; return handle->decoder->bit_rate; } + +int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle) { + if (!handle || !handle->is) return -1; + return handle->is->codecpar->bit_rate; +} + +std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags) { + auto re = av_dict_get(dict, key, nullptr, flags); + if (!re || !re->value) return L""; + std::string value(re->value); + std::wstring result; + unsigned int cps[CODEPAGE_SIZE] = { CP_UTF8, CP_ACP, CP_OEMCP }; + for (size_t i = 0; i < CODEPAGE_SIZE; i++) { + if (wchar_util::str_to_wstr(result, value, cps[i])) { + return result; + } + } + return L""; +} + +wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key) { + if (!handle | !key) return nullptr; + if (handle->fmt->metadata) { + auto re = get_metadata_str(handle->fmt->metadata, key, 0); + if (!re.empty()) { + wchar_t* r = nullptr; + if (cpp2c::string2char(re, r)) { + return r; + } + } + } + if (handle->is->metadata) { + auto re = get_metadata_str(handle->is->metadata, key, 0); + if (!re.empty()) { + wchar_t* r = nullptr; + if (cpp2c::string2char(re, r)) { + return r; + } + } + } + return nullptr; +} + +wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key) { + if (!handle || !key) return nullptr; + if (handle->fmt->metadata) { + auto re = get_metadata_str(handle->fmt->metadata, key, 0); + if (!re.empty()) { + wchar_t* r = nullptr; + if (cpp2c::string2char(re, r)) { + return r; + } + } + } + if (handle->is->metadata) { + auto re = get_metadata_str(handle->is->metadata, key, 0); + if (!re.empty()) { + wchar_t* r = nullptr; + if (cpp2c::string2char(re, r)) { + return r; + } + } + } + return nullptr; +} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index ee652c7a1..2ca2b5110 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -1,6 +1,7 @@ #ifndef _MUSICPLAYER2_CORE_H #define _MUSICPLAYER2_CORE_H #if __cplusplus +#include extern "C" { #endif #include "../ffmpeg_core.h" @@ -69,7 +70,12 @@ unsigned char is_seek : 1; unsigned char set_new_pts : 1; unsigned char is_playing : 1; } MusicHandle; +typedef struct MusicInfoHandle { +AVFormatContext* fmt; +AVStream* is; +} MusicInfoHandle; #if __cplusplus } +std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags); #endif #endif diff --git a/ffmpeg_core/src/open.c b/ffmpeg_core/src/open.c index 70fc982bd..6ad4fb832 100644 --- a/ffmpeg_core/src/open.c +++ b/ffmpeg_core/src/open.c @@ -13,6 +13,18 @@ int open_input(MusicHandle* handle, const char* url) { return FFMPEG_CORE_ERR_OK; } +int open_input2(MusicInfoHandle* handle, const char* url) { + if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; + int re = 0; + if ((re = avformat_open_input(&handle->fmt, url, NULL, NULL)) < 0) { + return re; + } + if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { + return re; + } + return FFMPEG_CORE_ERR_OK; +} + int find_audio_stream(MusicHandle* handle) { if (!handle || !handle->fmt) return FFMPEG_CORE_ERR_NULLPTR; for (unsigned int i = 0; i < handle->fmt->nb_streams; i++) { @@ -28,3 +40,15 @@ int find_audio_stream(MusicHandle* handle) { } return FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER; } + +int find_audio_stream2(MusicInfoHandle* handle) { + if (!handle || !handle->fmt) return FFMPEG_CORE_ERR_NULLPTR; + for (unsigned int i = 0; i < handle->fmt->nb_streams; i++) { + AVStream* is = handle->fmt->streams[i]; + if (is->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + handle->is = is; + return FFMPEG_CORE_ERR_OK; + } + } + return FFMPEG_CORE_ERR_NO_AUDIO; +} diff --git a/ffmpeg_core/src/open.h b/ffmpeg_core/src/open.h index 36e4b381b..95e33868c 100644 --- a/ffmpeg_core/src/open.h +++ b/ffmpeg_core/src/open.h @@ -7,6 +7,8 @@ extern "C" { /// 打开文件 int open_input(MusicHandle* handle, const char* url); int find_audio_stream(MusicHandle* handle); +int open_input2(MusicInfoHandle* handle, const char* url); +int find_audio_stream2(MusicInfoHandle* handle); #if __cplusplus } #endif From efdce87c7496db9323ce8408e4072e7c74648973 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 2 Feb 2022 20:22:25 +0800 Subject: [PATCH 06/41] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=B0=83=E8=8A=82?= =?UTF-8?q?=E5=A3=B0=E9=9F=B3=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 25 +++- MusicPlayer2/FfmpegCore.h | 12 ++ ffmpeg_core/CMakeLists.txt | 7 +- ffmpeg_core/cmake/FindAVFILTER.cmake | 29 +++++ ffmpeg_core/ffmpeg_core.h | 7 ++ ffmpeg_core/src/core.cpp | 77 ++++++++++++- ffmpeg_core/src/core.h | 27 +++++ ffmpeg_core/src/filter.c | 163 +++++++++++++++++++++++++++ ffmpeg_core/src/filter.h | 22 ++++ ffmpeg_core/src/loop.c | 11 ++ ffmpeg_core/src/output.c | 61 +++++++++- ffmpeg_core/src/volume.c | 48 ++++++++ ffmpeg_core/src/volume.h | 22 ++++ 13 files changed, 503 insertions(+), 8 deletions(-) create mode 100644 ffmpeg_core/cmake/FindAVFILTER.cmake create mode 100644 ffmpeg_core/src/filter.c create mode 100644 ffmpeg_core/src/filter.h create mode 100644 ffmpeg_core/src/volume.c create mode 100644 ffmpeg_core/src/volume.h diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 1c9e24fe2..0d71a291a 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -14,6 +14,9 @@ CFfmpegCore::CFfmpegCore() { } CFfmpegCore::~CFfmpegCore() { + if (settings) { + free_ffmpeg_core_settings(settings); + } if (handle) { Close(); } @@ -37,6 +40,7 @@ void CFfmpegCore::InitCore() { format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.flac;*.ape;*.mp4;*.mkv;*.m2ts"; CAudioCommon::m_surpported_format.push_back(format); CAudioCommon::m_all_surpported_extensions = format.extensions; + settings = ffmpeg_core_init_settings(); } } @@ -73,7 +77,8 @@ void CFfmpegCore::Open(const wchar_t* file_path) { } if (ffmpeg_core_open) { if (file_path) recent_file = file_path; - int re = ffmpeg_core_open(file_path, &handle); + if (!settings) settings = ffmpeg_core_init_settings(); + int re = ffmpeg_core_open2(file_path, &handle, settings); if (re) { err = re; } @@ -107,6 +112,14 @@ void CFfmpegCore::Stop() { } void CFfmpegCore::SetVolume(int volume) { + if (handle) { + int re = ffmpeg_core_set_volume(handle, volume); + if (re) { + err = re; + } + } else { + ffmpeg_core_settings_set_volume(settings, volume); + } } void CFfmpegCore::SetSpeed(float speed) { @@ -249,11 +262,14 @@ bool CFfmpegCore::GetFunction() { //ȡ free_music_handle = (_free_music_handle)::GetProcAddress(m_dll_module, "free_music_handle"); free_music_info_handle = (_free_music_info_handle)::GetProcAddress(m_dll_module, "free_music_info_handle"); + free_ffmpeg_core_settings = (_free_ffmpeg_core_settings)::GetProcAddress(m_dll_module, "free_ffmpeg_core_settings"); ffmpeg_core_open = (_ffmpeg_core_open)::GetProcAddress(m_dll_module, "ffmpeg_core_open"); + ffmpeg_core_open2 = (_ffmpeg_core_open2)::GetProcAddress(m_dll_module, "ffmpeg_core_open2"); ffmpeg_core_info_open = (_ffmpeg_core_info_open)::GetProcAddress(m_dll_module, "ffmpeg_core_info_open"); ffmpeg_core_play = (_ffmpeg_core_play)::GetProcAddress(m_dll_module, "ffmpeg_core_play"); ffmpeg_core_pause = (_ffmpeg_core_pause)::GetProcAddress(m_dll_module, "ffmpeg_core_pause"); ffmpeg_core_seek = (_ffmpeg_core_seek)::GetProcAddress(m_dll_module, "ffmpeg_core_seek"); + ffmpeg_core_set_volume = (_ffmpeg_core_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_set_volume"); ffmpeg_core_get_cur_position = (_ffmpeg_core_get_cur_position)::GetProcAddress(m_dll_module, "ffmpeg_core_get_cur_position"); ffmpeg_core_song_is_over = (_ffmpeg_core_song_is_over)::GetProcAddress(m_dll_module, "ffmpeg_core_song_is_over"); ffmpeg_core_get_song_length = (_ffmpeg_core_get_song_length)::GetProcAddress(m_dll_module, "ffmpeg_core_get_song_length"); @@ -269,14 +285,19 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_info_get_bitrate = (_ffmpeg_core_info_get_bitrate)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_bitrate"); ffmpeg_core_get_metadata = (_ffmpeg_core_get_metadata)::GetProcAddress(m_dll_module, "ffmpeg_core_get_metadata"); ffmpeg_core_info_get_metadata = (_ffmpeg_core_info_get_metadata)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_metadata"); + ffmpeg_core_init_settings = (_ffmpeg_core_init_settings)::GetProcAddress(m_dll_module, "ffmpeg_core_init_settings"); + ffmpeg_core_settings_set_volume = (_ffmpeg_core_settings_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_volume"); //жǷɹ rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); + rtn &= (free_ffmpeg_core_settings != NULL); rtn &= (ffmpeg_core_open != NULL); + rtn &= (ffmpeg_core_open2 != NULL); rtn &= (ffmpeg_core_info_open != NULL); rtn &= (ffmpeg_core_play != NULL); rtn &= (ffmpeg_core_pause != NULL); rtn &= (ffmpeg_core_seek != NULL); + rtn &= (ffmpeg_core_set_volume != NULL); rtn &= (ffmpeg_core_get_cur_position != NULL); rtn &= (ffmpeg_core_song_is_over != NULL); rtn &= (ffmpeg_core_get_song_length != NULL); @@ -292,6 +313,8 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_info_get_bitrate != NULL); rtn &= (ffmpeg_core_get_metadata != NULL); rtn &= (ffmpeg_core_info_get_metadata != NULL); + rtn &= (ffmpeg_core_init_settings != NULL); + rtn &= (ffmpeg_core_settings_set_volume != NULL); return rtn; } diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 7fbc38734..5225a3785 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -4,13 +4,17 @@ typedef struct MusicHandle MusicHandle; typedef struct MusicInfoHandle MusicInfoHandle; +typedef struct FfmpegCoreSettings FfmpegCoreSettings; typedef void(*_free_music_handle)(MusicHandle*); typedef void(*_free_music_info_handle)(MusicInfoHandle*); +typedef void(*_free_ffmpeg_core_settings)(FfmpegCoreSettings*); typedef int(*_ffmpeg_core_open)(const wchar_t*, MusicHandle**); +typedef int(*_ffmpeg_core_open2)(const wchar_t*, MusicHandle**, FfmpegCoreSettings*); typedef int(*_ffmpeg_core_info_open)(const wchar_t*, MusicInfoHandle**); typedef int(*_ffmpeg_core_play)(MusicHandle*); typedef int(*_ffmpeg_core_pause)(MusicHandle*); typedef int(*_ffmpeg_core_seek)(MusicHandle*, int64_t); +typedef int(*_ffmpeg_core_set_volume)(MusicHandle*, int); typedef int64_t(*_ffmpeg_core_get_cur_position)(MusicHandle*); typedef int(*_ffmpeg_core_song_is_over)(MusicHandle*); typedef int64_t(*_ffmpeg_core_get_song_length)(MusicHandle*); @@ -26,6 +30,8 @@ typedef int64_t(*_ffmpeg_core_get_bitrate)(MusicHandle*); typedef int64_t(*_ffmpeg_core_info_get_bitrate)(MusicInfoHandle*); typedef wchar_t*(*_ffmpeg_core_get_metadata)(MusicHandle*, const char* key); typedef wchar_t*(*_ffmpeg_core_info_get_metadata)(MusicInfoHandle*, const char* key); +typedef FfmpegCoreSettings*(*_ffmpeg_core_init_settings)(); +typedef int(*_ffmpeg_core_settings_set_volume)(FfmpegCoreSettings*, int volume); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -80,11 +86,14 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { virtual bool GetFunction() override; _free_music_handle free_music_handle = nullptr; _free_music_info_handle free_music_info_handle = nullptr; + _free_ffmpeg_core_settings free_ffmpeg_core_settings = nullptr; _ffmpeg_core_open ffmpeg_core_open = nullptr; + _ffmpeg_core_open2 ffmpeg_core_open2 = nullptr; _ffmpeg_core_info_open ffmpeg_core_info_open = nullptr; _ffmpeg_core_play ffmpeg_core_play = nullptr; _ffmpeg_core_pause ffmpeg_core_pause = nullptr; _ffmpeg_core_seek ffmpeg_core_seek = nullptr; + _ffmpeg_core_set_volume ffmpeg_core_set_volume = nullptr; _ffmpeg_core_get_cur_position ffmpeg_core_get_cur_position = nullptr; _ffmpeg_core_song_is_over ffmpeg_core_song_is_over = nullptr; _ffmpeg_core_get_song_length ffmpeg_core_get_song_length = nullptr; @@ -100,7 +109,10 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_info_get_bitrate ffmpeg_core_info_get_bitrate = nullptr; _ffmpeg_core_get_metadata ffmpeg_core_get_metadata = nullptr; _ffmpeg_core_info_get_metadata ffmpeg_core_info_get_metadata = nullptr; + _ffmpeg_core_init_settings ffmpeg_core_init_settings = nullptr; + _ffmpeg_core_settings_set_volume ffmpeg_core_settings_set_volume = nullptr; MusicHandle* handle; + FfmpegCoreSettings* settings; std::wstring recent_file; int err; }; diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index 3ab844952..9d26f9a6e 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -10,7 +10,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(AVFORMAT 58 REQUIRED) find_package(AVCODEC 58 REQUIRED) find_package(AVUTIL 56 REQUIRED) -# find_package(AVFILTER 7) +find_package(AVFILTER 7) find_package(SWRESAMPLE 4 REQUIRED) find_package(SDL2 REQUIRED) @@ -30,6 +30,10 @@ src/open.c "src/output.c" src/loop.h src/loop.c +"src/filter.h" +"src/filter.c" +src/volume.h +src/volume.c ) add_library(ffmpeg_core SHARED "${CORE_FILES}") @@ -38,6 +42,7 @@ target_compile_definitions(ffmpeg_core PRIVATE BUILD_FFMPEG_CORE) target_link_libraries(ffmpeg_core AVFORMAT::AVFORMAT) target_link_libraries(ffmpeg_core AVCODEC::AVCODEC) target_link_libraries(ffmpeg_core AVUTIL::AVUTIL) +target_link_libraries(ffmpeg_core AVFILTER::AVFILTER) target_link_libraries(ffmpeg_core SWRESAMPLE::SWRESAMPLE) target_link_libraries(ffmpeg_core SDL2::Core) target_link_libraries(ffmpeg_core SDL2::Main) diff --git a/ffmpeg_core/cmake/FindAVFILTER.cmake b/ffmpeg_core/cmake/FindAVFILTER.cmake new file mode 100644 index 000000000..65ab7fa13 --- /dev/null +++ b/ffmpeg_core/cmake/FindAVFILTER.cmake @@ -0,0 +1,29 @@ +find_package(PkgConfig) +if (PkgConfig_FOUND) + pkg_check_modules(PC_AVFILTER QUIET IMPORTED_TARGET GLOBAL libavfilter) +endif() + +if (PC_AVFILTER_FOUND) + set(AVFILTER_FOUND TRUE) + set(AVFILTER_VERSION ${PC_AVFILTER_VERSION}) + set(AVFILTER_VERSION_STRING ${PC_AVFILTER_STRING}) + set(AVFILTER_LIBRARYS ${PC_AVFILTER_LIBRARIES}) + if (USE_STATIC_LIBS) + set(AVFILTER_INCLUDE_DIRS ${PC_AVFILTER_STATIC_INCLUDE_DIRS}) + else() + set(AVFILTER_INCLUDE_DIRS ${PC_AVFILTER_INCLUDE_DIRS}) + endif() + if (NOT TARGET AVFILTER::AVFILTER) + add_library(AVFILTER::AVFILTER ALIAS PkgConfig::PC_AVFILTER) + endif() +else() + message(FATAL_ERROR "failed.") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AVFILTER + FOUND_VAR AVFILTER_FOUND + REQUIRED_VARS + AVFILTER_LIBRARYS + VERSION_VAR AVFILTER_VERSION +) diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 8ef6bfea5..a1cf9bd4c 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -12,6 +12,7 @@ extern "C" { #endif typedef struct MusicHandle MusicHandle; typedef struct MusicInfoHandle MusicInfoHandle; +typedef struct FfmpegCoreSettings FfmpegCoreSettings; // 负数即为来自ffmpeg的错误 #define FFMPEG_CORE_ERR_OK 0 @@ -25,13 +26,17 @@ typedef struct MusicInfoHandle MusicInfoHandle; #define FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX 8 #define FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED 9 #define FFMPEG_CORE_ERR_NO_AUDIO 10 +#define FFMPEG_CORE_ERR_FAILED_SET_VOLUME 11 FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); +FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); +FFMPEG_CORE_API int ffmpeg_core_open2(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s); FFMPEG_CORE_API int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle); FFMPEG_CORE_API int ffmpeg_core_play(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_seek(MusicHandle* handle, int64_t time); +FFMPEG_CORE_API int ffmpeg_core_set_volume(MusicHandle* handle, int volume); /** * @brief 获取当前播放位置 * @param handle Handle @@ -93,6 +98,8 @@ FFMPEG_CORE_API int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle); */ FFMPEG_CORE_API wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key); FFMPEG_CORE_API wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key); +FFMPEG_CORE_API FfmpegCoreSettings* ffmpeg_core_init_settings(); +FFMPEG_CORE_API int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index f7304abcf..1662a346c 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -8,6 +8,7 @@ #include "output.h" #include "loop.h" #include "decode.h" +#include "filter.h" #define CODEPAGE_SIZE 3 @@ -26,6 +27,10 @@ void free_music_handle(MusicHandle* handle) { } } } + if (handle->graph) { + avfilter_graph_free(&handle->graph); + } + c_linked_list_clear(&handle->filters, nullptr); if (handle->buffer) av_audio_fifo_free(handle->buffer); if (handle->swrac) swr_free(&handle->swrac); if (handle->decoder) avcodec_free_context(&handle->decoder); @@ -33,6 +38,9 @@ void free_music_handle(MusicHandle* handle) { if (handle->sdl_initialized) { SDL_QuitSubSystem(SDL_INIT_AUDIO); } + if (handle->s && handle->settings_is_alloc) { + free_ffmpeg_core_settings(handle->s); + } free(handle); } @@ -42,21 +50,44 @@ void free_music_info_handle(MusicInfoHandle* handle) { free(handle); } -int ffmpeg_core_open(const wchar_t* url, MusicHandle** h) { +void free_ffmpeg_core_settings(FfmpegCoreSettings* s) { + if (!s) return; + free(s); +} + +int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle) { + return ffmpeg_core_open2(url, handle, nullptr); +} + +int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s) { if (!url || !h) return FFMPEG_CORE_ERR_NULLPTR; std::string u; // 将文件名转为UTF-8,ffmpeg API处理的都是UTF-8文件名 if (!wchar_util::wstr_to_str(u, url, CP_UTF8)) { return FFMPEG_CORE_ERR_INVAILD_NAME; } +#if NDEBUG // 设置ffmpeg日志级别为Error av_log_set_level(AV_LOG_ERROR); +#else + av_log_set_level(AV_LOG_VERBOSE); +#endif MusicHandle* handle = (MusicHandle*)malloc(sizeof(MusicHandle)); int re = FFMPEG_CORE_ERR_OK; if (!handle) { return FFMPEG_CORE_ERR_OOM; } memset(handle, 0, sizeof(MusicHandle)); + if (s) { + handle->s = s; + } else { + handle->settings_is_alloc = 1; + handle->s = ffmpeg_core_init_settings(); + if (!handle->s) { + re = FFMPEG_CORE_ERR_OOM; + goto end; + } + } handle->first_pts = INT64_MIN; if ((re = open_input(handle, u.c_str()))) { goto end; @@ -70,6 +101,9 @@ int ffmpeg_core_open(const wchar_t* url, MusicHandle** h) { if ((re = init_output(handle))) { goto end; } + if ((re = init_filters(handle))) { + goto end; + } handle->mutex = CreateMutexW(nullptr, FALSE, nullptr); if (!handle->mutex) { re = FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX; @@ -94,8 +128,12 @@ int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle) { if (!wchar_util::wstr_to_str(u, url, CP_UTF8)) { return FFMPEG_CORE_ERR_INVAILD_NAME; } +#if NDEBUG // 设置ffmpeg日志级别为Error av_log_set_level(AV_LOG_ERROR); +#else + av_log_set_level(AV_LOG_VERBOSE); +#endif MusicInfoHandle* h = (MusicInfoHandle*)malloc(sizeof(MusicInfoHandle)); int re = FFMPEG_CORE_ERR_OK; if (!h) { @@ -273,3 +311,40 @@ wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key) } return nullptr; } + +FfmpegCoreSettings* ffmpeg_core_init_settings() { + FfmpegCoreSettings* s = (FfmpegCoreSettings*)malloc(sizeof(FfmpegCoreSettings)); + if (!s) return nullptr; + memset(s, 0, sizeof(FfmpegCoreSettings)); + s->speed = 1.0; + s->volume = 100; + return s; +} + +int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume) { + if (!s) return 0; + if (volume >= 0 && volume <= 100) { + s->volume = volume; + return 1; + } + return 0; +} + +int ffmpeg_core_set_volume(MusicHandle* handle, int volume) { + if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; + int r = ffmpeg_core_settings_set_volume(handle->s, volume); + if (!r) return FFMPEG_CORE_ERR_FAILED_SET_VOLUME; + DWORD re = WaitForSingleObject(handle->mutex, INFINITE); + if (re == WAIT_OBJECT_0) { + handle->need_reinit_filters = 1; + } else { + return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + } + handle->have_err = 0; + ReleaseMutex(handle->mutex); + while (1) { + if (!handle->is_seek) break; + Sleep(10); + } + return handle->have_err ? handle->err : FFMPEG_CORE_ERR_OK; +} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 2ca2b5110..c6deaacbc 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -9,10 +9,15 @@ extern "C" { #include "libavcodec/avcodec.h" #include "libavutil/avutil.h" #include "libavutil/audio_fifo.h" +#include "libavutil/opt.h" #include "libavutil/rational.h" +#include "libavfilter/avfilter.h" +#include "libavfilter/buffersink.h" +#include "libavfilter/buffersrc.h" #include "libswresample/swresample.h" #include "SDL2/SDL.h" #include +#include "c_linked_list.h" #ifndef __cplusplus #ifndef min @@ -56,6 +61,18 @@ int64_t end_pts; /// 第一个sample的pts int64_t first_pts; int64_t seek_pos; +/// 设置 +FfmpegCoreSettings* s; +/// 用于设置filter +AVFilterGraph* graph; +/// filter 输入口 +AVFilterContext* filter_inp; +/// filter 输出口 +AVFilterContext* filter_out; +/// filter 链 +c_linked_list* filters; +/// 输出时的声道布局 +uint64_t output_channel_layout; /// SDL是否被初始化 unsigned char sdl_initialized : 1; /// 让事件处理线程退出标志位 @@ -69,11 +86,21 @@ unsigned char is_seek : 1; /// 是否需要设置新的缓冲区时间 unsigned char set_new_pts : 1; unsigned char is_playing : 1; +/// 设置是内部分配 +unsigned char settings_is_alloc : 1; +/// 需要设置新的filters链 +unsigned char need_reinit_filters : 1; } MusicHandle; typedef struct MusicInfoHandle { AVFormatContext* fmt; AVStream* is; } MusicInfoHandle; +typedef struct FfmpegCoreSettings { +/// 音量 +int volume; +/// 速度 +float speed; +} FfmpegCoreSettings; #if __cplusplus } std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags); diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c new file mode 100644 index 000000000..292a515ff --- /dev/null +++ b/ffmpeg_core/src/filter.c @@ -0,0 +1,163 @@ +#include "filter.h" + +#include "output.h" +#include "volume.h" + +int need_filters(FfmpegCoreSettings* s) { + if (!s) return 0; + if (!avfilter_get_by_name("abuffersink") || !avfilter_get_by_name("abuffer")) { + return 0; + } + if (s->volume != 100 && avfilter_get_by_name("volume")) { + return 1; + } + return 0; +} + +int init_filters(MusicHandle* handle) { + if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; + if (!need_filters(handle->s)) return FFMPEG_CORE_ERR_OK; + int re = FFMPEG_CORE_ERR_OK; + if ((re = create_src_and_sink(&handle->graph, &handle->filter_inp, &handle->filter_out, handle))) { + return re; + } + if (handle->s->volume != 100 && avfilter_get_by_name("volume")) { + if ((re = create_volume_filter(0, handle->graph, handle->filter_inp, &handle->filters, handle->s->volume, handle->target_format))) { + return re; + } + } + if (c_linked_list_count(handle->filters) == 0) { + avfilter_graph_free(&handle->graph); + handle->graph = NULL; + handle->filter_inp = NULL; + handle->filter_out = NULL; + return FFMPEG_CORE_ERR_OK; + } + AVFilterContext* last = c_linked_list_tail(handle->filters)->d; + if ((re = avfilter_link(last, 0, handle->filter_out, 0)) < 0) { + return re; + } + if ((re = avfilter_graph_config(handle->graph, NULL)) < 0) { + return re; + } + return FFMPEG_CORE_ERR_OK; +} + +int reinit_filters(MusicHandle* handle) { + if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; + if (!need_filters(handle->s)) { + if (!handle->graph) return FFMPEG_CORE_ERR_OK; + DWORD re = WaitForSingleObject(handle->mutex, INFINITE); + if (re != WAIT_OBJECT_0) { + return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + } + avfilter_graph_free(&handle->graph); + handle->graph = NULL; + handle->filter_inp = NULL; + handle->filter_out = NULL; + c_linked_list_clear(&handle->filters, NULL); + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_OK; + } + int re = FFMPEG_CORE_ERR_OK; + AVFilterGraph* graph = NULL; + AVFilterContext* inc = NULL, * outc = NULL; + c_linked_list* list = NULL; + if ((re = create_src_and_sink(&graph, &inc, &outc, handle)) < 0) { + goto end; + } + if (handle->s->volume != 100 && avfilter_get_by_name("volume")) { + if ((re = create_volume_filter(0, graph, inc, &list, handle->s->volume, handle->target_format)) < 0) { + goto end; + } + } + if (c_linked_list_count(list) == 0) { + if (handle->graph) { + DWORD r = WaitForSingleObject(handle->mutex, INFINITE); + if (r != WAIT_OBJECT_0) { + re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + goto end; + } + avfilter_graph_free(&handle->graph); + handle->graph = NULL; + handle->filter_inp = NULL; + handle->filter_out = NULL; + c_linked_list_clear(&handle->filters, NULL); + ReleaseMutex(handle->mutex); + } + re = FFMPEG_CORE_ERR_OK; + goto end; + } + AVFilterContext* last = c_linked_list_tail(list)->d; + if ((re = avfilter_link(last, 0, outc, 0)) < 0) { + goto end; + } + if ((re = avfilter_graph_config(graph, NULL)) < 0) { + goto end; + } + DWORD r = WaitForSingleObject(handle->mutex, INFINITE); + if (r != WAIT_OBJECT_0) { + re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + goto end; + } + if (handle->graph) { + avfilter_graph_free(&handle->graph); + handle->graph = NULL; + handle->filter_inp = NULL; + handle->filter_out = NULL; + c_linked_list_clear(&handle->filters, NULL); + } + handle->graph = graph; + handle->filter_inp = inc; + handle->filter_out = outc; + handle->filters = list; + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_OK; +end: + if (graph) { + avfilter_graph_free(&graph); + c_linked_list_clear(&list, NULL); + } + return re; +} + +int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterContext** sink, MusicHandle* handle) { + if (!graph || !src || !sink) return FFMPEG_CORE_ERR_NULLPTR; + const AVFilter* buffersink = avfilter_get_by_name("abuffersink"), * buffer = avfilter_get_by_name("abuffer"); + if (!(*graph = avfilter_graph_alloc())) { + return FFMPEG_CORE_ERR_OOM; + } + int re = 0; + char args[1024]; + char channel_layout[512]; + // 输入的设置:描述见 ffmpeg -h filter=abuffer + uint64_t layout = handle->output_channel_layout; + av_get_channel_layout_string(channel_layout, sizeof(channel_layout), handle->decoder->channels, layout); + snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%d", handle->is->time_base.num, handle->is->time_base.den, handle->sdl_spec.freq, av_get_sample_fmt_name(handle->target_format), channel_layout, handle->decoder->channels); + if ((re = avfilter_graph_create_filter(src, buffer, "in", args, NULL, *graph)) < 0) { + return re; + } + if ((re = avfilter_graph_create_filter(sink, buffersink, "out", NULL, NULL, *graph)) < 0) { + return re; + } + // 输出设置 + // 描述见 ffmpeg -h filter=abuffersink + // 具体类型参考 ffmpeg 源代码 libavfilter/buffersink.c 里的 abuffersink_options + enum AVSampleFormat sample_fmts[2] = { handle->target_format , AV_SAMPLE_FMT_NONE }; + if ((re = av_opt_set_int_list(*sink, "sample_fmts", sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0) { + return re; + } + int sample_rates[2] = { handle->sdl_spec.freq , 0 }; + if ((re = av_opt_set_int_list(*sink, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { + return re; + } + int64_t channel_layouts[2] = { layout , 0 }; + if ((re = av_opt_set_int_list(*sink, "channel_layouts", channel_layouts, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { + return re; + } + int channel_counts[2] = { handle->decoder->channels, 0 }; + if ((re = av_opt_set_int_list(*sink, "channel_counts", channel_counts, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { + return re; + } + return FFMPEG_CORE_ERR_OK; +} diff --git a/ffmpeg_core/src/filter.h b/ffmpeg_core/src/filter.h new file mode 100644 index 000000000..55b493290 --- /dev/null +++ b/ffmpeg_core/src/filter.h @@ -0,0 +1,22 @@ +#ifndef _MUSICPLAYER2_FILTER_H +#define _MUSICPLAYER2_FILTER_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +int need_filters(FfmpegCoreSettings* s); +int init_filters(MusicHandle* handle); +int reinit_filters(MusicHandle* handle); +/** + * @brief 新建一个新的FilterGraph,并且分配好输出和输入 + * @param graph FilterGraph + * @param src 输入节点 + * @param sink 输出节点 + * @param handle 读取必要的数据用 + * @return +*/ +int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterContext** sink, MusicHandle* handle); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index 65182c839..365d862a1 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -1,6 +1,7 @@ #include "loop.h" #include "decode.h" +#include "filter.h" int seek_to_pos(MusicHandle* handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; @@ -62,6 +63,16 @@ DWORD WINAPI event_loop(LPVOID handle) { doing = 1; goto end; } + if (h->need_reinit_filters) { + int re = reinit_filters(h); + if (re) { + h->have_err = 1; + h->err = re; + } + doing = 1; + h->need_reinit_filters = 0; + goto end; + } if (!h->is_eof) { if (av_audio_fifo_size(h->buffer) < buffered_size) { int re = decode_audio(handle, &writed); diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index df6bcfdee..08f96e3da 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -22,7 +22,8 @@ int init_output(MusicHandle* handle) { return FFMPEG_CORE_ERR_SDL; } enum AVSampleFormat target_format = convert_to_sdl_supported_format(handle->decoder->sample_fmt); - handle->swrac = swr_alloc_set_opts(NULL, get_sdl_channel_layout(handle->decoder->channels), target_format, handle->sdl_spec.freq, handle->decoder->channel_layout, handle->decoder->sample_fmt, handle->decoder->sample_rate, 0, NULL); + handle->output_channel_layout = get_sdl_channel_layout(handle->decoder->channels); + handle->swrac = swr_alloc_set_opts(NULL, handle->output_channel_layout, target_format, handle->sdl_spec.freq, handle->decoder->channel_layout, handle->decoder->sample_fmt, handle->decoder->sample_rate, 0, NULL); if (!handle->swrac) { return FFMPEG_CORE_ERR_OOM; } @@ -78,14 +79,14 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { DWORD re = WaitForSingleObject(handle->mutex, 10); if (re != WAIT_OBJECT_0) { // 无法获取Mutex所有权,填充空白数据 - memset(stream, 0, sizeof(len)); + memset(stream, 0, len); return; } int samples_need = len / handle->target_format_pbytes / handle->sdl_spec.channels; if (av_audio_fifo_size(handle->buffer) == 0) { // 缓冲区没有数据,填充空白数据 - memset(stream, 0, sizeof(len)); - } else { + memset(stream, 0, len); + } else if (!handle->graph) { int writed = av_audio_fifo_read(handle->buffer, (void**)&stream, samples_need); if (writed > 0) { // 增大缓冲区开始时间 @@ -94,11 +95,61 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { } if (writed < 0) { // 读取发生错误,填充空白数据 - memset(stream, 0, sizeof(len)); + memset(stream, 0, len); } else if (writed < samples_need) { // 不足的区域用空白数据填充 memset(stream + (size_t)writed * handle->target_format_pbytes, 0, (((size_t)samples_need - writed) * handle->target_format_pbytes)); } + } else { + AVFrame* in = av_frame_alloc(), * out = av_frame_alloc(); + int writed = 0; + int samples_need_in = 0; + if (!in || !out) { + memset(stream, 0, len); + goto end; + } + samples_need_in = samples_need * handle->s->speed; + in->channels = handle->decoder->channels; + in->channel_layout = handle->output_channel_layout; + in->format = handle->target_format; + in->sample_rate = handle->sdl_spec.freq; + in->nb_samples = samples_need_in; + if (av_frame_get_buffer(in, 0) < 0) { + memset(stream, 0, len); + goto end; + } + // 从缓冲区读取数据 + writed = av_audio_fifo_read(handle->buffer, (void**)in->data, samples_need_in); + if (writed > 0) { + // 增大缓冲区开始时间 + AVRational base = { 1, handle->sdl_spec.freq }; + handle->pts += av_rescale_q_rnd(writed, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + } + if (writed < 0) { + memset(stream, 0, len); + goto end; + } + in->nb_samples = writed; + // 喂给 filters 数据 + if (av_buffersrc_add_frame(handle->filter_inp, in) < 0) { + memset(stream, 0, len); + goto end; + } + // 从 filters 拿回数据 + if (av_buffersink_get_frame(handle->filter_out, out) < 0) { + memset(stream, 0, len); + goto end; + } + if (out->nb_samples >= samples_need) { + memcpy(stream, out->data[0], len); + } else { + size_t le = (size_t)out->nb_samples * handle->target_format_pbytes * handle->sdl_spec.channels; + memcpy(stream, out->data[0], le); + memset(stream, 0, len - le); + } +end: + if (in) av_frame_free(&in); + if (out) av_frame_free(&out); } ReleaseMutex(handle->mutex); } diff --git a/ffmpeg_core/src/volume.c b/ffmpeg_core/src/volume.c new file mode 100644 index 000000000..2d77cfd4d --- /dev/null +++ b/ffmpeg_core/src/volume.c @@ -0,0 +1,48 @@ +#include "volume.h" + +int get_volume_precision(enum AVSampleFormat f) { + switch (f) { + case AV_SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_U8P: + case AV_SAMPLE_FMT_S16P: + case AV_SAMPLE_FMT_S32P: + return 0; + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_DBLP: + return 2; + case AV_SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLTP: + default: + return 1; + } +} + +int create_volume_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int volume, enum AVSampleFormat f) { + if (!graph || !src || !list) return FFMPEG_CORE_ERR_NULLPTR; + char args[128]; + char name[32]; + const AVFilter* vol = avfilter_get_by_name("volume"); + snprintf(args, sizeof(args), "volume=%.2f:precision=%d", volume / 100.0, get_volume_precision(f)); + snprintf(name, sizeof(name), "volume%d", index); + int re = 0; + AVFilterContext* context = NULL; + if ((re = avfilter_graph_create_filter(&context, vol, name, args, NULL, graph)) < 0) { + return re; + } + if (!c_linked_list_append(list, (void*)context)) { + return FFMPEG_CORE_ERR_OOM; + } + if (c_linked_list_count(*list) > 1) { + AVFilterContext* last = c_linked_list_tail(*list)->prev->d; + if ((re = avfilter_link(last, 0, context, 0)) < 0) { + return re; + } + } else { + if ((re = avfilter_link(src, 0, context, 0)) < 0) { + return re; + } + } + return FFMPEG_CORE_ERR_OK; +} diff --git a/ffmpeg_core/src/volume.h b/ffmpeg_core/src/volume.h new file mode 100644 index 000000000..f4f0efe26 --- /dev/null +++ b/ffmpeg_core/src/volume.h @@ -0,0 +1,22 @@ +#ifndef _MUSICPLAYER2_VOLUME_H +#define _MUSICPLAYER2_VOLUME_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +int get_volume_precision(enum AVSampleFormat f); +/** + * @brief 创建 volume Filter + * @param index Filter 序号 + * @param graph Graph + * @param src 输入 + * @param list Filters列表 + * @param volume 声音大小百分比 + * @param f 输入的格式 + * @return +*/ +int create_volume_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int volume, enum AVSampleFormat f); +#if __cplusplus +} +#endif +#endif From 37fd1e4e27dcacb04765528e06471bc525dcf9ae Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 3 Feb 2022 10:09:03 +0800 Subject: [PATCH 07/41] =?UTF-8?q?=E5=B0=86ffmpeg=E7=9A=84=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E9=87=8D=E5=AE=9A=E5=90=91=E5=88=B0VS=E7=9A=84output?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 23 +++++++++++++++++++++++ MusicPlayer2/FfmpegCore.h | 9 ++++++++- ffmpeg_core/ffmpeg_core.h | 5 +++++ ffmpeg_core/src/core.cpp | 8 ++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 0d71a291a..a7bd98979 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -2,6 +2,7 @@ #include "FfmpegCore.h" #include "AudioCommon.h" +#include "Common.h" #include "MusicPlayer2.h" @@ -41,6 +42,7 @@ void CFfmpegCore::InitCore() { CAudioCommon::m_surpported_format.push_back(format); CAudioCommon::m_all_surpported_extensions = format.extensions; settings = ffmpeg_core_init_settings(); + ffmpeg_core_log_set_callback(LogCallback); } } @@ -263,6 +265,8 @@ bool CFfmpegCore::GetFunction() { free_music_handle = (_free_music_handle)::GetProcAddress(m_dll_module, "free_music_handle"); free_music_info_handle = (_free_music_info_handle)::GetProcAddress(m_dll_module, "free_music_info_handle"); free_ffmpeg_core_settings = (_free_ffmpeg_core_settings)::GetProcAddress(m_dll_module, "free_ffmpeg_core_settings"); + ffmpeg_core_log_format_line = (_ffmpeg_core_log_format_line)::GetProcAddress(m_dll_module, "ffmpeg_core_log_format_line"); + ffmpeg_core_log_set_callback = (_ffmpeg_core_log_set_callback)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_callback"); ffmpeg_core_open = (_ffmpeg_core_open)::GetProcAddress(m_dll_module, "ffmpeg_core_open"); ffmpeg_core_open2 = (_ffmpeg_core_open2)::GetProcAddress(m_dll_module, "ffmpeg_core_open2"); ffmpeg_core_info_open = (_ffmpeg_core_info_open)::GetProcAddress(m_dll_module, "ffmpeg_core_info_open"); @@ -291,6 +295,8 @@ bool CFfmpegCore::GetFunction() { rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); rtn &= (free_ffmpeg_core_settings != NULL); + rtn &= (ffmpeg_core_log_format_line != NULL); + rtn &= (ffmpeg_core_log_set_callback != NULL); rtn &= (ffmpeg_core_open != NULL); rtn &= (ffmpeg_core_open2 != NULL); rtn &= (ffmpeg_core_info_open != NULL); @@ -379,3 +385,20 @@ int CFfmpegCore::GetTrackNum(MusicInfoHandle* h) { if (swscanf_s(r.c_str(), L"%i", &track) == 1) return track; return 0; } + +void CFfmpegCore::LogCallback(void* ptr, int level, const char* fmt, va_list vl) { + if (level > AV_LOG_VERBOSE) return; + auto re = LoadLibraryW(L"ffmpeg_core.dll"); + if (!re) return; + auto ffmpeg_core_log_format_line = (_ffmpeg_core_log_format_line)::GetProcAddress(re, "ffmpeg_core_log_format_line"); + char buf[1024]; + if (ffmpeg_core_log_format_line) { + int print = 1; + int re = ffmpeg_core_log_format_line(ptr, level, fmt, vl, buf, sizeof(buf), &print); + if (re > 0) { + std::wstring s = CCommon::StrToUnicode(std::string(buf, re), CodeType::UTF8); + OutputDebugStringW(s.c_str()); + } + } + FreeLibrary(re); +} diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 5225a3785..53eddd864 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -2,12 +2,16 @@ #include "IPlayerCore.h" #include "DllLib.h" +#define AV_LOG_ERROR 16 +#define AV_LOG_VERBOSE 40 typedef struct MusicHandle MusicHandle; typedef struct MusicInfoHandle MusicInfoHandle; typedef struct FfmpegCoreSettings FfmpegCoreSettings; typedef void(*_free_music_handle)(MusicHandle*); typedef void(*_free_music_info_handle)(MusicInfoHandle*); typedef void(*_free_ffmpeg_core_settings)(FfmpegCoreSettings*); +typedef int(*_ffmpeg_core_log_format_line)(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); +typedef void(*_ffmpeg_core_log_set_callback)(void(*callback)(void*, int, const char*, va_list)); typedef int(*_ffmpeg_core_open)(const wchar_t*, MusicHandle**); typedef int(*_ffmpeg_core_open2)(const wchar_t*, MusicHandle**, FfmpegCoreSettings*); typedef int(*_ffmpeg_core_info_open)(const wchar_t*, MusicInfoHandle**); @@ -84,9 +88,12 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { private: std::wstring GetMetadata(std::string key, MusicInfoHandle* h = nullptr); virtual bool GetFunction() override; + static void LogCallback(void*, int, const char*, va_list); _free_music_handle free_music_handle = nullptr; _free_music_info_handle free_music_info_handle = nullptr; _free_ffmpeg_core_settings free_ffmpeg_core_settings = nullptr; + _ffmpeg_core_log_format_line ffmpeg_core_log_format_line = nullptr; + _ffmpeg_core_log_set_callback ffmpeg_core_log_set_callback = nullptr; _ffmpeg_core_open ffmpeg_core_open = nullptr; _ffmpeg_core_open2 ffmpeg_core_open2 = nullptr; _ffmpeg_core_info_open ffmpeg_core_info_open = nullptr; @@ -112,7 +119,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_init_settings ffmpeg_core_init_settings = nullptr; _ffmpeg_core_settings_set_volume ffmpeg_core_settings_set_volume = nullptr; MusicHandle* handle; - FfmpegCoreSettings* settings; + FfmpegCoreSettings* settings = nullptr; std::wstring recent_file; int err; }; diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index a1cf9bd4c..348e15867 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -5,6 +5,7 @@ extern "C" { #endif #include #include +#include #if BUILD_FFMPEG_CORE #define FFMPEG_CORE_API __declspec(dllexport) #else @@ -30,6 +31,10 @@ typedef struct FfmpegCoreSettings FfmpegCoreSettings; FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); +/// 即 av_log_format_line2 +FFMPEG_CORE_API int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); +/// 即 av_log_set_callback +FFMPEG_CORE_API void ffmpeg_core_log_set_callback(void(*callback)(void*, int, const char*, va_list)); FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); FFMPEG_CORE_API int ffmpeg_core_open2(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s); FFMPEG_CORE_API int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle); diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 1662a346c..2f76aca49 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -55,6 +55,14 @@ void free_ffmpeg_core_settings(FfmpegCoreSettings* s) { free(s); } +int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix) { + return av_log_format_line2(ptr, level, fmt, vl, line, line_size, print_prefix); +} + +void ffmpeg_core_log_set_callback(void(*callback)(void*, int, const char*, va_list)) { + av_log_set_callback(callback); +} + int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle) { return ffmpeg_core_open2(url, handle, nullptr); } From a44dabf6178614adb32bed992d6ce599554b691a Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 3 Feb 2022 11:38:39 +0800 Subject: [PATCH 08/41] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=B0=83=E8=8A=82?= =?UTF-8?q?=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 12 ++++++++++ MusicPlayer2/FfmpegCore.h | 4 ++++ ffmpeg_core/CMakeLists.txt | 6 +++++ ffmpeg_core/ffmpeg_core.h | 3 +++ ffmpeg_core/src/core.cpp | 47 +++++++++++++++++++++++++++---------- ffmpeg_core/src/core.h | 3 +++ ffmpeg_core/src/decode.c | 4 ++-- ffmpeg_core/src/filter.c | 26 +++++++++++++++++++- ffmpeg_core/src/output.c | 4 +++- ffmpeg_core/src/speed.c | 37 +++++++++++++++++++++++++++++ ffmpeg_core/src/speed.h | 22 +++++++++++++++++ 11 files changed, 152 insertions(+), 16 deletions(-) create mode 100644 ffmpeg_core/src/speed.c create mode 100644 ffmpeg_core/src/speed.h diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index a7bd98979..47d7e4230 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -125,6 +125,14 @@ void CFfmpegCore::SetVolume(int volume) { } void CFfmpegCore::SetSpeed(float speed) { + if (handle) { + int re = ffmpeg_core_set_speed(handle, speed); + if (re) { + err = re; + } + } else { + ffmpeg_core_settings_set_speed(settings, speed); + } } bool CFfmpegCore::SongIsOver() { @@ -274,6 +282,7 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_pause = (_ffmpeg_core_pause)::GetProcAddress(m_dll_module, "ffmpeg_core_pause"); ffmpeg_core_seek = (_ffmpeg_core_seek)::GetProcAddress(m_dll_module, "ffmpeg_core_seek"); ffmpeg_core_set_volume = (_ffmpeg_core_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_set_volume"); + ffmpeg_core_set_speed = (_ffmpeg_core_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_set_speed"); ffmpeg_core_get_cur_position = (_ffmpeg_core_get_cur_position)::GetProcAddress(m_dll_module, "ffmpeg_core_get_cur_position"); ffmpeg_core_song_is_over = (_ffmpeg_core_song_is_over)::GetProcAddress(m_dll_module, "ffmpeg_core_song_is_over"); ffmpeg_core_get_song_length = (_ffmpeg_core_get_song_length)::GetProcAddress(m_dll_module, "ffmpeg_core_get_song_length"); @@ -291,6 +300,7 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_info_get_metadata = (_ffmpeg_core_info_get_metadata)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_metadata"); ffmpeg_core_init_settings = (_ffmpeg_core_init_settings)::GetProcAddress(m_dll_module, "ffmpeg_core_init_settings"); ffmpeg_core_settings_set_volume = (_ffmpeg_core_settings_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_volume"); + ffmpeg_core_settings_set_speed = (_ffmpeg_core_settings_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_speed"); //жǷɹ rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); @@ -304,6 +314,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_pause != NULL); rtn &= (ffmpeg_core_seek != NULL); rtn &= (ffmpeg_core_set_volume != NULL); + rtn &= (ffmpeg_core_set_speed != NULL); rtn &= (ffmpeg_core_get_cur_position != NULL); rtn &= (ffmpeg_core_song_is_over != NULL); rtn &= (ffmpeg_core_get_song_length != NULL); @@ -321,6 +332,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_info_get_metadata != NULL); rtn &= (ffmpeg_core_init_settings != NULL); rtn &= (ffmpeg_core_settings_set_volume != NULL); + rtn &= (ffmpeg_core_settings_set_speed != NULL); return rtn; } diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 53eddd864..a79c3bc2d 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -19,6 +19,7 @@ typedef int(*_ffmpeg_core_play)(MusicHandle*); typedef int(*_ffmpeg_core_pause)(MusicHandle*); typedef int(*_ffmpeg_core_seek)(MusicHandle*, int64_t); typedef int(*_ffmpeg_core_set_volume)(MusicHandle*, int); +typedef int(*_ffmpeg_core_set_speed)(MusicHandle*, float); typedef int64_t(*_ffmpeg_core_get_cur_position)(MusicHandle*); typedef int(*_ffmpeg_core_song_is_over)(MusicHandle*); typedef int64_t(*_ffmpeg_core_get_song_length)(MusicHandle*); @@ -36,6 +37,7 @@ typedef wchar_t*(*_ffmpeg_core_get_metadata)(MusicHandle*, const char* key); typedef wchar_t*(*_ffmpeg_core_info_get_metadata)(MusicInfoHandle*, const char* key); typedef FfmpegCoreSettings*(*_ffmpeg_core_init_settings)(); typedef int(*_ffmpeg_core_settings_set_volume)(FfmpegCoreSettings*, int volume); +typedef int(*_ffmpeg_core_settings_set_speed)(FfmpegCoreSettings*, float); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -101,6 +103,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_pause ffmpeg_core_pause = nullptr; _ffmpeg_core_seek ffmpeg_core_seek = nullptr; _ffmpeg_core_set_volume ffmpeg_core_set_volume = nullptr; + _ffmpeg_core_set_speed ffmpeg_core_set_speed = nullptr; _ffmpeg_core_get_cur_position ffmpeg_core_get_cur_position = nullptr; _ffmpeg_core_song_is_over ffmpeg_core_song_is_over = nullptr; _ffmpeg_core_get_song_length ffmpeg_core_get_song_length = nullptr; @@ -118,6 +121,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_info_get_metadata ffmpeg_core_info_get_metadata = nullptr; _ffmpeg_core_init_settings ffmpeg_core_init_settings = nullptr; _ffmpeg_core_settings_set_volume ffmpeg_core_settings_set_volume = nullptr; + _ffmpeg_core_settings_set_speed ffmpeg_core_settings_set_speed = nullptr; MusicHandle* handle; FfmpegCoreSettings* settings = nullptr; std::wstring recent_file; diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index 9d26f9a6e..b9a529723 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -34,6 +34,8 @@ src/loop.c "src/filter.c" src/volume.h src/volume.c +src/speed.h +src/speed.c ) add_library(ffmpeg_core SHARED "${CORE_FILES}") @@ -47,3 +49,7 @@ target_link_libraries(ffmpeg_core SWRESAMPLE::SWRESAMPLE) target_link_libraries(ffmpeg_core SDL2::Core) target_link_libraries(ffmpeg_core SDL2::Main) target_link_libraries(ffmpeg_core utils) + +if (NOT MSVC) + target_link_libraries(ffmpeg_core m) +endif() diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 348e15867..1e756a09c 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -28,6 +28,7 @@ typedef struct FfmpegCoreSettings FfmpegCoreSettings; #define FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED 9 #define FFMPEG_CORE_ERR_NO_AUDIO 10 #define FFMPEG_CORE_ERR_FAILED_SET_VOLUME 11 +#define FFMPEG_CORE_ERR_FAILED_SET_SPEED 12 FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); @@ -42,6 +43,7 @@ FFMPEG_CORE_API int ffmpeg_core_play(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_seek(MusicHandle* handle, int64_t time); FFMPEG_CORE_API int ffmpeg_core_set_volume(MusicHandle* handle, int volume); +FFMPEG_CORE_API int ffmpeg_core_set_speed(MusicHandle* handle, float speed); /** * @brief 获取当前播放位置 * @param handle Handle @@ -105,6 +107,7 @@ FFMPEG_CORE_API wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const cha FFMPEG_CORE_API wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key); FFMPEG_CORE_API FfmpegCoreSettings* ffmpeg_core_init_settings(); FFMPEG_CORE_API int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume); +FFMPEG_CORE_API int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 2f76aca49..fda6e85f8 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -9,6 +9,7 @@ #include "loop.h" #include "decode.h" #include "filter.h" +#include "speed.h" #define CODEPAGE_SIZE 3 @@ -320,6 +321,23 @@ wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key) return nullptr; } +int send_reinit_filters(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + DWORD re = WaitForSingleObject(handle->mutex, INFINITE); + if (re == WAIT_OBJECT_0) { + handle->need_reinit_filters = 1; + } else { + return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + } + handle->have_err = 0; + ReleaseMutex(handle->mutex); + while (1) { + if (!handle->is_seek) break; + Sleep(10); + } + return handle->have_err ? handle->err : FFMPEG_CORE_ERR_OK; +} + FfmpegCoreSettings* ffmpeg_core_init_settings() { FfmpegCoreSettings* s = (FfmpegCoreSettings*)malloc(sizeof(FfmpegCoreSettings)); if (!s) return nullptr; @@ -342,17 +360,22 @@ int ffmpeg_core_set_volume(MusicHandle* handle, int volume) { if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; int r = ffmpeg_core_settings_set_volume(handle->s, volume); if (!r) return FFMPEG_CORE_ERR_FAILED_SET_VOLUME; - DWORD re = WaitForSingleObject(handle->mutex, INFINITE); - if (re == WAIT_OBJECT_0) { - handle->need_reinit_filters = 1; - } else { - return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - } - handle->have_err = 0; - ReleaseMutex(handle->mutex); - while (1) { - if (!handle->is_seek) break; - Sleep(10); + return send_reinit_filters(handle); +} + +int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed) { + if (!s) return 0; + int sp = get_speed(speed); + if (sp >= 63 && sp <= 16000) { + s->speed = sp / 1000.0; + return 1; } - return handle->have_err ? handle->err : FFMPEG_CORE_ERR_OK; + return 0; +} + +int ffmpeg_core_set_speed(MusicHandle* handle, float speed) { + if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; + int r = ffmpeg_core_settings_set_speed(handle->s, speed); + if (!r) return FFMPEG_CORE_ERR_FAILED_SET_SPEED; + return send_reinit_filters(handle); } diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index c6deaacbc..8177a1684 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -23,6 +23,9 @@ extern "C" { #ifndef min #define min(x,y) (((x) < (y)) ? (x) : (y)) #endif +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif #endif typedef struct MusicHandle { diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 692b68321..3ad75207e 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -56,10 +56,10 @@ int decode_audio(MusicHandle* handle, char* writed) { if (re >= 0) { if (handle->first_pts == INT64_MIN) { handle->first_pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - printf("first_pts: %s\n", av_ts2timestr(handle->first_pts, &AV_TIME_BASE_Q)); + av_log(NULL, AV_LOG_VERBOSE, "first_pts: %s\n", av_ts2timestr(handle->first_pts, &AV_TIME_BASE_Q)); } if (handle->set_new_pts) { - printf("pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); + av_log(NULL, AV_LOG_VERBOSE, "pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); handle->pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX) - handle->first_pts; handle->end_pts = handle->pts; handle->set_new_pts = 0; diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index 292a515ff..521769169 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -2,6 +2,7 @@ #include "output.h" #include "volume.h" +#include "speed.h" int need_filters(FfmpegCoreSettings* s) { if (!s) return 0; @@ -11,6 +12,9 @@ int need_filters(FfmpegCoreSettings* s) { if (s->volume != 100 && avfilter_get_by_name("volume")) { return 1; } + if (get_speed(s->speed) != 1000 && avfilter_get_by_name("atempo")) { + return 1; + } return 0; } @@ -18,6 +22,7 @@ int init_filters(MusicHandle* handle) { if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; if (!need_filters(handle->s)) return FFMPEG_CORE_ERR_OK; int re = FFMPEG_CORE_ERR_OK; + int speed = get_speed(handle->s->speed); if ((re = create_src_and_sink(&handle->graph, &handle->filter_inp, &handle->filter_out, handle))) { return re; } @@ -26,6 +31,15 @@ int init_filters(MusicHandle* handle) { return re; } } + if (speed != 1000 && avfilter_get_by_name("atempo")) { + int index = 0; + while (speed != 1000) { + if ((re = create_speed_filter(index, handle->graph, handle->filter_inp, &handle->filters, &speed))) { + return re; + } + index++; + } + } if (c_linked_list_count(handle->filters) == 0) { avfilter_graph_free(&handle->graph); handle->graph = NULL; @@ -63,14 +77,24 @@ int reinit_filters(MusicHandle* handle) { AVFilterGraph* graph = NULL; AVFilterContext* inc = NULL, * outc = NULL; c_linked_list* list = NULL; + int speed = get_speed(handle->s->speed); if ((re = create_src_and_sink(&graph, &inc, &outc, handle)) < 0) { goto end; } if (handle->s->volume != 100 && avfilter_get_by_name("volume")) { - if ((re = create_volume_filter(0, graph, inc, &list, handle->s->volume, handle->target_format)) < 0) { + if ((re = create_volume_filter(0, graph, inc, &list, handle->s->volume, handle->target_format))) { goto end; } } + if (speed != 1000 && avfilter_get_by_name("atempo")) { + int index = 0; + while (speed != 1000) { + if ((re = create_speed_filter(index, graph, inc, &list, &speed))) { + goto end; + } + index++; + } + } if (c_linked_list_count(list) == 0) { if (handle->graph) { DWORD r = WaitForSingleObject(handle->mutex, INFINITE); diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index 08f96e3da..62cf81e66 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -1,5 +1,7 @@ #include "output.h" +#include "speed.h" + int init_output(MusicHandle* handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; if (!handle->sdl_initialized) { @@ -108,7 +110,7 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { memset(stream, 0, len); goto end; } - samples_need_in = samples_need * handle->s->speed; + samples_need_in = samples_need * get_speed(handle->s->speed) / 1000; in->channels = handle->decoder->channels; in->channel_layout = handle->output_channel_layout; in->format = handle->target_format; diff --git a/ffmpeg_core/src/speed.c b/ffmpeg_core/src/speed.c new file mode 100644 index 000000000..2260f02b7 --- /dev/null +++ b/ffmpeg_core/src/speed.c @@ -0,0 +1,37 @@ +#include "speed.h" + +#include + +int get_speed(float speed) { + return roundf(speed * 1000); +} + +int create_speed_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int* speed) { + if (!graph || !speed || !src || !list) return FFMPEG_CORE_ERR_NULLPTR; + char args[64]; + char name[32]; + const AVFilter* atempo = avfilter_get_by_name("atempo"); + int speed_now = min(max(*speed, 500), 2000); + *speed = 1000 * (*speed) / speed_now; + snprintf(args, sizeof(args), "tempo=%.3f", speed_now / 1000.0); + snprintf(name, sizeof(name), "atempo%d", index); + int re = 0; + AVFilterContext* context = NULL; + if ((re = avfilter_graph_create_filter(&context, atempo, name, args, NULL, graph)) < 0) { + return re; + } + if (!c_linked_list_append(list, (void*)context)) { + return FFMPEG_CORE_ERR_OOM; + } + if (c_linked_list_count(*list) > 1) { + AVFilterContext* last = c_linked_list_tail(*list)->prev->d; + if ((re = avfilter_link(last, 0, context, 0)) < 0) { + return re; + } + } else { + if ((re = avfilter_link(src, 0, context, 0)) < 0) { + return re; + } + } + return FFMPEG_CORE_ERR_OK; +} diff --git a/ffmpeg_core/src/speed.h b/ffmpeg_core/src/speed.h new file mode 100644 index 000000000..fbe554a85 --- /dev/null +++ b/ffmpeg_core/src/speed.h @@ -0,0 +1,22 @@ +#ifndef _MUSICPLAYER2_SPEED_H +#define _MUSICPLAYER2_SPEED_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +/// 将speed转为1000为1.0倍的数字,例如1.5倍转为1500 +int get_speed(float speed); +/** + * @brief 创建 atempo Filter + * @param index Filter 序号 + * @param graph Graph + * @param src 输入 + * @param list Filters列表 + * @param speed 指向目标速度的指针,会返回还需要设置的速度 + * @return +*/ +int create_speed_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int* speed); +#if __cplusplus +} +#endif +#endif From e4befa60c90a3e05aae9a61baf83e4d5c6cbfdaf Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 4 Feb 2022 13:29:42 +0800 Subject: [PATCH 09/41] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=A2=91=E8=B0=B1=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 9 +++ MusicPlayer2/FfmpegCore.h | 2 + MusicPlayer2/Player.cpp | 6 ++ ffmpeg_core/CMakeLists.txt | 2 + ffmpeg_core/ffmpeg_core.h | 2 + ffmpeg_core/src/core.h | 2 + ffmpeg_core/src/fft_data.c | 116 ++++++++++++++++++++++++++++++++++++ ffmpeg_core/src/fft_data.h | 10 ++++ 8 files changed, 149 insertions(+) create mode 100644 ffmpeg_core/src/fft_data.c create mode 100644 ffmpeg_core/src/fft_data.h diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 47d7e4230..3016bfd5b 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -5,6 +5,7 @@ #include "Common.h" #include "MusicPlayer2.h" +#define ft2ts(t) (((size_t)t.dwHighDateTime << 32) | (size_t)t.dwLowDateTime) CFfmpegCore::CFfmpegCore() { handle = nullptr; @@ -245,6 +246,12 @@ void CFfmpegCore::ClearReverb() { } void CFfmpegCore::GetFFTData(float fft_data[FFT_SAMPLE]) { + if (handle) { + memset(fft_data, 0, FFT_SAMPLE); + ffmpeg_core_get_fft_data(handle, fft_data, FFT_SAMPLE); + } else { + memset(fft_data, 0, FFT_SAMPLE); + } } int CFfmpegCore::GetErrorCode() { @@ -298,6 +305,7 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_info_get_bitrate = (_ffmpeg_core_info_get_bitrate)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_bitrate"); ffmpeg_core_get_metadata = (_ffmpeg_core_get_metadata)::GetProcAddress(m_dll_module, "ffmpeg_core_get_metadata"); ffmpeg_core_info_get_metadata = (_ffmpeg_core_info_get_metadata)::GetProcAddress(m_dll_module, "ffmpeg_core_info_get_metadata"); + ffmpeg_core_get_fft_data = (_ffmpeg_core_get_fft_data)::GetProcAddress(m_dll_module, "ffmpeg_core_get_fft_data"); ffmpeg_core_init_settings = (_ffmpeg_core_init_settings)::GetProcAddress(m_dll_module, "ffmpeg_core_init_settings"); ffmpeg_core_settings_set_volume = (_ffmpeg_core_settings_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_volume"); ffmpeg_core_settings_set_speed = (_ffmpeg_core_settings_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_speed"); @@ -330,6 +338,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_info_get_bitrate != NULL); rtn &= (ffmpeg_core_get_metadata != NULL); rtn &= (ffmpeg_core_info_get_metadata != NULL); + rtn &= (ffmpeg_core_get_fft_data != NULL); rtn &= (ffmpeg_core_init_settings != NULL); rtn &= (ffmpeg_core_settings_set_volume != NULL); rtn &= (ffmpeg_core_settings_set_speed != NULL); diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index a79c3bc2d..ff8d4d518 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -35,6 +35,7 @@ typedef int64_t(*_ffmpeg_core_get_bitrate)(MusicHandle*); typedef int64_t(*_ffmpeg_core_info_get_bitrate)(MusicInfoHandle*); typedef wchar_t*(*_ffmpeg_core_get_metadata)(MusicHandle*, const char* key); typedef wchar_t*(*_ffmpeg_core_info_get_metadata)(MusicInfoHandle*, const char* key); +typedef int(*_ffmpeg_core_get_fft_data)(MusicHandle*, float*, int); typedef FfmpegCoreSettings*(*_ffmpeg_core_init_settings)(); typedef int(*_ffmpeg_core_settings_set_volume)(FfmpegCoreSettings*, int volume); typedef int(*_ffmpeg_core_settings_set_speed)(FfmpegCoreSettings*, float); @@ -119,6 +120,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_info_get_bitrate ffmpeg_core_info_get_bitrate = nullptr; _ffmpeg_core_get_metadata ffmpeg_core_get_metadata = nullptr; _ffmpeg_core_info_get_metadata ffmpeg_core_info_get_metadata = nullptr; + _ffmpeg_core_get_fft_data ffmpeg_core_get_fft_data = nullptr; _ffmpeg_core_init_settings ffmpeg_core_init_settings = nullptr; _ffmpeg_core_settings_set_volume ffmpeg_core_settings_set_volume = nullptr; _ffmpeg_core_settings_set_speed ffmpeg_core_settings_set_speed = nullptr; diff --git a/MusicPlayer2/Player.cpp b/MusicPlayer2/Player.cpp index 4ee343a35..cb05b2ba2 100644 --- a/MusicPlayer2/Player.cpp +++ b/MusicPlayer2/Player.cpp @@ -665,6 +665,12 @@ void CPlayer::CalculateSpectralData() CSpectralDataHelper::SpectralDataMapOld(m_fft, m_spectral_data); else m_spectrum_data_helper.SpectralDataMap(m_fft, m_spectral_data); + } else if (m_pCore->GetCoreType() == PT_FFMPEG) { + m_pCore->GetFFTData(m_fft); + if (theApp.m_app_setting_data.use_old_style_specturm) + CSpectralDataHelper::SpectralDataMapOld(m_fft, m_spectral_data); + else + m_spectrum_data_helper.SpectralDataMap(m_fft, m_spectral_data); } else { diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index b9a529723..ecaa5bce8 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -36,6 +36,8 @@ src/volume.h src/volume.c src/speed.h src/speed.c +src/fft_data.h +src/fft_data.c ) add_library(ffmpeg_core SHARED "${CORE_FILES}") diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 1e756a09c..24418f9dc 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -29,6 +29,7 @@ typedef struct FfmpegCoreSettings FfmpegCoreSettings; #define FFMPEG_CORE_ERR_NO_AUDIO 10 #define FFMPEG_CORE_ERR_FAILED_SET_VOLUME 11 #define FFMPEG_CORE_ERR_FAILED_SET_SPEED 12 +#define FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN 13 FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); @@ -105,6 +106,7 @@ FFMPEG_CORE_API int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle); */ FFMPEG_CORE_API wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key); FFMPEG_CORE_API wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key); +FFMPEG_CORE_API int ffmpeg_core_get_fft_data(MusicHandle* handle, float* fft_data, int len); FFMPEG_CORE_API FfmpegCoreSettings* ffmpeg_core_init_settings(); FFMPEG_CORE_API int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume); FFMPEG_CORE_API int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed); diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 8177a1684..a9bc13c2f 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -28,6 +28,8 @@ extern "C" { #endif #endif +#define FFT_SAMPLE 1024 + typedef struct MusicHandle { /// Demux 用 AVFormatContext* fmt; diff --git a/ffmpeg_core/src/fft_data.c b/ffmpeg_core/src/fft_data.c new file mode 100644 index 000000000..fd0d7c6ae --- /dev/null +++ b/ffmpeg_core/src/fft_data.c @@ -0,0 +1,116 @@ +#include "fft_data.h" + +#include +#include "libavcodec/avfft.h" + +int ffmpeg_core_get_fft_data(MusicHandle* handle, float* fft_data, int len) { + if (!handle || !fft_data) return FFMPEG_CORE_ERR_NULLPTR; + if (len > FFT_SAMPLE / 2) return FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN; + int cal_samples = FFT_SAMPLE; + AVFrame* f = av_frame_alloc(), * f2 = NULL; + RDFTContext* context = NULL; + SwrContext* swr = NULL; + DWORD re = 0; + int r = FFMPEG_CORE_ERR_OK; + int nbits = log2(cal_samples); + int total_samples = FFT_SAMPLE * 10; + float* datas = NULL; + int inv = cal_samples / len; + if (!f) { + return FFMPEG_CORE_ERR_OOM; + } + f->format = handle->target_format; + f->nb_samples = total_samples; + f->channel_layout = handle->output_channel_layout; + f->channels = handle->sdl_spec.channels; + if ((r = av_frame_get_buffer(f, 0)) < 0) { + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + if ((r = av_frame_make_writable(f)) < 0) { + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + re = WaitForSingleObject(handle->mutex, INFINITE); + if (re != WAIT_OBJECT_0) { + r = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + if (av_audio_fifo_size(handle->buffer) < total_samples) { + ReleaseMutex(handle->mutex); + r = FFMPEG_CORE_ERR_OK; + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + if ((r = av_audio_fifo_peek(handle->buffer, (void**)f->data, total_samples)) < 0) { + ReleaseMutex(handle->mutex); + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + ReleaseMutex(handle->mutex); + r = 0; + if (!(context = av_rdft_init(nbits, DFT_R2C))) { + r = FFMPEG_CORE_ERR_OOM; + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + if (f->format == AV_SAMPLE_FMT_FLT && f->channels == 1) { + for (int j = 0; j < 10; j++) { + av_rdft_calc(context, (FFTSample*)f->data[0] + (size_t)FFT_SAMPLE * j); + } + } else { + swr = swr_alloc_set_opts(NULL, av_get_default_channel_layout(1), AV_SAMPLE_FMT_FLT, handle->sdl_spec.freq, handle->output_channel_layout, handle->target_format, handle->sdl_spec.freq, 0, NULL); + if (!swr) { + r = FFMPEG_CORE_ERR_OOM; + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + if ((r = swr_init(swr)) < 0) { + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + f2 = av_frame_alloc(); + if (!f2) { + r = FFMPEG_CORE_ERR_OOM; + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + f2->format = AV_SAMPLE_FMT_FLT; + f2->channels = 1; + f2->channel_layout = av_get_default_channel_layout(1); + f2->nb_samples = total_samples; + if ((r = av_frame_get_buffer(f2, 0)) < 0) { + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + if ((r = av_frame_make_writable(f2)) < 0) { + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + if ((r = swr_convert(swr, f2->data, f2->nb_samples, f->data, f->nb_samples)) < 0) { + memset(fft_data, 0, sizeof(float) * len); + goto end; + } + r = 0; + for (int j = 0; j < 10; j++) + av_rdft_calc(context, (FFTSample*)f2->data[0] + (size_t)j * FFT_SAMPLE); + } + datas = f2 ? (float*)f2->data[0] : (float*)f->data[0]; + memset(fft_data, 0, sizeof(float) * len); + for (int j = 0; j < 10; j++) { + for (int i = 0; i < len; i++) { + if (i == 0) + fft_data[i] += datas[i + FFT_SAMPLE * j] / FFT_SAMPLE / 10; + else + fft_data[i] += datas[i + FFT_SAMPLE * j] / FFT_SAMPLE / 5; + } + } + r = 0; +end: + if (f) av_frame_free(&f); + if (context) av_rdft_end(context); + if (swr) swr_free(&swr); + if (f2) av_frame_free(&f2); + return r; +} diff --git a/ffmpeg_core/src/fft_data.h b/ffmpeg_core/src/fft_data.h new file mode 100644 index 000000000..8bf18050e --- /dev/null +++ b/ffmpeg_core/src/fft_data.h @@ -0,0 +1,10 @@ +#ifndef _MUSICPLAYER2_FFT_DATA_H +#define _MUSICPLAYER2_FFT_DATA_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +#if __cplusplus +} +#endif +#endif From 2ecfb3d17fda7b01a711a34972384bb60496361b Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sun, 6 Feb 2022 10:54:16 +0800 Subject: [PATCH 10/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/CommonData.h | 2 ++ MusicPlayer2/FfmpegCore.cpp | 17 +++++++++++++++++ MusicPlayer2/FfmpegCore.h | 5 +++++ MusicPlayer2/MusicPlayer2.rc | Bin 551010 -> 552024 bytes MusicPlayer2/MusicPlayerDlg.cpp | 2 ++ MusicPlayer2/OptionsDlg.cpp | 1 + MusicPlayer2/PlaySettingsDlg.cpp | 14 ++++++++++++++ MusicPlayer2/PlaySettingsDlg.h | 3 +++ MusicPlayer2/resource.h | 1 + ffmpeg_core/ffmpeg_core.h | 1 + ffmpeg_core/src/core.cpp | 7 +++++++ ffmpeg_core/src/core.h | 2 ++ ffmpeg_core/src/loop.c | 3 ++- 13 files changed, 57 insertions(+), 1 deletion(-) diff --git a/MusicPlayer2/CommonData.h b/MusicPlayer2/CommonData.h index f4d8dab7d..8b6c87019 100644 --- a/MusicPlayer2/CommonData.h +++ b/MusicPlayer2/CommonData.h @@ -300,6 +300,8 @@ struct PlaySettingData bool use_mci{ false }; //是否使用MCI内核 /// 是否使用ffmpeg内核 bool use_ffmpeg{ false }; + /// ffmpeg内核缓存时长(单位:s) + int ffmpeg_core_cache_length { 15 }; }; struct GlobalHotKeySettingData diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 3016bfd5b..387a305b4 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -44,6 +44,7 @@ void CFfmpegCore::InitCore() { CAudioCommon::m_all_surpported_extensions = format.extensions; settings = ffmpeg_core_init_settings(); ffmpeg_core_log_set_callback(LogCallback); + UpdateSettings(); } } @@ -309,6 +310,7 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_init_settings = (_ffmpeg_core_init_settings)::GetProcAddress(m_dll_module, "ffmpeg_core_init_settings"); ffmpeg_core_settings_set_volume = (_ffmpeg_core_settings_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_volume"); ffmpeg_core_settings_set_speed = (_ffmpeg_core_settings_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_speed"); + ffmpeg_core_settings_set_cache_length = (_ffmpeg_core_settings_set_cache_length)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_cache_length"); //жǷɹ rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); @@ -342,6 +344,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_init_settings != NULL); rtn &= (ffmpeg_core_settings_set_volume != NULL); rtn &= (ffmpeg_core_settings_set_speed != NULL); + rtn &= (ffmpeg_core_settings_set_cache_length != NULL); return rtn; } @@ -423,3 +426,17 @@ void CFfmpegCore::LogCallback(void* ptr, int level, const char* fmt, va_list vl) } FreeLibrary(re); } + +void CFfmpegCore::UpdateSettings(PlaySettingData* s) { + if (s) { + SetCacheLength(s->ffmpeg_core_cache_length); + } else { + SetCacheLength(theApp.m_play_setting_data.ffmpeg_core_cache_length); + } +} + +void CFfmpegCore::SetCacheLength(int cache_length) { + if (settings) { + ffmpeg_core_settings_set_cache_length(settings, cache_length); + } +} diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index ff8d4d518..a1007fb9c 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -1,6 +1,7 @@ #pragma once #include "IPlayerCore.h" #include "DllLib.h" +#include "CommonData.h" #define AV_LOG_ERROR 16 #define AV_LOG_VERBOSE 40 @@ -39,6 +40,7 @@ typedef int(*_ffmpeg_core_get_fft_data)(MusicHandle*, float*, int); typedef FfmpegCoreSettings*(*_ffmpeg_core_init_settings)(); typedef int(*_ffmpeg_core_settings_set_volume)(FfmpegCoreSettings*, int volume); typedef int(*_ffmpeg_core_settings_set_speed)(FfmpegCoreSettings*, float); +typedef int(*_ffmpeg_core_settings_set_cache_length)(FfmpegCoreSettings*, int); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -88,6 +90,8 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { unsigned short GetYear(MusicInfoHandle* h = nullptr); std::wstring GetTrack(MusicInfoHandle* h = nullptr); int GetTrackNum(MusicInfoHandle* h = nullptr); + void UpdateSettings(PlaySettingData* s = nullptr); + void SetCacheLength(int cache_length = 15); private: std::wstring GetMetadata(std::string key, MusicInfoHandle* h = nullptr); virtual bool GetFunction() override; @@ -124,6 +128,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_init_settings ffmpeg_core_init_settings = nullptr; _ffmpeg_core_settings_set_volume ffmpeg_core_settings_set_volume = nullptr; _ffmpeg_core_settings_set_speed ffmpeg_core_settings_set_speed = nullptr; + _ffmpeg_core_settings_set_cache_length ffmpeg_core_settings_set_cache_length = nullptr; MusicHandle* handle; FfmpegCoreSettings* settings = nullptr; std::wstring recent_file; diff --git a/MusicPlayer2/MusicPlayer2.rc b/MusicPlayer2/MusicPlayer2.rc index ad148f8206d3f76dad4c6d58c67f278e25ebaeb5..1fcd4a6ae9df945306c7685cdfe7f0b94ac01874 100644 GIT binary patch delta 454 zcmaEKLGi{7#SMNe(-WLoyrx^ZvSdxSO5>1gV%^5d7#G8A#9*{NF_DpxXY#sgj>&aJ zO4Da$GxBgLFj!<9j^EdvS3mtj1fv4K1%m;D4ucVcF%au87*9S}V#py}9})d6wSMYk zg=s3xmJG(z`O{eKr)T-I@@YCVI5Idhcrds!#54E+Nk0a6h7cfMhrxuw5=a^XNsvKC zlLNP@Pk#^uw0^==rO5}j$xLVCWffpFn7(l#qx|#*-b^~v4N6(Rw9m+61Y#y2X5Ky{ zkEP=Z(Ba0@fez2zKEauF758MneH_yZ_OPlkzIQBIVDIoMFeEeN zGZZnTGAJ+7brW#^ji zqRT2Uz3&%`%yv09RvvCqP6lTNe+EB>5QZQi?Zdzcg4+WFSzj_v&YLMV{euyc!sH1F aa?>ZAVr6M>3uOgjHXvr--WJMnNelqy&pT)U diff --git a/MusicPlayer2/MusicPlayerDlg.cpp b/MusicPlayer2/MusicPlayerDlg.cpp index 5e04576bf..712df8bd0 100644 --- a/MusicPlayer2/MusicPlayerDlg.cpp +++ b/MusicPlayer2/MusicPlayerDlg.cpp @@ -445,6 +445,7 @@ void CMusicPlayerDlg::SaveConfig() ini.WriteString(L"config", L"output_device", theApp.m_play_setting_data.output_device); ini.WriteBool(L"config", L"use_mci", theApp.m_play_setting_data.use_mci); ini.WriteBool(L"config", L"use_ffmpeg", theApp.m_play_setting_data.use_ffmpeg); + ini.WriteInt(L"config", L"ffmpeg_core_cache_length", theApp.m_play_setting_data.ffmpeg_core_cache_length); ini.WriteInt(L"config", L"UI_selected", GetUiSelected()); //保存热键设置 @@ -618,6 +619,7 @@ void CMusicPlayerDlg::LoadConfig() theApp.m_play_setting_data.output_device = ini.GetString(L"config", L"output_device", L""); theApp.m_play_setting_data.use_mci = ini.GetBool(L"config", L"use_mci", false); theApp.m_play_setting_data.use_ffmpeg = ini.GetBool(L"config", L"use_ffmpeg", false); + theApp.m_play_setting_data.ffmpeg_core_cache_length = ini.GetInt(L"config", L"ffmpeg_core_cache_length", 15); int ui_selected = ini.GetInt(L"config", L"UI_selected", 1); SelectUi(ui_selected); diff --git a/MusicPlayer2/OptionsDlg.cpp b/MusicPlayer2/OptionsDlg.cpp index d993adbc9..737b703e8 100644 --- a/MusicPlayer2/OptionsDlg.cpp +++ b/MusicPlayer2/OptionsDlg.cpp @@ -104,6 +104,7 @@ void COptionsDlg::OnOK() m_tab1_dlg.OnOK(); m_tab2_dlg.OnOK(); m_tab3_dlg.OnOK(); + m_tab4_dlg.OnOK(); m_media_lib_dlg.OnOK(); CBaseDialog::OnOK(); diff --git a/MusicPlayer2/PlaySettingsDlg.cpp b/MusicPlayer2/PlaySettingsDlg.cpp index f8010565f..67365d505 100644 --- a/MusicPlayer2/PlaySettingsDlg.cpp +++ b/MusicPlayer2/PlaySettingsDlg.cpp @@ -5,6 +5,7 @@ #include "MusicPlayer2.h" #include "PlaySettingsDlg.h" #include "afxdialogex.h" +#include "FfmpegCore.h" // CPlaySettingsDlg 对话框 @@ -35,6 +36,7 @@ void CPlaySettingsDlg::DoDataExchange(CDataExchange* pDX) DDX_Control(pDX, IDC_BASS_RADIO, m_bass_radio); DDX_Control(pDX, IDC_MCI_RADIO, m_mci_radio); DDX_Control(pDX, IDC_FFMPEG_RADIO, m_ffmpeg_radio); + DDX_Control(pDX, IDC_FFMPEG_CACHE_LENGTH, m_ffmpeg_cache_length); } void CPlaySettingsDlg::ShowDeviceInfo() @@ -172,8 +174,12 @@ BOOL CPlaySettingsDlg::OnInitDialog() EnableControl(); + m_ffmpeg_cache_length.SetRange(1, 60); + m_ffmpeg_cache_length.SetValue(theApp.m_play_setting_data.ffmpeg_core_cache_length); + //设置控件不响应鼠标滚轮消息 m_output_device_combo.SetMouseWheelEnable(false); + m_ffmpeg_cache_length.SetMouseWheelEnable(false); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE @@ -260,3 +266,11 @@ BOOL CPlaySettingsDlg::PreTranslateMessage(MSG* pMsg) return CTabDlg::PreTranslateMessage(pMsg); } + +void CPlaySettingsDlg::OnOK() { + m_data.ffmpeg_core_cache_length = m_ffmpeg_cache_length.GetValue(); + if (CPlayer::GetInstance().IsFfmpegCore()) { + auto core = (CFfmpegCore*)CPlayer::GetInstance().GetPlayerCore(); + core->UpdateSettings(&m_data); + } +} diff --git a/MusicPlayer2/PlaySettingsDlg.h b/MusicPlayer2/PlaySettingsDlg.h index 2ded5505c..eb71a2b16 100644 --- a/MusicPlayer2/PlaySettingsDlg.h +++ b/MusicPlayer2/PlaySettingsDlg.h @@ -2,6 +2,7 @@ #include "ListCtrlEx.h" #include "TabDlg.h" #include "MyComboBox.h" +#include "SpinEdit.h" // CPlaySettingsDlg 对话框 @@ -32,6 +33,7 @@ class CPlaySettingsDlg : public CTabDlg CButton m_bass_radio; CButton m_mci_radio; CButton m_ffmpeg_radio; + CSpinEdit m_ffmpeg_cache_length; CToolTipCtrl m_toolTip; protected: @@ -42,6 +44,7 @@ class CPlaySettingsDlg : public CTabDlg DECLARE_MESSAGE_MAP() public: + virtual void OnOK(); virtual BOOL OnInitDialog(); afx_msg void OnBnClickedStopWhenError(); afx_msg void OnBnClickedShowTaskbarProgress(); diff --git a/MusicPlayer2/resource.h b/MusicPlayer2/resource.h index 1f123d04d..9d47c553d 100644 --- a/MusicPlayer2/resource.h +++ b/MusicPlayer2/resource.h @@ -1087,6 +1087,7 @@ #define IDC_SAVE_TO_APPDATA_RADIO 1188 #define IDC_SAVE_TO_PROGRAM_DIR_RADIO 1189 #define IDC_FFMPEG_RADIO 1190 +#define IDC_FFMPEG_CACHE_LENGTH 1191 #define ID_32771 32771 #define ID_32772 32772 #define ID_OPEN 32773 diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 24418f9dc..61bdbd109 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -110,6 +110,7 @@ FFMPEG_CORE_API int ffmpeg_core_get_fft_data(MusicHandle* handle, float* fft_dat FFMPEG_CORE_API FfmpegCoreSettings* ffmpeg_core_init_settings(); FFMPEG_CORE_API int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume); FFMPEG_CORE_API int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed); +FFMPEG_CORE_API int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index fda6e85f8..5a6bad4f6 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -344,6 +344,7 @@ FfmpegCoreSettings* ffmpeg_core_init_settings() { memset(s, 0, sizeof(FfmpegCoreSettings)); s->speed = 1.0; s->volume = 100; + s->cache_length = 15; return s; } @@ -379,3 +380,9 @@ int ffmpeg_core_set_speed(MusicHandle* handle, float speed) { if (!r) return FFMPEG_CORE_ERR_FAILED_SET_SPEED; return send_reinit_filters(handle); } + +int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length) { + if (!s) return 0; + if (length >= 1 && length <= 60) s->cache_length = length; + return 1; +} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index a9bc13c2f..56c547923 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -105,6 +105,8 @@ typedef struct FfmpegCoreSettings { int volume; /// 速度 float speed; +/// 缓存长度(单位s) +int cache_length; } FfmpegCoreSettings; #if __cplusplus } diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index 365d862a1..0459fad12 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -50,7 +50,7 @@ DWORD WINAPI event_loop(LPVOID handle) { char doing = 0; /// 是否往缓冲区加了数据 char writed = 0; - int buffered_size = h->sdl_spec.freq * 15; + int buffered_size = h->sdl_spec.freq * h->s->cache_length; while (1) { doing = 0; if (h->stoping) break; @@ -74,6 +74,7 @@ DWORD WINAPI event_loop(LPVOID handle) { goto end; } if (!h->is_eof) { + buffered_size = h->sdl_spec.freq * h->s->cache_length; if (av_audio_fifo_size(h->buffer) < buffered_size) { int re = decode_audio(handle, &writed); if (re) { From 7848cded603945d352a50b813287415f783b7474 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sun, 6 Feb 2022 11:25:24 +0800 Subject: [PATCH 11/41] =?UTF-8?q?=E5=A6=82=E6=9E=9C=E6=B5=81=E6=AF=94?= =?UTF-8?q?=E7=89=B9=E7=8E=87=E4=B8=8D=E5=8F=AF=E7=94=A8=EF=BC=8C=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E6=95=B4=E4=B8=AA=E6=96=87=E4=BB=B6=E7=9A=84=E6=AF=94?= =?UTF-8?q?=E7=89=B9=E7=8E=87=20=E8=BF=94=E5=9B=9E=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E7=9A=84=E4=BD=8D=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/core.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 5a6bad4f6..24819390a 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -243,22 +243,26 @@ int ffmpeg_core_is_playing(MusicHandle* handle) { int ffmpeg_core_get_bits(MusicHandle* handle) { if (!handle || !handle->decoder) return -1; - return handle->decoder->bits_per_coded_sample; + return handle->decoder->bits_per_raw_sample; } int ffmpeg_core_info_get_bits(MusicInfoHandle* handle) { if (!handle || !handle->is) return -1; - return handle->is->codecpar->bits_per_coded_sample; + return handle->is->codecpar->bits_per_raw_sample; } int64_t ffmpeg_core_get_bitrate(MusicHandle* handle) { if (!handle || !handle->decoder) return -1; - return handle->decoder->bit_rate; + if (handle->decoder->bit_rate) return handle->decoder->bit_rate; + if (handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; + return 0; } int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle) { if (!handle || !handle->is) return -1; - return handle->is->codecpar->bit_rate; + if (handle->is->codecpar->bit_rate > 0) return handle->is->codecpar->bit_rate; + if (handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; + return 0; } std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags) { From e953ba914402dd0b8083273d4ac910ced742903b Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sun, 6 Feb 2022 13:32:49 +0800 Subject: [PATCH 12/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 25 +++++++++++-- MusicPlayer2/FfmpegCore.h | 6 ++++ ffmpeg_core/ffmpeg_core.h | 13 +++++++ ffmpeg_core/src/core.cpp | 72 +++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 387a305b4..2dbd79d49 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -93,6 +93,7 @@ void CFfmpegCore::Close() { if (free_music_handle && handle) { free_music_handle(handle); handle = nullptr; + err = 0; } } @@ -256,15 +257,23 @@ void CFfmpegCore::GetFFTData(float fft_data[FFT_SAMPLE]) { } int CFfmpegCore::GetErrorCode() { - return err; + if (err) return err; + if (handle) return ffmpeg_core_get_error(handle); + return 0; } std::wstring CFfmpegCore::GetErrorInfo(int error_code) { + auto tmp = ffmpeg_core_get_err_msg(error_code); + if (tmp) { + std::wstring re(tmp); + free(tmp); + return re; + } return L""; } std::wstring CFfmpegCore::GetErrorInfo() { - return L""; + return GetErrorInfo(GetErrorCode()); } PlayerCoreType CFfmpegCore::GetCoreType() { @@ -291,6 +300,9 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_seek = (_ffmpeg_core_seek)::GetProcAddress(m_dll_module, "ffmpeg_core_seek"); ffmpeg_core_set_volume = (_ffmpeg_core_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_set_volume"); ffmpeg_core_set_speed = (_ffmpeg_core_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_set_speed"); + ffmpeg_core_get_error = (_ffmpeg_core_get_error)::GetProcAddress(m_dll_module, "ffmpeg_core_get_error"); + ffmpeg_core_get_err_msg = (_ffmpeg_core_get_err_msg)::GetProcAddress(m_dll_module, "ffmpeg_core_get_err_msg"); + ffmpeg_core_get_err_msg2 = (_ffmpeg_core_get_err_msg2)::GetProcAddress(m_dll_module, "ffmpeg_core_get_err_msg2"); ffmpeg_core_get_cur_position = (_ffmpeg_core_get_cur_position)::GetProcAddress(m_dll_module, "ffmpeg_core_get_cur_position"); ffmpeg_core_song_is_over = (_ffmpeg_core_song_is_over)::GetProcAddress(m_dll_module, "ffmpeg_core_song_is_over"); ffmpeg_core_get_song_length = (_ffmpeg_core_get_song_length)::GetProcAddress(m_dll_module, "ffmpeg_core_get_song_length"); @@ -325,6 +337,9 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_seek != NULL); rtn &= (ffmpeg_core_set_volume != NULL); rtn &= (ffmpeg_core_set_speed != NULL); + rtn &= (ffmpeg_core_get_error != NULL); + rtn &= (ffmpeg_core_get_err_msg != NULL); + rtn &= (ffmpeg_core_get_err_msg2 != NULL); rtn &= (ffmpeg_core_get_cur_position != NULL); rtn &= (ffmpeg_core_song_is_over != NULL); rtn &= (ffmpeg_core_get_song_length != NULL); @@ -422,6 +437,12 @@ void CFfmpegCore::LogCallback(void* ptr, int level, const char* fmt, va_list vl) if (re > 0) { std::wstring s = CCommon::StrToUnicode(std::string(buf, re), CodeType::UTF8); OutputDebugStringW(s.c_str()); + if (level <= AV_LOG_ERROR) { + if (s.back() == '\n') { + s.pop_back(); + } + theApp.WriteLog(s); + } } } FreeLibrary(re); diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index a1007fb9c..74d70bb63 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -21,6 +21,9 @@ typedef int(*_ffmpeg_core_pause)(MusicHandle*); typedef int(*_ffmpeg_core_seek)(MusicHandle*, int64_t); typedef int(*_ffmpeg_core_set_volume)(MusicHandle*, int); typedef int(*_ffmpeg_core_set_speed)(MusicHandle*, float); +typedef int(*_ffmpeg_core_get_error)(MusicHandle*); +typedef wchar_t*(*_ffmpeg_core_get_err_msg)(int); +typedef const wchar_t*(*_ffmpeg_core_get_err_msg2)(int); typedef int64_t(*_ffmpeg_core_get_cur_position)(MusicHandle*); typedef int(*_ffmpeg_core_song_is_over)(MusicHandle*); typedef int64_t(*_ffmpeg_core_get_song_length)(MusicHandle*); @@ -109,6 +112,9 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_seek ffmpeg_core_seek = nullptr; _ffmpeg_core_set_volume ffmpeg_core_set_volume = nullptr; _ffmpeg_core_set_speed ffmpeg_core_set_speed = nullptr; + _ffmpeg_core_get_error ffmpeg_core_get_error = nullptr; + _ffmpeg_core_get_err_msg ffmpeg_core_get_err_msg = nullptr; + _ffmpeg_core_get_err_msg2 ffmpeg_core_get_err_msg2 = nullptr; _ffmpeg_core_get_cur_position ffmpeg_core_get_cur_position = nullptr; _ffmpeg_core_song_is_over ffmpeg_core_song_is_over = nullptr; _ffmpeg_core_get_song_length ffmpeg_core_get_song_length = nullptr; diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 61bdbd109..2b858ddf8 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -45,6 +45,19 @@ FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_seek(MusicHandle* handle, int64_t time); FFMPEG_CORE_API int ffmpeg_core_set_volume(MusicHandle* handle, int volume); FFMPEG_CORE_API int ffmpeg_core_set_speed(MusicHandle* handle, float speed); +FFMPEG_CORE_API int ffmpeg_core_get_error(MusicHandle* handle); +/** + * @brief 返回错误代码对应的错误消息 + * @param err 错误代码 + * @return 错误消息,需要调用free释放内存 +*/ +FFMPEG_CORE_API wchar_t* ffmpeg_core_get_err_msg(int err); +/** + * @brief 返回错误代码对应的错误消息 + * @param err 错误代码(仅处理>=0的错误) + * @return 错误消息 +*/ +FFMPEG_CORE_API const wchar_t* ffmpeg_core_get_err_msg2(int err); /** * @brief 获取当前播放位置 * @param handle Handle diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 24819390a..94670c1b4 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -390,3 +390,75 @@ int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length) { if (length >= 1 && length <= 60) s->cache_length = length; return 1; } + +int ffmpeg_core_get_error(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + return handle->err; +} + +const wchar_t* ffmpeg_core_get_err_msg2(int err) { + if (err < 0) return L"An error occured in ffmpeg."; + switch (err) { + case FFMPEG_CORE_ERR_OK: + return L"No error occured."; + case FFMPEG_CORE_ERR_NULLPTR: + return L"Got an unexpected null pointer."; + case FFMPEG_CORE_ERR_INVAILD_NAME: + return L"URI contains invalid chars."; + case FFMPEG_CORE_ERR_OOM: + return L"Out of memory."; + case FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER: + return L"No audio tracks in file or decoder is not available."; + case FFMPEG_CORE_ERR_UNKNOWN_SAMPLE_FMT: + return L"The format of audio sample is not available."; + case FFMPEG_CORE_ERR_SDL: + return L"An error occured in SDL."; + case FFMPEG_CORE_ERR_FAILED_CREATE_THREAD: + return L"Failed to create new thread."; + case FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX: + return L"Failed to creare new mutex."; + case FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED: + return L"Failed to wait mutex."; + case FFMPEG_CORE_ERR_NO_AUDIO: + return L"No audio tracks in file."; + case FFMPEG_CORE_ERR_FAILED_SET_VOLUME: + return L"Failed to set volume."; + case FFMPEG_CORE_ERR_FAILED_SET_SPEED: + return L"Failed to set speed."; + case FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN: + return L"FFT data's length is too big."; + default: + return L"Unknown error."; + } +} + +wchar_t* ffmpeg_core_get_err_msg(int err) { + if (err < 0) { + char msg[AV_ERROR_MAX_STRING_SIZE]; + std::wstring wmsg; + av_make_error_string(msg, AV_ERROR_MAX_STRING_SIZE, err); + if (wchar_util::str_to_wstr(wmsg, msg, CP_UTF8)) { + wchar_t* tmp = nullptr; + if (cpp2c::string2char(wmsg, tmp)) { + return tmp; + } + } + } else if (err == FFMPEG_CORE_ERR_SDL) { + char msg[128]; + std::wstring wmsg; + SDL_GetErrorMsg(msg, 128); + if (wchar_util::str_to_wstr(wmsg, msg, CP_UTF8)) { + wchar_t* tmp = nullptr; + if (cpp2c::string2char(wmsg, tmp)) { + return tmp; + } + } + } else { + std::wstring wmsg = ffmpeg_core_get_err_msg2(err); + wchar_t* tmp = nullptr; + if (cpp2c::string2char(wmsg, tmp)) { + return tmp; + } + } + return nullptr; +} From efb8fd1e0a85eca3de69cc4bab6dbd6dbc3e42b5 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Mon, 7 Feb 2022 10:32:47 +0800 Subject: [PATCH 13/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dflac=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=A7=A3=E7=A0=81=E5=AE=8C=E4=B9=8B=E5=90=8E=EF=BC=8C=E7=BB=A7?= =?UTF-8?q?=E7=BB=AD=E8=B0=83=E7=94=A8=E8=A7=A3=E7=A0=81=E5=99=A8=E4=BC=9A?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=8A=A5=E9=94=99=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/decode.c | 23 +++++++++++++++++++++++ ffmpeg_core/src/decode.h | 1 + ffmpeg_core/src/loop.c | 8 ++++++++ 3 files changed, 32 insertions(+) diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 3ad75207e..abe1a2389 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -24,6 +24,29 @@ int open_decoder(MusicHandle* handle) { return FFMPEG_CORE_ERR_OK; } +int reopen_decoder(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + if (handle->decoder) { + avcodec_free_context(&handle->decoder); + } + handle->decoder = avcodec_alloc_context3(handle->codec); + if (!handle->decoder) return FFMPEG_CORE_ERR_OOM; + int re = 0; + // 从输入流复制参数 + if ((re = avcodec_parameters_to_context(handle->decoder, handle->is->codecpar)) < 0) { + return re; + } + if (handle->decoder->channel_layout == 0) { + // 如果未设置,设置为默认值 + handle->decoder->channel_layout = av_get_default_channel_layout(handle->decoder->channels); + } + // 打开解码器 + if ((re = avcodec_open2(handle->decoder, handle->codec, NULL)) < 0) { + return re; + } + return FFMPEG_CORE_ERR_OK; +} + int decode_audio(MusicHandle* handle, char* writed) { if (!handle | !writed) return FFMPEG_CORE_ERR_NULLPTR; AVPacket pkt; diff --git a/ffmpeg_core/src/decode.h b/ffmpeg_core/src/decode.h index 23f629fc9..9ea9df360 100644 --- a/ffmpeg_core/src/decode.h +++ b/ffmpeg_core/src/decode.h @@ -5,6 +5,7 @@ extern "C" { #endif #include "core.h" int open_decoder(MusicHandle* handle); +int reopen_decoder(MusicHandle* handle); /** * @brief 解码 * @param handle Handle diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index 0459fad12..e178d797e 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -24,6 +24,14 @@ int seek_to_pos(MusicHandle* handle) { } else { // 不在缓冲区,调用av_seek_frame并清空缓冲区 int flags = 0; + // 修复flac文件解码完之后,继续调用解码器会导致报错的BUG + if (handle->is_eof && handle->is->codecpar->codec_id == AV_CODEC_ID_FLAC) { + av_log(NULL, AV_LOG_VERBOSE, "Try to reopen decoder \"%s\".\n", handle->codec->name ? handle->codec->name : "(null)"); + if ((re = reopen_decoder(handle))) { + ReleaseMutex(handle->mutex); + goto end; + } + } if (handle->seek_pos < handle->end_pts) { flags |= AVSEEK_FLAG_BACKWARD; } From 2fb4dde27e1c7f8aaea42b493b08c7ff41aa985d Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 8 Feb 2022 11:56:30 +0800 Subject: [PATCH 14/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/core.cpp | 6 ++++++ ffmpeg_core/src/decode.c | 4 ++++ ffmpeg_core/src/filter.c | 11 +++++++++++ ffmpeg_core/src/loop.c | 3 +++ ffmpeg_core/src/open.c | 4 ++++ ffmpeg_core/src/output.c | 5 +++++ ffmpeg_core/src/speed.c | 4 ++++ ffmpeg_core/src/volume.c | 4 ++++ 8 files changed, 41 insertions(+) diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 94670c1b4..52a75352d 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -84,6 +84,7 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s MusicHandle* handle = (MusicHandle*)malloc(sizeof(MusicHandle)); int re = FFMPEG_CORE_ERR_OK; if (!handle) { + av_log(NULL, AV_LOG_FATAL, "Failed to allocate MusicHandle.\n"); return FFMPEG_CORE_ERR_OOM; } memset(handle, 0, sizeof(MusicHandle)); @@ -93,6 +94,7 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s handle->settings_is_alloc = 1; handle->s = ffmpeg_core_init_settings(); if (!handle->s) { + av_log(NULL, AV_LOG_FATAL, "Failed to allocate settings struct.\n"); re = FFMPEG_CORE_ERR_OOM; goto end; } @@ -101,7 +103,11 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s if ((re = open_input(handle, u.c_str()))) { goto end; } +#ifndef NDEBUG + av_dump_format(handle->fmt, 0, u.c_str(), 0); +#endif if ((re = find_audio_stream(handle))) { + av_log(NULL, AV_LOG_FATAL, "Failed to find suitable audio stream.\n"); goto end; } if ((re = open_decoder(handle))) { diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index abe1a2389..4d0c0e3a2 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -11,6 +11,7 @@ int open_decoder(MusicHandle* handle) { int re = 0; // 从输入流复制参数 if ((re = avcodec_parameters_to_context(handle->decoder, handle->is->codecpar)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to copy parameters from input stream: %s (%i)\n", av_err2str(re), re); return re; } if (handle->decoder->channel_layout == 0) { @@ -19,6 +20,7 @@ int open_decoder(MusicHandle* handle) { } // 打开解码器 if ((re = avcodec_open2(handle->decoder, handle->codec, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to open decoder \"%s\": %s (%i)\n", handle->codec->name, av_err2str(re), re); return re; } return FFMPEG_CORE_ERR_OK; @@ -34,6 +36,7 @@ int reopen_decoder(MusicHandle* handle) { int re = 0; // 从输入流复制参数 if ((re = avcodec_parameters_to_context(handle->decoder, handle->is->codecpar)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to copy parameters from input stream: %s (%i)\n", av_err2str(re), re); return re; } if (handle->decoder->channel_layout == 0) { @@ -42,6 +45,7 @@ int reopen_decoder(MusicHandle* handle) { } // 打开解码器 if ((re = avcodec_open2(handle->decoder, handle->codec, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to open decoder \"%s\": %s (%i)\n", handle->codec->name, av_err2str(re), re); return re; } return FFMPEG_CORE_ERR_OK; diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index 521769169..d883a72cf 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -49,9 +49,11 @@ int init_filters(MusicHandle* handle) { } AVFilterContext* last = c_linked_list_tail(handle->filters)->d; if ((re = avfilter_link(last, 0, handle->filter_out, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, handle->filter_out->name, 0, av_err2str(re), re); return re; } if ((re = avfilter_graph_config(handle->graph, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to check config of filters: %s (%i)\n", av_err2str(re), re); return re; } return FFMPEG_CORE_ERR_OK; @@ -114,9 +116,11 @@ int reinit_filters(MusicHandle* handle) { } AVFilterContext* last = c_linked_list_tail(list)->d; if ((re = avfilter_link(last, 0, outc, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, outc->name, 0, av_err2str(re), re); goto end; } if ((re = avfilter_graph_config(graph, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to check config of filters: %s (%i)\n", av_err2str(re), re); goto end; } DWORD r = WaitForSingleObject(handle->mutex, INFINITE); @@ -149,6 +153,7 @@ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterCo if (!graph || !src || !sink) return FFMPEG_CORE_ERR_NULLPTR; const AVFilter* buffersink = avfilter_get_by_name("abuffersink"), * buffer = avfilter_get_by_name("abuffer"); if (!(*graph = avfilter_graph_alloc())) { + av_log(NULL, AV_LOG_FATAL, "Failed to allocate filter graph.\n"); return FFMPEG_CORE_ERR_OOM; } int re = 0; @@ -159,9 +164,11 @@ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterCo av_get_channel_layout_string(channel_layout, sizeof(channel_layout), handle->decoder->channels, layout); snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%d", handle->is->time_base.num, handle->is->time_base.den, handle->sdl_spec.freq, av_get_sample_fmt_name(handle->target_format), channel_layout, handle->decoder->channels); if ((re = avfilter_graph_create_filter(src, buffer, "in", args, NULL, *graph)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to create input filter: %s (%i)\n", av_err2str(re), re); return re; } if ((re = avfilter_graph_create_filter(sink, buffersink, "out", NULL, NULL, *graph)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to create output filter: %s (%i)\n", av_err2str(re), re); return re; } // 输出设置 @@ -169,18 +176,22 @@ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterCo // 具体类型参考 ffmpeg 源代码 libavfilter/buffersink.c 里的 abuffersink_options enum AVSampleFormat sample_fmts[2] = { handle->target_format , AV_SAMPLE_FMT_NONE }; if ((re = av_opt_set_int_list(*sink, "sample_fmts", sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to set sample_fmts to output filter: %s (%i)\n", av_err2str(re), re); return re; } int sample_rates[2] = { handle->sdl_spec.freq , 0 }; if ((re = av_opt_set_int_list(*sink, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to set sample_rates to output filter: %s (%i)\n", av_err2str(re), re); return re; } int64_t channel_layouts[2] = { layout , 0 }; if ((re = av_opt_set_int_list(*sink, "channel_layouts", channel_layouts, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to set channel_layouts to output filter: %s (%i)\n", av_err2str(re), re); return re; } int channel_counts[2] = { handle->decoder->channels, 0 }; if ((re = av_opt_set_int_list(*sink, "channel_counts", channel_counts, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to set channel_counts to output filter: %s (%i)\n", av_err2str(re), re); return re; } return FFMPEG_CORE_ERR_OK; diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index e178d797e..67e29a770 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -1,5 +1,6 @@ #include "loop.h" +#include #include "decode.h" #include "filter.h" @@ -16,6 +17,7 @@ int seek_to_pos(MusicHandle* handle) { // 已经在缓冲区,直接从缓冲区移除不需要的数据 int64_t samples = min(av_rescale_q_rnd(handle->seek_pos - handle->pts, AV_TIME_BASE_Q, base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX), av_audio_fifo_size(handle->buffer)); if ((re = av_audio_fifo_drain(handle->buffer, samples)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to drain %" PRIi64 " samples in buffer: %s (%i)\n", samples, av_err2str(re), re); ReleaseMutex(handle->mutex); goto end; } @@ -36,6 +38,7 @@ int seek_to_pos(MusicHandle* handle) { flags |= AVSEEK_FLAG_BACKWARD; } if ((re = av_seek_frame(handle->fmt, -1, handle->seek_pos + handle->first_pts, flags)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to seek frame %" PRIi64 ": %s (%i)\n", handle->seek_pos + handle->first_pts, av_err2str(re), re); ReleaseMutex(handle->mutex); goto end; } diff --git a/ffmpeg_core/src/open.c b/ffmpeg_core/src/open.c index 6ad4fb832..28f6aafac 100644 --- a/ffmpeg_core/src/open.c +++ b/ffmpeg_core/src/open.c @@ -4,9 +4,11 @@ int open_input(MusicHandle* handle, const char* url) { if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; int re = 0; if ((re = avformat_open_input(&handle->fmt, url, NULL, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", url, av_err2str(re), re); return re; } if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", url, av_err2str(re), re); return re; } // handle->fmt->flags |= AVFMT_FLAG_FAST_SEEK; // 允许快速定位 @@ -17,9 +19,11 @@ int open_input2(MusicInfoHandle* handle, const char* url) { if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; int re = 0; if ((re = avformat_open_input(&handle->fmt, url, NULL, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", url, av_err2str(re), re); return re; } if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", url, av_err2str(re), re); return re; } return FFMPEG_CORE_ERR_OK; diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index 62cf81e66..12ba8d6b4 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -12,6 +12,8 @@ int init_output(MusicHandle* handle) { sdl_spec.freq = handle->decoder->sample_rate; sdl_spec.format = convert_to_sdl_format(handle->decoder->sample_fmt); if (!sdl_spec.format) { + const char* tmp = av_get_sample_fmt_name(handle->decoder->sample_fmt); + av_log(NULL, AV_LOG_FATAL, "Unknown sample format: %s (%i)\n", tmp ? tmp : "", handle->decoder->sample_fmt); return FFMPEG_CORE_ERR_UNKNOWN_SAMPLE_FMT; } sdl_spec.channels = handle->decoder->channels; @@ -21,12 +23,14 @@ int init_output(MusicHandle* handle) { memcpy(&handle->sdl_spec, &sdl_spec, sizeof(SDL_AudioSpec)); handle->device_id = SDL_OpenAudioDevice(NULL, 0, &sdl_spec, &handle->sdl_spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (!handle->device_id) { + av_log(NULL, AV_LOG_FATAL, "Failed to open audio device \"%s\": %s\n", "default", SDL_GetError()); return FFMPEG_CORE_ERR_SDL; } enum AVSampleFormat target_format = convert_to_sdl_supported_format(handle->decoder->sample_fmt); handle->output_channel_layout = get_sdl_channel_layout(handle->decoder->channels); handle->swrac = swr_alloc_set_opts(NULL, handle->output_channel_layout, target_format, handle->sdl_spec.freq, handle->decoder->channel_layout, handle->decoder->sample_fmt, handle->decoder->sample_rate, 0, NULL); if (!handle->swrac) { + av_log(NULL, AV_LOG_FATAL, "Failed to allocate resample context.\n"); return FFMPEG_CORE_ERR_OOM; } int re = 0; @@ -34,6 +38,7 @@ int init_output(MusicHandle* handle) { return re; } if (!(handle->buffer = av_audio_fifo_alloc(target_format, handle->decoder->channels, 1))) { + av_log(NULL, AV_LOG_FATAL, "Failed to allocate buffer.\n"); return FFMPEG_CORE_ERR_OOM; } handle->target_format = target_format; diff --git a/ffmpeg_core/src/speed.c b/ffmpeg_core/src/speed.c index 2260f02b7..4a1b9b4f7 100644 --- a/ffmpeg_core/src/speed.c +++ b/ffmpeg_core/src/speed.c @@ -18,18 +18,22 @@ int create_speed_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c int re = 0; AVFilterContext* context = NULL; if ((re = avfilter_graph_create_filter(&context, atempo, name, args, NULL, graph)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to create speed filter \"%s\": %s (%i)\n", name, av_err2str(re), re); return re; } if (!c_linked_list_append(list, (void*)context)) { + av_log(NULL, AV_LOG_FATAL, "Failed to append filter \"%s\" to list.\n", context->name); return FFMPEG_CORE_ERR_OOM; } if (c_linked_list_count(*list) > 1) { AVFilterContext* last = c_linked_list_tail(*list)->prev->d; if ((re = avfilter_link(last, 0, context, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, context->name, 0, av_err2str(re), re); return re; } } else { if ((re = avfilter_link(src, 0, context, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", src->name, 0, context->name, 0, av_err2str(re), re); return re; } } diff --git a/ffmpeg_core/src/volume.c b/ffmpeg_core/src/volume.c index 2d77cfd4d..9e67c8498 100644 --- a/ffmpeg_core/src/volume.c +++ b/ffmpeg_core/src/volume.c @@ -29,18 +29,22 @@ int create_volume_filter(int index, AVFilterGraph* graph, AVFilterContext* src, int re = 0; AVFilterContext* context = NULL; if ((re = avfilter_graph_create_filter(&context, vol, name, args, NULL, graph)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to create volume filter \"%s\": %s (%i)\n", name, av_err2str(re), re); return re; } if (!c_linked_list_append(list, (void*)context)) { + av_log(NULL, AV_LOG_FATAL, "Failed to append filter \"%s\" to list.\n", context->name); return FFMPEG_CORE_ERR_OOM; } if (c_linked_list_count(*list) > 1) { AVFilterContext* last = c_linked_list_tail(*list)->prev->d; if ((re = avfilter_link(last, 0, context, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, context->name, 0, av_err2str(re), re); return re; } } else { if ((re = avfilter_link(src, 0, context, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", src->name, 0, context->name, 0, av_err2str(re), re); return re; } } From e58425f9d2569aef4dccf4ed8a697b0e1291db7c Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 15 Feb 2022 13:44:39 +0800 Subject: [PATCH 15/41] =?UTF-8?q?=E6=94=AF=E6=8C=81CDA=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 9 +- ffmpeg_core/CMakeLists.txt | 6 +- ffmpeg_core/cmake/FindAVDEVICE.cmake | 29 ++++ ffmpeg_core/ffmpeg_core.h | 4 + ffmpeg_core/src/cda.c | 222 +++++++++++++++++++++++++++ ffmpeg_core/src/cda.h | 17 ++ ffmpeg_core/src/core.cpp | 56 ++++++- ffmpeg_core/src/core.h | 22 +++ ffmpeg_core/src/decode.c | 23 ++- ffmpeg_core/utils | 2 +- 10 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 ffmpeg_core/cmake/FindAVDEVICE.cmake create mode 100644 ffmpeg_core/src/cda.c create mode 100644 ffmpeg_core/src/cda.h diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 2dbd79d49..49f285856 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -34,12 +34,19 @@ void CFfmpegCore::InitCore() { format.extensions.push_back(L"wma"); format.extensions.push_back(L"wav"); format.extensions.push_back(L"m4a"); + format.extensions.push_back(L"ogg"); + format.extensions.push_back(L"oga"); format.extensions.push_back(L"flac"); format.extensions.push_back(L"ape"); + format.extensions.push_back(L"mp2"); + format.extensions.push_back(L"mp1"); + format.extensions.push_back(L"opus"); + format.extensions.push_back(L"ape"); + format.extensions.push_back(L"cda"); format.extensions.push_back(L"mp4"); format.extensions.push_back(L"mkv"); format.extensions.push_back(L"m2ts"); - format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.flac;*.ape;*.mp4;*.mkv;*.m2ts"; + format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.ogg;*.oga;*.flac;*.ape;*.mp2;*.mp1;*.opus;*.ape;*.cda;*.mp4;*.mkv;*.m2ts"; CAudioCommon::m_surpported_format.push_back(format); CAudioCommon::m_all_surpported_extensions = format.extensions; settings = ffmpeg_core_init_settings(); diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index ecaa5bce8..1eb2c0872 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -9,8 +9,9 @@ endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(AVFORMAT 58 REQUIRED) find_package(AVCODEC 58 REQUIRED) +find_package(AVDEVICE 58 REQUIRED) find_package(AVUTIL 56 REQUIRED) -find_package(AVFILTER 7) +find_package(AVFILTER 7 REQUIRED) find_package(SWRESAMPLE 4 REQUIRED) find_package(SDL2 REQUIRED) @@ -38,6 +39,8 @@ src/speed.h src/speed.c src/fft_data.h src/fft_data.c +src/cda.h +src/cda.c ) add_library(ffmpeg_core SHARED "${CORE_FILES}") @@ -45,6 +48,7 @@ set_target_properties(ffmpeg_core PROPERTIES PREFIX "") target_compile_definitions(ffmpeg_core PRIVATE BUILD_FFMPEG_CORE) target_link_libraries(ffmpeg_core AVFORMAT::AVFORMAT) target_link_libraries(ffmpeg_core AVCODEC::AVCODEC) +target_link_libraries(ffmpeg_core AVDEVICE::AVDEVICE) target_link_libraries(ffmpeg_core AVUTIL::AVUTIL) target_link_libraries(ffmpeg_core AVFILTER::AVFILTER) target_link_libraries(ffmpeg_core SWRESAMPLE::SWRESAMPLE) diff --git a/ffmpeg_core/cmake/FindAVDEVICE.cmake b/ffmpeg_core/cmake/FindAVDEVICE.cmake new file mode 100644 index 000000000..3fe830f8b --- /dev/null +++ b/ffmpeg_core/cmake/FindAVDEVICE.cmake @@ -0,0 +1,29 @@ +find_package(PkgConfig) +if (PkgConfig_FOUND) + pkg_check_modules(PC_AVDEVICE QUIET IMPORTED_TARGET GLOBAL libavdevice) +endif() + +if (PC_AVDEVICE_FOUND) + set(AVDEVICE_FOUND TRUE) + set(AVDEVICE_VERSION ${PC_AVDEVICE_VERSION}) + set(AVDEVICE_VERSION_STRING ${PC_AVDEVICE_STRING}) + set(AVDEVICE_LIBRARYS ${PC_AVDEVICE_LIBRARIES}) + if (USE_STATIC_LIBS) + set(AVDEVICE_INCLUDE_DIRS ${PC_AVDEVICE_STATIC_INCLUDE_DIRS}) + else() + set(AVDEVICE_INCLUDE_DIRS ${PC_AVDEVICE_INCLUDE_DIRS}) + endif() + if (NOT TARGET AVDEVICE::AVDEVICE) + add_library(AVDEVICE::AVDEVICE ALIAS PkgConfig::PC_AVDEVICE) + endif() +else() + message(FATAL_ERROR "failed.") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AVDEVICE + FOUND_VAR AVDEVICE_FOUND + REQUIRED_VARS + AVDEVICE_LIBRARYS + VERSION_VAR AVDEVICE_VERSION +) diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 2b858ddf8..4261c602f 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -30,6 +30,10 @@ typedef struct FfmpegCoreSettings FfmpegCoreSettings; #define FFMPEG_CORE_ERR_FAILED_SET_VOLUME 11 #define FFMPEG_CORE_ERR_FAILED_SET_SPEED 12 #define FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN 13 +#define FFMPEG_CORE_ERR_FAILED_OPEN_FILE 14 +#define FFMPEG_CORE_ERR_FAILED_READ_FILE 15 +#define FFMPEG_CORE_ERR_INVALID_CDA_FILE 16 +#define FFMPEG_CORE_ERR_NO_LIBCDIO 17 FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); diff --git a/ffmpeg_core/src/cda.c b/ffmpeg_core/src/cda.c new file mode 100644 index 000000000..8d557b505 --- /dev/null +++ b/ffmpeg_core/src/cda.c @@ -0,0 +1,222 @@ +#include "cda.h" + +#include +#include +#include +#include "cfileop.h" +#include "cstr_util.h" +#include "err.h" + +#ifndef _O_BINARY +#define _O_BINARY 0x8000 +#endif + +#ifndef _SH_DENYWR +#define _SH_DENYWR 0x20 +#endif + +#ifndef _S_IREAD +#define _S_IREAD 0x100 +#endif + +#define CDA_FILE_SIZE 44 +#define u8_buf(buf, offset) ((const uint8_t*)buf + offset) + +int is_cda_file(const char* url) { + if (!url) return 0; + char* dir = fileop_dirname(url); + if (!dir) return 0; + // 判断是否为根盘符 + if (!fileop_isdrive(dir)) { + free(dir); + return 0; + } + free(dir); + char* l = strrchr(url, '.'); + if (!l || cstr_stricmp(l, ".cda")) { + return 0; + } + size_t size = 0; + if (!fileop_get_file_size(url, &size)) return 0; + if (size != CDA_FILE_SIZE) return 0; + return 1; +} + +// CDA 文件格式见 https://en.wikipedia.org/wiki/.cda_file +int read_cda_file(MusicHandle* handle, const char* url) { + if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; + int fd = 0; + int re = 0; + FILE* f = NULL; + char buf[CDA_FILE_SIZE]; + int num_read = 0; + uint32_t chunk_size = 0; + if ((re = fileop_open(url, &fd, O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD))) { + char* errmsg = err_get_errno_message(re); + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%d)\n", url, errmsg ? errmsg : "", re); + if (errmsg) free(errmsg); + return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; + } + f = fileop_fdopen(fd, "r"); + if (!f) { + fileop_close(fd); + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": Can not open file descriptor %d\n", url, fd); + return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; + } + handle->cda = malloc(sizeof(CDAData)); + if (!handle->cda) { + re = FFMPEG_CORE_ERR_OOM; + goto end; + } + memset(handle->cda, 0, sizeof(CDAData)); + if ((num_read = fread(buf, 1, CDA_FILE_SIZE, f)) < CDA_FILE_SIZE) { + av_log(NULL, AV_LOG_FATAL, "Failed to read file \"%s\": %d bytes is needed, but only bytes was readed.\n", url, CDA_FILE_SIZE, num_read); + re = FFMPEG_CORE_ERR_FAILED_READ_FILE; + goto end; + } + if (strncmp(buf, "RIFF", 4)) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + chunk_size = cstr_read_uint32(u8_buf(buf, 4), 0); + if (chunk_size != 36) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + if (strncmp(buf + 8, "CDDAfmt ", 8)) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + chunk_size = cstr_read_uint32(u8_buf(buf, 16), 0); + if (chunk_size != 24) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + handle->cda->cd_format_version = cstr_read_uint16(u8_buf(buf, 20), 0); + handle->cda->no = cstr_read_uint16(u8_buf(buf, 22), 0); + handle->cda->range_offset = cstr_read_uint32(u8_buf(buf, 28), 0); + handle->cda->duration = cstr_read_uint32(u8_buf(buf, 32), 0); + re = FFMPEG_CORE_ERR_OK; +end: + if (f) fileop_fclose(f); + return re; +} + +int read_cda_file2(MusicInfoHandle* handle, const char* url) { + if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; + int fd = 0; + int re = 0; + FILE* f = NULL; + char buf[CDA_FILE_SIZE]; + int num_read = 0; + uint32_t chunk_size = 0; + if ((re = fileop_open(url, &fd, O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD))) { + char* errmsg = err_get_errno_message(re); + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%d)\n", url, errmsg ? errmsg : "", re); + if (errmsg) free(errmsg); + return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; + } + f = fileop_fdopen(fd, "r"); + if (!f) { + fileop_close(fd); + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": Can not open file descriptor %d\n", url, fd); + return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; + } + handle->cda = malloc(sizeof(CDAData)); + if (!handle->cda) { + re = FFMPEG_CORE_ERR_OOM; + goto end; + } + memset(handle->cda, 0, sizeof(CDAData)); + if ((num_read = fread(buf, 1, CDA_FILE_SIZE, f)) < CDA_FILE_SIZE) { + av_log(NULL, AV_LOG_FATAL, "Failed to read file \"%s\": %d bytes is needed, but only bytes was readed.\n", url, CDA_FILE_SIZE, num_read); + re = FFMPEG_CORE_ERR_FAILED_READ_FILE; + goto end; + } + if (strncmp(buf, "RIFF", 4)) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + chunk_size = cstr_read_uint32(u8_buf(buf, 4), 0); + if (chunk_size != 36) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + if (strncmp(buf + 8, "CDDAfmt ", 8)) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + chunk_size = cstr_read_uint32(u8_buf(buf, 16), 0); + if (chunk_size != 24) { + re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; + goto end; + } + handle->cda->cd_format_version = cstr_read_uint16(u8_buf(buf, 20), 0); + handle->cda->no = cstr_read_uint16(u8_buf(buf, 22), 0); + handle->cda->range_offset = cstr_read_uint32(u8_buf(buf, 28), 0); + handle->cda->duration = cstr_read_uint32(u8_buf(buf, 32), 0); + re = FFMPEG_CORE_ERR_OK; +end: + if (f) fileop_fclose(f); + return re; +} + +const AVInputFormat* find_libcdio() { + const AVInputFormat* f = NULL; + f = av_input_audio_device_next(f); + while (f) { + if (f && !strcmp(f->name, "libcdio")) return f; + f = av_input_audio_device_next(f); + } + return NULL; +} + +int open_cd_device(MusicHandle* handle, const char* device) { + if (!handle || !device) return FFMPEG_CORE_ERR_NULLPTR; + avdevice_register_all(); + const AVInputFormat* f = find_libcdio(); + AVRational cda_time_base = { 1, 75 }; + if (!f) { + return FFMPEG_CORE_ERR_NO_LIBCDIO; + } + int re = 0; + if ((re = avformat_open_input(&handle->fmt, device, f, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", device, av_err2str(re), re); + return re; + } + if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", device, av_err2str(re), re); + return re; + } + handle->only_part = 1; + handle->part_start_pts = av_rescale_q_rnd(handle->cda->range_offset, cda_time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + handle->part_end_pts = av_rescale_q_rnd((int64_t)handle->cda->duration + handle->cda->range_offset, cda_time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + // handle->fmt->flags |= AVFMT_FLAG_FAST_SEEK; // 允许快速定位 + return FFMPEG_CORE_ERR_OK; +} + +int open_cd_device2(MusicInfoHandle* handle, const char* device) { + if (!handle || !device) return FFMPEG_CORE_ERR_NULLPTR; + avdevice_register_all(); + const AVInputFormat* f = find_libcdio(); + AVRational cda_time_base = { 1, 75 }; + if (!f) { + return FFMPEG_CORE_ERR_NO_LIBCDIO; + } + int re = 0; + if ((re = avformat_open_input(&handle->fmt, device, f, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", device, av_err2str(re), re); + return re; + } + if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", device, av_err2str(re), re); + return re; + } + return FFMPEG_CORE_ERR_OK; +} + +int64_t get_cda_duration(CDAData* d) { + if (!d) return 0; + AVRational t = { 1, 75 }; + return av_rescale_q_rnd(d->duration, t, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); +} diff --git a/ffmpeg_core/src/cda.h b/ffmpeg_core/src/cda.h new file mode 100644 index 000000000..4fe56a077 --- /dev/null +++ b/ffmpeg_core/src/cda.h @@ -0,0 +1,17 @@ +#ifndef _MUSICPLAYER2_CDA_H +#define _MUSICPLAYER2_CDA_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +int is_cda_file(const char* url); +int read_cda_file(MusicHandle* handle, const char* url); +int read_cda_file2(MusicInfoHandle* handle, const char* url); +const AVInputFormat* find_libcdio(); +int open_cd_device(MusicHandle* handle, const char* device); +int open_cd_device2(MusicInfoHandle* handle, const char* device); +int64_t get_cda_duration(CDAData* d); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 52a75352d..1f2b92b87 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -10,9 +10,15 @@ #include "decode.h" #include "filter.h" #include "speed.h" +#include "cda.h" +#include "fileop.h" #define CODEPAGE_SIZE 3 +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + void free_music_handle(MusicHandle* handle) { if (!handle) return; if (handle->device_id) SDL_CloseAudioDevice(handle->device_id); @@ -42,12 +48,14 @@ void free_music_handle(MusicHandle* handle) { if (handle->s && handle->settings_is_alloc) { free_ffmpeg_core_settings(handle->s); } + if (handle->cda) free(handle->cda); free(handle); } void free_music_info_handle(MusicInfoHandle* handle) { if (!handle) return; if (handle->fmt) avformat_close_input(&handle->fmt); + if (handle->cda) free(handle->cda); free(handle); } @@ -100,8 +108,20 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s } } handle->first_pts = INT64_MIN; - if ((re = open_input(handle, u.c_str()))) { - goto end; + handle->part_end_pts = INT64_MAX; + handle->is_cda = is_cda_file(u.c_str()); + if (handle->is_cda) { + if ((re = read_cda_file(handle, u.c_str()))) { + goto end; + } + u = fileop::dirname(u); + if ((re = open_cd_device(handle, u.c_str()))) { + goto end; + } + } else { + if ((re = open_input(handle, u.c_str()))) { + goto end; + } } #ifndef NDEBUG av_dump_format(handle->fmt, 0, u.c_str(), 0); @@ -151,12 +171,24 @@ int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle) { #endif MusicInfoHandle* h = (MusicInfoHandle*)malloc(sizeof(MusicInfoHandle)); int re = FFMPEG_CORE_ERR_OK; + int is_cda = 0; if (!h) { return FFMPEG_CORE_ERR_OOM; } memset(h, 0, sizeof(MusicInfoHandle)); - if ((re = open_input2(h, u.c_str()))) { - goto end; + is_cda = is_cda_file(u.c_str()); + if (is_cda) { + if ((re = read_cda_file2(h, u.c_str()))) { + goto end; + } + u = fileop::dirname(u); + if ((re = open_cd_device2(h, u.c_str()))) { + goto end; + } + } else { + if ((re = open_input2(h, u.c_str()))) { + goto end; + } } if ((re = find_audio_stream2(h))) { goto end; @@ -184,6 +216,7 @@ int ffmpeg_core_pause(MusicHandle* handle) { int64_t ffmpeg_core_get_cur_position(MusicHandle* handle) { if (!handle) return -1; + if (handle->only_part) return handle->pts - handle->part_start_pts; // 忽略 SDL 可能长达 0.01s 的 buffer return handle->pts; } @@ -196,11 +229,15 @@ int ffmpeg_core_song_is_over(MusicHandle* handle) { int64_t ffmpeg_core_get_song_length(MusicHandle* handle) { if (!handle || !handle->fmt) return -1; + if (handle->only_part) { + return min(handle->part_end_pts - handle->part_start_pts, handle->fmt->duration - handle->part_start_pts); + } return handle->fmt->duration; } int64_t ffmpeg_core_info_get_song_length(MusicInfoHandle* handle) { if (!handle || !handle->fmt) return -1; + if (handle->cda) return get_cda_duration(handle->cda); return handle->fmt->duration; } @@ -230,6 +267,9 @@ int ffmpeg_core_seek(MusicHandle* handle, int64_t time) { if (re == WAIT_OBJECT_0) { handle->is_seek = 1; handle->seek_pos = time; + if (handle->only_part) { + handle->seek_pos += handle->part_start_pts; + } } else { return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; } @@ -433,6 +473,14 @@ const wchar_t* ffmpeg_core_get_err_msg2(int err) { return L"Failed to set speed."; case FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN: return L"FFT data's length is too big."; + case FFMPEG_CORE_ERR_FAILED_OPEN_FILE: + return L"Failed to open file."; + case FFMPEG_CORE_ERR_FAILED_READ_FILE: + return L"Failed to read file."; + case FFMPEG_CORE_ERR_INVALID_CDA_FILE: + return L"Invalid CDA file."; + case FFMPEG_CORE_ERR_NO_LIBCDIO: + return L"libcdio not found."; default: return L"Unknown error."; } diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 56c547923..21fb42ff3 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -7,6 +7,7 @@ extern "C" { #include "../ffmpeg_core.h" #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" +#include "libavdevice/avdevice.h" #include "libavutil/avutil.h" #include "libavutil/audio_fifo.h" #include "libavutil/opt.h" @@ -30,6 +31,16 @@ extern "C" { #define FFT_SAMPLE 1024 +typedef struct CDAData { +/// version of the CD format. In May 2006, always equal to 1. +uint16_t cd_format_version; +/// number of the range. The first track has the number 1. +uint16_t no; +/// range offset, in number of frames. +uint32_t range_offset; +/// duration of the track, total number of frames +uint32_t duration; +} CDAData; typedef struct MusicHandle { /// Demux 用 AVFormatContext* fmt; @@ -66,6 +77,10 @@ int64_t end_pts; /// 第一个sample的pts int64_t first_pts; int64_t seek_pos; +/// 要播放的部分的开始时间(相对于first_pts的偏移量) +int64_t part_start_pts; +/// 要播放的部分的结束时间(相对于first_pts的偏移量) +int64_t part_end_pts; /// 设置 FfmpegCoreSettings* s; /// 用于设置filter @@ -76,6 +91,8 @@ AVFilterContext* filter_inp; AVFilterContext* filter_out; /// filter 链 c_linked_list* filters; +/// CDA 文件信息 +CDAData* cda; /// 输出时的声道布局 uint64_t output_channel_layout; /// SDL是否被初始化 @@ -95,10 +112,15 @@ unsigned char is_playing : 1; unsigned char settings_is_alloc : 1; /// 需要设置新的filters链 unsigned char need_reinit_filters : 1; +/// 是否正在播放CDA文件 +unsigned char is_cda : 1; +/// 是否仅播放一部分内容 +unsigned char only_part: 1; } MusicHandle; typedef struct MusicInfoHandle { AVFormatContext* fmt; AVStream* is; +CDAData* cda; } MusicInfoHandle; typedef struct FfmpegCoreSettings { /// 音量 diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 4d0c0e3a2..07c1552fd 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -84,6 +84,13 @@ int decode_audio(MusicHandle* handle, char* writed) { if (handle->first_pts == INT64_MIN) { handle->first_pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); av_log(NULL, AV_LOG_VERBOSE, "first_pts: %s\n", av_ts2timestr(handle->first_pts, &AV_TIME_BASE_Q)); + if (handle->only_part) { + // 定位到开始位置 + if (handle->part_start_pts > 0) { + handle->is_seek = 1; + handle->seek_pos = handle->part_start_pts; + } + } } if (handle->set_new_pts) { av_log(NULL, AV_LOG_VERBOSE, "pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); @@ -91,6 +98,10 @@ int decode_audio(MusicHandle* handle, char* writed) { handle->end_pts = handle->pts; handle->set_new_pts = 0; } + if (handle->only_part && (frame->pts - handle->first_pts) >= handle->part_end_pts) { + handle->is_eof = 1; + goto end; + } re = convert_samples_and_add_to_fifo(handle, frame, writed); goto end; } else if (re == AVERROR(EAGAIN)) { @@ -113,8 +124,16 @@ int convert_samples_and_add_to_fifo(MusicHandle* handle, AVFrame* frame, char* w uint8_t** converted_input_samples = NULL; int re = FFMPEG_CORE_ERR_OK; AVRational base = { 1, handle->decoder->sample_rate }, target = { 1, handle->sdl_spec.freq }; + int samples = frame->nb_samples; + if (handle->only_part) { + int tmp = av_rescale_q_rnd(handle->part_end_pts - handle->end_pts + handle->first_pts, AV_TIME_BASE_Q, base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + if (samples >= tmp) { + samples = tmp; + handle->is_eof = 1; + } + } /// 输出的样本数 - int64_t frames = av_rescale_q_rnd(frame->nb_samples, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + int64_t frames = av_rescale_q_rnd(samples, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); /// 实际输出样本数 int converted_samples = 0; DWORD res = 0; @@ -128,7 +147,7 @@ int convert_samples_and_add_to_fifo(MusicHandle* handle, AVFrame* frame, char* w goto end; } re = 0; - if ((converted_samples = swr_convert(handle->swrac, converted_input_samples, frames, (const uint8_t**)frame->extended_data, frame->nb_samples)) < 0) { + if ((converted_samples = swr_convert(handle->swrac, converted_input_samples, frames, (const uint8_t**)frame->extended_data, samples)) < 0) { re = converted_samples; goto end; } diff --git a/ffmpeg_core/utils b/ffmpeg_core/utils index 0c8dba0a0..a64c19d11 160000 --- a/ffmpeg_core/utils +++ b/ffmpeg_core/utils @@ -1 +1 @@ -Subproject commit 0c8dba0a0acc0fd4fb525539cee3d92dd798030a +Subproject commit a64c19d111d6117cc5e68d017fb3ad1d6c16ed0a From f8354b6d66d03688115a374b997d0c3fdfb805ef Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 15 Feb 2022 13:56:17 +0800 Subject: [PATCH 16/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8DMingW=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/fft_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffmpeg_core/src/fft_data.c b/ffmpeg_core/src/fft_data.c index fd0d7c6ae..27bb0802e 100644 --- a/ffmpeg_core/src/fft_data.c +++ b/ffmpeg_core/src/fft_data.c @@ -88,7 +88,7 @@ int ffmpeg_core_get_fft_data(MusicHandle* handle, float* fft_data, int len) { memset(fft_data, 0, sizeof(float) * len); goto end; } - if ((r = swr_convert(swr, f2->data, f2->nb_samples, f->data, f->nb_samples)) < 0) { + if ((r = swr_convert(swr, f2->data, f2->nb_samples, (const uint8_t**)f->data, f->nb_samples)) < 0) { memset(fft_data, 0, sizeof(float) * len); goto end; } From f4d61de03f4f57ee0e5f133d075fc515e2524c0c Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 16 Feb 2022 15:58:31 +0800 Subject: [PATCH 17/41] =?UTF-8?q?=E5=8F=91=E7=94=9F=E8=A7=A3=E7=A0=81/IO?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=97=B6=E5=B0=9D=E8=AF=95=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E6=89=93=E5=BC=80=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 36 ++++++++--- MusicPlayer2/FfmpegCore.h | 6 ++ ffmpeg_core/CMakeLists.txt | 2 + ffmpeg_core/ffmpeg_core.h | 4 ++ ffmpeg_core/src/core.cpp | 42 ++++++++++++- ffmpeg_core/src/core.h | 17 +++++- ffmpeg_core/src/decode.c | 2 + ffmpeg_core/src/file.cpp | 36 +++++++++++ ffmpeg_core/src/file.h | 12 ++++ ffmpeg_core/src/filter.c | 6 +- ffmpeg_core/src/loop.c | 115 ++++++++++++++++++++++++++++++++++-- ffmpeg_core/src/loop.h | 3 + ffmpeg_core/src/output.c | 2 +- 13 files changed, 262 insertions(+), 21 deletions(-) create mode 100644 ffmpeg_core/src/file.cpp create mode 100644 ffmpeg_core/src/file.h diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 49f285856..add2f3dc6 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -7,6 +7,8 @@ #define ft2ts(t) (((size_t)t.dwHighDateTime << 32) | (size_t)t.dwLowDateTime) +static std::wstring last_ffmpeg_core_error_cache; + CFfmpegCore::CFfmpegCore() { handle = nullptr; err = 0; @@ -42,14 +44,17 @@ void CFfmpegCore::InitCore() { format.extensions.push_back(L"mp1"); format.extensions.push_back(L"opus"); format.extensions.push_back(L"ape"); + format.extensions.push_back(L"aif"); + format.extensions.push_back(L"aiff"); format.extensions.push_back(L"cda"); format.extensions.push_back(L"mp4"); format.extensions.push_back(L"mkv"); format.extensions.push_back(L"m2ts"); - format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.ogg;*.oga;*.flac;*.ape;*.mp2;*.mp1;*.opus;*.ape;*.cda;*.mp4;*.mkv;*.m2ts"; + format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.ogg;*.oga;*.flac;*.ape;*.mp2;*.mp1;*.opus;*.ape;*.cda;*.aif;*.aiff;*.mp4;*.mkv;*.m2ts"; CAudioCommon::m_surpported_format.push_back(format); CAudioCommon::m_all_surpported_extensions = format.extensions; settings = ffmpeg_core_init_settings(); + ffmpeg_core_log_set_flags(AV_LOG_SKIP_REPEATED | AV_LOG_PRINT_LEVEL); ffmpeg_core_log_set_callback(LogCallback); UpdateSettings(); } @@ -264,12 +269,18 @@ void CFfmpegCore::GetFFTData(float fft_data[FFT_SAMPLE]) { } int CFfmpegCore::GetErrorCode() { - if (err) return err; - if (handle) return ffmpeg_core_get_error(handle); + if (err) { + int tmp = err; + err = 0; + return tmp; + } + // ²Ĵ⵱²´ļʱʹﷵ0ϲΪд + // if (handle) return ffmpeg_core_get_error(handle); return 0; } std::wstring CFfmpegCore::GetErrorInfo(int error_code) { + if (error_code == 0) return L""; auto tmp = ffmpeg_core_get_err_msg(error_code); if (tmp) { std::wstring re(tmp); @@ -299,6 +310,7 @@ bool CFfmpegCore::GetFunction() { free_ffmpeg_core_settings = (_free_ffmpeg_core_settings)::GetProcAddress(m_dll_module, "free_ffmpeg_core_settings"); ffmpeg_core_log_format_line = (_ffmpeg_core_log_format_line)::GetProcAddress(m_dll_module, "ffmpeg_core_log_format_line"); ffmpeg_core_log_set_callback = (_ffmpeg_core_log_set_callback)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_callback"); + ffmpeg_core_log_set_flags = (_ffmpeg_core_log_set_flags)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_flags"); ffmpeg_core_open = (_ffmpeg_core_open)::GetProcAddress(m_dll_module, "ffmpeg_core_open"); ffmpeg_core_open2 = (_ffmpeg_core_open2)::GetProcAddress(m_dll_module, "ffmpeg_core_open2"); ffmpeg_core_info_open = (_ffmpeg_core_info_open)::GetProcAddress(m_dll_module, "ffmpeg_core_info_open"); @@ -330,12 +342,14 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_settings_set_volume = (_ffmpeg_core_settings_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_volume"); ffmpeg_core_settings_set_speed = (_ffmpeg_core_settings_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_speed"); ffmpeg_core_settings_set_cache_length = (_ffmpeg_core_settings_set_cache_length)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_cache_length"); + ffmpeg_core_settings_set_max_retry_count = (_ffmpeg_core_settings_set_max_retry_count)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_max_retry_count"); //жǷɹ rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); rtn &= (free_ffmpeg_core_settings != NULL); rtn &= (ffmpeg_core_log_format_line != NULL); rtn &= (ffmpeg_core_log_set_callback != NULL); + rtn &= (ffmpeg_core_log_set_flags != NULL); rtn &= (ffmpeg_core_open != NULL); rtn &= (ffmpeg_core_open2 != NULL); rtn &= (ffmpeg_core_info_open != NULL); @@ -367,6 +381,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_settings_set_volume != NULL); rtn &= (ffmpeg_core_settings_set_speed != NULL); rtn &= (ffmpeg_core_settings_set_cache_length != NULL); + rtn &= (ffmpeg_core_settings_set_max_retry_count != NULL); return rtn; } @@ -440,15 +455,22 @@ void CFfmpegCore::LogCallback(void* ptr, int level, const char* fmt, va_list vl) char buf[1024]; if (ffmpeg_core_log_format_line) { int print = 1; - int re = ffmpeg_core_log_format_line(ptr, level, fmt, vl, buf, sizeof(buf), &print); - if (re > 0) { - std::wstring s = CCommon::StrToUnicode(std::string(buf, re), CodeType::UTF8); + int r = ffmpeg_core_log_format_line(ptr, level, fmt, vl, buf, sizeof(buf), &print); + if (r > 0) { + std::wstring s = CCommon::StrToUnicode(std::string(buf, r), CodeType::UTF8); + if (s.find(L"Last message repeated") != -1) { + FreeLibrary(re); + return; + } OutputDebugStringW(s.c_str()); if (level <= AV_LOG_ERROR) { if (s.back() == '\n') { s.pop_back(); } - theApp.WriteLog(s); + if (last_ffmpeg_core_error_cache.empty() || last_ffmpeg_core_error_cache != s) { + last_ffmpeg_core_error_cache = s; + theApp.WriteLog(s); + } } } } diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 74d70bb63..314a64bd2 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -5,6 +5,8 @@ #define AV_LOG_ERROR 16 #define AV_LOG_VERBOSE 40 +#define AV_LOG_SKIP_REPEATED 1 +#define AV_LOG_PRINT_LEVEL 2 typedef struct MusicHandle MusicHandle; typedef struct MusicInfoHandle MusicInfoHandle; typedef struct FfmpegCoreSettings FfmpegCoreSettings; @@ -13,6 +15,7 @@ typedef void(*_free_music_info_handle)(MusicInfoHandle*); typedef void(*_free_ffmpeg_core_settings)(FfmpegCoreSettings*); typedef int(*_ffmpeg_core_log_format_line)(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); typedef void(*_ffmpeg_core_log_set_callback)(void(*callback)(void*, int, const char*, va_list)); +typedef void(*_ffmpeg_core_log_set_flags)(int); typedef int(*_ffmpeg_core_open)(const wchar_t*, MusicHandle**); typedef int(*_ffmpeg_core_open2)(const wchar_t*, MusicHandle**, FfmpegCoreSettings*); typedef int(*_ffmpeg_core_info_open)(const wchar_t*, MusicInfoHandle**); @@ -44,6 +47,7 @@ typedef FfmpegCoreSettings*(*_ffmpeg_core_init_settings)(); typedef int(*_ffmpeg_core_settings_set_volume)(FfmpegCoreSettings*, int volume); typedef int(*_ffmpeg_core_settings_set_speed)(FfmpegCoreSettings*, float); typedef int(*_ffmpeg_core_settings_set_cache_length)(FfmpegCoreSettings*, int); +typedef int(*_ffmpeg_core_settings_set_max_retry_count)(FfmpegCoreSettings*, int); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -104,6 +108,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _free_ffmpeg_core_settings free_ffmpeg_core_settings = nullptr; _ffmpeg_core_log_format_line ffmpeg_core_log_format_line = nullptr; _ffmpeg_core_log_set_callback ffmpeg_core_log_set_callback = nullptr; + _ffmpeg_core_log_set_flags ffmpeg_core_log_set_flags = nullptr; _ffmpeg_core_open ffmpeg_core_open = nullptr; _ffmpeg_core_open2 ffmpeg_core_open2 = nullptr; _ffmpeg_core_info_open ffmpeg_core_info_open = nullptr; @@ -135,6 +140,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_settings_set_volume ffmpeg_core_settings_set_volume = nullptr; _ffmpeg_core_settings_set_speed ffmpeg_core_settings_set_speed = nullptr; _ffmpeg_core_settings_set_cache_length ffmpeg_core_settings_set_cache_length = nullptr; + _ffmpeg_core_settings_set_max_retry_count ffmpeg_core_settings_set_max_retry_count = nullptr; MusicHandle* handle; FfmpegCoreSettings* settings = nullptr; std::wstring recent_file; diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index 1eb2c0872..d94f589ff 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -41,6 +41,8 @@ src/fft_data.h src/fft_data.c src/cda.h src/cda.c +src/file.h +src/file.cpp ) add_library(ffmpeg_core SHARED "${CORE_FILES}") diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 4261c602f..029229bf4 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -34,6 +34,7 @@ typedef struct FfmpegCoreSettings FfmpegCoreSettings; #define FFMPEG_CORE_ERR_FAILED_READ_FILE 15 #define FFMPEG_CORE_ERR_INVALID_CDA_FILE 16 #define FFMPEG_CORE_ERR_NO_LIBCDIO 17 +#define FFMEPG_CORE_ERR_FAILED_PARSE_URL 18 FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); @@ -41,6 +42,8 @@ FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); FFMPEG_CORE_API int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); /// 即 av_log_set_callback FFMPEG_CORE_API void ffmpeg_core_log_set_callback(void(*callback)(void*, int, const char*, va_list)); +/// 即 av_log_set_flags +FFMPEG_CORE_API void ffmpeg_core_log_set_flags(int arg); FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); FFMPEG_CORE_API int ffmpeg_core_open2(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s); FFMPEG_CORE_API int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle); @@ -128,6 +131,7 @@ FFMPEG_CORE_API FfmpegCoreSettings* ffmpeg_core_init_settings(); FFMPEG_CORE_API int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume); FFMPEG_CORE_API int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed); FFMPEG_CORE_API int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length); +FFMPEG_CORE_API int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retry_count); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 1f2b92b87..8235eff09 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -12,6 +12,7 @@ #include "speed.h" #include "cda.h" #include "fileop.h" +#include "file.h" #define CODEPAGE_SIZE 3 @@ -49,6 +50,8 @@ void free_music_handle(MusicHandle* handle) { free_ffmpeg_core_settings(handle->s); } if (handle->cda) free(handle->cda); + if (handle->url) free(handle->url); + if (handle->parsed_url) free_url_parse_result(handle->parsed_url); free(handle); } @@ -72,6 +75,10 @@ void ffmpeg_core_log_set_callback(void(*callback)(void*, int, const char*, va_li av_log_set_callback(callback); } +void ffmpeg_core_log_set_flags(int arg) { + av_log_set_flags(arg); +} + int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle) { return ffmpeg_core_open2(url, handle, nullptr); } @@ -126,6 +133,16 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s #ifndef NDEBUG av_dump_format(handle->fmt, 0, u.c_str(), 0); #endif + if (!cpp2c::string2char(u, handle->url)) { + re = FFMPEG_CORE_ERR_OOM; + goto end; + } + handle->parsed_url = urlparse(handle->url, nullptr, 1); + if (!handle->parsed_url) { + re = FFMEPG_CORE_ERR_FAILED_PARSE_URL; + goto end; + } + handle->is_file = is_file(handle->parsed_url); if ((re = find_audio_stream(handle))) { av_log(NULL, AV_LOG_FATAL, "Failed to find suitable audio stream.\n"); goto end; @@ -273,6 +290,10 @@ int ffmpeg_core_seek(MusicHandle* handle, int64_t time) { } else { return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; } + if (handle->is_reopen) { + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_OK; + } handle->have_err = 0; ReleaseMutex(handle->mutex); while (1) { @@ -379,10 +400,13 @@ int send_reinit_filters(MusicHandle* handle) { } else { return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; } + if (handle->is_reopen) { + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_OK; + } handle->have_err = 0; ReleaseMutex(handle->mutex); - while (1) { - if (!handle->is_seek) break; + while (handle->need_reinit_filters) { Sleep(10); } return handle->have_err ? handle->err : FFMPEG_CORE_ERR_OK; @@ -395,6 +419,7 @@ FfmpegCoreSettings* ffmpeg_core_init_settings() { s->speed = 1.0; s->volume = 100; s->cache_length = 15; + s->max_retry_count = 10; return s; } @@ -439,7 +464,7 @@ int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length) { int ffmpeg_core_get_error(MusicHandle* handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - return handle->err; + return handle->have_err ? handle->err : 0; } const wchar_t* ffmpeg_core_get_err_msg2(int err) { @@ -481,6 +506,8 @@ const wchar_t* ffmpeg_core_get_err_msg2(int err) { return L"Invalid CDA file."; case FFMPEG_CORE_ERR_NO_LIBCDIO: return L"libcdio not found."; + case FFMEPG_CORE_ERR_FAILED_PARSE_URL: + return L"Failed to parse url."; default: return L"Unknown error."; } @@ -516,3 +543,12 @@ wchar_t* ffmpeg_core_get_err_msg(int err) { } return nullptr; } + +int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retry_count) { + if (!s) return 0; + if (max_retry_count >= -1) { + s->max_retry_count = max_retry_count; + return 1; + } + return 0; +} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 21fb42ff3..422b40ec6 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -19,6 +19,7 @@ extern "C" { #include "SDL2/SDL.h" #include #include "c_linked_list.h" +#include "urlparse.h" #ifndef __cplusplus #ifndef min @@ -95,6 +96,14 @@ c_linked_list* filters; CDAData* cda; /// 输出时的声道布局 uint64_t output_channel_layout; +/// 播放地址 +char* url; +/// 解析后的播放地址 +UrlParseResult* parsed_url; +/// 当前重新打开次数 +int retry_count; +/// 最近一个包的时间 +int64_t last_pkt_pts; /// SDL是否被初始化 unsigned char sdl_initialized : 1; /// 让事件处理线程退出标志位 @@ -115,7 +124,11 @@ unsigned char need_reinit_filters : 1; /// 是否正在播放CDA文件 unsigned char is_cda : 1; /// 是否仅播放一部分内容 -unsigned char only_part: 1; +unsigned char only_part : 1; +/// 是否为本地文件 +unsigned char is_file : 1; +/// 是否正在重新打开文件 +unsigned char is_reopen : 1; } MusicHandle; typedef struct MusicInfoHandle { AVFormatContext* fmt; @@ -129,6 +142,8 @@ int volume; float speed; /// 缓存长度(单位s) int cache_length; +/// 最大重试次数 +int max_retry_count; } FfmpegCoreSettings; #if __cplusplus } diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 07c1552fd..8040ad246 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -74,6 +74,7 @@ int decode_audio(MusicHandle* handle, char* writed) { av_packet_unref(&pkt); continue; } + handle->last_pkt_pts = av_rescale_q_rnd(pkt.pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); if ((re = avcodec_send_packet(handle->decoder, &pkt)) < 0) { av_packet_unref(&pkt); goto end; @@ -98,6 +99,7 @@ int decode_audio(MusicHandle* handle, char* writed) { handle->end_pts = handle->pts; handle->set_new_pts = 0; } + // 整段数据在结束位置之后,跳过 if (handle->only_part && (frame->pts - handle->first_pts) >= handle->part_end_pts) { handle->is_eof = 1; goto end; diff --git a/ffmpeg_core/src/file.cpp b/ffmpeg_core/src/file.cpp new file mode 100644 index 000000000..ea8729d91 --- /dev/null +++ b/ffmpeg_core/src/file.cpp @@ -0,0 +1,36 @@ +#include "file.h" + +#include +#include "fileop.h" + +#define chkstr(s) (s ? s : "") + +int is_file(UrlParseResult* url) { + if (!url) return 0; + std::string scheme(chkstr(url->scheme)); + if (scheme.empty() || scheme == "file") return 1; + if (scheme.length() == 1 && isalpha(scheme[0])) return 1; + return 0; +} + +int is_file_exists(MusicHandle* handle) { + if (!handle || !handle->is_file) return 0; + std::string scheme(chkstr(handle->parsed_url->scheme)); + if (scheme != "file") { + return fileop::exists(handle->url); + } else { + std::string fn(chkstr(handle->parsed_url->netloc)); + fn += chkstr(handle->parsed_url->path); + std::string query(chkstr(handle->parsed_url->query)); + if (!query.empty()) { + fn += "?"; + fn += query; + } + std::string fragment(chkstr(handle->parsed_url->fragment)); + if (!fragment.empty()) { + fn += "#"; + fn += fragment; + } + return fileop::exists(fn); + } +} diff --git a/ffmpeg_core/src/file.h b/ffmpeg_core/src/file.h new file mode 100644 index 000000000..bb2d4c305 --- /dev/null +++ b/ffmpeg_core/src/file.h @@ -0,0 +1,12 @@ +#ifndef _MUSICPLAYER2_FILE_H +#define _MUSICPLAYER2_FILE_H +#include "core.h" +#if __cplusplus +extern "C" { +#endif +int is_file(UrlParseResult* url); +int is_file_exists(MusicHandle* handle); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index d883a72cf..9b7c60c2f 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -161,8 +161,8 @@ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterCo char channel_layout[512]; // 输入的设置:描述见 ffmpeg -h filter=abuffer uint64_t layout = handle->output_channel_layout; - av_get_channel_layout_string(channel_layout, sizeof(channel_layout), handle->decoder->channels, layout); - snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%d", handle->is->time_base.num, handle->is->time_base.den, handle->sdl_spec.freq, av_get_sample_fmt_name(handle->target_format), channel_layout, handle->decoder->channels); + av_get_channel_layout_string(channel_layout, sizeof(channel_layout), handle->sdl_spec.channels, layout); + snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%d", handle->is->time_base.num, handle->is->time_base.den, handle->sdl_spec.freq, av_get_sample_fmt_name(handle->target_format), channel_layout, handle->sdl_spec.channels); if ((re = avfilter_graph_create_filter(src, buffer, "in", args, NULL, *graph)) < 0) { av_log(NULL, AV_LOG_FATAL, "Failed to create input filter: %s (%i)\n", av_err2str(re), re); return re; @@ -189,7 +189,7 @@ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterCo av_log(NULL, AV_LOG_FATAL, "Failed to set channel_layouts to output filter: %s (%i)\n", av_err2str(re), re); return re; } - int channel_counts[2] = { handle->decoder->channels, 0 }; + int channel_counts[2] = { handle->sdl_spec.channels, 0 }; if ((re = av_opt_set_int_list(*sink, "channel_counts", channel_counts, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { av_log(NULL, AV_LOG_FATAL, "Failed to set channel_counts to output filter: %s (%i)\n", av_err2str(re), re); return re; diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index 67e29a770..c34f37542 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -1,8 +1,12 @@ #include "loop.h" #include +#include "libavutil/timestamp.h" #include "decode.h" #include "filter.h" +#include "file.h" +#include "cda.h" +#include "open.h" int seek_to_pos(MusicHandle* handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; @@ -53,6 +57,78 @@ int seek_to_pos(MusicHandle* handle) { return re; } +int reopen_file(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + int re = FFMPEG_CORE_ERR_OK; + if (handle->is_file) { + int doing = 0; + while (1) { + doing = 0; + if (handle->stoping) return FFMPEG_CORE_ERR_OK; + if (basic_event_handle(handle)) { + doing = 1; + } + if (is_file_exists(handle)) break; + if (!doing) Sleep(10); + } + } + if (handle->fmt) avformat_close_input(&handle->fmt); + if (handle->decoder) avcodec_free_context(&handle->decoder); + if (handle->is_cda) { + if ((re = open_cd_device(handle, handle->url))) { + return re; + } + } else { + if ((re = open_input(handle, handle->url))) { + return re; + } + } + if ((re = find_audio_stream(handle))) { + return re; + } + if ((re = open_decoder(handle))) { + return re; + } + av_log(NULL, AV_LOG_VERBOSE, "The target pts: %s\n", av_ts2timestr(handle->last_pkt_pts, &AV_TIME_BASE_Q)); + if ((re = av_seek_frame(handle->fmt, -1, handle->last_pkt_pts, AVSEEK_FLAG_ANY)) < 0) { + return re; + } + AVPacket pkt; + while (1) { + int64_t tmppts = 0; + if ((re = av_read_frame(handle->fmt, &pkt)) < 0) { + return re; + } + if (pkt.stream_index != handle->is->index) { + av_packet_unref(&pkt); + continue; + } + tmppts = av_rescale_q_rnd(pkt.pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + if (tmppts < handle->last_pkt_pts) { + continue; + } + break; + } + av_log(NULL, AV_LOG_VERBOSE, "The packet pts after seek: %s\n", av_ts2timestr(pkt.pts, &handle->is->time_base)); + av_packet_unref(&pkt); + handle->is_reopen = 0; + return FFMPEG_CORE_ERR_OK; +} + +int basic_event_handle(MusicHandle* h) { + if (!h) return 0; + if (h->need_reinit_filters) { + int re = reinit_filters(h); + if (re) { + h->have_err = 1; + h->err = re; + } + h->need_reinit_filters = 0; + return 1; + } + return 0; +} + DWORD WINAPI event_loop(LPVOID handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; MusicHandle* h = (MusicHandle*)handle; @@ -65,23 +141,36 @@ DWORD WINAPI event_loop(LPVOID handle) { while (1) { doing = 0; if (h->stoping) break; - if (h->is_seek && h->first_pts != INT64_MIN) { - int re = seek_to_pos(h); + if (basic_event_handle(h)) { + doing = 1; + goto end; + } + if (h->is_reopen) { + /// 禁用了重试或重试次数达上限 + if (!h->s->max_retry_count || (h->s->max_retry_count > 0 && h->retry_count < h->s->max_retry_count)) { + goto end; + } + h->retry_count += 1; + av_log(NULL, AV_LOG_VERBOSE, "Try to reopen file \"%s\" %i times.\n", h->url, h->retry_count); + int re = reopen_file(h); if (re) { - h->have_err = 1; + av_log(NULL, AV_LOG_VERBOSE, "Reopen failed: %i.\n", re); h->err = re; + } else { + h->have_err = 0; + h->err = 0; + h->retry_count = 0; } doing = 1; goto end; } - if (h->need_reinit_filters) { - int re = reinit_filters(h); + if (h->is_seek && h->first_pts != INT64_MIN) { + int re = seek_to_pos(h); if (re) { h->have_err = 1; h->err = re; } doing = 1; - h->need_reinit_filters = 0; goto end; } if (!h->is_eof) { @@ -91,6 +180,20 @@ DWORD WINAPI event_loop(LPVOID handle) { if (re) { h->have_err = 1; h->err = re; + av_log(NULL, AV_LOG_VERBOSE, "Try to reopen file \"%s\".\n", h->url); + h->is_reopen = 1; + if (h->s->max_retry_count) { + h->retry_count += 1; + re = reopen_file(h); + if (re) { + av_log(NULL, AV_LOG_VERBOSE, "Reopen failed: %i.\n", re); + h->err = re; + } else { + h->have_err = 0; + h->err = 0; + h->retry_count = 0; + } + } } doing = 1; } diff --git a/ffmpeg_core/src/loop.h b/ffmpeg_core/src/loop.h index 884c56b66..65728eb3c 100644 --- a/ffmpeg_core/src/loop.h +++ b/ffmpeg_core/src/loop.h @@ -5,6 +5,9 @@ extern "C" { #endif #include "core.h" int seek_to_pos(MusicHandle* handle); +int reopen_file(MusicHandle* handle); +/// 基础事件处理,如果处理过返回1反之0 +int basic_event_handle(MusicHandle* handle); DWORD WINAPI event_loop(LPVOID handle); #if __cplusplus } diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index 12ba8d6b4..581bfb48a 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -116,7 +116,7 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { goto end; } samples_need_in = samples_need * get_speed(handle->s->speed) / 1000; - in->channels = handle->decoder->channels; + in->channels = handle->sdl_spec.channels; in->channel_layout = handle->output_channel_layout; in->format = handle->target_format; in->sample_rate = handle->sdl_spec.freq; From 37114ebffee41f4898584ecfde4dbd4ef35bc239 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 16 Feb 2022 18:11:11 +0800 Subject: [PATCH 18/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E6=AC=A1=E6=95=B0=E5=92=8C=E9=87=8D=E8=AF=95?= =?UTF-8?q?=E9=97=B4=E9=9A=94=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/CommonData.h | 4 ++++ MusicPlayer2/FfmpegCore.cpp | 18 ++++++++++++++++++ MusicPlayer2/FfmpegCore.h | 4 ++++ MusicPlayer2/MusicPlayer2.rc | Bin 554460 -> 556198 bytes MusicPlayer2/MusicPlayerDlg.cpp | 4 ++++ MusicPlayer2/PlaySettingsDlg.cpp | 10 ++++++++++ MusicPlayer2/PlaySettingsDlg.h | 2 ++ MusicPlayer2/resource.h | 2 ++ ffmpeg_core/ffmpeg_core.h | 1 + ffmpeg_core/src/core.cpp | 12 +++++++++++- ffmpeg_core/src/core.h | 4 +++- ffmpeg_core/src/loop.c | 17 +++++++++++++++++ 12 files changed, 76 insertions(+), 2 deletions(-) diff --git a/MusicPlayer2/CommonData.h b/MusicPlayer2/CommonData.h index 26cf01075..1a406b4b4 100644 --- a/MusicPlayer2/CommonData.h +++ b/MusicPlayer2/CommonData.h @@ -303,6 +303,10 @@ struct PlaySettingData bool use_ffmpeg{ false }; /// ffmpeg内核缓存时长(单位:s) int ffmpeg_core_cache_length { 15 }; + /// ffmpeg内核最大重试次数 + int ffmpeg_core_max_retry_count { 3 }; + /// ffmpeg内核非本地文件重试间隔时间(单位s) + int ffmpeg_core_url_retry_interval { 5 }; }; struct GlobalHotKeySettingData diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index add2f3dc6..582519726 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -343,6 +343,7 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_settings_set_speed = (_ffmpeg_core_settings_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_speed"); ffmpeg_core_settings_set_cache_length = (_ffmpeg_core_settings_set_cache_length)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_cache_length"); ffmpeg_core_settings_set_max_retry_count = (_ffmpeg_core_settings_set_max_retry_count)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_max_retry_count"); + ffmpeg_core_settings_set_url_retry_interval = (_ffmpeg_core_settings_set_url_retry_interval)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_url_retry_interval"); //жǷɹ rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); @@ -382,6 +383,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_settings_set_speed != NULL); rtn &= (ffmpeg_core_settings_set_cache_length != NULL); rtn &= (ffmpeg_core_settings_set_max_retry_count != NULL); + rtn &= (ffmpeg_core_settings_set_url_retry_interval != NULL); return rtn; } @@ -480,8 +482,12 @@ void CFfmpegCore::LogCallback(void* ptr, int level, const char* fmt, va_list vl) void CFfmpegCore::UpdateSettings(PlaySettingData* s) { if (s) { SetCacheLength(s->ffmpeg_core_cache_length); + SetMaxRetryCount(s->ffmpeg_core_max_retry_count); + SetUrlRetryInterval(s->ffmpeg_core_url_retry_interval); } else { SetCacheLength(theApp.m_play_setting_data.ffmpeg_core_cache_length); + SetMaxRetryCount(theApp.m_play_setting_data.ffmpeg_core_max_retry_count); + SetUrlRetryInterval(theApp.m_play_setting_data.ffmpeg_core_url_retry_interval); } } @@ -490,3 +496,15 @@ void CFfmpegCore::SetCacheLength(int cache_length) { ffmpeg_core_settings_set_cache_length(settings, cache_length); } } + +void CFfmpegCore::SetMaxRetryCount(int max_retry_count) { + if (settings) { + ffmpeg_core_settings_set_max_retry_count(settings, max_retry_count); + } +} + +void CFfmpegCore::SetUrlRetryInterval(int url_retry_interval) { + if (settings) { + ffmpeg_core_settings_set_url_retry_interval(settings, url_retry_interval); + } +} diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 314a64bd2..3b72d0205 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -48,6 +48,7 @@ typedef int(*_ffmpeg_core_settings_set_volume)(FfmpegCoreSettings*, int volume); typedef int(*_ffmpeg_core_settings_set_speed)(FfmpegCoreSettings*, float); typedef int(*_ffmpeg_core_settings_set_cache_length)(FfmpegCoreSettings*, int); typedef int(*_ffmpeg_core_settings_set_max_retry_count)(FfmpegCoreSettings*, int); +typedef int(*_ffmpeg_core_settings_set_url_retry_interval)(FfmpegCoreSettings*, int); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -99,6 +100,8 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { int GetTrackNum(MusicInfoHandle* h = nullptr); void UpdateSettings(PlaySettingData* s = nullptr); void SetCacheLength(int cache_length = 15); + void SetMaxRetryCount(int max_retry_count = 3); + void SetUrlRetryInterval(int url_retry_interval = 5); private: std::wstring GetMetadata(std::string key, MusicInfoHandle* h = nullptr); virtual bool GetFunction() override; @@ -141,6 +144,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_settings_set_speed ffmpeg_core_settings_set_speed = nullptr; _ffmpeg_core_settings_set_cache_length ffmpeg_core_settings_set_cache_length = nullptr; _ffmpeg_core_settings_set_max_retry_count ffmpeg_core_settings_set_max_retry_count = nullptr; + _ffmpeg_core_settings_set_url_retry_interval ffmpeg_core_settings_set_url_retry_interval = nullptr; MusicHandle* handle; FfmpegCoreSettings* settings = nullptr; std::wstring recent_file; diff --git a/MusicPlayer2/MusicPlayer2.rc b/MusicPlayer2/MusicPlayer2.rc index 9d37ebdfd6ae0d0a3cbc4a71ead12da5b65cb0b0..620ea7b48902b41dde28134644c4ea24d27937aa 100644 GIT binary patch delta 961 zcmb7DO=uHA6n@jr=10XMLJNhOjR>`pV1Bp6gHl4kly)txu?fOft!b=Hlh8!bQv`(^ z1VL6immc&|4<59=tT*xCv63DX4<6d9f<_U+Q~YMrl;B0n@@D4kn|a?i?|bvY3!4j)@7h%rO~iee6vCbqxwsYBicSJRKsDSazDv8+wHO}622x3_BI%yr&p z*54ggHU`uP#cU2>A>I_(#U5Alt!@Zk zE-+oo{?gszsh17($cu&WNfN`@$@Vjkc%XOr%+e*bwq9beE^EEi%|GtB+NQz#t*x_s zl=%`6DQAmPhGbHx6#mRp#TH0~Y!$j}du770d)BK_?r1X2dYx3B9fV3TlifKf)aMGf zR?^DpmRtklICKl?mXM}`@ix}8RHPe7UJ)^1e1x*}1X+OLPmJr6}M5v^W=R&kwFeL;wix@`>cJQ?+Ax6mUpv_o6RCJK1Fa g#wb?X%qTDi%xP_AT+_XKm9$@=6IQJsCD&*E2k-LaaR2}S delta 202 zcmZ4XQ}NDg#SMNe0=j>gX7tyQ^jMIUpi%l*#UpdateSettings(&m_data); diff --git a/MusicPlayer2/PlaySettingsDlg.h b/MusicPlayer2/PlaySettingsDlg.h index eb71a2b16..8644ff451 100644 --- a/MusicPlayer2/PlaySettingsDlg.h +++ b/MusicPlayer2/PlaySettingsDlg.h @@ -34,6 +34,8 @@ class CPlaySettingsDlg : public CTabDlg CButton m_mci_radio; CButton m_ffmpeg_radio; CSpinEdit m_ffmpeg_cache_length; + CSpinEdit m_ffmpeg_max_retry_count; + CSpinEdit m_ffmpeg_url_retry_interval; CToolTipCtrl m_toolTip; protected: diff --git a/MusicPlayer2/resource.h b/MusicPlayer2/resource.h index 86dd802d9..661a22c78 100644 --- a/MusicPlayer2/resource.h +++ b/MusicPlayer2/resource.h @@ -1092,6 +1092,8 @@ #define IDC_SAVE_TO_PROGRAM_DIR_RADIO 1189 #define IDC_FFMPEG_RADIO 1190 #define IDC_FFMPEG_CACHE_LENGTH 1191 +#define IDC_FFMPEG_MAX_RETRY_COUNT 1192 +#define IDC_FFMPEG_URL_RETRY_INTERVAL 1193 #define ID_32771 32771 #define ID_32772 32772 #define ID_OPEN 32773 diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 029229bf4..9fee89110 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -132,6 +132,7 @@ FFMPEG_CORE_API int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int v FFMPEG_CORE_API int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed); FFMPEG_CORE_API int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length); FFMPEG_CORE_API int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retry_count); +FFMPEG_CORE_API int ffmpeg_core_settings_set_url_retry_interval(FfmpegCoreSettings* s, int url_retry_interval); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 8235eff09..dabc80677 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -419,7 +419,8 @@ FfmpegCoreSettings* ffmpeg_core_init_settings() { s->speed = 1.0; s->volume = 100; s->cache_length = 15; - s->max_retry_count = 10; + s->max_retry_count = 3; + s->url_retry_interval = 5; return s; } @@ -552,3 +553,12 @@ int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retr } return 0; } + +int ffmpeg_core_settings_set_url_retry_interval(FfmpegCoreSettings* s, int url_retry_interval) { + if (!s) return 0; + if (url_retry_interval >= 1 && url_retry_interval <= 120) { + s->url_retry_interval = url_retry_interval; + return 1; + } + return 0; +} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 422b40ec6..235e1b274 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -108,7 +108,7 @@ int64_t last_pkt_pts; unsigned char sdl_initialized : 1; /// 让事件处理线程退出标志位 unsigned char stoping : 1; -/// 是否已读到文件尾部 +/// 是否已读到文件尾部/读取位置达到要播放的部分的结束时间 unsigned char is_eof : 1; /// 是否有错误 unsigned char have_err : 1; @@ -144,6 +144,8 @@ float speed; int cache_length; /// 最大重试次数 int max_retry_count; +/// 非本地文件重试间隔时间(单位s) +int url_retry_interval; } FfmpegCoreSettings; #if __cplusplus } diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index c34f37542..443bc6c7b 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -8,6 +8,8 @@ #include "cda.h" #include "open.h" +#define ft2ts(t) (((size_t)t.dwHighDateTime << 32) | (size_t)t.dwLowDateTime) + int seek_to_pos(MusicHandle* handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; int re = FFMPEG_CORE_ERR_OK; @@ -71,6 +73,21 @@ int reopen_file(MusicHandle* handle) { if (is_file_exists(handle)) break; if (!doing) Sleep(10); } + } else { + int doing = 0; + FILETIME st; + GetSystemTimePreciseAsFileTime(&st); + FILETIME now; + memcpy(&now, &st, sizeof(FILETIME)); + while ((ft2ts(now) - ft2ts(st)) < ((size_t)10000000 * handle->s->url_retry_interval)) { + doing = 0; + if (handle->stoping) return FFMPEG_CORE_ERR_OK; + if (basic_event_handle(handle)) { + doing = 1; + } + if (!doing) Sleep(10); + GetSystemTimePreciseAsFileTime(&now); + } } if (handle->fmt) avformat_close_input(&handle->fmt); if (handle->decoder) avcodec_free_context(&handle->decoder); From fac2f6a17b15bac1c7c6728b245143bedd8c1e22 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 12:42:03 +0800 Subject: [PATCH 19/41] =?UTF-8?q?=E4=B8=BA=E8=BE=83=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E7=9A=84filters=E5=BC=95=E5=85=A5=E4=B8=80=E4=B8=AA=E5=8D=95?= =?UTF-8?q?=E7=8B=AC=E7=9A=84=E7=BC=93=E5=86=B2=E5=8C=BA=E4=BB=A5=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E5=8F=98=E9=80=9F=E6=97=B6=E5=87=BA=E7=8E=B0=E5=99=AA?= =?UTF-8?q?=E9=9F=B3=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/core.cpp | 24 +++++++++++ ffmpeg_core/src/core.h | 10 +++++ ffmpeg_core/src/filter.c | 92 ++++++++++++++++++++++++++++++++++++++++ ffmpeg_core/src/filter.h | 3 ++ ffmpeg_core/src/loop.c | 26 ++++++++++++ ffmpeg_core/src/loop.h | 1 + ffmpeg_core/src/output.c | 22 +++++++++- 7 files changed, 177 insertions(+), 1 deletion(-) diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index dabc80677..a9245f619 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -35,11 +35,24 @@ void free_music_handle(MusicHandle* handle) { } } } + if (handle->filter_thread) { + DWORD status; + while (GetExitCodeThread(handle->filter_thread, &status)) { + if (status == STILL_ACTIVE) { + status = 0; + handle->stoping = 1; + Sleep(10); + } else { + break; + } + } + } if (handle->graph) { avfilter_graph_free(&handle->graph); } c_linked_list_clear(&handle->filters, nullptr); if (handle->buffer) av_audio_fifo_free(handle->buffer); + if (handle->filters_buffer) av_audio_fifo_free(handle->filters_buffer); if (handle->swrac) swr_free(&handle->swrac); if (handle->decoder) avcodec_free_context(&handle->decoder); if (handle->fmt) avformat_close_input(&handle->fmt); @@ -156,6 +169,12 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s if ((re = init_filters(handle))) { goto end; } + handle->filters_buffer = av_audio_fifo_alloc(handle->target_format, handle->sdl_spec.channels, 1); + if (!handle->filters_buffer) { + re = FFMPEG_CORE_ERR_OOM; + av_log(NULL, AV_LOG_FATAL, "Failed to allocate buffer for filters.\n"); + goto end; + } handle->mutex = CreateMutexW(nullptr, FALSE, nullptr); if (!handle->mutex) { re = FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX; @@ -166,6 +185,11 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s re = FFMPEG_CORE_ERR_FAILED_CREATE_THREAD; goto end; } + handle->filter_thread = CreateThread(nullptr, 0, filter_loop, handle, 0, &handle->filter_thread_id); + if (!handle->filter_thread) { + re = FFMPEG_CORE_ERR_FAILED_CREATE_THREAD; + goto end; + } *h = handle; return re; end: diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 235e1b274..5b85927bf 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -59,8 +59,14 @@ SDL_AudioSpec sdl_spec; HANDLE thread; /// 事件处理线程线程ID DWORD thread_id; +/// 维护filters处理后缓冲区线程 +HANDLE filter_thread; +/// 维护filters处理后缓冲区线程线程ID +DWORD filter_thread_id; /// 音频缓冲区 AVAudioFifo* buffer; +/// 经过filters处理后的缓冲区 +AVAudioFifo* filters_buffer; /// 输出格式 enum AVSampleFormat target_format; /// 每样本的字节数 @@ -129,6 +135,10 @@ unsigned char only_part : 1; unsigned char is_file : 1; /// 是否正在重新打开文件 unsigned char is_reopen : 1; +/// 是否是简单的filters链 +unsigned char is_easy_filters : 1; +/// 刚初始化完复杂的filters,等待filters填充数据 +unsigned char is_wait_filters : 1; } MusicHandle; typedef struct MusicInfoHandle { AVFormatContext* fmt; diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index 9b7c60c2f..bbbf96a71 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -22,6 +22,7 @@ int init_filters(MusicHandle* handle) { if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; if (!need_filters(handle->s)) return FFMPEG_CORE_ERR_OK; int re = FFMPEG_CORE_ERR_OK; + int is_easy_filters = 1; int speed = get_speed(handle->s->speed); if ((re = create_src_and_sink(&handle->graph, &handle->filter_inp, &handle->filter_out, handle))) { return re; @@ -39,6 +40,7 @@ int init_filters(MusicHandle* handle) { } index++; } + is_easy_filters = 0; } if (c_linked_list_count(handle->filters) == 0) { avfilter_graph_free(&handle->graph); @@ -56,6 +58,10 @@ int init_filters(MusicHandle* handle) { av_log(NULL, AV_LOG_FATAL, "Failed to check config of filters: %s (%i)\n", av_err2str(re), re); return re; } + handle->is_easy_filters = is_easy_filters; + if (!handle->is_easy_filters) { + handle->is_wait_filters = 1; + } return FFMPEG_CORE_ERR_OK; } @@ -72,6 +78,7 @@ int reinit_filters(MusicHandle* handle) { handle->filter_inp = NULL; handle->filter_out = NULL; c_linked_list_clear(&handle->filters, NULL); + av_audio_fifo_reset(handle->filters_buffer); ReleaseMutex(handle->mutex); return FFMPEG_CORE_ERR_OK; } @@ -79,6 +86,7 @@ int reinit_filters(MusicHandle* handle) { AVFilterGraph* graph = NULL; AVFilterContext* inc = NULL, * outc = NULL; c_linked_list* list = NULL; + int is_easy_filters = 1; int speed = get_speed(handle->s->speed); if ((re = create_src_and_sink(&graph, &inc, &outc, handle)) < 0) { goto end; @@ -96,6 +104,7 @@ int reinit_filters(MusicHandle* handle) { } index++; } + is_easy_filters = 0; } if (c_linked_list_count(list) == 0) { if (handle->graph) { @@ -109,6 +118,7 @@ int reinit_filters(MusicHandle* handle) { handle->filter_inp = NULL; handle->filter_out = NULL; c_linked_list_clear(&handle->filters, NULL); + av_audio_fifo_reset(handle->filters_buffer); ReleaseMutex(handle->mutex); } re = FFMPEG_CORE_ERR_OK; @@ -133,12 +143,17 @@ int reinit_filters(MusicHandle* handle) { handle->graph = NULL; handle->filter_inp = NULL; handle->filter_out = NULL; + av_audio_fifo_reset(handle->filters_buffer); c_linked_list_clear(&handle->filters, NULL); } handle->graph = graph; handle->filter_inp = inc; handle->filter_out = outc; handle->filters = list; + handle->is_easy_filters = is_easy_filters; + if (!handle->is_easy_filters) { + handle->is_wait_filters = 1; + } ReleaseMutex(handle->mutex); return FFMPEG_CORE_ERR_OK; end: @@ -196,3 +211,80 @@ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterCo } return FFMPEG_CORE_ERR_OK; } + +int add_data_to_filters_buffer(MusicHandle* handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + DWORD re = WaitForSingleObject(handle->mutex, 10); + int r = FFMPEG_CORE_ERR_OK; + AVFrame* in = NULL, * out = NULL; + int samples_need = 1000; + int samples_need_in = 0; + /// 音频缓冲区buffer要peek的起始位置 + int input_samples_offset = 0; + int buffer_size = 0; + int writed = 0; + AVRational base = { 1000, 1000 }, target = { 1, 1 }; + if (re == WAIT_TIMEOUT) return FFMPEG_CORE_ERR_OK; + else if (re != WAIT_OBJECT_0) return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + if (!handle->graph || handle->is_easy_filters) { + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_OK; + } + buffer_size = av_audio_fifo_size(handle->filters_buffer); + if (buffer_size > handle->sdl_spec.freq) { + r = FFMPEG_CORE_ERR_OK; + goto end; + } + base.num = get_speed(handle->s->speed); + input_samples_offset = av_rescale_q_rnd(buffer_size, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + samples_need_in = av_rescale_q_rnd((int64_t)samples_need + buffer_size, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX) - input_samples_offset; + if (av_audio_fifo_size(handle->buffer) <= input_samples_offset) { + r = FFMPEG_CORE_ERR_OK; + goto end; + } + in = av_frame_alloc(); + out = av_frame_alloc(); + if (!in || !out) { + r = FFMPEG_CORE_ERR_OOM; + goto end; + } + in->channels = handle->sdl_spec.channels; + in->channel_layout = handle->output_channel_layout; + in->format = handle->target_format; + in->sample_rate = handle->sdl_spec.freq; + in->nb_samples = samples_need_in; + if ((r = av_frame_get_buffer(in, 0)) < 0) { + goto end; + } + writed = av_audio_fifo_peek_at(handle->buffer, (void**)in->data, samples_need_in, input_samples_offset); + if (writed < 0) { + if (writed != AVERROR(EINVAL)) r = writed; + else r = FFMPEG_CORE_ERR_OK; + goto end; + } + in->nb_samples = writed; + if ((r = av_buffersrc_add_frame(handle->filter_inp, in)) < 0) { + goto end; + } + if ((r = av_buffersink_get_frame(handle->filter_out, out)) < 0) { + if (r == AVERROR(EAGAIN)) r = FFMPEG_CORE_ERR_OK; + goto end; + } + if ((r = av_audio_fifo_write(handle->filters_buffer, (void*)out->data, out->nb_samples)) < 0) { + goto end; + } + r = FFMPEG_CORE_ERR_OK; +end: + if (in) av_frame_free(&in); + if (out) av_frame_free(&out); + ReleaseMutex(handle->mutex); + return r; +} + +void reset_filters_buffer(MusicHandle* handle) { + if (!handle) return; + av_audio_fifo_reset(handle->filters_buffer); + if (!handle->is_easy_filters) { + handle->is_wait_filters = 1; + } +} diff --git a/ffmpeg_core/src/filter.h b/ffmpeg_core/src/filter.h index 55b493290..99fb6f1e4 100644 --- a/ffmpeg_core/src/filter.h +++ b/ffmpeg_core/src/filter.h @@ -16,6 +16,9 @@ int reinit_filters(MusicHandle* handle); * @return */ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterContext** sink, MusicHandle* handle); +/// 往filters_buffer塞数据 +int add_data_to_filters_buffer(MusicHandle* handle); +void reset_filters_buffer(MusicHandle* handle); #if __cplusplus } #endif diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index 443bc6c7b..a762912a9 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -53,6 +53,7 @@ int seek_to_pos(MusicHandle* handle) { handle->set_new_pts = 1; handle->is_eof = 0; } + reset_filters_buffer(handle); ReleaseMutex(handle->mutex); end: handle->is_seek = 0; @@ -139,6 +140,7 @@ int basic_event_handle(MusicHandle* h) { if (re) { h->have_err = 1; h->err = re; + av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling reinit_filters: %s (%i).\n", __FILE__, __LINE__, av_err2str(re), re); } h->need_reinit_filters = 0; return 1; @@ -186,6 +188,7 @@ DWORD WINAPI event_loop(LPVOID handle) { if (re) { h->have_err = 1; h->err = re; + av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling seek_to_pos: %i.\n", __FILE__, __LINE__, re); } doing = 1; goto end; @@ -228,3 +231,26 @@ DWORD WINAPI event_loop(LPVOID handle) { } return FFMPEG_CORE_ERR_OK; } + +DWORD WINAPI filter_loop(LPVOID handle) { + if (!handle) return FFMPEG_CORE_ERR_NULLPTR; + MusicHandle* h = (MusicHandle*)handle; + char doing = 0; + while (1) { + doing = 0; + if (h->stoping) break; + if (h->graph && !h->is_easy_filters) { + int re = add_data_to_filters_buffer(h); + if (re) { + h->have_err = 1; + h->err = re; + av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling add_data_to_filters_buffer: %s (%i).\n", __FILE__, __LINE__, av_err2str(re), re); + } + doing = 1; + } + if (!doing) { + Sleep(10); + } + } + return FFMPEG_CORE_ERR_OK; +} diff --git a/ffmpeg_core/src/loop.h b/ffmpeg_core/src/loop.h index 65728eb3c..dbff38440 100644 --- a/ffmpeg_core/src/loop.h +++ b/ffmpeg_core/src/loop.h @@ -9,6 +9,7 @@ int reopen_file(MusicHandle* handle); /// 基础事件处理,如果处理过返回1反之0 int basic_event_handle(MusicHandle* handle); DWORD WINAPI event_loop(LPVOID handle); +DWORD WINAPI filter_loop(LPVOID handle); #if __cplusplus } #endif diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index 581bfb48a..5eae39425 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -90,6 +90,7 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { return; } int samples_need = len / handle->target_format_pbytes / handle->sdl_spec.channels; + int buffer_size = handle->sdl_spec.freq / 5; if (av_audio_fifo_size(handle->buffer) == 0) { // 缓冲区没有数据,填充空白数据 memset(stream, 0, len); @@ -107,7 +108,7 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { // 不足的区域用空白数据填充 memset(stream + (size_t)writed * handle->target_format_pbytes, 0, (((size_t)samples_need - writed) * handle->target_format_pbytes)); } - } else { + } else if (handle->is_easy_filters) { AVFrame* in = av_frame_alloc(), * out = av_frame_alloc(); int writed = 0; int samples_need_in = 0; @@ -157,6 +158,25 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { end: if (in) av_frame_free(&in); if (out) av_frame_free(&out); + } else if (!handle->is_wait_filters || av_audio_fifo_size(handle->filters_buffer) > buffer_size) { + handle->is_wait_filters = 0; + int writed = av_audio_fifo_read(handle->filters_buffer, (void**)&stream, samples_need); + if (writed > 0) { + // 增大缓冲区开始时间 + AVRational base = { 1, handle->sdl_spec.freq }, base2 = { get_speed(handle->s->speed), 1000 }, tar = { 1, 1 }; + int samples_in = av_rescale_q_rnd(writed, base2, tar, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + handle->pts += av_rescale_q_rnd(samples_in, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + av_audio_fifo_drain(handle->buffer, samples_in); + } + if (writed < 0) { + // 读取发生错误,填充空白数据 + memset(stream, 0, len); + } else if (writed < samples_need) { + // 不足的区域用空白数据填充 + memset(stream + (size_t)writed * handle->target_format_pbytes, 0, (((size_t)samples_need - writed) * handle->target_format_pbytes)); + } + } else { + memset(stream, 0, len); } ReleaseMutex(handle->mutex); } From ef04146af704467c0d71f76dbc123f736057864a Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 14:32:14 +0800 Subject: [PATCH 20/41] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=8D=95=E7=8B=AC?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E6=9D=A5=E8=AE=A1=E7=AE=97buffer=E4=B8=AD?= =?UTF-8?q?=E5=B7=B2=E5=A1=9E=E5=85=A5filters=E7=9A=84=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/core.h | 2 ++ ffmpeg_core/src/filter.c | 9 +++++++-- ffmpeg_core/src/output.c | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 5b85927bf..f75e975b5 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -110,6 +110,8 @@ UrlParseResult* parsed_url; int retry_count; /// 最近一个包的时间 int64_t last_pkt_pts; +/// 当去filters链从buffer读入的数据量(仅复杂的filters链) +int64_t filters_buffer_offset; /// SDL是否被初始化 unsigned char sdl_initialized : 1; /// 让事件处理线程退出标志位 diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index bbbf96a71..10ba9e668 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -79,6 +79,7 @@ int reinit_filters(MusicHandle* handle) { handle->filter_out = NULL; c_linked_list_clear(&handle->filters, NULL); av_audio_fifo_reset(handle->filters_buffer); + handle->filters_buffer_offset = 0; ReleaseMutex(handle->mutex); return FFMPEG_CORE_ERR_OK; } @@ -119,6 +120,7 @@ int reinit_filters(MusicHandle* handle) { handle->filter_out = NULL; c_linked_list_clear(&handle->filters, NULL); av_audio_fifo_reset(handle->filters_buffer); + handle->filters_buffer_offset = 0; ReleaseMutex(handle->mutex); } re = FFMPEG_CORE_ERR_OK; @@ -144,6 +146,7 @@ int reinit_filters(MusicHandle* handle) { handle->filter_inp = NULL; handle->filter_out = NULL; av_audio_fifo_reset(handle->filters_buffer); + handle->filters_buffer_offset = 0; c_linked_list_clear(&handle->filters, NULL); } handle->graph = graph; @@ -236,8 +239,8 @@ int add_data_to_filters_buffer(MusicHandle* handle) { goto end; } base.num = get_speed(handle->s->speed); - input_samples_offset = av_rescale_q_rnd(buffer_size, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); - samples_need_in = av_rescale_q_rnd((int64_t)samples_need + buffer_size, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX) - input_samples_offset; + input_samples_offset = handle->filters_buffer_offset; + samples_need_in = av_rescale_q_rnd(samples_need, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); if (av_audio_fifo_size(handle->buffer) <= input_samples_offset) { r = FFMPEG_CORE_ERR_OK; goto end; @@ -262,6 +265,7 @@ int add_data_to_filters_buffer(MusicHandle* handle) { else r = FFMPEG_CORE_ERR_OK; goto end; } + handle->filters_buffer_offset += writed; in->nb_samples = writed; if ((r = av_buffersrc_add_frame(handle->filter_inp, in)) < 0) { goto end; @@ -284,6 +288,7 @@ int add_data_to_filters_buffer(MusicHandle* handle) { void reset_filters_buffer(MusicHandle* handle) { if (!handle) return; av_audio_fifo_reset(handle->filters_buffer); + handle->filters_buffer_offset = 0; if (!handle->is_easy_filters) { handle->is_wait_filters = 1; } diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index 5eae39425..5532f6994 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -165,6 +165,7 @@ void SDL_callback(void* userdata, uint8_t* stream, int len) { // 增大缓冲区开始时间 AVRational base = { 1, handle->sdl_spec.freq }, base2 = { get_speed(handle->s->speed), 1000 }, tar = { 1, 1 }; int samples_in = av_rescale_q_rnd(writed, base2, tar, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + handle->filters_buffer_offset -= samples_in; handle->pts += av_rescale_q_rnd(samples_in, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); av_audio_fifo_drain(handle->buffer, samples_in); } From c12e50173effca3775b2d40238575c3a4f4b4935 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 15:02:09 +0800 Subject: [PATCH 21/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=A4=8D=E6=9D=82filters=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B?= =?UTF-8?q?=EF=BC=8C=E6=92=AD=E6=94=BE=E4=B8=8D=E4=BC=9A=E5=81=9C=E6=AD=A2?= =?UTF-8?q?=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/core.h | 2 +- ffmpeg_core/src/filter.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index f75e975b5..652dd48a5 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -111,7 +111,7 @@ int retry_count; /// 最近一个包的时间 int64_t last_pkt_pts; /// 当去filters链从buffer读入的数据量(仅复杂的filters链) -int64_t filters_buffer_offset; +int filters_buffer_offset; /// SDL是否被初始化 unsigned char sdl_initialized : 1; /// 让事件处理线程退出标志位 diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index 10ba9e668..35c1102a4 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -242,6 +242,17 @@ int add_data_to_filters_buffer(MusicHandle* handle) { input_samples_offset = handle->filters_buffer_offset; samples_need_in = av_rescale_q_rnd(samples_need, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); if (av_audio_fifo_size(handle->buffer) <= input_samples_offset) { + if (handle->is_eof) { + if ((r = av_buffersrc_add_frame(handle->filter_inp, NULL)) < 0) { + goto end; + } + out = av_frame_alloc(); + if (!out) { + r = FFMPEG_CORE_ERR_OOM; + goto end; + } + goto outp; + } r = FFMPEG_CORE_ERR_OK; goto end; } @@ -255,14 +266,14 @@ int add_data_to_filters_buffer(MusicHandle* handle) { in->channel_layout = handle->output_channel_layout; in->format = handle->target_format; in->sample_rate = handle->sdl_spec.freq; + samples_need_in = min(samples_need_in, av_audio_fifo_size(handle->buffer) - input_samples_offset); in->nb_samples = samples_need_in; if ((r = av_frame_get_buffer(in, 0)) < 0) { goto end; } writed = av_audio_fifo_peek_at(handle->buffer, (void**)in->data, samples_need_in, input_samples_offset); if (writed < 0) { - if (writed != AVERROR(EINVAL)) r = writed; - else r = FFMPEG_CORE_ERR_OK; + r = writed; goto end; } handle->filters_buffer_offset += writed; @@ -270,6 +281,7 @@ int add_data_to_filters_buffer(MusicHandle* handle) { if ((r = av_buffersrc_add_frame(handle->filter_inp, in)) < 0) { goto end; } +outp: if ((r = av_buffersink_get_frame(handle->filter_out, out)) < 0) { if (r == AVERROR(EAGAIN)) r = FFMPEG_CORE_ERR_OK; goto end; From 086d0aedbad25ab6d4acbf05021c4ca42c54523b Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 15:41:38 +0800 Subject: [PATCH 22/41] =?UTF-8?q?=E5=87=8F=E5=B0=91filter=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E5=AF=B9SDL=E6=92=AD=E6=94=BE=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E5=BD=B1=E5=93=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/core.cpp | 5 +++++ ffmpeg_core/src/core.h | 2 ++ ffmpeg_core/src/filter.c | 43 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index a9245f619..01d925783 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -180,6 +180,11 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s re = FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX; goto end; } + handle->mutex2 = CreateMutexW(nullptr, FALSE, nullptr); + if (!handle->mutex2) { + re = FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX; + goto end; + } handle->thread = CreateThread(nullptr, 0, event_loop, handle, 0, &handle->thread_id); if (!handle->thread) { re = FFMPEG_CORE_ERR_FAILED_CREATE_THREAD; diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index 652dd48a5..c23fec0d5 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -77,6 +77,8 @@ SDL_AudioDeviceID device_id; int err; /// Mutex对象,作为线程锁(用于保护缓冲区和时间) HANDLE mutex; +/// 用来确保filter graph对象可用 +HANDLE mutex2; /// 缓冲区开始时间 int64_t pts; /// 缓冲区结束时间 diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index 35c1102a4..a0eeded10 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -73,6 +73,11 @@ int reinit_filters(MusicHandle* handle) { if (re != WAIT_OBJECT_0) { return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; } + re = WaitForSingleObject(handle->mutex2, INFINITE); + if (re != WAIT_OBJECT_0) { + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + } avfilter_graph_free(&handle->graph); handle->graph = NULL; handle->filter_inp = NULL; @@ -81,6 +86,7 @@ int reinit_filters(MusicHandle* handle) { av_audio_fifo_reset(handle->filters_buffer); handle->filters_buffer_offset = 0; ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); return FFMPEG_CORE_ERR_OK; } int re = FFMPEG_CORE_ERR_OK; @@ -114,6 +120,12 @@ int reinit_filters(MusicHandle* handle) { re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; goto end; } + r = WaitForSingleObject(handle->mutex2, INFINITE); + if (r != WAIT_OBJECT_0) { + ReleaseMutex(handle->mutex); + re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + goto end; + } avfilter_graph_free(&handle->graph); handle->graph = NULL; handle->filter_inp = NULL; @@ -122,6 +134,7 @@ int reinit_filters(MusicHandle* handle) { av_audio_fifo_reset(handle->filters_buffer); handle->filters_buffer_offset = 0; ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); } re = FFMPEG_CORE_ERR_OK; goto end; @@ -140,6 +153,12 @@ int reinit_filters(MusicHandle* handle) { re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; goto end; } + r = WaitForSingleObject(handle->mutex2, INFINITE); + if (r != WAIT_OBJECT_0) { + ReleaseMutex(handle->mutex); + re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + goto end; + } if (handle->graph) { avfilter_graph_free(&handle->graph); handle->graph = NULL; @@ -158,6 +177,7 @@ int reinit_filters(MusicHandle* handle) { handle->is_wait_filters = 1; } ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); return FFMPEG_CORE_ERR_OK; end: if (graph) { @@ -226,13 +246,23 @@ int add_data_to_filters_buffer(MusicHandle* handle) { int input_samples_offset = 0; int buffer_size = 0; int writed = 0; + unsigned char have_mutex = 0; AVRational base = { 1000, 1000 }, target = { 1, 1 }; if (re == WAIT_TIMEOUT) return FFMPEG_CORE_ERR_OK; else if (re != WAIT_OBJECT_0) return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + have_mutex = 1; if (!handle->graph || handle->is_easy_filters) { ReleaseMutex(handle->mutex); return FFMPEG_CORE_ERR_OK; } + re = WaitForSingleObject(handle->mutex2, INFINITE); + if (re == WAIT_TIMEOUT) { + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_OK; + } else if (re != WAIT_OBJECT_0) { + ReleaseMutex(handle->mutex); + return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + } buffer_size = av_audio_fifo_size(handle->filters_buffer); if (buffer_size > handle->sdl_spec.freq) { r = FFMPEG_CORE_ERR_OK; @@ -242,6 +272,8 @@ int add_data_to_filters_buffer(MusicHandle* handle) { input_samples_offset = handle->filters_buffer_offset; samples_need_in = av_rescale_q_rnd(samples_need, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); if (av_audio_fifo_size(handle->buffer) <= input_samples_offset) { + ReleaseMutex(handle->mutex); + have_mutex = 0; if (handle->is_eof) { if ((r = av_buffersrc_add_frame(handle->filter_inp, NULL)) < 0) { goto end; @@ -278,6 +310,8 @@ int add_data_to_filters_buffer(MusicHandle* handle) { } handle->filters_buffer_offset += writed; in->nb_samples = writed; + ReleaseMutex(handle->mutex); + have_mutex = 0; if ((r = av_buffersrc_add_frame(handle->filter_inp, in)) < 0) { goto end; } @@ -286,6 +320,12 @@ int add_data_to_filters_buffer(MusicHandle* handle) { if (r == AVERROR(EAGAIN)) r = FFMPEG_CORE_ERR_OK; goto end; } + re = WaitForSingleObject(handle->mutex, INFINITE); + if (re != WAIT_OBJECT_0) { + r = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; + goto end; + } + have_mutex = 1; if ((r = av_audio_fifo_write(handle->filters_buffer, (void*)out->data, out->nb_samples)) < 0) { goto end; } @@ -293,7 +333,8 @@ int add_data_to_filters_buffer(MusicHandle* handle) { end: if (in) av_frame_free(&in); if (out) av_frame_free(&out); - ReleaseMutex(handle->mutex); + if (have_mutex) ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); return r; } From 4eee5f28a5bc98ae5f2b138be1178eae47520d94 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 18:21:47 +0800 Subject: [PATCH 23/41] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=9D=87=E8=A1=A1?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 40 +++++++++++++++++++ MusicPlayer2/FfmpegCore.h | 5 +++ ffmpeg_core/CMakeLists.txt | 4 ++ ffmpeg_core/ffmpeg_core.h | 2 + ffmpeg_core/src/core.cpp | 14 +++++++ ffmpeg_core/src/core.h | 13 +++++++ ffmpeg_core/src/equalizer.c | 53 ++++++++++++++++++++++++++ ffmpeg_core/src/equalizer.h | 22 +++++++++++ ffmpeg_core/src/equalizer_settings.cpp | 34 +++++++++++++++++ ffmpeg_core/src/equalizer_settings.h | 16 ++++++++ ffmpeg_core/src/filter.c | 30 +++++++++++++++ ffmpeg_core/utils | 2 +- 12 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 ffmpeg_core/src/equalizer.c create mode 100644 ffmpeg_core/src/equalizer.h create mode 100644 ffmpeg_core/src/equalizer_settings.cpp create mode 100644 ffmpeg_core/src/equalizer_settings.h diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 582519726..842423879 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -251,6 +251,15 @@ PlayingState CFfmpegCore::GetPlayingState() { } void CFfmpegCore::ApplyEqualizer(int channel, int gain) { + channel = GetEqChannelFreq(channel); + if (handle) { + int re = ffmpeg_core_set_equalizer_channel(handle, channel, gain); + if (re) { + err = re; + } + } else { + ffmpeg_core_settings_set_equalizer_channel(settings, channel, gain); + } } void CFfmpegCore::SetReverb(int mix, int time) { @@ -319,6 +328,7 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_seek = (_ffmpeg_core_seek)::GetProcAddress(m_dll_module, "ffmpeg_core_seek"); ffmpeg_core_set_volume = (_ffmpeg_core_set_volume)::GetProcAddress(m_dll_module, "ffmpeg_core_set_volume"); ffmpeg_core_set_speed = (_ffmpeg_core_set_speed)::GetProcAddress(m_dll_module, "ffmpeg_core_set_speed"); + ffmpeg_core_set_equalizer_channel = (_ffmpeg_core_set_equalizer_channel)::GetProcAddress(m_dll_module, "ffmpeg_core_set_equalizer_channel"); ffmpeg_core_get_error = (_ffmpeg_core_get_error)::GetProcAddress(m_dll_module, "ffmpeg_core_get_error"); ffmpeg_core_get_err_msg = (_ffmpeg_core_get_err_msg)::GetProcAddress(m_dll_module, "ffmpeg_core_get_err_msg"); ffmpeg_core_get_err_msg2 = (_ffmpeg_core_get_err_msg2)::GetProcAddress(m_dll_module, "ffmpeg_core_get_err_msg2"); @@ -344,6 +354,7 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_settings_set_cache_length = (_ffmpeg_core_settings_set_cache_length)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_cache_length"); ffmpeg_core_settings_set_max_retry_count = (_ffmpeg_core_settings_set_max_retry_count)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_max_retry_count"); ffmpeg_core_settings_set_url_retry_interval = (_ffmpeg_core_settings_set_url_retry_interval)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_url_retry_interval"); + ffmpeg_core_settings_set_equalizer_channel = (_ffmpeg_core_settings_set_equalizer_channel)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_equalizer_channel"); //жǷɹ rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); @@ -359,6 +370,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_seek != NULL); rtn &= (ffmpeg_core_set_volume != NULL); rtn &= (ffmpeg_core_set_speed != NULL); + rtn &= (ffmpeg_core_set_equalizer_channel != NULL); rtn &= (ffmpeg_core_get_error != NULL); rtn &= (ffmpeg_core_get_err_msg != NULL); rtn &= (ffmpeg_core_get_err_msg2 != NULL); @@ -384,6 +396,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_settings_set_cache_length != NULL); rtn &= (ffmpeg_core_settings_set_max_retry_count != NULL); rtn &= (ffmpeg_core_settings_set_url_retry_interval != NULL); + rtn &= (ffmpeg_core_settings_set_equalizer_channel != NULL); return rtn; } @@ -508,3 +521,30 @@ void CFfmpegCore::SetUrlRetryInterval(int url_retry_interval) { ffmpeg_core_settings_set_url_retry_interval(settings, url_retry_interval); } } + +int CFfmpegCore::GetEqChannelFreq(int channel) { + switch (channel) { + case 0: + return 80; + case 1: + return 125; + case 2: + return 250; + case 3: + return 500; + case 4: + return 1000; + case 5: + return 1500; + case 6: + return 2000; + case 7: + return 4000; + case 8: + return 8000; + case 9: + return 16000; + default: + return 0; + } +} diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 3b72d0205..4c93a98a3 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -24,6 +24,7 @@ typedef int(*_ffmpeg_core_pause)(MusicHandle*); typedef int(*_ffmpeg_core_seek)(MusicHandle*, int64_t); typedef int(*_ffmpeg_core_set_volume)(MusicHandle*, int); typedef int(*_ffmpeg_core_set_speed)(MusicHandle*, float); +typedef int(*_ffmpeg_core_set_equalizer_channel)(MusicHandle*, int, int); typedef int(*_ffmpeg_core_get_error)(MusicHandle*); typedef wchar_t*(*_ffmpeg_core_get_err_msg)(int); typedef const wchar_t*(*_ffmpeg_core_get_err_msg2)(int); @@ -49,6 +50,7 @@ typedef int(*_ffmpeg_core_settings_set_speed)(FfmpegCoreSettings*, float); typedef int(*_ffmpeg_core_settings_set_cache_length)(FfmpegCoreSettings*, int); typedef int(*_ffmpeg_core_settings_set_max_retry_count)(FfmpegCoreSettings*, int); typedef int(*_ffmpeg_core_settings_set_url_retry_interval)(FfmpegCoreSettings*, int); +typedef int(*_ffmpeg_core_settings_set_equalizer_channel)(FfmpegCoreSettings*, int, int); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -120,6 +122,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_seek ffmpeg_core_seek = nullptr; _ffmpeg_core_set_volume ffmpeg_core_set_volume = nullptr; _ffmpeg_core_set_speed ffmpeg_core_set_speed = nullptr; + _ffmpeg_core_set_equalizer_channel ffmpeg_core_set_equalizer_channel = nullptr; _ffmpeg_core_get_error ffmpeg_core_get_error = nullptr; _ffmpeg_core_get_err_msg ffmpeg_core_get_err_msg = nullptr; _ffmpeg_core_get_err_msg2 ffmpeg_core_get_err_msg2 = nullptr; @@ -145,6 +148,8 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_settings_set_cache_length ffmpeg_core_settings_set_cache_length = nullptr; _ffmpeg_core_settings_set_max_retry_count ffmpeg_core_settings_set_max_retry_count = nullptr; _ffmpeg_core_settings_set_url_retry_interval ffmpeg_core_settings_set_url_retry_interval = nullptr; + _ffmpeg_core_settings_set_equalizer_channel ffmpeg_core_settings_set_equalizer_channel = nullptr; + int GetEqChannelFreq(int channel); MusicHandle* handle; FfmpegCoreSettings* settings = nullptr; std::wstring recent_file; diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index d94f589ff..31df74200 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -43,6 +43,10 @@ src/cda.h src/cda.c src/file.h src/file.cpp +src/equalizer_settings.h +src/equalizer_settings.cpp +src/equalizer.h +src/equalizer.c ) add_library(ffmpeg_core SHARED "${CORE_FILES}") diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 9fee89110..dcc5f53b2 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -52,6 +52,7 @@ FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_seek(MusicHandle* handle, int64_t time); FFMPEG_CORE_API int ffmpeg_core_set_volume(MusicHandle* handle, int volume); FFMPEG_CORE_API int ffmpeg_core_set_speed(MusicHandle* handle, float speed); +FFMPEG_CORE_API int ffmpeg_core_set_equalizer_channel(MusicHandle* handle, int channel, int gain); FFMPEG_CORE_API int ffmpeg_core_get_error(MusicHandle* handle); /** * @brief 返回错误代码对应的错误消息 @@ -133,6 +134,7 @@ FFMPEG_CORE_API int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float FFMPEG_CORE_API int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length); FFMPEG_CORE_API int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retry_count); FFMPEG_CORE_API int ffmpeg_core_settings_set_url_retry_interval(FfmpegCoreSettings* s, int url_retry_interval); +FFMPEG_CORE_API int ffmpeg_core_settings_set_equalizer_channel(FfmpegCoreSettings* s, int channel, int gain); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 01d925783..0636da559 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -13,6 +13,7 @@ #include "cda.h" #include "fileop.h" #include "file.h" +#include "equalizer_settings.h" #define CODEPAGE_SIZE 3 @@ -77,6 +78,7 @@ void free_music_info_handle(MusicInfoHandle* handle) { void free_ffmpeg_core_settings(FfmpegCoreSettings* s) { if (!s) return; + free_equalizer_channels(&s->equalizer_channels); free(s); } @@ -591,3 +593,15 @@ int ffmpeg_core_settings_set_url_retry_interval(FfmpegCoreSettings* s, int url_r } return 0; } + +int ffmpeg_core_settings_set_equalizer_channel(FfmpegCoreSettings* s, int channel, int gain) { + if (channel < 0 || channel > 999999 || gain < -900 || gain > 900) return 0; + return set_equalizer_channel(&s->equalizer_channels, channel, gain) ? 0 : 1; +} + +int ffmpeg_core_set_equalizer_channel(MusicHandle* handle, int channel, int gain) { + if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; + int r = ffmpeg_core_settings_set_equalizer_channel(handle->s, channel, gain); + if (!r) return FFMPEG_CORE_ERR_FAILED_SET_SPEED; + return send_reinit_filters(handle); +} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h index c23fec0d5..4cddac0f1 100644 --- a/ffmpeg_core/src/core.h +++ b/ffmpeg_core/src/core.h @@ -42,6 +42,17 @@ uint32_t range_offset; /// duration of the track, total number of frames uint32_t duration; } CDAData; +typedef struct EqualizerChannel { +/// The filter’s central frequency in Hz. +int channel; +/// The required gain or attenuation in dB. +int gain; +} EqualizerChannel; +typedef struct EqualizerChannels { +EqualizerChannel d; +struct EqualizerChannels* prev; +struct EqualizerChannels* next; +} EqualizerChannels; typedef struct MusicHandle { /// Demux 用 AVFormatContext* fmt; @@ -160,6 +171,8 @@ int cache_length; int max_retry_count; /// 非本地文件重试间隔时间(单位s) int url_retry_interval; +/// 均衡器 +EqualizerChannels* equalizer_channels; } FfmpegCoreSettings; #if __cplusplus } diff --git a/ffmpeg_core/src/equalizer.c b/ffmpeg_core/src/equalizer.c new file mode 100644 index 000000000..b8c8315d7 --- /dev/null +++ b/ffmpeg_core/src/equalizer.c @@ -0,0 +1,53 @@ +#include "equalizer.h" + +int get_equalizer_precision(enum AVSampleFormat f) { + switch (f) { + case AV_SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_U8P: + case AV_SAMPLE_FMT_S16P: + return 0; + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32P: + return 1; + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_DBLP: + return 3; + case AV_SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLTP: + default: + return 2; + } +} + +int create_equalizer_filter(AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int channel, int gain, enum AVSampleFormat f) { + if (!graph || !src || !list) return FFMPEG_CORE_ERR_NULLPTR; + char args[128]; + char name[32]; + const AVFilter* eq = avfilter_get_by_name("equalizer"); + snprintf(args, sizeof(args), "f=%d:g=%d:r=%d", channel, gain, get_equalizer_precision(f)); + snprintf(name, sizeof(name), "equalizer%d", channel); + int re = 0; + AVFilterContext* context = NULL; + if ((re = avfilter_graph_create_filter(&context, eq, name, args, NULL, graph)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to create equalizer filter \"%s\": %s (%i)\n", name, av_err2str(re), re); + return re; + } + if (!c_linked_list_append(list, (void*)context)) { + av_log(NULL, AV_LOG_FATAL, "Failed to append filter \"%s\" to list.\n", context->name); + return FFMPEG_CORE_ERR_OOM; + } + if (c_linked_list_count(*list) > 1) { + AVFilterContext* last = c_linked_list_tail(*list)->prev->d; + if ((re = avfilter_link(last, 0, context, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, context->name, 0, av_err2str(re), re); + return re; + } + } else { + if ((re = avfilter_link(src, 0, context, 0)) < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", src->name, 0, context->name, 0, av_err2str(re), re); + return re; + } + } + return FFMPEG_CORE_ERR_OK; +} diff --git a/ffmpeg_core/src/equalizer.h b/ffmpeg_core/src/equalizer.h new file mode 100644 index 000000000..e1db8dcf5 --- /dev/null +++ b/ffmpeg_core/src/equalizer.h @@ -0,0 +1,22 @@ +#ifndef _MUSICPLAYER2_EQUALIZER_H +#define _MUSICPLAYER2_EQUALIZER_H +#if __cplusplus +extern "C" { +#endif +#include "core.h" +int get_equalizer_precision(enum AVSampleFormat f); +/** + * @brief 创建 equalizer filter + * @param index Filter 序号 + * @param graph Graph + * @param src Graph + * @param list Filters 列表 + * @param channel 中心频率(hz) + * @param gain + * @return +*/ +int create_equalizer_filter(AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int channel, int gain, enum AVSampleFormat f); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/equalizer_settings.cpp b/ffmpeg_core/src/equalizer_settings.cpp new file mode 100644 index 000000000..331b5f787 --- /dev/null +++ b/ffmpeg_core/src/equalizer_settings.cpp @@ -0,0 +1,34 @@ +#include "equalizer_settings.h" + +#include "dict.h" + +#define INTYPE Dict* +#define OUTTYPE EqualizerChannels* + +void free_equalizer_channels(EqualizerChannels** channels) { + auto c = (INTYPE*)channels; + if (c) { + auto c2 = *c; + dict_free(c2); + *channels = (OUTTYPE)c2; + } +} + +int set_equalizer_channel(EqualizerChannels** channels, int channel, int gain) { + if (!channels) return FFMPEG_CORE_ERR_NULLPTR; + auto c = (INTYPE)(*channels); + if (gain == 0) { + dict_delete(c, channel); + } else { + if (!dict_set(c, channel, gain)) { + *channels = (OUTTYPE)c; + return FFMPEG_CORE_ERR_OOM; + } + } + *channels = (OUTTYPE)c; + return FFMPEG_CORE_ERR_OK; +} + +size_t equalizer_channel_count(EqualizerChannels* channels) { + return dict_count((INTYPE)channels); +} diff --git a/ffmpeg_core/src/equalizer_settings.h b/ffmpeg_core/src/equalizer_settings.h new file mode 100644 index 000000000..e68afc3ba --- /dev/null +++ b/ffmpeg_core/src/equalizer_settings.h @@ -0,0 +1,16 @@ +#ifndef _MUSICPLAYER2_EQUALIZER_SETTINGS_H +#define _MUSICPLAYER2_EQUALIZER_SETTINGS_H +#if __cplusplus +#include "core.h" +extern "C" { +#endif +#if !__cplusplus +#include "core.h" +#endif +void free_equalizer_channels(EqualizerChannels** channels); +int set_equalizer_channel(EqualizerChannels** channels, int channel, int gain); +size_t equalizer_channel_count(EqualizerChannels* channels); +#if __cplusplus +} +#endif +#endif diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index a0eeded10..36859be58 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -3,6 +3,7 @@ #include "output.h" #include "volume.h" #include "speed.h" +#include "equalizer.h" int need_filters(FfmpegCoreSettings* s) { if (!s) return 0; @@ -15,6 +16,9 @@ int need_filters(FfmpegCoreSettings* s) { if (get_speed(s->speed) != 1000 && avfilter_get_by_name("atempo")) { return 1; } + if (s->equalizer_channels && avfilter_get_by_name("equalizer")) { + return 1; + } return 0; } @@ -42,6 +46,19 @@ int init_filters(MusicHandle* handle) { } is_easy_filters = 0; } + if (handle->s->equalizer_channels && avfilter_get_by_name("equalizer")) { + EqualizerChannels* now = handle->s->equalizer_channels; + if ((re = create_equalizer_filter(handle->graph, handle->filter_inp, &handle->filters, now->d.channel, now->d.gain, handle->target_format))) { + return re; + } + while (now->next) { + now = now->next; + if ((re = create_equalizer_filter(handle->graph, handle->filter_inp, &handle->filters, now->d.channel, now->d.gain, handle->target_format))) { + return re; + } + } + is_easy_filters = 0; + } if (c_linked_list_count(handle->filters) == 0) { avfilter_graph_free(&handle->graph); handle->graph = NULL; @@ -113,6 +130,19 @@ int reinit_filters(MusicHandle* handle) { } is_easy_filters = 0; } + if (handle->s->equalizer_channels && avfilter_get_by_name("equalizer")) { + EqualizerChannels* now = handle->s->equalizer_channels; + if ((re = create_equalizer_filter(graph, inc, &list, now->d.channel, now->d.gain, handle->target_format))) { + goto end; + } + while (now->next) { + now = now->next; + if ((re = create_equalizer_filter(graph, inc, &list, now->d.channel, now->d.gain, handle->target_format))) { + goto end; + } + } + is_easy_filters = 0; + } if (c_linked_list_count(list) == 0) { if (handle->graph) { DWORD r = WaitForSingleObject(handle->mutex, INFINITE); diff --git a/ffmpeg_core/utils b/ffmpeg_core/utils index a64c19d11..4bdef58fe 160000 --- a/ffmpeg_core/utils +++ b/ffmpeg_core/utils @@ -1 +1 @@ -Subproject commit a64c19d111d6117cc5e68d017fb3ad1d6c16ed0a +Subproject commit 4bdef58fe5965a511b2682f393499e9a34f4bd13 From 0c20187ad4fa2939e33057db849590b5fd98a9eb Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 18:33:00 +0800 Subject: [PATCH 24/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/filter.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index 36859be58..cd5cd7f1a 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -86,13 +86,13 @@ int reinit_filters(MusicHandle* handle) { if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; if (!need_filters(handle->s)) { if (!handle->graph) return FFMPEG_CORE_ERR_OK; - DWORD re = WaitForSingleObject(handle->mutex, INFINITE); + DWORD re = WaitForSingleObject(handle->mutex2, INFINITE); if (re != WAIT_OBJECT_0) { return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; } - re = WaitForSingleObject(handle->mutex2, INFINITE); + re = WaitForSingleObject(handle->mutex, INFINITE); if (re != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; } avfilter_graph_free(&handle->graph); @@ -145,14 +145,14 @@ int reinit_filters(MusicHandle* handle) { } if (c_linked_list_count(list) == 0) { if (handle->graph) { - DWORD r = WaitForSingleObject(handle->mutex, INFINITE); + DWORD r = WaitForSingleObject(handle->mutex2, INFINITE); if (r != WAIT_OBJECT_0) { re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; goto end; } - r = WaitForSingleObject(handle->mutex2, INFINITE); + r = WaitForSingleObject(handle->mutex, INFINITE); if (r != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; goto end; } @@ -178,14 +178,14 @@ int reinit_filters(MusicHandle* handle) { av_log(NULL, AV_LOG_FATAL, "Failed to check config of filters: %s (%i)\n", av_err2str(re), re); goto end; } - DWORD r = WaitForSingleObject(handle->mutex, INFINITE); + DWORD r = WaitForSingleObject(handle->mutex2, INFINITE); if (r != WAIT_OBJECT_0) { re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; goto end; } - r = WaitForSingleObject(handle->mutex2, INFINITE); + r = WaitForSingleObject(handle->mutex, INFINITE); if (r != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; goto end; } From 16187439e1066bab9cbe5dc6ff6bf140a3d93f00 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 18:42:24 +0800 Subject: [PATCH 25/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/filter.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c index cd5cd7f1a..d9ea721a1 100644 --- a/ffmpeg_core/src/filter.c +++ b/ffmpeg_core/src/filter.c @@ -267,7 +267,7 @@ int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterCo int add_data_to_filters_buffer(MusicHandle* handle) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - DWORD re = WaitForSingleObject(handle->mutex, 10); + DWORD re = WaitForSingleObject(handle->mutex2, INFINITE); int r = FFMPEG_CORE_ERR_OK; AVFrame* in = NULL, * out = NULL; int samples_need = 1000; @@ -280,19 +280,20 @@ int add_data_to_filters_buffer(MusicHandle* handle) { AVRational base = { 1000, 1000 }, target = { 1, 1 }; if (re == WAIT_TIMEOUT) return FFMPEG_CORE_ERR_OK; else if (re != WAIT_OBJECT_0) return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - have_mutex = 1; - if (!handle->graph || handle->is_easy_filters) { - ReleaseMutex(handle->mutex); - return FFMPEG_CORE_ERR_OK; - } - re = WaitForSingleObject(handle->mutex2, INFINITE); + re = WaitForSingleObject(handle->mutex, 10); if (re == WAIT_TIMEOUT) { - ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); return FFMPEG_CORE_ERR_OK; } else if (re != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; } + have_mutex = 1; + if (!handle->graph || handle->is_easy_filters) { + ReleaseMutex(handle->mutex); + ReleaseMutex(handle->mutex2); + return FFMPEG_CORE_ERR_OK; + } buffer_size = av_audio_fifo_size(handle->filters_buffer); if (buffer_size > handle->sdl_spec.freq) { r = FFMPEG_CORE_ERR_OK; From 0ca4b17876003d4ddc6ecf8a1cf745938398e2e4 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 17 Feb 2022 20:16:03 +0800 Subject: [PATCH 26/41] =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E5=8A=A8=E6=80=81=E5=8A=A0=E8=BD=BD=E5=92=8C?= =?UTF-8?q?=E5=8D=B8=E8=BD=BD=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=A9=BA=E6=8C=87?= =?UTF-8?q?=E9=92=88=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 30 +++++++++++++----------------- MusicPlayer2/FfmpegCore.h | 3 +-- MusicPlayer2/MusicPlayerDlg.cpp | 7 ++++++- MusicPlayer2/PlaySettingsDlg.cpp | 5 ----- MusicPlayer2/Player.cpp | 2 +- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 842423879..953e994f9 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -18,13 +18,7 @@ CFfmpegCore::CFfmpegCore() { } CFfmpegCore::~CFfmpegCore() { - if (settings) { - free_ffmpeg_core_settings(settings); - } - if (handle) { - Close(); - } - CDllLib::UnInit(); + if (handle || settings) UnInitCore(); } void CFfmpegCore::InitCore() { @@ -61,6 +55,14 @@ void CFfmpegCore::InitCore() { } void CFfmpegCore::UnInitCore() { + if (settings) { + free_ffmpeg_core_settings(settings); + settings = nullptr; + } + if (handle) { + Close(); + } + CDllLib::UnInit(); } unsigned int CFfmpegCore::GetHandle() { @@ -492,16 +494,10 @@ void CFfmpegCore::LogCallback(void* ptr, int level, const char* fmt, va_list vl) FreeLibrary(re); } -void CFfmpegCore::UpdateSettings(PlaySettingData* s) { - if (s) { - SetCacheLength(s->ffmpeg_core_cache_length); - SetMaxRetryCount(s->ffmpeg_core_max_retry_count); - SetUrlRetryInterval(s->ffmpeg_core_url_retry_interval); - } else { - SetCacheLength(theApp.m_play_setting_data.ffmpeg_core_cache_length); - SetMaxRetryCount(theApp.m_play_setting_data.ffmpeg_core_max_retry_count); - SetUrlRetryInterval(theApp.m_play_setting_data.ffmpeg_core_url_retry_interval); - } +void CFfmpegCore::UpdateSettings() { + SetCacheLength(theApp.m_play_setting_data.ffmpeg_core_cache_length); + SetMaxRetryCount(theApp.m_play_setting_data.ffmpeg_core_max_retry_count); + SetUrlRetryInterval(theApp.m_play_setting_data.ffmpeg_core_url_retry_interval); } void CFfmpegCore::SetCacheLength(int cache_length) { diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 4c93a98a3..aa7df2121 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -1,7 +1,6 @@ #pragma once #include "IPlayerCore.h" #include "DllLib.h" -#include "CommonData.h" #define AV_LOG_ERROR 16 #define AV_LOG_VERBOSE 40 @@ -100,7 +99,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { unsigned short GetYear(MusicInfoHandle* h = nullptr); std::wstring GetTrack(MusicInfoHandle* h = nullptr); int GetTrackNum(MusicInfoHandle* h = nullptr); - void UpdateSettings(PlaySettingData* s = nullptr); + void UpdateSettings(); void SetCacheLength(int cache_length = 15); void SetMaxRetryCount(int max_retry_count = 3); void SetUrlRetryInterval(int url_retry_interval = 5); diff --git a/MusicPlayer2/MusicPlayerDlg.cpp b/MusicPlayer2/MusicPlayerDlg.cpp index d18f82367..0603f5810 100644 --- a/MusicPlayer2/MusicPlayerDlg.cpp +++ b/MusicPlayer2/MusicPlayerDlg.cpp @@ -34,6 +34,7 @@ #include "TagLibHelper.h" #include "RecentFolderAndPlaylist.h" #include "UserUi.h" +#include "FfmpegCore.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -1081,7 +1082,7 @@ void CMusicPlayerDlg::ApplySettings(const COptionsDlg& optionDlg) || theApp.m_app_setting_data.album_cover_as_background != optionDlg.m_tab2_dlg.m_data.album_cover_as_background || theApp.m_app_setting_data.enable_background != optionDlg.m_tab2_dlg.m_data.enable_background }; bool output_device_changed{ theApp.m_play_setting_data.device_selected != optionDlg.m_tab4_dlg.m_data.device_selected }; - bool player_core_changed{ theApp.m_play_setting_data.use_mci != optionDlg.m_tab4_dlg.m_data.use_mci }; + bool player_core_changed{ theApp.m_play_setting_data.use_mci != optionDlg.m_tab4_dlg.m_data.use_mci || theApp.m_play_setting_data.use_ffmpeg != optionDlg.m_tab4_dlg.m_data.use_ffmpeg }; bool media_lib_setting_changed{ theApp.m_media_lib_setting_data.hide_only_one_classification != optionDlg.m_media_lib_dlg.m_data.hide_only_one_classification || theApp.m_media_lib_setting_data.media_folders != optionDlg.m_media_lib_dlg.m_data.media_folders || theApp.m_media_lib_setting_data.recent_played_range != optionDlg.m_media_lib_dlg.m_data.recent_played_range @@ -1196,6 +1197,10 @@ void CMusicPlayerDlg::ApplySettings(const COptionsDlg& optionDlg) SaveConfig(); //将设置写入到ini文件 theApp.SaveConfig(); CPlayer::GetInstance().SaveConfig(); + if (CPlayer::GetInstance().IsFfmpegCore()) { + CFfmpegCore* core = (CFfmpegCore*)CPlayer::GetInstance().GetPlayerCore(); + core->UpdateSettings(); + } DrawInfo(true); } diff --git a/MusicPlayer2/PlaySettingsDlg.cpp b/MusicPlayer2/PlaySettingsDlg.cpp index 4bc0f29e5..33b4a8378 100644 --- a/MusicPlayer2/PlaySettingsDlg.cpp +++ b/MusicPlayer2/PlaySettingsDlg.cpp @@ -5,7 +5,6 @@ #include "MusicPlayer2.h" #include "PlaySettingsDlg.h" #include "afxdialogex.h" -#include "FfmpegCore.h" // CPlaySettingsDlg 对话框 @@ -279,8 +278,4 @@ void CPlaySettingsDlg::OnOK() { m_data.ffmpeg_core_cache_length = m_ffmpeg_cache_length.GetValue(); m_data.ffmpeg_core_max_retry_count = m_ffmpeg_max_retry_count.GetValue(); m_data.ffmpeg_core_url_retry_interval = m_ffmpeg_url_retry_interval.GetValue(); - if (CPlayer::GetInstance().IsFfmpegCore()) { - auto core = (CFfmpegCore*)CPlayer::GetInstance().GetPlayerCore(); - core->UpdateSettings(&m_data); - } } diff --git a/MusicPlayer2/Player.cpp b/MusicPlayer2/Player.cpp index 7610afcb4..08e156dfc 100644 --- a/MusicPlayer2/Player.cpp +++ b/MusicPlayer2/Player.cpp @@ -2739,7 +2739,7 @@ wstring CPlayer::GetPlaylistPath() const bool CPlayer::IsMciCore() const { - return m_pCore->GetCoreType() == PT_MCI; + return m_pCore ? m_pCore->GetCoreType() == PT_MCI : false; } bool CPlayer::IsFfmpegCore() const { From eac56d00056719ac6612eb6ec7442e6fd8e2909b Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 18 Feb 2022 11:54:12 +0800 Subject: [PATCH 27/41] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E8=AE=BE=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 57 ++++++++++++++++++++++++---- MusicPlayer2/FfmpegCore.h | 12 ++++++ MusicPlayer2/MusicPlayer2.rc | Bin 556198 -> 556394 bytes MusicPlayer2/PlaySettingsDlg.cpp | 4 ++ MusicPlayer2/resource.h | 1 + ffmpeg_core/ffmpeg_core.h | 9 +++++ ffmpeg_core/src/core.cpp | 62 +++++++++++++++++++++++++++++-- ffmpeg_core/src/output.c | 8 ++-- ffmpeg_core/src/output.h | 2 +- 9 files changed, 140 insertions(+), 15 deletions(-) diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 953e994f9..f5fb0e36f 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -51,6 +51,23 @@ void CFfmpegCore::InitCore() { ffmpeg_core_log_set_flags(AV_LOG_SKIP_REPEATED | AV_LOG_PRINT_LEVEL); ffmpeg_core_log_set_callback(LogCallback); UpdateSettings(); + auto devices = GetAudioDevices(); + DeviceInfo d; + d.index = -1; + d.name = CCommon::LoadText(IDS_SDL_DEFAULT_DEVICE); + d.driver = L""; + d.flags = 0; + theApp.m_output_devices.clear(); + theApp.m_output_devices.push_back(d); + int device_index = 0; + for (auto i = devices.begin(); i != devices.end(); i++) { + DeviceInfo d; + d.index = device_index++; + d.name = *i; + d.driver = L""; + d.flags = 0; + theApp.m_output_devices.push_back(d); + } } } @@ -74,7 +91,7 @@ std::wstring CFfmpegCore::GetAudioType() { } int CFfmpegCore::GetChannels() { - if (ffmpeg_core_get_channels && handle) { + if (IsSucceed() && handle) { return ffmpeg_core_get_channels(handle); } else return 0; } @@ -90,16 +107,19 @@ std::wstring CFfmpegCore::GetSoundFontName() { } void CFfmpegCore::Open(const wchar_t* file_path) { + if (!IsSucceed()) return; if (handle) { Close(); } - if (ffmpeg_core_open) { - if (file_path) recent_file = file_path; - if (!settings) settings = ffmpeg_core_init_settings(); - int re = ffmpeg_core_open2(file_path, &handle, settings); - if (re) { - err = re; - } + if (file_path) recent_file = file_path; + const wchar_t* device = nullptr; + if (theApp.m_play_setting_data.device_selected < theApp.m_output_devices.size() && theApp.m_play_setting_data.device_selected) { + device = theApp.m_output_devices[theApp.m_play_setting_data.device_selected].name.c_str(); + } + if (!settings) settings = ffmpeg_core_init_settings(); + int re = ffmpeg_core_open3(file_path, &handle, settings, device); + if (re) { + err = re; } } @@ -319,11 +339,13 @@ bool CFfmpegCore::GetFunction() { free_music_handle = (_free_music_handle)::GetProcAddress(m_dll_module, "free_music_handle"); free_music_info_handle = (_free_music_info_handle)::GetProcAddress(m_dll_module, "free_music_info_handle"); free_ffmpeg_core_settings = (_free_ffmpeg_core_settings)::GetProcAddress(m_dll_module, "free_ffmpeg_core_settings"); + free_device_name_list = (_free_device_name_list)::GetProcAddress(m_dll_module, "free_device_name_list"); ffmpeg_core_log_format_line = (_ffmpeg_core_log_format_line)::GetProcAddress(m_dll_module, "ffmpeg_core_log_format_line"); ffmpeg_core_log_set_callback = (_ffmpeg_core_log_set_callback)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_callback"); ffmpeg_core_log_set_flags = (_ffmpeg_core_log_set_flags)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_flags"); ffmpeg_core_open = (_ffmpeg_core_open)::GetProcAddress(m_dll_module, "ffmpeg_core_open"); ffmpeg_core_open2 = (_ffmpeg_core_open2)::GetProcAddress(m_dll_module, "ffmpeg_core_open2"); + ffmpeg_core_open3 = (_ffmpeg_core_open3)::GetProcAddress(m_dll_module, "ffmpeg_core_open3"); ffmpeg_core_info_open = (_ffmpeg_core_info_open)::GetProcAddress(m_dll_module, "ffmpeg_core_info_open"); ffmpeg_core_play = (_ffmpeg_core_play)::GetProcAddress(m_dll_module, "ffmpeg_core_play"); ffmpeg_core_pause = (_ffmpeg_core_pause)::GetProcAddress(m_dll_module, "ffmpeg_core_pause"); @@ -357,7 +379,9 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_settings_set_max_retry_count = (_ffmpeg_core_settings_set_max_retry_count)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_max_retry_count"); ffmpeg_core_settings_set_url_retry_interval = (_ffmpeg_core_settings_set_url_retry_interval)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_url_retry_interval"); ffmpeg_core_settings_set_equalizer_channel = (_ffmpeg_core_settings_set_equalizer_channel)::GetProcAddress(m_dll_module, "ffmpeg_core_settings_set_equalizer_channel"); + ffmpeg_core_get_audio_devices = (_ffmpeg_core_get_audio_devices)::GetProcAddress(m_dll_module, "ffmpeg_core_get_audio_devices"); //жǷɹ + rtn &= (free_device_name_list != NULL); rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); rtn &= (free_ffmpeg_core_settings != NULL); @@ -366,6 +390,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_log_set_flags != NULL); rtn &= (ffmpeg_core_open != NULL); rtn &= (ffmpeg_core_open2 != NULL); + rtn &= (ffmpeg_core_open3 != NULL); rtn &= (ffmpeg_core_info_open != NULL); rtn &= (ffmpeg_core_play != NULL); rtn &= (ffmpeg_core_pause != NULL); @@ -399,6 +424,7 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_settings_set_max_retry_count != NULL); rtn &= (ffmpeg_core_settings_set_url_retry_interval != NULL); rtn &= (ffmpeg_core_settings_set_equalizer_channel != NULL); + rtn &= (ffmpeg_core_get_audio_devices != NULL); return rtn; } @@ -544,3 +570,18 @@ int CFfmpegCore::GetEqChannelFreq(int channel) { return 0; } } + +std::list CFfmpegCore::GetAudioDevices() { + if (!IsSucceed()) return {}; + auto d = ffmpeg_core_get_audio_devices(); + if (!d) return {}; + std::list l; + l.push_back(CCommon::StrToUnicode(d->device, CodeType::UTF8)); + auto t = d; + while (t->next) { + t = t->next; + l.push_back(CCommon::StrToUnicode(t->device, CodeType::UTF8)); + } + free_device_name_list(&d); + return l; +} diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index aa7df2121..4bd82866b 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -9,14 +9,21 @@ typedef struct MusicHandle MusicHandle; typedef struct MusicInfoHandle MusicInfoHandle; typedef struct FfmpegCoreSettings FfmpegCoreSettings; +typedef struct DeviceNameList { + char* device; + struct DeviceNameList* prev; + struct DeviceNameList* next; +} DeviceNameList; typedef void(*_free_music_handle)(MusicHandle*); typedef void(*_free_music_info_handle)(MusicInfoHandle*); typedef void(*_free_ffmpeg_core_settings)(FfmpegCoreSettings*); +typedef void(*_free_device_name_list)(DeviceNameList**); typedef int(*_ffmpeg_core_log_format_line)(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); typedef void(*_ffmpeg_core_log_set_callback)(void(*callback)(void*, int, const char*, va_list)); typedef void(*_ffmpeg_core_log_set_flags)(int); typedef int(*_ffmpeg_core_open)(const wchar_t*, MusicHandle**); typedef int(*_ffmpeg_core_open2)(const wchar_t*, MusicHandle**, FfmpegCoreSettings*); +typedef int(*_ffmpeg_core_open3)(const wchar_t*, MusicHandle**, FfmpegCoreSettings*, const wchar_t*); typedef int(*_ffmpeg_core_info_open)(const wchar_t*, MusicInfoHandle**); typedef int(*_ffmpeg_core_play)(MusicHandle*); typedef int(*_ffmpeg_core_pause)(MusicHandle*); @@ -50,6 +57,7 @@ typedef int(*_ffmpeg_core_settings_set_cache_length)(FfmpegCoreSettings*, int); typedef int(*_ffmpeg_core_settings_set_max_retry_count)(FfmpegCoreSettings*, int); typedef int(*_ffmpeg_core_settings_set_url_retry_interval)(FfmpegCoreSettings*, int); typedef int(*_ffmpeg_core_settings_set_equalizer_channel)(FfmpegCoreSettings*, int, int); +typedef DeviceNameList*(*_ffmpeg_core_get_audio_devices)(); class CFfmpegCore : public IPlayerCore, public CDllLib { public: @@ -103,6 +111,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { void SetCacheLength(int cache_length = 15); void SetMaxRetryCount(int max_retry_count = 3); void SetUrlRetryInterval(int url_retry_interval = 5); + std::list GetAudioDevices(); private: std::wstring GetMetadata(std::string key, MusicInfoHandle* h = nullptr); virtual bool GetFunction() override; @@ -110,11 +119,13 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _free_music_handle free_music_handle = nullptr; _free_music_info_handle free_music_info_handle = nullptr; _free_ffmpeg_core_settings free_ffmpeg_core_settings = nullptr; + _free_device_name_list free_device_name_list = nullptr; _ffmpeg_core_log_format_line ffmpeg_core_log_format_line = nullptr; _ffmpeg_core_log_set_callback ffmpeg_core_log_set_callback = nullptr; _ffmpeg_core_log_set_flags ffmpeg_core_log_set_flags = nullptr; _ffmpeg_core_open ffmpeg_core_open = nullptr; _ffmpeg_core_open2 ffmpeg_core_open2 = nullptr; + _ffmpeg_core_open3 ffmpeg_core_open3 = nullptr; _ffmpeg_core_info_open ffmpeg_core_info_open = nullptr; _ffmpeg_core_play ffmpeg_core_play = nullptr; _ffmpeg_core_pause ffmpeg_core_pause = nullptr; @@ -148,6 +159,7 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_settings_set_max_retry_count ffmpeg_core_settings_set_max_retry_count = nullptr; _ffmpeg_core_settings_set_url_retry_interval ffmpeg_core_settings_set_url_retry_interval = nullptr; _ffmpeg_core_settings_set_equalizer_channel ffmpeg_core_settings_set_equalizer_channel = nullptr; + _ffmpeg_core_get_audio_devices ffmpeg_core_get_audio_devices = nullptr; int GetEqChannelFreq(int channel); MusicHandle* handle; FfmpegCoreSettings* settings = nullptr; diff --git a/MusicPlayer2/MusicPlayer2.rc b/MusicPlayer2/MusicPlayer2.rc index 620ea7b48902b41dde28134644c4ea24d27937aa..3099c23700d5248e77e1ee8db8d223653b479f80 100644 GIT binary patch delta 184 zcmZ4XQ}NX=#fBEf7N!>FEiALVr~f$2EWjGf;KJZDdE-H0br%L#hA;+C24^6x0E9{m zU^yV`#=IrnpQlfpv9FswQfac{4dM0`-Yh`O3dC$c%)WeuH;4B0RoWa#<~iavFNGnM mA&nuCp_Czqp#*3?h?T;S$&e3ZgXGG9>|}=2?F-8|SlIw&butA2 delta 58 zcmaF$OL5sx#fBEf7N!>FEiALVC#&7yYyacT0>rF9%m&2l%l~+DXixvq$<8+&C?mCf JN;$_hRsaTm8BqWL diff --git a/MusicPlayer2/PlaySettingsDlg.cpp b/MusicPlayer2/PlaySettingsDlg.cpp index 33b4a8378..f1f27da4b 100644 --- a/MusicPlayer2/PlaySettingsDlg.cpp +++ b/MusicPlayer2/PlaySettingsDlg.cpp @@ -95,9 +95,13 @@ void CPlaySettingsDlg::ShowDeviceInfo() void CPlaySettingsDlg::EnableControl() { bool enable = !CPlayer::GetInstance().IsMciCore(); + bool ffmpeg_enable = CPlayer::GetInstance().IsFfmpegCore(); m_sound_fade_chk.EnableWindow(enable); m_device_info_list.EnableWindow(enable); m_output_device_combo.EnableWindow(enable); + m_ffmpeg_cache_length.EnableWindow(ffmpeg_enable); + m_ffmpeg_max_retry_count.EnableWindow(ffmpeg_enable); + m_ffmpeg_url_retry_interval.EnableWindow(ffmpeg_enable); } diff --git a/MusicPlayer2/resource.h b/MusicPlayer2/resource.h index 661a22c78..0f3a282d4 100644 --- a/MusicPlayer2/resource.h +++ b/MusicPlayer2/resource.h @@ -704,6 +704,7 @@ #define IDI_ICON23 552 #define IDI_ADD 552 #define IDS_ITALIC 552 +#define IDS_SDL_DEFAULT_DEVICE 553 #define IDC_STATIC_PATH 1001 #define IDC_LIST1 1002 #define IDC_PATH_LIST 1002 diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index dcc5f53b2..cb8890f39 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -14,6 +14,11 @@ extern "C" { typedef struct MusicHandle MusicHandle; typedef struct MusicInfoHandle MusicInfoHandle; typedef struct FfmpegCoreSettings FfmpegCoreSettings; +typedef struct DeviceNameList { +char* device; +struct DeviceNameList* prev; +struct DeviceNameList* next; +} DeviceNameList; // 负数即为来自ffmpeg的错误 #define FFMPEG_CORE_ERR_OK 0 @@ -35,9 +40,11 @@ typedef struct FfmpegCoreSettings FfmpegCoreSettings; #define FFMPEG_CORE_ERR_INVALID_CDA_FILE 16 #define FFMPEG_CORE_ERR_NO_LIBCDIO 17 #define FFMEPG_CORE_ERR_FAILED_PARSE_URL 18 +#define FFMPEG_CORE_ERR_FAILED_SET_EQUALIZER_CHANNEL 19 FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); +FFMPEG_CORE_API void free_device_name_list(DeviceNameList** list); /// 即 av_log_format_line2 FFMPEG_CORE_API int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); /// 即 av_log_set_callback @@ -46,6 +53,7 @@ FFMPEG_CORE_API void ffmpeg_core_log_set_callback(void(*callback)(void*, int, co FFMPEG_CORE_API void ffmpeg_core_log_set_flags(int arg); FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); FFMPEG_CORE_API int ffmpeg_core_open2(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s); +FFMPEG_CORE_API int ffmpeg_core_open3(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s, const wchar_t* device); FFMPEG_CORE_API int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle); FFMPEG_CORE_API int ffmpeg_core_play(MusicHandle* handle); FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); @@ -135,6 +143,7 @@ FFMPEG_CORE_API int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, FFMPEG_CORE_API int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retry_count); FFMPEG_CORE_API int ffmpeg_core_settings_set_url_retry_interval(FfmpegCoreSettings* s, int url_retry_interval); FFMPEG_CORE_API int ffmpeg_core_settings_set_equalizer_channel(FfmpegCoreSettings* s, int channel, int gain); +FFMPEG_CORE_API DeviceNameList* ffmpeg_core_get_audio_devices(); #ifdef __cplusplus } #endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 0636da559..329924e75 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -14,13 +14,21 @@ #include "fileop.h" #include "file.h" #include "equalizer_settings.h" +#include "linked_list.h" +#include "cstr_util.h" #define CODEPAGE_SIZE 3 +#define DEVICE_NAME_LIST struct LinkedList* #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif +template +void tfree(T* data) { + free((void*)data); +} + void free_music_handle(MusicHandle* handle) { if (!handle) return; if (handle->device_id) SDL_CloseAudioDevice(handle->device_id); @@ -82,6 +90,14 @@ void free_ffmpeg_core_settings(FfmpegCoreSettings* s) { free(s); } +void free_device_name_list(DeviceNameList** list) { + if (list) { + auto l = (DEVICE_NAME_LIST)(*list); + linked_list_clear(l, tfree); + *list = (DeviceNameList*)l; + } +} + int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix) { return av_log_format_line2(ptr, level, fmt, vl, line, line_size, print_prefix); } @@ -95,16 +111,24 @@ void ffmpeg_core_log_set_flags(int arg) { } int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle) { - return ffmpeg_core_open2(url, handle, nullptr); + return ffmpeg_core_open3(url, handle, nullptr, nullptr); } int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s) { + return ffmpeg_core_open3(url, h, s, nullptr); +} + +int ffmpeg_core_open3(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s, const wchar_t* device) { if (!url || !h) return FFMPEG_CORE_ERR_NULLPTR; std::string u; // 将文件名转为UTF-8,ffmpeg API处理的都是UTF-8文件名 if (!wchar_util::wstr_to_str(u, url, CP_UTF8)) { return FFMPEG_CORE_ERR_INVAILD_NAME; } + std::string d; + if (device && !wchar_util::wstr_to_str(d, device, CP_UTF8)) { + return FFMPEG_CORE_ERR_INVAILD_NAME; + } #if NDEBUG // 设置ffmpeg日志级别为Error av_log_set_level(AV_LOG_ERROR); @@ -165,7 +189,7 @@ int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s if ((re = open_decoder(handle))) { goto end; } - if ((re = init_output(handle))) { + if ((re = init_output(handle, d.empty() ? nullptr : d.c_str()))) { goto end; } if ((re = init_filters(handle))) { @@ -540,6 +564,8 @@ const wchar_t* ffmpeg_core_get_err_msg2(int err) { return L"libcdio not found."; case FFMEPG_CORE_ERR_FAILED_PARSE_URL: return L"Failed to parse url."; + case FFMPEG_CORE_ERR_FAILED_SET_EQUALIZER_CHANNEL: + return L"Failed to set equalizer."; default: return L"Unknown error."; } @@ -602,6 +628,36 @@ int ffmpeg_core_settings_set_equalizer_channel(FfmpegCoreSettings* s, int channe int ffmpeg_core_set_equalizer_channel(MusicHandle* handle, int channel, int gain) { if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; int r = ffmpeg_core_settings_set_equalizer_channel(handle->s, channel, gain); - if (!r) return FFMPEG_CORE_ERR_FAILED_SET_SPEED; + if (!r) return FFMPEG_CORE_ERR_FAILED_SET_EQUALIZER_CHANNEL; return send_reinit_filters(handle); } + +DeviceNameList* ffmpeg_core_get_audio_devices() { + DEVICE_NAME_LIST list = nullptr; + int r = SDL_InitSubSystem(SDL_INIT_AUDIO); + if (r) return nullptr; + int count = SDL_GetNumAudioDevices(0); + if (count <= 0) { + goto end; + } + for (int i = 0; i < count; i++) { + const char* n = SDL_GetAudioDeviceName(i, 0); + char* p = nullptr; + if (!n) { + linked_list_clear(list, tfree); + goto end; + } + if (cstr_util_copy_str(&p, n)) { + linked_list_clear(list, tfree); + goto end; + } + if (!linked_list_append(list, &p)) { + free(p); + linked_list_clear(list, tfree); + goto end; + } + } +end: + SDL_QuitSubSystem(SDL_INIT_AUDIO); + return (DeviceNameList*)list; +} diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c index 5532f6994..b8dca1df3 100644 --- a/ffmpeg_core/src/output.c +++ b/ffmpeg_core/src/output.c @@ -2,10 +2,12 @@ #include "speed.h" -int init_output(MusicHandle* handle) { +int init_output(MusicHandle* handle, const char* device) { if (!handle) return FFMPEG_CORE_ERR_NULLPTR; if (!handle->sdl_initialized) { - SDL_InitSubSystem(SDL_INIT_AUDIO); + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { + return FFMPEG_CORE_ERR_SDL; + } handle->sdl_initialized = 1; } SDL_AudioSpec sdl_spec; @@ -21,7 +23,7 @@ int init_output(MusicHandle* handle) { sdl_spec.callback = SDL_callback; sdl_spec.userdata = handle; memcpy(&handle->sdl_spec, &sdl_spec, sizeof(SDL_AudioSpec)); - handle->device_id = SDL_OpenAudioDevice(NULL, 0, &sdl_spec, &handle->sdl_spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + handle->device_id = SDL_OpenAudioDevice(device, 0, &sdl_spec, &handle->sdl_spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (!handle->device_id) { av_log(NULL, AV_LOG_FATAL, "Failed to open audio device \"%s\": %s\n", "default", SDL_GetError()); return FFMPEG_CORE_ERR_SDL; diff --git a/ffmpeg_core/src/output.h b/ffmpeg_core/src/output.h index d69630ef3..e0240bc02 100644 --- a/ffmpeg_core/src/output.h +++ b/ffmpeg_core/src/output.h @@ -4,7 +4,7 @@ extern "C" { #endif #include "core.h" -int init_output(MusicHandle* handle); +int init_output(MusicHandle* handle, const char* device); enum AVSampleFormat convert_to_sdl_supported_format(enum AVSampleFormat fmt); void SDL_callback(void* userdata, uint8_t* stream, int len); SDL_AudioFormat convert_to_sdl_format(enum AVSampleFormat fmt); From cfdc6f292d4835354c6018a891ed488befe197dc Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 18 Feb 2022 21:04:46 +0800 Subject: [PATCH 28/41] =?UTF-8?q?=E5=AE=8C=E5=96=84UI=E6=8F=90=E7=A4=BA=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=A9=BA=E6=8C=87=E9=92=88=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=E7=89=88=E6=9C=AC=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 58 +++++++++++++++++-------- MusicPlayer2/FfmpegCore.h | 9 ++++ MusicPlayer2/MusicPlayer2.rc | Bin 556394 -> 557544 bytes MusicPlayer2/MusicPlayerCmdHelper.cpp | 3 ++ MusicPlayer2/PlaySettingsDlg.cpp | 13 ++++++ MusicPlayer2/ReverbDlg.cpp | 9 +++- MusicPlayer2/resource.h | 5 ++- ffmpeg_core/CMakeLists.txt | 11 +++++ ffmpeg_core/FFMPEG_CORE_VERSION.cmake | 9 ++++ ffmpeg_core/ffmpeg_core.h | 4 ++ ffmpeg_core/ffmpeg_core.rc.in | 35 +++++++++++++++ ffmpeg_core/ffmpeg_core_version.h.in | 9 ++++ ffmpeg_core/src/core.cpp | 59 +++++++++++++++++++++++--- 13 files changed, 197 insertions(+), 27 deletions(-) create mode 100644 ffmpeg_core/FFMPEG_CORE_VERSION.cmake create mode 100644 ffmpeg_core/ffmpeg_core.rc.in create mode 100644 ffmpeg_core/ffmpeg_core_version.h.in diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index f5fb0e36f..def6808ef 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -14,18 +14,21 @@ CFfmpegCore::CFfmpegCore() { err = 0; Init(L"ffmpeg_core.dll"); if (!IsSucceed()) { + CString strInfo = CCommon::LoadText(IDS_FFMPEG_INIT_FAILED); + theApp.WriteLog(wstring(strInfo)); } } CFfmpegCore::~CFfmpegCore() { if (handle || settings) UnInitCore(); + CDllLib::UnInit(); } void CFfmpegCore::InitCore() { if (IsSucceed()) { CAudioCommon::m_surpported_format.clear(); SupportedFormat format; - format.description = L"FFMPEG"; + format.description = CCommon::LoadText(IDS_BASIC_AUDIO_FORMAT); format.extensions.push_back(L"mp3"); format.extensions.push_back(L"wma"); format.extensions.push_back(L"wav"); @@ -68,18 +71,23 @@ void CFfmpegCore::InitCore() { d.flags = 0; theApp.m_output_devices.push_back(d); } + std::string ver("ffmpeg core version: "); + ver += ffmpeg_core_version_str(); + ver += "\n"; + OutputDebugStringA(ver.c_str()); + ffmpeg_core_dump_library_version(1, AV_LOG_INFO); + ffmpeg_core_dump_ffmpeg_configuration(1, AV_LOG_INFO); } } void CFfmpegCore::UnInitCore() { - if (settings) { + if (settings && IsSucceed()) { free_ffmpeg_core_settings(settings); settings = nullptr; } if (handle) { Close(); } - CDllLib::UnInit(); } unsigned int CFfmpegCore::GetHandle() { @@ -97,7 +105,7 @@ int CFfmpegCore::GetChannels() { } int CFfmpegCore::GetFReq() { - if (ffmpeg_core_get_freq && handle) { + if (IsSucceed() && handle) { return ffmpeg_core_get_freq(handle); } else return 0; } @@ -124,7 +132,7 @@ void CFfmpegCore::Open(const wchar_t* file_path) { } void CFfmpegCore::Close() { - if (free_music_handle && handle) { + if (IsSucceed() && handle) { free_music_handle(handle); handle = nullptr; err = 0; @@ -132,16 +140,17 @@ void CFfmpegCore::Close() { } void CFfmpegCore::Play() { + if (!IsSucceed()) return; if (!handle && !recent_file.empty()) { Open(recent_file.c_str()); } - if (ffmpeg_core_play && handle) { + if (handle) { ffmpeg_core_play(handle); } } void CFfmpegCore::Pause() { - if (ffmpeg_core_pause && handle) { + if (IsSucceed() && handle) { ffmpeg_core_pause(handle); } } @@ -151,6 +160,7 @@ void CFfmpegCore::Stop() { } void CFfmpegCore::SetVolume(int volume) { + if (!IsSucceed()) return; if (handle) { int re = ffmpeg_core_set_volume(handle, volume); if (re) { @@ -162,6 +172,7 @@ void CFfmpegCore::SetVolume(int volume) { } void CFfmpegCore::SetSpeed(float speed) { + if (!IsSucceed()) return; if (handle) { int re = ffmpeg_core_set_speed(handle, speed); if (re) { @@ -173,25 +184,25 @@ void CFfmpegCore::SetSpeed(float speed) { } bool CFfmpegCore::SongIsOver() { - if (ffmpeg_core_song_is_over && handle) { + if (IsSucceed() && handle) { return ffmpeg_core_song_is_over(handle); } else return false; } int CFfmpegCore::GetCurPosition() { - if (ffmpeg_core_get_cur_position && handle) { + if (IsSucceed() && handle) { return ffmpeg_core_get_cur_position(handle) / 1000; } else return 0; } int CFfmpegCore::GetSongLength() { - if (ffmpeg_core_get_song_length && handle) { + if (IsSucceed() && handle) { return ffmpeg_core_get_song_length(handle) / 1000; } else return 0; } void CFfmpegCore::SetCurPosition(int position) { - if (ffmpeg_core_seek && handle) { + if (IsSucceed() && handle) { int re = ffmpeg_core_seek(handle, (int64_t)position * 1000); if (re) { err = re; @@ -200,7 +211,7 @@ void CFfmpegCore::SetCurPosition(int position) { } void CFfmpegCore::GetAudioInfo(SongInfo& song_info, int flag) { - if (!handle) return; + if (!handle || !IsSucceed()) return; if (flag & AF_LENGTH) song_info.lengh = GetSongLength(); if (flag & AF_CHANNEL_INFO) { song_info.freq = ffmpeg_core_get_freq(handle); @@ -222,6 +233,7 @@ void CFfmpegCore::GetAudioInfo(SongInfo& song_info, int flag) { } void CFfmpegCore::GetAudioInfo(const wchar_t* file_path, SongInfo& song_info, int flag) { + if (!IsSucceed()) return; MusicInfoHandle* h = nullptr; int re = ffmpeg_core_info_open(file_path, &h); if (re || !h) return; @@ -267,12 +279,13 @@ bool CFfmpegCore::MidiNoLyric() { } PlayingState CFfmpegCore::GetPlayingState() { - if (ffmpeg_core_is_playing && handle) { + if (IsSucceed() && handle) { return ffmpeg_core_is_playing(handle) ? PlayingState::PS_PLAYING : PlayingState::PS_PAUSED; } else return PlayingState::PS_STOPED; } void CFfmpegCore::ApplyEqualizer(int channel, int gain) { + if (!IsSucceed()) return; channel = GetEqChannelFreq(channel); if (handle) { int re = ffmpeg_core_set_equalizer_channel(handle, channel, gain); @@ -291,7 +304,7 @@ void CFfmpegCore::ClearReverb() { } void CFfmpegCore::GetFFTData(float fft_data[FFT_SAMPLE]) { - if (handle) { + if (handle && IsSucceed()) { memset(fft_data, 0, FFT_SAMPLE); ffmpeg_core_get_fft_data(handle, fft_data, FFT_SAMPLE); } else { @@ -311,7 +324,7 @@ int CFfmpegCore::GetErrorCode() { } std::wstring CFfmpegCore::GetErrorInfo(int error_code) { - if (error_code == 0) return L""; + if (error_code == 0 || !IsSucceed()) return L""; auto tmp = ffmpeg_core_get_err_msg(error_code); if (tmp) { std::wstring re(tmp); @@ -343,6 +356,10 @@ bool CFfmpegCore::GetFunction() { ffmpeg_core_log_format_line = (_ffmpeg_core_log_format_line)::GetProcAddress(m_dll_module, "ffmpeg_core_log_format_line"); ffmpeg_core_log_set_callback = (_ffmpeg_core_log_set_callback)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_callback"); ffmpeg_core_log_set_flags = (_ffmpeg_core_log_set_flags)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_flags"); + ffmpeg_core_version_str = (_ffmpeg_core_version_str)::GetProcAddress(m_dll_module, "ffmpeg_core_version_str"); + ffmpeg_core_version = (_ffmpeg_core_version)::GetProcAddress(m_dll_module, "ffmpeg_core_version"); + ffmpeg_core_dump_library_version = (_ffmpeg_core_dump_library_version)::GetProcAddress(m_dll_module, "ffmpeg_core_dump_library_version"); + ffmpeg_core_dump_ffmpeg_configuration = (_ffmpeg_core_dump_ffmpeg_configuration)::GetProcAddress(m_dll_module, "ffmpeg_core_dump_ffmpeg_configuration"); ffmpeg_core_open = (_ffmpeg_core_open)::GetProcAddress(m_dll_module, "ffmpeg_core_open"); ffmpeg_core_open2 = (_ffmpeg_core_open2)::GetProcAddress(m_dll_module, "ffmpeg_core_open2"); ffmpeg_core_open3 = (_ffmpeg_core_open3)::GetProcAddress(m_dll_module, "ffmpeg_core_open3"); @@ -388,6 +405,10 @@ bool CFfmpegCore::GetFunction() { rtn &= (ffmpeg_core_log_format_line != NULL); rtn &= (ffmpeg_core_log_set_callback != NULL); rtn &= (ffmpeg_core_log_set_flags != NULL); + rtn &= (ffmpeg_core_version_str != NULL); + rtn &= (ffmpeg_core_version != NULL); + rtn &= (ffmpeg_core_dump_library_version != NULL); + rtn &= (ffmpeg_core_dump_ffmpeg_configuration != NULL); rtn &= (ffmpeg_core_open != NULL); rtn &= (ffmpeg_core_open2 != NULL); rtn &= (ffmpeg_core_open3 != NULL); @@ -429,6 +450,7 @@ bool CFfmpegCore::GetFunction() { } std::wstring CFfmpegCore::GetMetadata(std::string key, MusicInfoHandle* h) { + if (!IsSucceed()) return L""; if (h) { auto r = ffmpeg_core_info_get_metadata(h, key.c_str()); if (!r) return L""; @@ -527,19 +549,19 @@ void CFfmpegCore::UpdateSettings() { } void CFfmpegCore::SetCacheLength(int cache_length) { - if (settings) { + if (settings && IsSucceed()) { ffmpeg_core_settings_set_cache_length(settings, cache_length); } } void CFfmpegCore::SetMaxRetryCount(int max_retry_count) { - if (settings) { + if (settings && IsSucceed()) { ffmpeg_core_settings_set_max_retry_count(settings, max_retry_count); } } void CFfmpegCore::SetUrlRetryInterval(int url_retry_interval) { - if (settings) { + if (settings && IsSucceed()) { ffmpeg_core_settings_set_url_retry_interval(settings, url_retry_interval); } } diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 4bd82866b..624c44056 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -3,6 +3,7 @@ #include "DllLib.h" #define AV_LOG_ERROR 16 +#define AV_LOG_INFO 32 #define AV_LOG_VERBOSE 40 #define AV_LOG_SKIP_REPEATED 1 #define AV_LOG_PRINT_LEVEL 2 @@ -21,6 +22,10 @@ typedef void(*_free_device_name_list)(DeviceNameList**); typedef int(*_ffmpeg_core_log_format_line)(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); typedef void(*_ffmpeg_core_log_set_callback)(void(*callback)(void*, int, const char*, va_list)); typedef void(*_ffmpeg_core_log_set_flags)(int); +typedef const char*(*_ffmpeg_core_version_str)(); +typedef int32_t(*_ffmpeg_core_version)(); +typedef void(*_ffmpeg_core_dump_library_version)(int, int); +typedef void(*_ffmpeg_core_dump_ffmpeg_configuration)(int, int); typedef int(*_ffmpeg_core_open)(const wchar_t*, MusicHandle**); typedef int(*_ffmpeg_core_open2)(const wchar_t*, MusicHandle**, FfmpegCoreSettings*); typedef int(*_ffmpeg_core_open3)(const wchar_t*, MusicHandle**, FfmpegCoreSettings*, const wchar_t*); @@ -123,6 +128,10 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _ffmpeg_core_log_format_line ffmpeg_core_log_format_line = nullptr; _ffmpeg_core_log_set_callback ffmpeg_core_log_set_callback = nullptr; _ffmpeg_core_log_set_flags ffmpeg_core_log_set_flags = nullptr; + _ffmpeg_core_version_str ffmpeg_core_version_str = nullptr; + _ffmpeg_core_version ffmpeg_core_version = nullptr; + _ffmpeg_core_dump_library_version ffmpeg_core_dump_library_version = nullptr; + _ffmpeg_core_dump_ffmpeg_configuration ffmpeg_core_dump_ffmpeg_configuration = nullptr; _ffmpeg_core_open ffmpeg_core_open = nullptr; _ffmpeg_core_open2 ffmpeg_core_open2 = nullptr; _ffmpeg_core_open3 ffmpeg_core_open3 = nullptr; diff --git a/MusicPlayer2/MusicPlayer2.rc b/MusicPlayer2/MusicPlayer2.rc index 3099c23700d5248e77e1ee8db8d223653b479f80..2a23457d107384ee1c369ec393ad3a26fa69bca4 100644 GIT binary patch delta 689 zcmaF$OYudsQo|O;GX;$1(IE^uQ`n*C$3!v=7To&=k zAI>SY3luQ|G1GQ|BIbKHr@s(j5#V=YaAWXg2w-q!aA$~TaGw0oS90=)U2OHp;;n%e z8Ei3sqQ5kL&pjJ5Kj?2mPl!zhe|%1VQu2e;t2y=l8l^n{Mf`aE^ivs=*QVH|`Z9Pj zxBzi`>Nh_o1BU61ot)_k3~4}^%TU0O%8(AUE14mmp$N#;69o@UxY<^dd59w zzV<)fEZhHhv%V8#He|4bgqKwus}{Qja(L~FV+{h!gFoWB8P+6biaS>Sc7-Ez)E(O z>0D|YLMZN6RO8SW1cgT#5En6&FeCxPp?LcICG3){i41uRDbsz`IMk=JOko$8zD$in zPY@Qypm5CuhF|LR=V~0<%J>6W0jRWidZjvtI*R4+z~xn7$OhY=2PAW*uT|l2MNtR} q<>{9Wu)9ppQ{`}xMha`7Ymsy^C{F*_$u2q_80=Epr<8MCV+8;j3AOD2 delta 180 zcmaFStn})aV#5~3GX;!h)9s5GeWyzmGHNlJPJbBB8aaJlA)^IQygz}}vR$Bv5r~<# z3luTmyE$3y248!HFU$4{U)Fbm%tj0baLo$b<_v}mIt)fY(h_9MYOplWj9Q?vKr4!; zHzcxhF`7(&n9mqF{YfHg9J?t{y(Lh!A~&P_& songs) { GetOwner()->MessageBox(CCommon::LoadText(IDS_MCI_NO_THIS_FUNCTION_WARNING), NULL, MB_ICONWARNING | MB_OK); return; + } else if (CPlayer::GetInstance().IsFfmpegCore()) { + GetOwner()->MessageBox(CCommon::LoadText(IDS_FFMPEG_NO_THIS_FUNCTION), NULL, MB_ICONWARNING | MB_OK); + return; } pPlayerDlg->m_pFormatConvertDlg = new CFormatConvertDlg(songs, GetOwner()); diff --git a/MusicPlayer2/PlaySettingsDlg.cpp b/MusicPlayer2/PlaySettingsDlg.cpp index f1f27da4b..2a9e86c77 100644 --- a/MusicPlayer2/PlaySettingsDlg.cpp +++ b/MusicPlayer2/PlaySettingsDlg.cpp @@ -131,6 +131,7 @@ BOOL CPlaySettingsDlg::OnInitDialog() m_toolTip.Create(this); m_toolTip.SetMaxTipWidth(theApp.DPI(300)); m_toolTip.AddTool(GetDlgItem(IDC_MCI_RADIO), CCommon::LoadText(IDS_MCI_KERNAL_TIP)); + m_toolTip.AddTool(GetDlgItem(IDC_FFMPEG_RADIO), CCommon::LoadText(IDS_FFMPEG_CORE)); m_toolTip.SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); //初始化各控件的状态 @@ -149,6 +150,18 @@ BOOL CPlaySettingsDlg::OnInitDialog() m_show_play_state_icon_chk.EnableWindow(FALSE); } + bool enable_ffmpeg = false; + if (CPlayer::GetInstance().IsFfmpegCore()) { + enable_ffmpeg = true; + } else { + auto h = LoadLibraryW(L"ffmpeg_core.dll"); + if (h) { + enable_ffmpeg = true; + FreeLibrary(h); + } + } + m_ffmpeg_radio.EnableWindow(enable_ffmpeg); + if (m_data.use_mci) m_mci_radio.SetCheck(TRUE); else if (m_data.use_ffmpeg) diff --git a/MusicPlayer2/ReverbDlg.cpp b/MusicPlayer2/ReverbDlg.cpp index c61ce8841..c77a981a4 100644 --- a/MusicPlayer2/ReverbDlg.cpp +++ b/MusicPlayer2/ReverbDlg.cpp @@ -75,8 +75,13 @@ BOOL CReverbDlg::OnInitDialog() //初始化混响开关复选框 m_enable_reverb_chk.SetCheck(CPlayer::GetInstance().GetReverbEnable()); - //初始化控件的启用状态 - EnableControls(CPlayer::GetInstance().GetReverbEnable()); + if (CPlayer::GetInstance().IsFfmpegCore() || CPlayer::GetInstance().IsMciCore()) { + EnableControls(false); + m_enable_reverb_chk.EnableWindow(FALSE); + } else { + //初始化控件的启用状态 + EnableControls(CPlayer::GetInstance().GetReverbEnable()); + } return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE diff --git a/MusicPlayer2/resource.h b/MusicPlayer2/resource.h index 0f3a282d4..c660f2aef 100644 --- a/MusicPlayer2/resource.h +++ b/MusicPlayer2/resource.h @@ -705,6 +705,9 @@ #define IDI_ADD 552 #define IDS_ITALIC 552 #define IDS_SDL_DEFAULT_DEVICE 553 +#define IDS_FFMPEG_CORE 554 +#define IDS_FFMPEG_INIT_FAILED 555 +#define IDS_FFMPEG_NO_THIS_FUNCTION 556 #define IDC_STATIC_PATH 1001 #define IDC_LIST1 1002 #define IDC_PATH_LIST 1002 @@ -1524,7 +1527,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 552 +#define _APS_NEXT_RESOURCE_VALUE 555 #define _APS_NEXT_COMMAND_VALUE 33387 #define _APS_NEXT_CONTROL_VALUE 1190 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index 31df74200..292a77241 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -47,9 +47,20 @@ src/equalizer_settings.h src/equalizer_settings.cpp src/equalizer.h src/equalizer.c +"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc" +"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h" ) +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc" PROPERTIES GENERATED TRUE) +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h" PROPERTIES GENERATED TRUE) + +add_custom_target(ffmpeg_core_version + ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/FFMPEG_CORE_VERSION.cmake" +) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + add_library(ffmpeg_core SHARED "${CORE_FILES}") +add_dependencies(ffmpeg_core ffmpeg_core_version) set_target_properties(ffmpeg_core PROPERTIES PREFIX "") target_compile_definitions(ffmpeg_core PRIVATE BUILD_FFMPEG_CORE) target_link_libraries(ffmpeg_core AVFORMAT::AVFORMAT) diff --git a/ffmpeg_core/FFMPEG_CORE_VERSION.cmake b/ffmpeg_core/FFMPEG_CORE_VERSION.cmake new file mode 100644 index 000000000..c6ab10278 --- /dev/null +++ b/ffmpeg_core/FFMPEG_CORE_VERSION.cmake @@ -0,0 +1,9 @@ +set(FFMPEG_CORE_VERSION_MAJOR 1) +set(FFMPEG_CORE_VERSION_MINOR 0) +set(FFMPEG_CORE_VERSION_MICRO 0) +set(FFMPEG_CORE_VERSION_REV 0) +set(FFMPEG_CORE_VERSION ${FFMPEG_CORE_VERSION_MAJOR}.${FFMPEG_CORE_VERSION_MINOR}.${FFMPEG_CORE_VERSION_MICRO}.${FFMPEG_CORE_VERSION_REV}) +message(STATUS "Generate \"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h\"") +configure_file("${CMAKE_CURRENT_LIST_DIR}/ffmpeg_core_version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h") +message(STATUS "Generate \"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc\"") +configure_file("${CMAKE_CURRENT_LIST_DIR}/ffmpeg_core.rc.in" "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc") diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index cb8890f39..1ba07a2f9 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -51,6 +51,10 @@ FFMPEG_CORE_API int ffmpeg_core_log_format_line(void* ptr, int level, const char FFMPEG_CORE_API void ffmpeg_core_log_set_callback(void(*callback)(void*, int, const char*, va_list)); /// 即 av_log_set_flags FFMPEG_CORE_API void ffmpeg_core_log_set_flags(int arg); +FFMPEG_CORE_API const char* ffmpeg_core_version_str(); +FFMPEG_CORE_API int32_t ffmpeg_core_version(); +FFMPEG_CORE_API void ffmpeg_core_dump_library_version(int use_av_log, int av_log_level); +FFMPEG_CORE_API void ffmpeg_core_dump_ffmpeg_configuration(int use_av_log, int av_log_level); FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); FFMPEG_CORE_API int ffmpeg_core_open2(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s); FFMPEG_CORE_API int ffmpeg_core_open3(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s, const wchar_t* device); diff --git a/ffmpeg_core/ffmpeg_core.rc.in b/ffmpeg_core/ffmpeg_core.rc.in new file mode 100644 index 000000000..789e909eb --- /dev/null +++ b/ffmpeg_core/ffmpeg_core.rc.in @@ -0,0 +1,35 @@ +#include +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +VS_VERSION_INFO VERSIONINFO +FILEVERSION @FFMPEG_CORE_VERSION_MAJOR@,@FFMPEG_CORE_VERSION_MINOR@,@FFMPEG_CORE_VERSION_MICRO@,@FFMPEG_CORE_VERSION_REV@ +PRODUCTVERSION @FFMPEG_CORE_VERSION_MAJOR@,@FFMPEG_CORE_VERSION_MINOR@,@FFMPEG_CORE_VERSION_MICRO@,@FFMPEG_CORE_VERSION_REV@ +FILEFLAGSMASK VS_FF_DEBUG +FILEFLAGS VER_DEBUG +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" + BEGIN + VALUE "CompanyName", "lifegpc\0" + VALUE "FileDescription", "A music player core which use FFMPEG and SDL2.\0" + VALUE "FileVersion", "@FFMPEG_CORE_VERSION@\0" + VALUE "InternalName", "ffmpeg_core\0" + VALUE "LegalCopyright", "Copyright (C) 2022 lifegpc\0" + VALUE "OriginalFilename", "ffmpeg_core.dll\0" + VALUE "ProductName", "ffmpeg_core\0" + VALUE "ProductVersion", "@FFMPEG_CORE_VERSION@\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 0 + END +END diff --git a/ffmpeg_core/ffmpeg_core_version.h.in b/ffmpeg_core/ffmpeg_core_version.h.in new file mode 100644 index 000000000..35cb9d79e --- /dev/null +++ b/ffmpeg_core/ffmpeg_core_version.h.in @@ -0,0 +1,9 @@ +#ifndef _FFMPEG_CORE_VERSION +#define _FFMPEG_CORE_VERSION +#define FFMPEG_CORE_VERSION_MAJOR @FFMPEG_CORE_VERSION_MAJOR@ +#define FFMPEG_CORE_VERSION_MINOR @FFMPEG_CORE_VERSION_MINOR@ +#define FFMPEG_CORE_VERSION_MICRO @FFMPEG_CORE_VERSION_MICRO@ +#define FFMPEG_CORE_VERSION_REV @FFMPEG_CORE_VERSION_REV@ +#define FFMPEG_CORE_VERSION "@FFMPEG_CORE_VERSION@" +#define FFMPEG_CORE_VERSION_INT (((int32_t)FFMPEG_CORE_VERSION_MAJOR << 24) | ((int32_t)FFMPEG_CORE_VERSION_MINOR << 16) | ((int32_t)FFMPEG_CORE_VERSION_MICRO << 8) | (int32_t)FFMPEG_CORE_VERSION_REV) +#endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 329924e75..5198eaf98 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -16,6 +16,7 @@ #include "equalizer_settings.h" #include "linked_list.h" #include "cstr_util.h" +#include "ffmpeg_core_version.h" #define CODEPAGE_SIZE 3 #define DEVICE_NAME_LIST struct LinkedList* @@ -110,6 +111,52 @@ void ffmpeg_core_log_set_flags(int arg) { av_log_set_flags(arg); } +const char* ffmpeg_core_version_str() { + return FFMPEG_CORE_VERSION; +} + +int32_t ffmpeg_core_version() { + return FFMPEG_CORE_VERSION_INT; +} + +#define PRINTF(f, ...) use_av_log ? av_log(NULL, av_log_level, f, __VA_ARGS__) : printf_s(f, __VA_ARGS__) + +void ffmpeg_core_dump_library_version(int use_av_log, int av_log_level) { + unsigned int v = avutil_version(); + PRINTF("FFMPEG libraries: \n"); + PRINTF("libavutil %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); + v = avcodec_version(); + PRINTF("libavcodec %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); + v = avformat_version(); + PRINTF("libavformat %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); + v = avdevice_version(); + PRINTF("libavdevice %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); + v = avfilter_version(); + PRINTF("libavfilter %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); + v = swresample_version(); + PRINTF("libswresample %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); + PRINTF("Other thirdparty libraries: \n"); + SDL_version sv; + SDL_GetVersion(&sv); + PRINTF("SDL2 %3u.%3u.%3u\n", sv.major, sv.minor, sv.patch); +} + +#define QUICK_PRINT_CONF(f, name) if (basic != f()) { \ +PRINTF(name " have different configuration: %s\n", f()); } + +void ffmpeg_core_dump_ffmpeg_configuration(int use_av_log, int av_log_level) { + std::string basic = avutil_configuration(); + PRINTF("configuration: %s\n", basic.c_str()); + QUICK_PRINT_CONF(avcodec_configuration, "libavcodec") + QUICK_PRINT_CONF(avformat_configuration, "libavformat") + QUICK_PRINT_CONF(avdevice_configuration, "libavdevice") + QUICK_PRINT_CONF(avfilter_configuration, "libavfilter") + QUICK_PRINT_CONF(swresample_configuration, "libswresample") +} + +#undef QUICK_PRINT_CONF +#undef PRINTF + int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle) { return ffmpeg_core_open3(url, handle, nullptr, nullptr); } @@ -376,14 +423,14 @@ int ffmpeg_core_info_get_bits(MusicInfoHandle* handle) { int64_t ffmpeg_core_get_bitrate(MusicHandle* handle) { if (!handle || !handle->decoder) return -1; if (handle->decoder->bit_rate) return handle->decoder->bit_rate; - if (handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; + if (handle->fmt && handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; return 0; } int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle) { if (!handle || !handle->is) return -1; if (handle->is->codecpar->bit_rate > 0) return handle->is->codecpar->bit_rate; - if (handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; + if (handle->fmt && handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; return 0; } @@ -403,7 +450,7 @@ std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags) { wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key) { if (!handle | !key) return nullptr; - if (handle->fmt->metadata) { + if (handle->fmt && handle->fmt->metadata) { auto re = get_metadata_str(handle->fmt->metadata, key, 0); if (!re.empty()) { wchar_t* r = nullptr; @@ -412,7 +459,7 @@ wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key) { } } } - if (handle->is->metadata) { + if (handle->is && handle->is->metadata) { auto re = get_metadata_str(handle->is->metadata, key, 0); if (!re.empty()) { wchar_t* r = nullptr; @@ -426,7 +473,7 @@ wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key) { wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key) { if (!handle || !key) return nullptr; - if (handle->fmt->metadata) { + if (handle->fmt && handle->fmt->metadata) { auto re = get_metadata_str(handle->fmt->metadata, key, 0); if (!re.empty()) { wchar_t* r = nullptr; @@ -435,7 +482,7 @@ wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key) } } } - if (handle->is->metadata) { + if (handle->is && handle->is->metadata) { auto re = get_metadata_str(handle->is->metadata, key, 0); if (!re.empty()) { wchar_t* r = nullptr; From f80063b3606dafefd527171c6b69b6890750b93e Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 18 Feb 2022 21:29:05 +0800 Subject: [PATCH 29/41] =?UTF-8?q?=E4=B8=BAffmpeg=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0README=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 ffmpeg_core/README.md diff --git a/ffmpeg_core/README.md b/ffmpeg_core/README.md new file mode 100644 index 000000000..0dbdacfbb --- /dev/null +++ b/ffmpeg_core/README.md @@ -0,0 +1,23 @@ +# FFMPEG 核心 +## 编译需求 +### 第三方库 +* `FFMPEG` 库,包含 + * `libavutil` + * `libavcodec` + * `libavformat` + * `libavdevice` + * `libavfilter` + * `libswresample` +* `SDL2` 库 + +FFMPEG库采用pkg-config来寻找,请确保正确的设置了环境变量`PKG_CONFIG_PATH`和CMAKE选项`CMAKE_PREFIX_PATH` +### FFMPEG 库要求 +* 需要链接任意一TLS库以支持HTTPS(例如 `gnutls` / `openssl`) +* 需要链接 `libcdio` 以支持播放CD(ffmpeg官网的预编译版本可能无法正常工作即使其链接了 `libcdio`) +#### libavfilter +以下 `filters` 在核心中被使用到: +* `volume`:用于调节声音大小 +* `atempo`:用于调节速度 +* `equalizer`:用于均衡器 + +其他 `filters` 可以删除以减小体积 From 47c75fd92f1ccd31cbbf9171ce843c5b69d49868 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 19 Feb 2022 12:20:22 +0800 Subject: [PATCH 30/41] =?UTF-8?q?=E6=94=AF=E6=8C=81install?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index 292a77241..55ae748e8 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -7,6 +7,7 @@ if (MSVC) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(GNUInstallDirs) find_package(AVFORMAT 58 REQUIRED) find_package(AVCODEC 58 REQUIRED) find_package(AVDEVICE 58 REQUIRED) @@ -59,9 +60,12 @@ add_custom_target(ffmpeg_core_version ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) +set(PUBLIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_core.h" "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h") + add_library(ffmpeg_core SHARED "${CORE_FILES}") add_dependencies(ffmpeg_core ffmpeg_core_version) set_target_properties(ffmpeg_core PROPERTIES PREFIX "") +set_target_properties(ffmpeg_core PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}") target_compile_definitions(ffmpeg_core PRIVATE BUILD_FFMPEG_CORE) target_link_libraries(ffmpeg_core AVFORMAT::AVFORMAT) target_link_libraries(ffmpeg_core AVCODEC::AVCODEC) @@ -76,3 +80,6 @@ target_link_libraries(ffmpeg_core utils) if (NOT MSVC) target_link_libraries(ffmpeg_core m) endif() + +install(TARGETS ffmpeg_core) +install(FILES $ DESTINATION bin OPTIONAL) From 626c38547dfc4a86e08d747ce94af836760ea434 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 19 Feb 2022 12:30:47 +0800 Subject: [PATCH 31/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E7=9A=84filters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ffmpeg_core/README.md b/ffmpeg_core/README.md index 0dbdacfbb..b0c638903 100644 --- a/ffmpeg_core/README.md +++ b/ffmpeg_core/README.md @@ -19,5 +19,6 @@ FFMPEG库采用pkg-config来寻找,请确保正确的设置了环境变量`PKG * `volume`:用于调节声音大小 * `atempo`:用于调节速度 * `equalizer`:用于均衡器 +* `aresample`:用于格式自动转换 其他 `filters` 可以删除以减小体积 From c4a0e5738b006b5a897081fa8c2ec7584a6c138e Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 19 Feb 2022 17:38:40 +0800 Subject: [PATCH 32/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dfree=E7=AD=89=E5=86=85?= =?UTF-8?q?=E5=AD=98=E5=88=86=E9=85=8D=E5=87=BD=E6=95=B0=E4=B8=8D=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E5=AF=BC=E8=87=B4=E7=9A=84crash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 12 +++++++++--- MusicPlayer2/FfmpegCore.h | 6 ++++++ ffmpeg_core/ffmpeg_core.h | 4 ++++ ffmpeg_core/src/core.cpp | 12 ++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index def6808ef..44730110b 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -328,7 +328,7 @@ std::wstring CFfmpegCore::GetErrorInfo(int error_code) { auto tmp = ffmpeg_core_get_err_msg(error_code); if (tmp) { std::wstring re(tmp); - free(tmp); + ffmpeg_core_free(tmp); return re; } return L""; @@ -353,6 +353,9 @@ bool CFfmpegCore::GetFunction() { free_music_info_handle = (_free_music_info_handle)::GetProcAddress(m_dll_module, "free_music_info_handle"); free_ffmpeg_core_settings = (_free_ffmpeg_core_settings)::GetProcAddress(m_dll_module, "free_ffmpeg_core_settings"); free_device_name_list = (_free_device_name_list)::GetProcAddress(m_dll_module, "free_device_name_list"); + ffmpeg_core_free = (_ffmpeg_core_free)::GetProcAddress(m_dll_module, "ffmpeg_core_free"); + ffmpeg_core_malloc = (_ffmpeg_core_malloc)::GetProcAddress(m_dll_module, "ffmpeg_core_malloc"); + ffmpeg_core_realloc = (_ffmpeg_core_realloc)::GetProcAddress(m_dll_module, "ffmpeg_core_realloc"); ffmpeg_core_log_format_line = (_ffmpeg_core_log_format_line)::GetProcAddress(m_dll_module, "ffmpeg_core_log_format_line"); ffmpeg_core_log_set_callback = (_ffmpeg_core_log_set_callback)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_callback"); ffmpeg_core_log_set_flags = (_ffmpeg_core_log_set_flags)::GetProcAddress(m_dll_module, "ffmpeg_core_log_set_flags"); @@ -402,6 +405,9 @@ bool CFfmpegCore::GetFunction() { rtn &= (free_music_handle != NULL); rtn &= (free_music_info_handle != NULL); rtn &= (free_ffmpeg_core_settings != NULL); + rtn &= (ffmpeg_core_free != NULL); + rtn &= (ffmpeg_core_malloc != NULL); + rtn &= (ffmpeg_core_realloc != NULL); rtn &= (ffmpeg_core_log_format_line != NULL); rtn &= (ffmpeg_core_log_set_callback != NULL); rtn &= (ffmpeg_core_log_set_flags != NULL); @@ -455,14 +461,14 @@ std::wstring CFfmpegCore::GetMetadata(std::string key, MusicInfoHandle* h) { auto r = ffmpeg_core_info_get_metadata(h, key.c_str()); if (!r) return L""; std::wstring re(r); - free(r); + ffmpeg_core_free(r); return re; } if (!handle) return L""; auto r = ffmpeg_core_get_metadata(handle, key.c_str()); if (!r) return L""; std::wstring re(r); - free(r); + ffmpeg_core_free(r); return re; } diff --git a/MusicPlayer2/FfmpegCore.h b/MusicPlayer2/FfmpegCore.h index 624c44056..b8fcb14a5 100644 --- a/MusicPlayer2/FfmpegCore.h +++ b/MusicPlayer2/FfmpegCore.h @@ -19,6 +19,9 @@ typedef void(*_free_music_handle)(MusicHandle*); typedef void(*_free_music_info_handle)(MusicInfoHandle*); typedef void(*_free_ffmpeg_core_settings)(FfmpegCoreSettings*); typedef void(*_free_device_name_list)(DeviceNameList**); +typedef void(*_ffmpeg_core_free)(void*); +typedef void*(*_ffmpeg_core_malloc)(size_t); +typedef void*(*_ffmpeg_core_realloc)(void*, size_t); typedef int(*_ffmpeg_core_log_format_line)(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); typedef void(*_ffmpeg_core_log_set_callback)(void(*callback)(void*, int, const char*, va_list)); typedef void(*_ffmpeg_core_log_set_flags)(int); @@ -125,6 +128,9 @@ class CFfmpegCore : public IPlayerCore, public CDllLib { _free_music_info_handle free_music_info_handle = nullptr; _free_ffmpeg_core_settings free_ffmpeg_core_settings = nullptr; _free_device_name_list free_device_name_list = nullptr; + _ffmpeg_core_free ffmpeg_core_free = nullptr; + _ffmpeg_core_malloc ffmpeg_core_malloc = nullptr; + _ffmpeg_core_realloc ffmpeg_core_realloc = nullptr; _ffmpeg_core_log_format_line ffmpeg_core_log_format_line = nullptr; _ffmpeg_core_log_set_callback ffmpeg_core_log_set_callback = nullptr; _ffmpeg_core_log_set_flags ffmpeg_core_log_set_flags = nullptr; diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h index 1ba07a2f9..4096008bd 100644 --- a/ffmpeg_core/ffmpeg_core.h +++ b/ffmpeg_core/ffmpeg_core.h @@ -4,6 +4,7 @@ extern "C" { #endif #include +#include #include #include #if BUILD_FFMPEG_CORE @@ -45,6 +46,9 @@ FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); FFMPEG_CORE_API void free_device_name_list(DeviceNameList** list); +FFMPEG_CORE_API void ffmpeg_core_free(void* data); +FFMPEG_CORE_API void* ffmpeg_core_malloc(size_t size); +FFMPEG_CORE_API void* ffmpeg_core_realloc(void* data, size_t size); /// 即 av_log_format_line2 FFMPEG_CORE_API int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); /// 即 av_log_set_callback diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 5198eaf98..a5ac0f423 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -99,6 +99,18 @@ void free_device_name_list(DeviceNameList** list) { } } +void ffmpeg_core_free(void* data) { + free(data); +} + +void* ffmpeg_core_malloc(size_t size) { + return malloc(size); +} + +void* ffmpeg_core_realloc(void* data, size_t size) { + return realloc(data, size); +} + int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix) { return av_log_format_line2(ptr, level, fmt, vl, line, line_size, print_prefix); } From 4a2f98212698f2a183b101e43b8a8a994cfbda11 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 19 Feb 2022 18:01:48 +0800 Subject: [PATCH 33/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8DMingW=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/CMakeLists.txt | 4 +++- ffmpeg_core/src/core.cpp | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt index 55ae748e8..47f5e2e77 100644 --- a/ffmpeg_core/CMakeLists.txt +++ b/ffmpeg_core/CMakeLists.txt @@ -82,4 +82,6 @@ if (NOT MSVC) endif() install(TARGETS ffmpeg_core) -install(FILES $ DESTINATION bin OPTIONAL) +if (MSVC) + install(FILES $ DESTINATION bin OPTIONAL) +endif() diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index a5ac0f423..3d29fe522 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -131,7 +131,12 @@ int32_t ffmpeg_core_version() { return FFMPEG_CORE_VERSION_INT; } +#if _MSC_VER #define PRINTF(f, ...) use_av_log ? av_log(NULL, av_log_level, f, __VA_ARGS__) : printf_s(f, __VA_ARGS__) +#else +#define PRINTF(f, ...) if (use_av_log) { av_log(NULL, av_log_level, f, ##__VA_ARGS__); } else { printf_s(f, ##__VA_ARGS__); } +#endif + void ffmpeg_core_dump_library_version(int use_av_log, int av_log_level) { unsigned int v = avutil_version(); From 12b0eb9acb4f79dbba393fb9172205eba93630f3 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 19 Feb 2022 19:45:46 +0800 Subject: [PATCH 34/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCD=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E5=8D=A1=E4=BD=8F=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/cda.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ffmpeg_core/src/cda.c b/ffmpeg_core/src/cda.c index 8d557b505..b29a72735 100644 --- a/ffmpeg_core/src/cda.c +++ b/ffmpeg_core/src/cda.c @@ -180,18 +180,23 @@ int open_cd_device(MusicHandle* handle, const char* device) { return FFMPEG_CORE_ERR_NO_LIBCDIO; } int re = 0; - if ((re = avformat_open_input(&handle->fmt, device, f, NULL)) < 0) { + AVDictionary* d = NULL; + av_dict_set(&d, "paranoia_mode", "verify", 0); + if ((re = avformat_open_input(&handle->fmt, device, f, &d)) < 0) { av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", device, av_err2str(re), re); + av_dict_free(&d); return re; } if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", device, av_err2str(re), re); + av_dict_free(&d); return re; } handle->only_part = 1; handle->part_start_pts = av_rescale_q_rnd(handle->cda->range_offset, cda_time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); handle->part_end_pts = av_rescale_q_rnd((int64_t)handle->cda->duration + handle->cda->range_offset, cda_time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); // handle->fmt->flags |= AVFMT_FLAG_FAST_SEEK; // 允许快速定位 + av_dict_free(&d); return FFMPEG_CORE_ERR_OK; } From c69793838c196b86d79635bf9ff8d2ee854a527b Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 22 Feb 2022 17:38:49 +0800 Subject: [PATCH 35/41] Minor fix --- ffmpeg_core/src/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp index 3d29fe522..68bfd85bf 100644 --- a/ffmpeg_core/src/core.cpp +++ b/ffmpeg_core/src/core.cpp @@ -466,7 +466,7 @@ std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags) { } wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key) { - if (!handle | !key) return nullptr; + if (!handle || !key) return nullptr; if (handle->fmt && handle->fmt->metadata) { auto re = get_metadata_str(handle->fmt->metadata, key, 0); if (!re.empty()) { From 822545217299b49d46e7f8a54a85b3472543f3d9 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 22 Feb 2022 19:06:21 +0800 Subject: [PATCH 36/41] Fix bug in decode --- ffmpeg_core/src/decode.c | 90 +++++++++++++++++++++++++--------------- ffmpeg_core/src/loop.c | 1 + 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 8040ad246..9c392d724 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -51,8 +51,50 @@ int reopen_decoder(MusicHandle* handle) { return FFMPEG_CORE_ERR_OK; } +int decode_audio_internal(MusicHandle* handle, char* writed, AVFrame* frame) { + if (!handle || !writed || !frame) return FFMPEG_CORE_ERR_NULLPTR; + int re = 0; + re = avcodec_receive_frame(handle->decoder, frame); + if (re >= 0) { + if (handle->first_pts == INT64_MIN) { + handle->first_pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + av_log(NULL, AV_LOG_VERBOSE, "first_pts: %s\n", av_ts2timestr(handle->first_pts, &AV_TIME_BASE_Q)); + if (handle->only_part) { + // 定位到开始位置 + if (handle->part_start_pts > 0) { + handle->is_seek = 1; + handle->seek_pos = handle->part_start_pts; + } + } + } + if (handle->set_new_pts) { + av_log(NULL, AV_LOG_VERBOSE, "pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); + handle->pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX) - handle->first_pts; + handle->end_pts = handle->pts; + handle->set_new_pts = 0; + } + // 整段数据在结束位置之后,跳过 + if (handle->only_part && (frame->pts - handle->first_pts) >= handle->part_end_pts) { + handle->is_eof = 1; + goto end; + } + re = convert_samples_and_add_to_fifo(handle, frame, writed); + goto end; + } else if (re == AVERROR(EAGAIN)) { + // 数据不够,继续读取 + re = FFMPEG_CORE_ERR_OK; + goto end; + } else if (re == AVERROR_EOF) { + handle->is_eof = 1; + re = FFMPEG_CORE_ERR_OK; + goto end; + } +end: + return re; +} + int decode_audio(MusicHandle* handle, char* writed) { - if (!handle | !writed) return FFMPEG_CORE_ERR_NULLPTR; + if (!handle || !writed) return FFMPEG_CORE_ERR_NULLPTR; AVPacket pkt; AVFrame* frame = av_frame_alloc(); *writed = 0; @@ -61,6 +103,12 @@ int decode_audio(MusicHandle* handle, char* writed) { } int re = FFMPEG_CORE_ERR_OK; while (1) { + if ((re = decode_audio_internal(handle, writed, frame))) { + goto end; + } + if (*writed) { + goto end; + } if ((re = av_read_frame(handle->fmt, &pkt)) < 0) { if (re == AVERROR_EOF) { handle->is_eof = 1; @@ -76,45 +124,19 @@ int decode_audio(MusicHandle* handle, char* writed) { } handle->last_pkt_pts = av_rescale_q_rnd(pkt.pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); if ((re = avcodec_send_packet(handle->decoder, &pkt)) < 0) { + if (re == AVERROR(EAGAIN)) { + re = 0; + av_packet_unref(&pkt); + continue; + } av_packet_unref(&pkt); goto end; } av_packet_unref(&pkt); - re = avcodec_receive_frame(handle->decoder, frame); - if (re >= 0) { - if (handle->first_pts == INT64_MIN) { - handle->first_pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - av_log(NULL, AV_LOG_VERBOSE, "first_pts: %s\n", av_ts2timestr(handle->first_pts, &AV_TIME_BASE_Q)); - if (handle->only_part) { - // 定位到开始位置 - if (handle->part_start_pts > 0) { - handle->is_seek = 1; - handle->seek_pos = handle->part_start_pts; - } - } - } - if (handle->set_new_pts) { - av_log(NULL, AV_LOG_VERBOSE, "pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); - handle->pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX) - handle->first_pts; - handle->end_pts = handle->pts; - handle->set_new_pts = 0; - } - // 整段数据在结束位置之后,跳过 - if (handle->only_part && (frame->pts - handle->first_pts) >= handle->part_end_pts) { - handle->is_eof = 1; - goto end; - } - re = convert_samples_and_add_to_fifo(handle, frame, writed); - goto end; - } else if (re == AVERROR(EAGAIN)) { - // 数据不够,继续读取 - re = FFMPEG_CORE_ERR_OK; - continue; - } else if (re == AVERROR_EOF) { - handle->is_eof = 1; - re = FFMPEG_CORE_ERR_OK; + if ((re = decode_audio_internal(handle, writed, frame))) { goto end; } + if (*writed) break; } end: if (frame) av_frame_free(&frame); diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index a762912a9..824d6de51 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -198,6 +198,7 @@ DWORD WINAPI event_loop(LPVOID handle) { if (av_audio_fifo_size(h->buffer) < buffered_size) { int re = decode_audio(handle, &writed); if (re) { + av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling decode_audio: %s (%i).\n", __FILE__, __LINE__, av_err2str(re), re); h->have_err = 1; h->err = re; av_log(NULL, AV_LOG_VERBOSE, "Try to reopen file \"%s\".\n", h->url); From 7f37c3fe9233c574b8e44c4fafbfd0fca3472f7e Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 22 Feb 2022 19:09:10 +0800 Subject: [PATCH 37/41] Minor fix --- ffmpeg_core/src/decode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 9c392d724..6e5a6fcad 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -106,7 +106,7 @@ int decode_audio(MusicHandle* handle, char* writed) { if ((re = decode_audio_internal(handle, writed, frame))) { goto end; } - if (*writed) { + if (*writed || handle->is_eof) { goto end; } if ((re = av_read_frame(handle->fmt, &pkt)) < 0) { @@ -136,7 +136,7 @@ int decode_audio(MusicHandle* handle, char* writed) { if ((re = decode_audio_internal(handle, writed, frame))) { goto end; } - if (*writed) break; + if (*writed || handle->is_eof) break; } end: if (frame) av_frame_free(&frame); From de6d727438137cc54c594ebfaffc29faea4768a9 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 22 Feb 2022 20:40:24 +0800 Subject: [PATCH 38/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dape=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=AE=9A=E4=BD=8D=E6=97=B6=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/decode.c | 6 +++++- ffmpeg_core/src/loop.c | 11 +++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c index 6e5a6fcad..340d15cc8 100644 --- a/ffmpeg_core/src/decode.c +++ b/ffmpeg_core/src/decode.c @@ -67,11 +67,15 @@ int decode_audio_internal(MusicHandle* handle, char* writed, AVFrame* frame) { } } } - if (handle->set_new_pts) { + if (handle->set_new_pts && frame->pts != AV_NOPTS_VALUE) { av_log(NULL, AV_LOG_VERBOSE, "pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); handle->pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX) - handle->first_pts; handle->end_pts = handle->pts; handle->set_new_pts = 0; + } else if (handle->set_new_pts) { + av_log(NULL, AV_LOG_VERBOSE, "skip NOPTS frame.\n"); + // 跳过NOPTS的frame + goto end; } // 整段数据在结束位置之后,跳过 if (handle->only_part && (frame->pts - handle->first_pts) >= handle->part_end_pts) { diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index 824d6de51..e16304710 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -33,12 +33,11 @@ int seek_to_pos(MusicHandle* handle) { // 不在缓冲区,调用av_seek_frame并清空缓冲区 int flags = 0; // 修复flac文件解码完之后,继续调用解码器会导致报错的BUG - if (handle->is_eof && handle->is->codecpar->codec_id == AV_CODEC_ID_FLAC) { - av_log(NULL, AV_LOG_VERBOSE, "Try to reopen decoder \"%s\".\n", handle->codec->name ? handle->codec->name : "(null)"); - if ((re = reopen_decoder(handle))) { - ReleaseMutex(handle->mutex); - goto end; - } + // 同时解决decoder内多余buffer的问题 + av_log(NULL, AV_LOG_VERBOSE, "Try to reopen decoder \"%s\".\n", handle->codec->name ? handle->codec->name : "(null)"); + if ((re = reopen_decoder(handle))) { + ReleaseMutex(handle->mutex); + goto end; } if (handle->seek_pos < handle->end_pts) { flags |= AVSEEK_FLAG_BACKWARD; From e316ad091bbf48e97dcc0f0500ac0c50f9d67c99 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 22 Feb 2022 21:08:32 +0800 Subject: [PATCH 39/41] =?UTF-8?q?=E6=B7=BB=E5=8A=A0.cue=E5=88=B0=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=9A=84=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MusicPlayer2/FfmpegCore.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MusicPlayer2/FfmpegCore.cpp b/MusicPlayer2/FfmpegCore.cpp index 44730110b..0f05db214 100644 --- a/MusicPlayer2/FfmpegCore.cpp +++ b/MusicPlayer2/FfmpegCore.cpp @@ -44,10 +44,11 @@ void CFfmpegCore::InitCore() { format.extensions.push_back(L"aif"); format.extensions.push_back(L"aiff"); format.extensions.push_back(L"cda"); + format.extensions.push_back(L"cue"); format.extensions.push_back(L"mp4"); format.extensions.push_back(L"mkv"); format.extensions.push_back(L"m2ts"); - format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.ogg;*.oga;*.flac;*.ape;*.mp2;*.mp1;*.opus;*.ape;*.cda;*.aif;*.aiff;*.mp4;*.mkv;*.m2ts"; + format.extensions_list = L"*.mp3;*.wma;*.wav;*.m4a;*.ogg;*.oga;*.flac;*.ape;*.mp2;*.mp1;*.opus;*.ape;*.cda;*.aif;*.aiff;*.cue;*.mp4;*.mkv;*.m2ts"; CAudioCommon::m_surpported_format.push_back(format); CAudioCommon::m_all_surpported_extensions = format.extensions; settings = ffmpeg_core_init_settings(); From 4e9631998f2753cde323a767da4a5d9ca4640ef8 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 23 Feb 2022 18:19:04 +0800 Subject: [PATCH 40/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=9C=89=E4=B8=80=E9=83=A8=E5=88=86=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E4=B8=8D=E5=88=B0=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ffmpeg_core/src/loop.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c index e16304710..36c27b181 100644 --- a/ffmpeg_core/src/loop.c +++ b/ffmpeg_core/src/loop.c @@ -39,9 +39,8 @@ int seek_to_pos(MusicHandle* handle) { ReleaseMutex(handle->mutex); goto end; } - if (handle->seek_pos < handle->end_pts) { - flags |= AVSEEK_FLAG_BACKWARD; - } + // 指的是定位到指定位置之前的关键帧,而不是从后往前定位 + flags |= AVSEEK_FLAG_BACKWARD; if ((re = av_seek_frame(handle->fmt, -1, handle->seek_pos + handle->first_pts, flags)) < 0) { av_log(NULL, AV_LOG_FATAL, "Failed to seek frame %" PRIi64 ": %s (%i)\n", handle->seek_pos + handle->first_pts, av_err2str(re), re); ReleaseMutex(handle->mutex); From bb8bec5ed6ac4edf60772070959f70429b231777 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 23 Feb 2022 21:17:57 +0800 Subject: [PATCH 41/41] =?UTF-8?q?=E7=A7=BB=E9=99=A4ffmepg=5Fcore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 - ffmpeg_core/.gitignore | 2 - ffmpeg_core/CMakeLists.txt | 87 --- ffmpeg_core/FFMPEG_CORE_VERSION.cmake | 9 - ffmpeg_core/README.md | 24 - ffmpeg_core/cmake/FindAVCODEC.cmake | 29 - ffmpeg_core/cmake/FindAVDEVICE.cmake | 29 - ffmpeg_core/cmake/FindAVFILTER.cmake | 29 - ffmpeg_core/cmake/FindAVFORMAT.cmake | 29 - ffmpeg_core/cmake/FindAVUTIL.cmake | 29 - ffmpeg_core/cmake/FindSDL2.cmake | 388 ------------- ffmpeg_core/cmake/FindSWRESAMPLE.cmake | 30 - ffmpeg_core/ffmpeg_core.h | 158 ------ ffmpeg_core/ffmpeg_core.rc.in | 35 -- ffmpeg_core/ffmpeg_core_version.h.in | 9 - ffmpeg_core/src/cda.c | 227 -------- ffmpeg_core/src/cda.h | 17 - ffmpeg_core/src/core.cpp | 727 ------------------------- ffmpeg_core/src/core.h | 181 ------ ffmpeg_core/src/decode.c | 201 ------- ffmpeg_core/src/decode.h | 20 - ffmpeg_core/src/equalizer.c | 53 -- ffmpeg_core/src/equalizer.h | 22 - ffmpeg_core/src/equalizer_settings.cpp | 34 -- ffmpeg_core/src/equalizer_settings.h | 16 - ffmpeg_core/src/fft_data.c | 116 ---- ffmpeg_core/src/fft_data.h | 10 - ffmpeg_core/src/file.cpp | 36 -- ffmpeg_core/src/file.h | 12 - ffmpeg_core/src/filter.c | 379 ------------- ffmpeg_core/src/filter.h | 25 - ffmpeg_core/src/loop.c | 255 --------- ffmpeg_core/src/loop.h | 16 - ffmpeg_core/src/open.c | 58 -- ffmpeg_core/src/open.h | 15 - ffmpeg_core/src/output.c | 206 ------- ffmpeg_core/src/output.h | 20 - ffmpeg_core/src/speed.c | 41 -- ffmpeg_core/src/speed.h | 22 - ffmpeg_core/src/volume.c | 52 -- ffmpeg_core/src/volume.h | 22 - ffmpeg_core/utils | 1 - 42 files changed, 3674 deletions(-) delete mode 100644 .gitmodules delete mode 100644 ffmpeg_core/.gitignore delete mode 100644 ffmpeg_core/CMakeLists.txt delete mode 100644 ffmpeg_core/FFMPEG_CORE_VERSION.cmake delete mode 100644 ffmpeg_core/README.md delete mode 100644 ffmpeg_core/cmake/FindAVCODEC.cmake delete mode 100644 ffmpeg_core/cmake/FindAVDEVICE.cmake delete mode 100644 ffmpeg_core/cmake/FindAVFILTER.cmake delete mode 100644 ffmpeg_core/cmake/FindAVFORMAT.cmake delete mode 100644 ffmpeg_core/cmake/FindAVUTIL.cmake delete mode 100644 ffmpeg_core/cmake/FindSDL2.cmake delete mode 100644 ffmpeg_core/cmake/FindSWRESAMPLE.cmake delete mode 100644 ffmpeg_core/ffmpeg_core.h delete mode 100644 ffmpeg_core/ffmpeg_core.rc.in delete mode 100644 ffmpeg_core/ffmpeg_core_version.h.in delete mode 100644 ffmpeg_core/src/cda.c delete mode 100644 ffmpeg_core/src/cda.h delete mode 100644 ffmpeg_core/src/core.cpp delete mode 100644 ffmpeg_core/src/core.h delete mode 100644 ffmpeg_core/src/decode.c delete mode 100644 ffmpeg_core/src/decode.h delete mode 100644 ffmpeg_core/src/equalizer.c delete mode 100644 ffmpeg_core/src/equalizer.h delete mode 100644 ffmpeg_core/src/equalizer_settings.cpp delete mode 100644 ffmpeg_core/src/equalizer_settings.h delete mode 100644 ffmpeg_core/src/fft_data.c delete mode 100644 ffmpeg_core/src/fft_data.h delete mode 100644 ffmpeg_core/src/file.cpp delete mode 100644 ffmpeg_core/src/file.h delete mode 100644 ffmpeg_core/src/filter.c delete mode 100644 ffmpeg_core/src/filter.h delete mode 100644 ffmpeg_core/src/loop.c delete mode 100644 ffmpeg_core/src/loop.h delete mode 100644 ffmpeg_core/src/open.c delete mode 100644 ffmpeg_core/src/open.h delete mode 100644 ffmpeg_core/src/output.c delete mode 100644 ffmpeg_core/src/output.h delete mode 100644 ffmpeg_core/src/speed.c delete mode 100644 ffmpeg_core/src/speed.h delete mode 100644 ffmpeg_core/src/volume.c delete mode 100644 ffmpeg_core/src/volume.h delete mode 160000 ffmpeg_core/utils diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 2ff362cb6..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "ffmpeg_core/utils"] - path = ffmpeg_core/utils - url = https://github.com/lifegpc/c-utils diff --git a/ffmpeg_core/.gitignore b/ffmpeg_core/.gitignore deleted file mode 100644 index dc84959d1..000000000 --- a/ffmpeg_core/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build/ - diff --git a/ffmpeg_core/CMakeLists.txt b/ffmpeg_core/CMakeLists.txt deleted file mode 100644 index 47f5e2e77..000000000 --- a/ffmpeg_core/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -project(MusicPlayer2_ffmpeg_core) - -if (MSVC) - add_compile_options(/utf-8) # 让编译器使用UTF-8编码 -endif() - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -include(GNUInstallDirs) -find_package(AVFORMAT 58 REQUIRED) -find_package(AVCODEC 58 REQUIRED) -find_package(AVDEVICE 58 REQUIRED) -find_package(AVUTIL 56 REQUIRED) -find_package(AVFILTER 7 REQUIRED) -find_package(SWRESAMPLE 4 REQUIRED) -find_package(SDL2 REQUIRED) - -set(ENABLE_ICONV OFF CACHE BOOL "Libiconv is not needed.") -add_subdirectory(utils) -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/utils") - -set(CORE_FILES -ffmpeg_core.h -src/core.h -src/core.cpp -src/decode.h -src/decode.c -src/open.h -src/open.c -"src/output.h" -"src/output.c" -src/loop.h -src/loop.c -"src/filter.h" -"src/filter.c" -src/volume.h -src/volume.c -src/speed.h -src/speed.c -src/fft_data.h -src/fft_data.c -src/cda.h -src/cda.c -src/file.h -src/file.cpp -src/equalizer_settings.h -src/equalizer_settings.cpp -src/equalizer.h -src/equalizer.c -"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc" -"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h" -) - -set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc" PROPERTIES GENERATED TRUE) -set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h" PROPERTIES GENERATED TRUE) - -add_custom_target(ffmpeg_core_version - ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/FFMPEG_CORE_VERSION.cmake" -) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -set(PUBLIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_core.h" "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h") - -add_library(ffmpeg_core SHARED "${CORE_FILES}") -add_dependencies(ffmpeg_core ffmpeg_core_version) -set_target_properties(ffmpeg_core PROPERTIES PREFIX "") -set_target_properties(ffmpeg_core PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}") -target_compile_definitions(ffmpeg_core PRIVATE BUILD_FFMPEG_CORE) -target_link_libraries(ffmpeg_core AVFORMAT::AVFORMAT) -target_link_libraries(ffmpeg_core AVCODEC::AVCODEC) -target_link_libraries(ffmpeg_core AVDEVICE::AVDEVICE) -target_link_libraries(ffmpeg_core AVUTIL::AVUTIL) -target_link_libraries(ffmpeg_core AVFILTER::AVFILTER) -target_link_libraries(ffmpeg_core SWRESAMPLE::SWRESAMPLE) -target_link_libraries(ffmpeg_core SDL2::Core) -target_link_libraries(ffmpeg_core SDL2::Main) -target_link_libraries(ffmpeg_core utils) - -if (NOT MSVC) - target_link_libraries(ffmpeg_core m) -endif() - -install(TARGETS ffmpeg_core) -if (MSVC) - install(FILES $ DESTINATION bin OPTIONAL) -endif() diff --git a/ffmpeg_core/FFMPEG_CORE_VERSION.cmake b/ffmpeg_core/FFMPEG_CORE_VERSION.cmake deleted file mode 100644 index c6ab10278..000000000 --- a/ffmpeg_core/FFMPEG_CORE_VERSION.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set(FFMPEG_CORE_VERSION_MAJOR 1) -set(FFMPEG_CORE_VERSION_MINOR 0) -set(FFMPEG_CORE_VERSION_MICRO 0) -set(FFMPEG_CORE_VERSION_REV 0) -set(FFMPEG_CORE_VERSION ${FFMPEG_CORE_VERSION_MAJOR}.${FFMPEG_CORE_VERSION_MINOR}.${FFMPEG_CORE_VERSION_MICRO}.${FFMPEG_CORE_VERSION_REV}) -message(STATUS "Generate \"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h\"") -configure_file("${CMAKE_CURRENT_LIST_DIR}/ffmpeg_core_version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core_version.h") -message(STATUS "Generate \"${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc\"") -configure_file("${CMAKE_CURRENT_LIST_DIR}/ffmpeg_core.rc.in" "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg_core.rc") diff --git a/ffmpeg_core/README.md b/ffmpeg_core/README.md deleted file mode 100644 index b0c638903..000000000 --- a/ffmpeg_core/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# FFMPEG 核心 -## 编译需求 -### 第三方库 -* `FFMPEG` 库,包含 - * `libavutil` - * `libavcodec` - * `libavformat` - * `libavdevice` - * `libavfilter` - * `libswresample` -* `SDL2` 库 - -FFMPEG库采用pkg-config来寻找,请确保正确的设置了环境变量`PKG_CONFIG_PATH`和CMAKE选项`CMAKE_PREFIX_PATH` -### FFMPEG 库要求 -* 需要链接任意一TLS库以支持HTTPS(例如 `gnutls` / `openssl`) -* 需要链接 `libcdio` 以支持播放CD(ffmpeg官网的预编译版本可能无法正常工作即使其链接了 `libcdio`) -#### libavfilter -以下 `filters` 在核心中被使用到: -* `volume`:用于调节声音大小 -* `atempo`:用于调节速度 -* `equalizer`:用于均衡器 -* `aresample`:用于格式自动转换 - -其他 `filters` 可以删除以减小体积 diff --git a/ffmpeg_core/cmake/FindAVCODEC.cmake b/ffmpeg_core/cmake/FindAVCODEC.cmake deleted file mode 100644 index bdcb29539..000000000 --- a/ffmpeg_core/cmake/FindAVCODEC.cmake +++ /dev/null @@ -1,29 +0,0 @@ -find_package(PkgConfig) -if (PkgConfig_FOUND) - pkg_check_modules(PC_AVCODEC QUIET IMPORTED_TARGET GLOBAL libavcodec) -endif() - -if (PC_AVCODEC_FOUND) - set(AVCODEC_FOUND TRUE) - set(AVCODEC_VERSION ${PC_AVCODEC_VERSION}) - set(AVCODEC_VERSION_STRING ${PC_AVCODEC_STRING}) - set(AVCODEC_LIBRARYS ${PC_AVCODEC_LIBRARIES}) - if (USE_STATIC_LIBS) - set(AVCODEC_INCLUDE_DIRS ${PC_AVCODEC_STATIC_INCLUDE_DIRS}) - else() - set(AVCODEC_INCLUDE_DIRS ${PC_AVCODEC_INCLUDE_DIRS}) - endif() - if (NOT TARGET AVCODEC::AVCODEC) - add_library(AVCODEC::AVCODEC ALIAS PkgConfig::PC_AVCODEC) - endif() -else() - message(FATAL_ERROR "failed.") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(AVCODEC - FOUND_VAR AVCODEC_FOUND - REQUIRED_VARS - AVCODEC_LIBRARYS - VERSION_VAR AVCODEC_VERSION -) diff --git a/ffmpeg_core/cmake/FindAVDEVICE.cmake b/ffmpeg_core/cmake/FindAVDEVICE.cmake deleted file mode 100644 index 3fe830f8b..000000000 --- a/ffmpeg_core/cmake/FindAVDEVICE.cmake +++ /dev/null @@ -1,29 +0,0 @@ -find_package(PkgConfig) -if (PkgConfig_FOUND) - pkg_check_modules(PC_AVDEVICE QUIET IMPORTED_TARGET GLOBAL libavdevice) -endif() - -if (PC_AVDEVICE_FOUND) - set(AVDEVICE_FOUND TRUE) - set(AVDEVICE_VERSION ${PC_AVDEVICE_VERSION}) - set(AVDEVICE_VERSION_STRING ${PC_AVDEVICE_STRING}) - set(AVDEVICE_LIBRARYS ${PC_AVDEVICE_LIBRARIES}) - if (USE_STATIC_LIBS) - set(AVDEVICE_INCLUDE_DIRS ${PC_AVDEVICE_STATIC_INCLUDE_DIRS}) - else() - set(AVDEVICE_INCLUDE_DIRS ${PC_AVDEVICE_INCLUDE_DIRS}) - endif() - if (NOT TARGET AVDEVICE::AVDEVICE) - add_library(AVDEVICE::AVDEVICE ALIAS PkgConfig::PC_AVDEVICE) - endif() -else() - message(FATAL_ERROR "failed.") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(AVDEVICE - FOUND_VAR AVDEVICE_FOUND - REQUIRED_VARS - AVDEVICE_LIBRARYS - VERSION_VAR AVDEVICE_VERSION -) diff --git a/ffmpeg_core/cmake/FindAVFILTER.cmake b/ffmpeg_core/cmake/FindAVFILTER.cmake deleted file mode 100644 index 65ab7fa13..000000000 --- a/ffmpeg_core/cmake/FindAVFILTER.cmake +++ /dev/null @@ -1,29 +0,0 @@ -find_package(PkgConfig) -if (PkgConfig_FOUND) - pkg_check_modules(PC_AVFILTER QUIET IMPORTED_TARGET GLOBAL libavfilter) -endif() - -if (PC_AVFILTER_FOUND) - set(AVFILTER_FOUND TRUE) - set(AVFILTER_VERSION ${PC_AVFILTER_VERSION}) - set(AVFILTER_VERSION_STRING ${PC_AVFILTER_STRING}) - set(AVFILTER_LIBRARYS ${PC_AVFILTER_LIBRARIES}) - if (USE_STATIC_LIBS) - set(AVFILTER_INCLUDE_DIRS ${PC_AVFILTER_STATIC_INCLUDE_DIRS}) - else() - set(AVFILTER_INCLUDE_DIRS ${PC_AVFILTER_INCLUDE_DIRS}) - endif() - if (NOT TARGET AVFILTER::AVFILTER) - add_library(AVFILTER::AVFILTER ALIAS PkgConfig::PC_AVFILTER) - endif() -else() - message(FATAL_ERROR "failed.") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(AVFILTER - FOUND_VAR AVFILTER_FOUND - REQUIRED_VARS - AVFILTER_LIBRARYS - VERSION_VAR AVFILTER_VERSION -) diff --git a/ffmpeg_core/cmake/FindAVFORMAT.cmake b/ffmpeg_core/cmake/FindAVFORMAT.cmake deleted file mode 100644 index a17bf9499..000000000 --- a/ffmpeg_core/cmake/FindAVFORMAT.cmake +++ /dev/null @@ -1,29 +0,0 @@ -find_package(PkgConfig) -if (PkgConfig_FOUND) - pkg_check_modules(PC_AVFORMAT QUIET IMPORTED_TARGET GLOBAL libavformat) -endif() - -if (PC_AVFORMAT_FOUND) - set(AVFORMAT_FOUND TRUE) - set(AVFORMAT_VERSION ${PC_AVFORMAT_VERSION}) - set(AVFORMAT_VERSION_STRING ${PC_AVFORMAT_STRING}) - set(AVFORMAT_LIBRARYS ${PC_AVFORMAT_LIBRARIES}) - if (USE_STATIC_LIBS) - set(AVFORMAT_INCLUDE_DIRS ${PC_AVFORMAT_STATIC_INCLUDE_DIRS}) - else() - set(AVFORMAT_INCLUDE_DIRS ${PC_AVFORMAT_INCLUDE_DIRS}) - endif() - if (NOT TARGET AVFORMAT::AVFORMAT) - add_library(AVFORMAT::AVFORMAT ALIAS PkgConfig::PC_AVFORMAT) - endif() -else() - message(FATAL_ERROR "failed.") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(AVFORMAT - FOUND_VAR AVFORMAT_FOUND - REQUIRED_VARS - AVFORMAT_LIBRARYS - VERSION_VAR AVFORMAT_VERSION -) diff --git a/ffmpeg_core/cmake/FindAVUTIL.cmake b/ffmpeg_core/cmake/FindAVUTIL.cmake deleted file mode 100644 index 0a0e7bf19..000000000 --- a/ffmpeg_core/cmake/FindAVUTIL.cmake +++ /dev/null @@ -1,29 +0,0 @@ -find_package(PkgConfig) -if (PkgConfig_FOUND) - pkg_check_modules(PC_AVUTIL QUIET IMPORTED_TARGET GLOBAL libavutil) -endif() - -if (PC_AVUTIL_FOUND) - set(AVUTIL_FOUND TRUE) - set(AVUTIL_VERSION ${PC_AVUTIL_VERSION}) - set(AVUTIL_VERSION_STRING ${PC_AVUTIL_STRING}) - set(AVUTIL_LIBRARYS ${PC_AVUTIL_LIBRARIES}) - if (USE_STATIC_LIBS) - set(AVUTIL_INCLUDE_DIRS ${PC_AVUTIL_STATIC_INCLUDE_DIRS}) - else() - set(AVUTIL_INCLUDE_DIRS ${PC_AVUTIL_INCLUDE_DIRS}) - endif() - if (NOT TARGET AVUTIL::AVUTIL) - add_library(AVUTIL::AVUTIL ALIAS PkgConfig::PC_AVUTIL) - endif() -else() - message(FATAL_ERROR "failed.") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(AVUTIL - FOUND_VAR AVUTIL_FOUND - REQUIRED_VARS - AVUTIL_LIBRARYS - VERSION_VAR AVUTIL_VERSION -) diff --git a/ffmpeg_core/cmake/FindSDL2.cmake b/ffmpeg_core/cmake/FindSDL2.cmake deleted file mode 100644 index 2445d36ec..000000000 --- a/ffmpeg_core/cmake/FindSDL2.cmake +++ /dev/null @@ -1,388 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -# Copyright 2019 Amine Ben Hassouna -# Copyright 2000-2019 Kitware, Inc. and Contributors -# All rights reserved. - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: - -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. - -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. - -# * Neither the name of Kitware, Inc. nor the names of Contributors -# may be used to endorse or promote products derived from this -# software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#[=======================================================================[.rst: -FindSDL2 --------- - -Locate SDL2 library - -This module defines the following 'IMPORTED' targets: - -:: - - SDL2::Core - The SDL2 library, if found. - Libraries should link to SDL2::Core - - SDL2::Main - The SDL2main library, if found. - Applications should link to SDL2::Main instead of SDL2::Core - - - -This module will set the following variables in your project: - -:: - - SDL2_LIBRARIES, the name of the library to link against - SDL2_INCLUDE_DIRS, where to find SDL.h - SDL2_FOUND, if false, do not try to link to SDL2 - SDL2MAIN_FOUND, if false, do not try to link to SDL2main - SDL2_VERSION_STRING, human-readable string containing the version of SDL2 - - - -This module responds to the following cache variables: - -:: - - SDL2_PATH - Set a custom SDL2 Library path (default: empty) - - SDL2_NO_DEFAULT_PATH - Disable search SDL2 Library in default path. - If SDL2_PATH (default: ON) - Else (default: OFF) - - SDL2_INCLUDE_DIR - SDL2 headers path. - - SDL2_LIBRARY - SDL2 Library (.dll, .so, .a, etc) path. - - SDL2MAIN_LIBRAY - SDL2main Library (.a) path. - - SDL2_BUILDING_LIBRARY - This flag is useful only when linking to SDL2_LIBRARIES insead of - SDL2::Main. It is required only when building a library that links to - SDL2_LIBRARIES, because only applications need main() (No need to also - link to SDL2main). - If this flag is defined, then no SDL2main will be added to SDL2_LIBRARIES - and no SDL2::Main target will be created. - - -Don't forget to include SDLmain.h and SDLmain.m in your project for the -OS X framework based version. (Other versions link to -lSDL2main which -this module will try to find on your behalf.) Also for OS X, this -module will automatically add the -framework Cocoa on your behalf. - - -Additional Note: If you see an empty SDL2_LIBRARY in your project -configuration, it means CMake did not find your SDL2 library -(SDL2.dll, libsdl2.so, SDL2.framework, etc). Set SDL2_LIBRARY to point -to your SDL2 library, and configure again. Similarly, if you see an -empty SDL2MAIN_LIBRARY, you should set this value as appropriate. These -values are used to generate the final SDL2_LIBRARIES variable and the -SDL2::Core and SDL2::Main targets, but when these values are unset, -SDL2_LIBRARIES, SDL2::Core and SDL2::Main does not get created. - - -$SDL2DIR is an environment variable that would correspond to the -./configure --prefix=$SDL2DIR used in building SDL2. l.e.galup 9-20-02 - - - -Created by Amine Ben Hassouna: - Adapt FindSDL.cmake to SDL2 (FindSDL2.cmake). - Add cache variables for more flexibility: - SDL2_PATH, SDL2_NO_DEFAULT_PATH (for details, see doc above). - Mark 'Threads' as a required dependency for non-OSX systems. - Modernize the FindSDL2.cmake module by creating specific targets: - SDL2::Core and SDL2::Main (for details, see doc above). - - -Original FindSDL.cmake module: - Modified by Eric Wing. Added code to assist with automated building - by using environmental variables and providing a more - controlled/consistent search behavior. Added new modifications to - recognize OS X frameworks and additional Unix paths (FreeBSD, etc). - Also corrected the header search path to follow "proper" SDL - guidelines. Added a search for SDLmain which is needed by some - platforms. Added a search for threads which is needed by some - platforms. Added needed compile switches for MinGW. - -On OSX, this will prefer the Framework version (if found) over others. -People will have to manually change the cache value of SDL2_LIBRARY to -override this selection or set the SDL2_PATH variable or the CMake -environment CMAKE_INCLUDE_PATH to modify the search paths. - -Note that the header path has changed from SDL/SDL.h to just SDL.h -This needed to change because "proper" SDL convention is #include -"SDL.h", not . This is done for portability reasons -because not all systems place things in SDL/ (see FreeBSD). -#]=======================================================================] - -# Define options for searching SDL2 Library in a custom path - -set(SDL2_PATH "" CACHE STRING "Custom SDL2 Library path") - -set(_SDL2_NO_DEFAULT_PATH OFF) -if(SDL2_PATH) - set(_SDL2_NO_DEFAULT_PATH ON) -endif() - -set(SDL2_NO_DEFAULT_PATH ${_SDL2_NO_DEFAULT_PATH} - CACHE BOOL "Disable search SDL2 Library in default path") -unset(_SDL2_NO_DEFAULT_PATH) - -set(SDL2_NO_DEFAULT_PATH_CMD) -if(SDL2_NO_DEFAULT_PATH) - set(SDL2_NO_DEFAULT_PATH_CMD NO_DEFAULT_PATH) -endif() - -# Search for the SDL2 include directory -find_path(SDL2_INCLUDE_DIR SDL.h - HINTS - ENV SDL2DIR - ${SDL2_NO_DEFAULT_PATH_CMD} - PATH_SUFFIXES SDL2 - # path suffixes to search inside ENV{SDL2DIR} - include/SDL2 include - PATHS ${SDL2_PATH} - DOC "Where the SDL2 headers can be found" -) - -set(SDL2_INCLUDE_DIRS "${SDL2_INCLUDE_DIR}") - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(VC_LIB_PATH_SUFFIX lib/x64) -else() - set(VC_LIB_PATH_SUFFIX lib/x86) -endif() - -# SDL-2.0 is the name used by FreeBSD ports... -# don't confuse it for the version number. -find_library(SDL2_LIBRARY - NAMES SDL2 SDL-2.0 - HINTS - ENV SDL2DIR - ${SDL2_NO_DEFAULT_PATH_CMD} - PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} - PATHS ${SDL2_PATH} - DOC "Where the SDL2 Library can be found" -) - -set(SDL2_LIBRARIES "${SDL2_LIBRARY}") - -if(NOT SDL2_BUILDING_LIBRARY) - if(NOT SDL2_INCLUDE_DIR MATCHES ".framework") - # Non-OS X framework versions expect you to also dynamically link to - # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms - # seem to provide SDL2main for compatibility even though they don't - # necessarily need it. - - if(SDL2_PATH) - set(SDL2MAIN_LIBRARY_PATHS "${SDL2_PATH}") - endif() - - if(NOT SDL2_NO_DEFAULT_PATH) - set(SDL2MAIN_LIBRARY_PATHS - /sw - /opt/local - /opt/csw - /opt - "${SDL2MAIN_LIBRARY_PATHS}" - ) - endif() - - find_library(SDL2MAIN_LIBRARY - NAMES SDL2main - HINTS - ENV SDL2DIR - ${SDL2_NO_DEFAULT_PATH_CMD} - PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} - PATHS ${SDL2MAIN_LIBRARY_PATHS} - DOC "Where the SDL2main library can be found" - ) - unset(SDL2MAIN_LIBRARY_PATHS) - endif() -endif() - -# SDL2 may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -if(NOT APPLE) - find_package(Threads QUIET) - if(NOT Threads_FOUND) - set(SDL2_THREADS_NOT_FOUND "Could NOT find Threads (Threads is required by SDL2).") - if(SDL2_FIND_REQUIRED) - message(FATAL_ERROR ${SDL2_THREADS_NOT_FOUND}) - else() - if(NOT SDL2_FIND_QUIETLY) - message(STATUS ${SDL2_THREADS_NOT_FOUND}) - endif() - return() - endif() - unset(SDL2_THREADS_NOT_FOUND) - endif() -endif() - -# MinGW needs an additional link flag, -mwindows -# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows -if(MINGW) - set(MINGW32_LIBRARY mingw32 "-mwindows" CACHE STRING "link flags for MinGW") -endif() - -if(SDL2_LIBRARY) - # For SDL2main - if(SDL2MAIN_LIBRARY AND NOT SDL2_BUILDING_LIBRARY) - list(FIND SDL2_LIBRARIES "${SDL2MAIN_LIBRARY}" _SDL2_MAIN_INDEX) - if(_SDL2_MAIN_INDEX EQUAL -1) - set(SDL2_LIBRARIES "${SDL2MAIN_LIBRARY}" ${SDL2_LIBRARIES}) - endif() - unset(_SDL2_MAIN_INDEX) - endif() - - # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - if(APPLE) - set(SDL2_LIBRARIES ${SDL2_LIBRARIES} -framework Cocoa) - endif() - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - if(NOT APPLE) - set(SDL2_LIBRARIES ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) - endif() - - # For MinGW library - if(MINGW) - set(SDL2_LIBRARIES ${MINGW32_LIBRARY} ${SDL2_LIBRARIES}) - endif() - -endif() - -# Read SDL2 version -if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL_version.h") - file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") - file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") - file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") - string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") - string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") - string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") - set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}) - unset(SDL2_VERSION_MAJOR_LINE) - unset(SDL2_VERSION_MINOR_LINE) - unset(SDL2_VERSION_PATCH_LINE) - unset(SDL2_VERSION_MAJOR) - unset(SDL2_VERSION_MINOR) - unset(SDL2_VERSION_PATCH) -endif() - -include(FindPackageHandleStandardArgs) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 - REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR - VERSION_VAR SDL2_VERSION_STRING) - -if(SDL2MAIN_LIBRARY) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main - REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR - VERSION_VAR SDL2_VERSION_STRING) -endif() - - -mark_as_advanced(SDL2_PATH - SDL2_NO_DEFAULT_PATH - SDL2_LIBRARY - SDL2MAIN_LIBRARY - SDL2_INCLUDE_DIR - SDL2_BUILDING_LIBRARY) - - -# SDL2:: targets (SDL2::Core and SDL2::Main) -if(SDL2_FOUND) - - # SDL2::Core target - if(SDL2_LIBRARY AND NOT TARGET SDL2::Core) - add_library(SDL2::Core UNKNOWN IMPORTED) - set_target_properties(SDL2::Core PROPERTIES - IMPORTED_LOCATION "${SDL2_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}") - - if(APPLE) - # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. - # For more details, please see above. - set_property(TARGET SDL2::Core APPEND PROPERTY - INTERFACE_LINK_OPTIONS -framework Cocoa) - else() - # For threads, as mentioned Apple doesn't need this. - # For more details, please see above. - set_property(TARGET SDL2::Core APPEND PROPERTY - INTERFACE_LINK_LIBRARIES Threads::Threads) - endif() - endif() - - # SDL2::Main target - # Applications should link to SDL2::Main instead of SDL2::Core - # For more details, please see above. - if(NOT SDL2_BUILDING_LIBRARY AND NOT TARGET SDL2::Main) - - if(SDL2_INCLUDE_DIR MATCHES ".framework" OR NOT SDL2MAIN_LIBRARY) - add_library(SDL2::Main INTERFACE IMPORTED) - set_property(TARGET SDL2::Main PROPERTY - INTERFACE_LINK_LIBRARIES SDL2::Core) - elseif(SDL2MAIN_LIBRARY) - # MinGW requires that the mingw32 library is specified before the - # libSDL2main.a static library when linking. - # The SDL2::MainInternal target is used internally to make sure that - # CMake respects this condition. - add_library(SDL2::MainInternal UNKNOWN IMPORTED) - set_property(TARGET SDL2::MainInternal PROPERTY - IMPORTED_LOCATION "${SDL2MAIN_LIBRARY}") - set_property(TARGET SDL2::MainInternal PROPERTY - INTERFACE_LINK_LIBRARIES SDL2::Core) - - add_library(SDL2::Main INTERFACE IMPORTED) - - if(MINGW) - # MinGW needs an additional link flag '-mwindows' and link to mingw32 - set_property(TARGET SDL2::Main PROPERTY - INTERFACE_LINK_LIBRARIES "mingw32" "-mwindows") - endif() - - set_property(TARGET SDL2::Main APPEND PROPERTY - INTERFACE_LINK_LIBRARIES SDL2::MainInternal) - endif() - - endif() -endif() diff --git a/ffmpeg_core/cmake/FindSWRESAMPLE.cmake b/ffmpeg_core/cmake/FindSWRESAMPLE.cmake deleted file mode 100644 index cc6c44e0d..000000000 --- a/ffmpeg_core/cmake/FindSWRESAMPLE.cmake +++ /dev/null @@ -1,30 +0,0 @@ -find_package(PkgConfig) -if (PkgConfig_FOUND) - pkg_check_modules(PC_SWRESAMPLE QUIET IMPORTED_TARGET GLOBAL libswresample) -endif() - -if (PC_SWRESAMPLE_FOUND) - set(SWRESAMPLE_FOUND TRUE) - set(SWRESAMPLE_VERSION ${PC_SWRESAMPLE_VERSION}) - set(SWRESAMPLE_VERSION_STRING ${PC_SWRESAMPLE_STRING}) - set(SWRESAMPLE_LIBRARYS ${PC_SWRESAMPLE_LIBRARIES}) - if (USE_STATIC_LIBS) - set(SWRESAMPLE_INCLUDE_DIRS ${PC_SWRESAMPLE_STATIC_INCLUDE_DIRS}) - else() - set(SWRESAMPLE_INCLUDE_DIRS ${PC_SWRESAMPLE_INCLUDE_DIRS}) - endif() - if (NOT TARGET SWRESAMPLE::SWRESAMPLE) - add_library(SWRESAMPLE::SWRESAMPLE ALIAS PkgConfig::PC_SWRESAMPLE) - endif() -else() - message(FATAL_ERROR "failed.") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SWRESAMPLE - FOUND_VAR SWRESAMPLE_FOUND - REQUIRED_VARS - SWRESAMPLE_LIBRARYS - SWRESAMPLE_INCLUDE_DIRS - VERSION_VAR SWRESAMPLE_VERSION -) diff --git a/ffmpeg_core/ffmpeg_core.h b/ffmpeg_core/ffmpeg_core.h deleted file mode 100644 index 4096008bd..000000000 --- a/ffmpeg_core/ffmpeg_core.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef _MUSICPLAYER2_FFMPEG_CORE_H -#define _MUSICPLAYER2_FFMPEG_CORE_H -#ifdef __cplusplus -extern "C" { -#endif -#include -#include -#include -#include -#if BUILD_FFMPEG_CORE -#define FFMPEG_CORE_API __declspec(dllexport) -#else -#define FFMPEG_CORE_API __declspec(dllimport) -#endif -typedef struct MusicHandle MusicHandle; -typedef struct MusicInfoHandle MusicInfoHandle; -typedef struct FfmpegCoreSettings FfmpegCoreSettings; -typedef struct DeviceNameList { -char* device; -struct DeviceNameList* prev; -struct DeviceNameList* next; -} DeviceNameList; -// 负数即为来自ffmpeg的错误 - -#define FFMPEG_CORE_ERR_OK 0 -#define FFMPEG_CORE_ERR_NULLPTR 1 -#define FFMPEG_CORE_ERR_INVAILD_NAME 2 -#define FFMPEG_CORE_ERR_OOM 3 -#define FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER 4 -#define FFMPEG_CORE_ERR_UNKNOWN_SAMPLE_FMT 5 -#define FFMPEG_CORE_ERR_SDL 6 -#define FFMPEG_CORE_ERR_FAILED_CREATE_THREAD 7 -#define FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX 8 -#define FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED 9 -#define FFMPEG_CORE_ERR_NO_AUDIO 10 -#define FFMPEG_CORE_ERR_FAILED_SET_VOLUME 11 -#define FFMPEG_CORE_ERR_FAILED_SET_SPEED 12 -#define FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN 13 -#define FFMPEG_CORE_ERR_FAILED_OPEN_FILE 14 -#define FFMPEG_CORE_ERR_FAILED_READ_FILE 15 -#define FFMPEG_CORE_ERR_INVALID_CDA_FILE 16 -#define FFMPEG_CORE_ERR_NO_LIBCDIO 17 -#define FFMEPG_CORE_ERR_FAILED_PARSE_URL 18 -#define FFMPEG_CORE_ERR_FAILED_SET_EQUALIZER_CHANNEL 19 -FFMPEG_CORE_API void free_music_handle(MusicHandle* handle); -FFMPEG_CORE_API void free_music_info_handle(MusicInfoHandle* handle); -FFMPEG_CORE_API void free_ffmpeg_core_settings(FfmpegCoreSettings* s); -FFMPEG_CORE_API void free_device_name_list(DeviceNameList** list); -FFMPEG_CORE_API void ffmpeg_core_free(void* data); -FFMPEG_CORE_API void* ffmpeg_core_malloc(size_t size); -FFMPEG_CORE_API void* ffmpeg_core_realloc(void* data, size_t size); -/// 即 av_log_format_line2 -FFMPEG_CORE_API int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix); -/// 即 av_log_set_callback -FFMPEG_CORE_API void ffmpeg_core_log_set_callback(void(*callback)(void*, int, const char*, va_list)); -/// 即 av_log_set_flags -FFMPEG_CORE_API void ffmpeg_core_log_set_flags(int arg); -FFMPEG_CORE_API const char* ffmpeg_core_version_str(); -FFMPEG_CORE_API int32_t ffmpeg_core_version(); -FFMPEG_CORE_API void ffmpeg_core_dump_library_version(int use_av_log, int av_log_level); -FFMPEG_CORE_API void ffmpeg_core_dump_ffmpeg_configuration(int use_av_log, int av_log_level); -FFMPEG_CORE_API int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle); -FFMPEG_CORE_API int ffmpeg_core_open2(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s); -FFMPEG_CORE_API int ffmpeg_core_open3(const wchar_t* url, MusicHandle** handle, FfmpegCoreSettings* s, const wchar_t* device); -FFMPEG_CORE_API int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle); -FFMPEG_CORE_API int ffmpeg_core_play(MusicHandle* handle); -FFMPEG_CORE_API int ffmpeg_core_pause(MusicHandle* handle); -FFMPEG_CORE_API int ffmpeg_core_seek(MusicHandle* handle, int64_t time); -FFMPEG_CORE_API int ffmpeg_core_set_volume(MusicHandle* handle, int volume); -FFMPEG_CORE_API int ffmpeg_core_set_speed(MusicHandle* handle, float speed); -FFMPEG_CORE_API int ffmpeg_core_set_equalizer_channel(MusicHandle* handle, int channel, int gain); -FFMPEG_CORE_API int ffmpeg_core_get_error(MusicHandle* handle); -/** - * @brief 返回错误代码对应的错误消息 - * @param err 错误代码 - * @return 错误消息,需要调用free释放内存 -*/ -FFMPEG_CORE_API wchar_t* ffmpeg_core_get_err_msg(int err); -/** - * @brief 返回错误代码对应的错误消息 - * @param err 错误代码(仅处理>=0的错误) - * @return 错误消息 -*/ -FFMPEG_CORE_API const wchar_t* ffmpeg_core_get_err_msg2(int err); -/** - * @brief 获取当前播放位置 - * @param handle Handle - * @return 如果Handle为NULL,返回-1,反之返回以AV_TIME_BASE为基准的时间(1相当于1/1000000s) -*/ -FFMPEG_CORE_API int64_t ffmpeg_core_get_cur_position(MusicHandle* handle); -/** - * @brief 是否已经播放完 - * @param handle Handle - * @return 如果Handle为NULL或未播放完,返回0,反之返回1 -*/ -FFMPEG_CORE_API int ffmpeg_core_song_is_over(MusicHandle* handle); -/** - * @brief 获取长度(由demuxer回报,可能不准或者不存在) - * @param handle Handle - * @return 如果Handle为NULL,返回-1,反之返回以AV_TIME_BASE为基准的时间(1相当于1/1000000s) -*/ -FFMPEG_CORE_API int64_t ffmpeg_core_get_song_length(MusicHandle* handle); -FFMPEG_CORE_API int64_t ffmpeg_core_info_get_song_length(MusicInfoHandle* handle); -/** - * @brief 返回音频文件声道数(SDL可能会改如果音频设备不支持) - * @param handle Handle - * @return 如果Handle为NULL,返回-1,反之返回声道数 -*/ -FFMPEG_CORE_API int ffmpeg_core_get_channels(MusicHandle* handle); -FFMPEG_CORE_API int ffmpeg_core_info_get_channels(MusicInfoHandle* handle); -/** - * @brief 返回音频文件采样频率(SDL可能会修改) - * @param handle Handle - * @return 如果Handle为NULL,返回-1,反之返回采样频率 -*/ -FFMPEG_CORE_API int ffmpeg_core_get_freq(MusicHandle* handle); -FFMPEG_CORE_API int ffmpeg_core_info_get_freq(MusicInfoHandle* handle); -/** - * @brief 返回当前状态 - * @param handle Handle - * @return 如果Handle为NULL,返回-1,正在播放返回1,未播放返回0 -*/ -FFMPEG_CORE_API int ffmpeg_core_is_playing(MusicHandle* handle); -/** - * @brief 返回位数 - * @param handle Handle - * @return 如果Handle为NULL,返回-1 -*/ -FFMPEG_CORE_API int ffmpeg_core_get_bits(MusicHandle* handle); -FFMPEG_CORE_API int ffmpeg_core_info_get_bits(MusicInfoHandle* handle); -/** - * @brief 返回比特率 - * @param handle Handle - * @return 如果Handle为NULL,返回-1 -*/ -FFMPEG_CORE_API int64_t ffmpeg_core_get_bitrate(MusicHandle* handle); -FFMPEG_CORE_API int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle); -/** - * @brief 获取元数据 - * @param handle Handle - * @param key 元数据Key - * @return 结果,需要手动调用free释放内存 -*/ -FFMPEG_CORE_API wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key); -FFMPEG_CORE_API wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key); -FFMPEG_CORE_API int ffmpeg_core_get_fft_data(MusicHandle* handle, float* fft_data, int len); -FFMPEG_CORE_API FfmpegCoreSettings* ffmpeg_core_init_settings(); -FFMPEG_CORE_API int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume); -FFMPEG_CORE_API int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed); -FFMPEG_CORE_API int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length); -FFMPEG_CORE_API int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retry_count); -FFMPEG_CORE_API int ffmpeg_core_settings_set_url_retry_interval(FfmpegCoreSettings* s, int url_retry_interval); -FFMPEG_CORE_API int ffmpeg_core_settings_set_equalizer_channel(FfmpegCoreSettings* s, int channel, int gain); -FFMPEG_CORE_API DeviceNameList* ffmpeg_core_get_audio_devices(); -#ifdef __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/ffmpeg_core.rc.in b/ffmpeg_core/ffmpeg_core.rc.in deleted file mode 100644 index 789e909eb..000000000 --- a/ffmpeg_core/ffmpeg_core.rc.in +++ /dev/null @@ -1,35 +0,0 @@ -#include -#ifndef DEBUG -#define VER_DEBUG 0 -#else -#define VER_DEBUG VS_FF_DEBUG -#endif - -VS_VERSION_INFO VERSIONINFO -FILEVERSION @FFMPEG_CORE_VERSION_MAJOR@,@FFMPEG_CORE_VERSION_MINOR@,@FFMPEG_CORE_VERSION_MICRO@,@FFMPEG_CORE_VERSION_REV@ -PRODUCTVERSION @FFMPEG_CORE_VERSION_MAJOR@,@FFMPEG_CORE_VERSION_MINOR@,@FFMPEG_CORE_VERSION_MICRO@,@FFMPEG_CORE_VERSION_REV@ -FILEFLAGSMASK VS_FF_DEBUG -FILEFLAGS VER_DEBUG -FILEOS VOS__WINDOWS32 -FILETYPE VFT_DLL -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "04090000" - BEGIN - VALUE "CompanyName", "lifegpc\0" - VALUE "FileDescription", "A music player core which use FFMPEG and SDL2.\0" - VALUE "FileVersion", "@FFMPEG_CORE_VERSION@\0" - VALUE "InternalName", "ffmpeg_core\0" - VALUE "LegalCopyright", "Copyright (C) 2022 lifegpc\0" - VALUE "OriginalFilename", "ffmpeg_core.dll\0" - VALUE "ProductName", "ffmpeg_core\0" - VALUE "ProductVersion", "@FFMPEG_CORE_VERSION@\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 0 - END -END diff --git a/ffmpeg_core/ffmpeg_core_version.h.in b/ffmpeg_core/ffmpeg_core_version.h.in deleted file mode 100644 index 35cb9d79e..000000000 --- a/ffmpeg_core/ffmpeg_core_version.h.in +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _FFMPEG_CORE_VERSION -#define _FFMPEG_CORE_VERSION -#define FFMPEG_CORE_VERSION_MAJOR @FFMPEG_CORE_VERSION_MAJOR@ -#define FFMPEG_CORE_VERSION_MINOR @FFMPEG_CORE_VERSION_MINOR@ -#define FFMPEG_CORE_VERSION_MICRO @FFMPEG_CORE_VERSION_MICRO@ -#define FFMPEG_CORE_VERSION_REV @FFMPEG_CORE_VERSION_REV@ -#define FFMPEG_CORE_VERSION "@FFMPEG_CORE_VERSION@" -#define FFMPEG_CORE_VERSION_INT (((int32_t)FFMPEG_CORE_VERSION_MAJOR << 24) | ((int32_t)FFMPEG_CORE_VERSION_MINOR << 16) | ((int32_t)FFMPEG_CORE_VERSION_MICRO << 8) | (int32_t)FFMPEG_CORE_VERSION_REV) -#endif diff --git a/ffmpeg_core/src/cda.c b/ffmpeg_core/src/cda.c deleted file mode 100644 index b29a72735..000000000 --- a/ffmpeg_core/src/cda.c +++ /dev/null @@ -1,227 +0,0 @@ -#include "cda.h" - -#include -#include -#include -#include "cfileop.h" -#include "cstr_util.h" -#include "err.h" - -#ifndef _O_BINARY -#define _O_BINARY 0x8000 -#endif - -#ifndef _SH_DENYWR -#define _SH_DENYWR 0x20 -#endif - -#ifndef _S_IREAD -#define _S_IREAD 0x100 -#endif - -#define CDA_FILE_SIZE 44 -#define u8_buf(buf, offset) ((const uint8_t*)buf + offset) - -int is_cda_file(const char* url) { - if (!url) return 0; - char* dir = fileop_dirname(url); - if (!dir) return 0; - // 判断是否为根盘符 - if (!fileop_isdrive(dir)) { - free(dir); - return 0; - } - free(dir); - char* l = strrchr(url, '.'); - if (!l || cstr_stricmp(l, ".cda")) { - return 0; - } - size_t size = 0; - if (!fileop_get_file_size(url, &size)) return 0; - if (size != CDA_FILE_SIZE) return 0; - return 1; -} - -// CDA 文件格式见 https://en.wikipedia.org/wiki/.cda_file -int read_cda_file(MusicHandle* handle, const char* url) { - if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; - int fd = 0; - int re = 0; - FILE* f = NULL; - char buf[CDA_FILE_SIZE]; - int num_read = 0; - uint32_t chunk_size = 0; - if ((re = fileop_open(url, &fd, O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD))) { - char* errmsg = err_get_errno_message(re); - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%d)\n", url, errmsg ? errmsg : "", re); - if (errmsg) free(errmsg); - return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; - } - f = fileop_fdopen(fd, "r"); - if (!f) { - fileop_close(fd); - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": Can not open file descriptor %d\n", url, fd); - return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; - } - handle->cda = malloc(sizeof(CDAData)); - if (!handle->cda) { - re = FFMPEG_CORE_ERR_OOM; - goto end; - } - memset(handle->cda, 0, sizeof(CDAData)); - if ((num_read = fread(buf, 1, CDA_FILE_SIZE, f)) < CDA_FILE_SIZE) { - av_log(NULL, AV_LOG_FATAL, "Failed to read file \"%s\": %d bytes is needed, but only bytes was readed.\n", url, CDA_FILE_SIZE, num_read); - re = FFMPEG_CORE_ERR_FAILED_READ_FILE; - goto end; - } - if (strncmp(buf, "RIFF", 4)) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - chunk_size = cstr_read_uint32(u8_buf(buf, 4), 0); - if (chunk_size != 36) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - if (strncmp(buf + 8, "CDDAfmt ", 8)) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - chunk_size = cstr_read_uint32(u8_buf(buf, 16), 0); - if (chunk_size != 24) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - handle->cda->cd_format_version = cstr_read_uint16(u8_buf(buf, 20), 0); - handle->cda->no = cstr_read_uint16(u8_buf(buf, 22), 0); - handle->cda->range_offset = cstr_read_uint32(u8_buf(buf, 28), 0); - handle->cda->duration = cstr_read_uint32(u8_buf(buf, 32), 0); - re = FFMPEG_CORE_ERR_OK; -end: - if (f) fileop_fclose(f); - return re; -} - -int read_cda_file2(MusicInfoHandle* handle, const char* url) { - if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; - int fd = 0; - int re = 0; - FILE* f = NULL; - char buf[CDA_FILE_SIZE]; - int num_read = 0; - uint32_t chunk_size = 0; - if ((re = fileop_open(url, &fd, O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD))) { - char* errmsg = err_get_errno_message(re); - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%d)\n", url, errmsg ? errmsg : "", re); - if (errmsg) free(errmsg); - return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; - } - f = fileop_fdopen(fd, "r"); - if (!f) { - fileop_close(fd); - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": Can not open file descriptor %d\n", url, fd); - return FFMPEG_CORE_ERR_FAILED_OPEN_FILE; - } - handle->cda = malloc(sizeof(CDAData)); - if (!handle->cda) { - re = FFMPEG_CORE_ERR_OOM; - goto end; - } - memset(handle->cda, 0, sizeof(CDAData)); - if ((num_read = fread(buf, 1, CDA_FILE_SIZE, f)) < CDA_FILE_SIZE) { - av_log(NULL, AV_LOG_FATAL, "Failed to read file \"%s\": %d bytes is needed, but only bytes was readed.\n", url, CDA_FILE_SIZE, num_read); - re = FFMPEG_CORE_ERR_FAILED_READ_FILE; - goto end; - } - if (strncmp(buf, "RIFF", 4)) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - chunk_size = cstr_read_uint32(u8_buf(buf, 4), 0); - if (chunk_size != 36) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - if (strncmp(buf + 8, "CDDAfmt ", 8)) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - chunk_size = cstr_read_uint32(u8_buf(buf, 16), 0); - if (chunk_size != 24) { - re = FFMPEG_CORE_ERR_INVALID_CDA_FILE; - goto end; - } - handle->cda->cd_format_version = cstr_read_uint16(u8_buf(buf, 20), 0); - handle->cda->no = cstr_read_uint16(u8_buf(buf, 22), 0); - handle->cda->range_offset = cstr_read_uint32(u8_buf(buf, 28), 0); - handle->cda->duration = cstr_read_uint32(u8_buf(buf, 32), 0); - re = FFMPEG_CORE_ERR_OK; -end: - if (f) fileop_fclose(f); - return re; -} - -const AVInputFormat* find_libcdio() { - const AVInputFormat* f = NULL; - f = av_input_audio_device_next(f); - while (f) { - if (f && !strcmp(f->name, "libcdio")) return f; - f = av_input_audio_device_next(f); - } - return NULL; -} - -int open_cd_device(MusicHandle* handle, const char* device) { - if (!handle || !device) return FFMPEG_CORE_ERR_NULLPTR; - avdevice_register_all(); - const AVInputFormat* f = find_libcdio(); - AVRational cda_time_base = { 1, 75 }; - if (!f) { - return FFMPEG_CORE_ERR_NO_LIBCDIO; - } - int re = 0; - AVDictionary* d = NULL; - av_dict_set(&d, "paranoia_mode", "verify", 0); - if ((re = avformat_open_input(&handle->fmt, device, f, &d)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", device, av_err2str(re), re); - av_dict_free(&d); - return re; - } - if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", device, av_err2str(re), re); - av_dict_free(&d); - return re; - } - handle->only_part = 1; - handle->part_start_pts = av_rescale_q_rnd(handle->cda->range_offset, cda_time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - handle->part_end_pts = av_rescale_q_rnd((int64_t)handle->cda->duration + handle->cda->range_offset, cda_time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - // handle->fmt->flags |= AVFMT_FLAG_FAST_SEEK; // 允许快速定位 - av_dict_free(&d); - return FFMPEG_CORE_ERR_OK; -} - -int open_cd_device2(MusicInfoHandle* handle, const char* device) { - if (!handle || !device) return FFMPEG_CORE_ERR_NULLPTR; - avdevice_register_all(); - const AVInputFormat* f = find_libcdio(); - AVRational cda_time_base = { 1, 75 }; - if (!f) { - return FFMPEG_CORE_ERR_NO_LIBCDIO; - } - int re = 0; - if ((re = avformat_open_input(&handle->fmt, device, f, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", device, av_err2str(re), re); - return re; - } - if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", device, av_err2str(re), re); - return re; - } - return FFMPEG_CORE_ERR_OK; -} - -int64_t get_cda_duration(CDAData* d) { - if (!d) return 0; - AVRational t = { 1, 75 }; - return av_rescale_q_rnd(d->duration, t, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); -} diff --git a/ffmpeg_core/src/cda.h b/ffmpeg_core/src/cda.h deleted file mode 100644 index 4fe56a077..000000000 --- a/ffmpeg_core/src/cda.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _MUSICPLAYER2_CDA_H -#define _MUSICPLAYER2_CDA_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -int is_cda_file(const char* url); -int read_cda_file(MusicHandle* handle, const char* url); -int read_cda_file2(MusicInfoHandle* handle, const char* url); -const AVInputFormat* find_libcdio(); -int open_cd_device(MusicHandle* handle, const char* device); -int open_cd_device2(MusicInfoHandle* handle, const char* device); -int64_t get_cda_duration(CDAData* d); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/core.cpp b/ffmpeg_core/src/core.cpp deleted file mode 100644 index 68bfd85bf..000000000 --- a/ffmpeg_core/src/core.cpp +++ /dev/null @@ -1,727 +0,0 @@ -#include "core.h" - -#include -#include -#include "cpp2c.h" -#include "wchar_util.h" -#include "open.h" -#include "output.h" -#include "loop.h" -#include "decode.h" -#include "filter.h" -#include "speed.h" -#include "cda.h" -#include "fileop.h" -#include "file.h" -#include "equalizer_settings.h" -#include "linked_list.h" -#include "cstr_util.h" -#include "ffmpeg_core_version.h" - -#define CODEPAGE_SIZE 3 -#define DEVICE_NAME_LIST struct LinkedList* - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -template -void tfree(T* data) { - free((void*)data); -} - -void free_music_handle(MusicHandle* handle) { - if (!handle) return; - if (handle->device_id) SDL_CloseAudioDevice(handle->device_id); - if (handle->thread) { - DWORD status; - while (GetExitCodeThread(handle->thread, &status)) { - if (status == STILL_ACTIVE) { - status = 0; - handle->stoping = 1; - Sleep(10); - } else { - break; - } - } - } - if (handle->filter_thread) { - DWORD status; - while (GetExitCodeThread(handle->filter_thread, &status)) { - if (status == STILL_ACTIVE) { - status = 0; - handle->stoping = 1; - Sleep(10); - } else { - break; - } - } - } - if (handle->graph) { - avfilter_graph_free(&handle->graph); - } - c_linked_list_clear(&handle->filters, nullptr); - if (handle->buffer) av_audio_fifo_free(handle->buffer); - if (handle->filters_buffer) av_audio_fifo_free(handle->filters_buffer); - if (handle->swrac) swr_free(&handle->swrac); - if (handle->decoder) avcodec_free_context(&handle->decoder); - if (handle->fmt) avformat_close_input(&handle->fmt); - if (handle->sdl_initialized) { - SDL_QuitSubSystem(SDL_INIT_AUDIO); - } - if (handle->s && handle->settings_is_alloc) { - free_ffmpeg_core_settings(handle->s); - } - if (handle->cda) free(handle->cda); - if (handle->url) free(handle->url); - if (handle->parsed_url) free_url_parse_result(handle->parsed_url); - free(handle); -} - -void free_music_info_handle(MusicInfoHandle* handle) { - if (!handle) return; - if (handle->fmt) avformat_close_input(&handle->fmt); - if (handle->cda) free(handle->cda); - free(handle); -} - -void free_ffmpeg_core_settings(FfmpegCoreSettings* s) { - if (!s) return; - free_equalizer_channels(&s->equalizer_channels); - free(s); -} - -void free_device_name_list(DeviceNameList** list) { - if (list) { - auto l = (DEVICE_NAME_LIST)(*list); - linked_list_clear(l, tfree); - *list = (DeviceNameList*)l; - } -} - -void ffmpeg_core_free(void* data) { - free(data); -} - -void* ffmpeg_core_malloc(size_t size) { - return malloc(size); -} - -void* ffmpeg_core_realloc(void* data, size_t size) { - return realloc(data, size); -} - -int ffmpeg_core_log_format_line(void* ptr, int level, const char* fmt, va_list vl, char* line, int line_size, int* print_prefix) { - return av_log_format_line2(ptr, level, fmt, vl, line, line_size, print_prefix); -} - -void ffmpeg_core_log_set_callback(void(*callback)(void*, int, const char*, va_list)) { - av_log_set_callback(callback); -} - -void ffmpeg_core_log_set_flags(int arg) { - av_log_set_flags(arg); -} - -const char* ffmpeg_core_version_str() { - return FFMPEG_CORE_VERSION; -} - -int32_t ffmpeg_core_version() { - return FFMPEG_CORE_VERSION_INT; -} - -#if _MSC_VER -#define PRINTF(f, ...) use_av_log ? av_log(NULL, av_log_level, f, __VA_ARGS__) : printf_s(f, __VA_ARGS__) -#else -#define PRINTF(f, ...) if (use_av_log) { av_log(NULL, av_log_level, f, ##__VA_ARGS__); } else { printf_s(f, ##__VA_ARGS__); } -#endif - - -void ffmpeg_core_dump_library_version(int use_av_log, int av_log_level) { - unsigned int v = avutil_version(); - PRINTF("FFMPEG libraries: \n"); - PRINTF("libavutil %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); - v = avcodec_version(); - PRINTF("libavcodec %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); - v = avformat_version(); - PRINTF("libavformat %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); - v = avdevice_version(); - PRINTF("libavdevice %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); - v = avfilter_version(); - PRINTF("libavfilter %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); - v = swresample_version(); - PRINTF("libswresample %3u.%3u.%3u\n", v >> 16, (v & 0xff00) >> 8, v & 0xff); - PRINTF("Other thirdparty libraries: \n"); - SDL_version sv; - SDL_GetVersion(&sv); - PRINTF("SDL2 %3u.%3u.%3u\n", sv.major, sv.minor, sv.patch); -} - -#define QUICK_PRINT_CONF(f, name) if (basic != f()) { \ -PRINTF(name " have different configuration: %s\n", f()); } - -void ffmpeg_core_dump_ffmpeg_configuration(int use_av_log, int av_log_level) { - std::string basic = avutil_configuration(); - PRINTF("configuration: %s\n", basic.c_str()); - QUICK_PRINT_CONF(avcodec_configuration, "libavcodec") - QUICK_PRINT_CONF(avformat_configuration, "libavformat") - QUICK_PRINT_CONF(avdevice_configuration, "libavdevice") - QUICK_PRINT_CONF(avfilter_configuration, "libavfilter") - QUICK_PRINT_CONF(swresample_configuration, "libswresample") -} - -#undef QUICK_PRINT_CONF -#undef PRINTF - -int ffmpeg_core_open(const wchar_t* url, MusicHandle** handle) { - return ffmpeg_core_open3(url, handle, nullptr, nullptr); -} - -int ffmpeg_core_open2(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s) { - return ffmpeg_core_open3(url, h, s, nullptr); -} - -int ffmpeg_core_open3(const wchar_t* url, MusicHandle** h, FfmpegCoreSettings* s, const wchar_t* device) { - if (!url || !h) return FFMPEG_CORE_ERR_NULLPTR; - std::string u; - // 将文件名转为UTF-8,ffmpeg API处理的都是UTF-8文件名 - if (!wchar_util::wstr_to_str(u, url, CP_UTF8)) { - return FFMPEG_CORE_ERR_INVAILD_NAME; - } - std::string d; - if (device && !wchar_util::wstr_to_str(d, device, CP_UTF8)) { - return FFMPEG_CORE_ERR_INVAILD_NAME; - } -#if NDEBUG - // 设置ffmpeg日志级别为Error - av_log_set_level(AV_LOG_ERROR); -#else - av_log_set_level(AV_LOG_VERBOSE); -#endif - MusicHandle* handle = (MusicHandle*)malloc(sizeof(MusicHandle)); - int re = FFMPEG_CORE_ERR_OK; - if (!handle) { - av_log(NULL, AV_LOG_FATAL, "Failed to allocate MusicHandle.\n"); - return FFMPEG_CORE_ERR_OOM; - } - memset(handle, 0, sizeof(MusicHandle)); - if (s) { - handle->s = s; - } else { - handle->settings_is_alloc = 1; - handle->s = ffmpeg_core_init_settings(); - if (!handle->s) { - av_log(NULL, AV_LOG_FATAL, "Failed to allocate settings struct.\n"); - re = FFMPEG_CORE_ERR_OOM; - goto end; - } - } - handle->first_pts = INT64_MIN; - handle->part_end_pts = INT64_MAX; - handle->is_cda = is_cda_file(u.c_str()); - if (handle->is_cda) { - if ((re = read_cda_file(handle, u.c_str()))) { - goto end; - } - u = fileop::dirname(u); - if ((re = open_cd_device(handle, u.c_str()))) { - goto end; - } - } else { - if ((re = open_input(handle, u.c_str()))) { - goto end; - } - } -#ifndef NDEBUG - av_dump_format(handle->fmt, 0, u.c_str(), 0); -#endif - if (!cpp2c::string2char(u, handle->url)) { - re = FFMPEG_CORE_ERR_OOM; - goto end; - } - handle->parsed_url = urlparse(handle->url, nullptr, 1); - if (!handle->parsed_url) { - re = FFMEPG_CORE_ERR_FAILED_PARSE_URL; - goto end; - } - handle->is_file = is_file(handle->parsed_url); - if ((re = find_audio_stream(handle))) { - av_log(NULL, AV_LOG_FATAL, "Failed to find suitable audio stream.\n"); - goto end; - } - if ((re = open_decoder(handle))) { - goto end; - } - if ((re = init_output(handle, d.empty() ? nullptr : d.c_str()))) { - goto end; - } - if ((re = init_filters(handle))) { - goto end; - } - handle->filters_buffer = av_audio_fifo_alloc(handle->target_format, handle->sdl_spec.channels, 1); - if (!handle->filters_buffer) { - re = FFMPEG_CORE_ERR_OOM; - av_log(NULL, AV_LOG_FATAL, "Failed to allocate buffer for filters.\n"); - goto end; - } - handle->mutex = CreateMutexW(nullptr, FALSE, nullptr); - if (!handle->mutex) { - re = FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX; - goto end; - } - handle->mutex2 = CreateMutexW(nullptr, FALSE, nullptr); - if (!handle->mutex2) { - re = FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX; - goto end; - } - handle->thread = CreateThread(nullptr, 0, event_loop, handle, 0, &handle->thread_id); - if (!handle->thread) { - re = FFMPEG_CORE_ERR_FAILED_CREATE_THREAD; - goto end; - } - handle->filter_thread = CreateThread(nullptr, 0, filter_loop, handle, 0, &handle->filter_thread_id); - if (!handle->filter_thread) { - re = FFMPEG_CORE_ERR_FAILED_CREATE_THREAD; - goto end; - } - *h = handle; - return re; -end: - free_music_handle(handle); - return re; -} - -int ffmpeg_core_info_open(const wchar_t* url, MusicInfoHandle** handle) { - if (!url || !handle) return FFMPEG_CORE_ERR_NULLPTR; - std::string u; - // 将文件名转为UTF-8,ffmpeg API处理的都是UTF-8文件名 - if (!wchar_util::wstr_to_str(u, url, CP_UTF8)) { - return FFMPEG_CORE_ERR_INVAILD_NAME; - } -#if NDEBUG - // 设置ffmpeg日志级别为Error - av_log_set_level(AV_LOG_ERROR); -#else - av_log_set_level(AV_LOG_VERBOSE); -#endif - MusicInfoHandle* h = (MusicInfoHandle*)malloc(sizeof(MusicInfoHandle)); - int re = FFMPEG_CORE_ERR_OK; - int is_cda = 0; - if (!h) { - return FFMPEG_CORE_ERR_OOM; - } - memset(h, 0, sizeof(MusicInfoHandle)); - is_cda = is_cda_file(u.c_str()); - if (is_cda) { - if ((re = read_cda_file2(h, u.c_str()))) { - goto end; - } - u = fileop::dirname(u); - if ((re = open_cd_device2(h, u.c_str()))) { - goto end; - } - } else { - if ((re = open_input2(h, u.c_str()))) { - goto end; - } - } - if ((re = find_audio_stream2(h))) { - goto end; - } - *handle = h; - return FFMPEG_CORE_ERR_OK; -end: - free_music_info_handle(h); - return re; -} - -int ffmpeg_core_play(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - SDL_PauseAudioDevice(handle->device_id, 0); - handle->is_playing = 1; - return FFMPEG_CORE_ERR_OK; -} - -int ffmpeg_core_pause(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - SDL_PauseAudioDevice(handle->device_id, 1); - handle->is_playing = 0; - return FFMPEG_CORE_ERR_OK; -} - -int64_t ffmpeg_core_get_cur_position(MusicHandle* handle) { - if (!handle) return -1; - if (handle->only_part) return handle->pts - handle->part_start_pts; - // 忽略 SDL 可能长达 0.01s 的 buffer - return handle->pts; -} - -int ffmpeg_core_song_is_over(MusicHandle* handle) { - if (!handle || !handle->buffer) return 0; - if (handle->is_eof && av_audio_fifo_size(handle->buffer) == 0) return 1; - else return 0; -} - -int64_t ffmpeg_core_get_song_length(MusicHandle* handle) { - if (!handle || !handle->fmt) return -1; - if (handle->only_part) { - return min(handle->part_end_pts - handle->part_start_pts, handle->fmt->duration - handle->part_start_pts); - } - return handle->fmt->duration; -} - -int64_t ffmpeg_core_info_get_song_length(MusicInfoHandle* handle) { - if (!handle || !handle->fmt) return -1; - if (handle->cda) return get_cda_duration(handle->cda); - return handle->fmt->duration; -} - -int ffmpeg_core_get_channels(MusicHandle* handle) { - if (!handle || !handle->decoder) return -1; - return handle->decoder->channels; -} - -int ffmpeg_core_info_get_channels(MusicInfoHandle* handle) { - if (!handle || !handle->is) return -1; - return handle->is->codecpar->channels; -} - -int ffmpeg_core_get_freq(MusicHandle* handle) { - if (!handle || !handle->decoder) return -1; - return handle->decoder->sample_rate; -} - -int ffmpeg_core_info_get_freq(MusicInfoHandle* handle) { - if (!handle || !handle->is) return -1; - return handle->is->codecpar->sample_rate; -} - -int ffmpeg_core_seek(MusicHandle* handle, int64_t time) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - DWORD re = WaitForSingleObject(handle->mutex, INFINITE); - if (re == WAIT_OBJECT_0) { - handle->is_seek = 1; - handle->seek_pos = time; - if (handle->only_part) { - handle->seek_pos += handle->part_start_pts; - } - } else { - return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - } - if (handle->is_reopen) { - ReleaseMutex(handle->mutex); - return FFMPEG_CORE_ERR_OK; - } - handle->have_err = 0; - ReleaseMutex(handle->mutex); - while (1) { - if (!handle->is_seek) break; - Sleep(10); - } - return handle->have_err ? handle->err : FFMPEG_CORE_ERR_OK; -} - -int ffmpeg_core_is_playing(MusicHandle* handle) { - if (!handle) return -1; - return handle->is_playing; -} - -int ffmpeg_core_get_bits(MusicHandle* handle) { - if (!handle || !handle->decoder) return -1; - return handle->decoder->bits_per_raw_sample; -} - -int ffmpeg_core_info_get_bits(MusicInfoHandle* handle) { - if (!handle || !handle->is) return -1; - return handle->is->codecpar->bits_per_raw_sample; -} - -int64_t ffmpeg_core_get_bitrate(MusicHandle* handle) { - if (!handle || !handle->decoder) return -1; - if (handle->decoder->bit_rate) return handle->decoder->bit_rate; - if (handle->fmt && handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; - return 0; -} - -int64_t ffmpeg_core_info_get_bitrate(MusicInfoHandle* handle) { - if (!handle || !handle->is) return -1; - if (handle->is->codecpar->bit_rate > 0) return handle->is->codecpar->bit_rate; - if (handle->fmt && handle->fmt->bit_rate > 0) return handle->fmt->bit_rate; - return 0; -} - -std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags) { - auto re = av_dict_get(dict, key, nullptr, flags); - if (!re || !re->value) return L""; - std::string value(re->value); - std::wstring result; - unsigned int cps[CODEPAGE_SIZE] = { CP_UTF8, CP_ACP, CP_OEMCP }; - for (size_t i = 0; i < CODEPAGE_SIZE; i++) { - if (wchar_util::str_to_wstr(result, value, cps[i])) { - return result; - } - } - return L""; -} - -wchar_t* ffmpeg_core_get_metadata(MusicHandle* handle, const char* key) { - if (!handle || !key) return nullptr; - if (handle->fmt && handle->fmt->metadata) { - auto re = get_metadata_str(handle->fmt->metadata, key, 0); - if (!re.empty()) { - wchar_t* r = nullptr; - if (cpp2c::string2char(re, r)) { - return r; - } - } - } - if (handle->is && handle->is->metadata) { - auto re = get_metadata_str(handle->is->metadata, key, 0); - if (!re.empty()) { - wchar_t* r = nullptr; - if (cpp2c::string2char(re, r)) { - return r; - } - } - } - return nullptr; -} - -wchar_t* ffmpeg_core_info_get_metadata(MusicInfoHandle* handle, const char* key) { - if (!handle || !key) return nullptr; - if (handle->fmt && handle->fmt->metadata) { - auto re = get_metadata_str(handle->fmt->metadata, key, 0); - if (!re.empty()) { - wchar_t* r = nullptr; - if (cpp2c::string2char(re, r)) { - return r; - } - } - } - if (handle->is && handle->is->metadata) { - auto re = get_metadata_str(handle->is->metadata, key, 0); - if (!re.empty()) { - wchar_t* r = nullptr; - if (cpp2c::string2char(re, r)) { - return r; - } - } - } - return nullptr; -} - -int send_reinit_filters(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - DWORD re = WaitForSingleObject(handle->mutex, INFINITE); - if (re == WAIT_OBJECT_0) { - handle->need_reinit_filters = 1; - } else { - return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - } - if (handle->is_reopen) { - ReleaseMutex(handle->mutex); - return FFMPEG_CORE_ERR_OK; - } - handle->have_err = 0; - ReleaseMutex(handle->mutex); - while (handle->need_reinit_filters) { - Sleep(10); - } - return handle->have_err ? handle->err : FFMPEG_CORE_ERR_OK; -} - -FfmpegCoreSettings* ffmpeg_core_init_settings() { - FfmpegCoreSettings* s = (FfmpegCoreSettings*)malloc(sizeof(FfmpegCoreSettings)); - if (!s) return nullptr; - memset(s, 0, sizeof(FfmpegCoreSettings)); - s->speed = 1.0; - s->volume = 100; - s->cache_length = 15; - s->max_retry_count = 3; - s->url_retry_interval = 5; - return s; -} - -int ffmpeg_core_settings_set_volume(FfmpegCoreSettings* s, int volume) { - if (!s) return 0; - if (volume >= 0 && volume <= 100) { - s->volume = volume; - return 1; - } - return 0; -} - -int ffmpeg_core_set_volume(MusicHandle* handle, int volume) { - if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; - int r = ffmpeg_core_settings_set_volume(handle->s, volume); - if (!r) return FFMPEG_CORE_ERR_FAILED_SET_VOLUME; - return send_reinit_filters(handle); -} - -int ffmpeg_core_settings_set_speed(FfmpegCoreSettings* s, float speed) { - if (!s) return 0; - int sp = get_speed(speed); - if (sp >= 63 && sp <= 16000) { - s->speed = sp / 1000.0; - return 1; - } - return 0; -} - -int ffmpeg_core_set_speed(MusicHandle* handle, float speed) { - if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; - int r = ffmpeg_core_settings_set_speed(handle->s, speed); - if (!r) return FFMPEG_CORE_ERR_FAILED_SET_SPEED; - return send_reinit_filters(handle); -} - -int ffmpeg_core_settings_set_cache_length(FfmpegCoreSettings* s, int length) { - if (!s) return 0; - if (length >= 1 && length <= 60) s->cache_length = length; - return 1; -} - -int ffmpeg_core_get_error(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - return handle->have_err ? handle->err : 0; -} - -const wchar_t* ffmpeg_core_get_err_msg2(int err) { - if (err < 0) return L"An error occured in ffmpeg."; - switch (err) { - case FFMPEG_CORE_ERR_OK: - return L"No error occured."; - case FFMPEG_CORE_ERR_NULLPTR: - return L"Got an unexpected null pointer."; - case FFMPEG_CORE_ERR_INVAILD_NAME: - return L"URI contains invalid chars."; - case FFMPEG_CORE_ERR_OOM: - return L"Out of memory."; - case FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER: - return L"No audio tracks in file or decoder is not available."; - case FFMPEG_CORE_ERR_UNKNOWN_SAMPLE_FMT: - return L"The format of audio sample is not available."; - case FFMPEG_CORE_ERR_SDL: - return L"An error occured in SDL."; - case FFMPEG_CORE_ERR_FAILED_CREATE_THREAD: - return L"Failed to create new thread."; - case FFMPEG_CORE_ERR_FAILED_CREATE_MUTEX: - return L"Failed to creare new mutex."; - case FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED: - return L"Failed to wait mutex."; - case FFMPEG_CORE_ERR_NO_AUDIO: - return L"No audio tracks in file."; - case FFMPEG_CORE_ERR_FAILED_SET_VOLUME: - return L"Failed to set volume."; - case FFMPEG_CORE_ERR_FAILED_SET_SPEED: - return L"Failed to set speed."; - case FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN: - return L"FFT data's length is too big."; - case FFMPEG_CORE_ERR_FAILED_OPEN_FILE: - return L"Failed to open file."; - case FFMPEG_CORE_ERR_FAILED_READ_FILE: - return L"Failed to read file."; - case FFMPEG_CORE_ERR_INVALID_CDA_FILE: - return L"Invalid CDA file."; - case FFMPEG_CORE_ERR_NO_LIBCDIO: - return L"libcdio not found."; - case FFMEPG_CORE_ERR_FAILED_PARSE_URL: - return L"Failed to parse url."; - case FFMPEG_CORE_ERR_FAILED_SET_EQUALIZER_CHANNEL: - return L"Failed to set equalizer."; - default: - return L"Unknown error."; - } -} - -wchar_t* ffmpeg_core_get_err_msg(int err) { - if (err < 0) { - char msg[AV_ERROR_MAX_STRING_SIZE]; - std::wstring wmsg; - av_make_error_string(msg, AV_ERROR_MAX_STRING_SIZE, err); - if (wchar_util::str_to_wstr(wmsg, msg, CP_UTF8)) { - wchar_t* tmp = nullptr; - if (cpp2c::string2char(wmsg, tmp)) { - return tmp; - } - } - } else if (err == FFMPEG_CORE_ERR_SDL) { - char msg[128]; - std::wstring wmsg; - SDL_GetErrorMsg(msg, 128); - if (wchar_util::str_to_wstr(wmsg, msg, CP_UTF8)) { - wchar_t* tmp = nullptr; - if (cpp2c::string2char(wmsg, tmp)) { - return tmp; - } - } - } else { - std::wstring wmsg = ffmpeg_core_get_err_msg2(err); - wchar_t* tmp = nullptr; - if (cpp2c::string2char(wmsg, tmp)) { - return tmp; - } - } - return nullptr; -} - -int ffmpeg_core_settings_set_max_retry_count(FfmpegCoreSettings* s, int max_retry_count) { - if (!s) return 0; - if (max_retry_count >= -1) { - s->max_retry_count = max_retry_count; - return 1; - } - return 0; -} - -int ffmpeg_core_settings_set_url_retry_interval(FfmpegCoreSettings* s, int url_retry_interval) { - if (!s) return 0; - if (url_retry_interval >= 1 && url_retry_interval <= 120) { - s->url_retry_interval = url_retry_interval; - return 1; - } - return 0; -} - -int ffmpeg_core_settings_set_equalizer_channel(FfmpegCoreSettings* s, int channel, int gain) { - if (channel < 0 || channel > 999999 || gain < -900 || gain > 900) return 0; - return set_equalizer_channel(&s->equalizer_channels, channel, gain) ? 0 : 1; -} - -int ffmpeg_core_set_equalizer_channel(MusicHandle* handle, int channel, int gain) { - if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; - int r = ffmpeg_core_settings_set_equalizer_channel(handle->s, channel, gain); - if (!r) return FFMPEG_CORE_ERR_FAILED_SET_EQUALIZER_CHANNEL; - return send_reinit_filters(handle); -} - -DeviceNameList* ffmpeg_core_get_audio_devices() { - DEVICE_NAME_LIST list = nullptr; - int r = SDL_InitSubSystem(SDL_INIT_AUDIO); - if (r) return nullptr; - int count = SDL_GetNumAudioDevices(0); - if (count <= 0) { - goto end; - } - for (int i = 0; i < count; i++) { - const char* n = SDL_GetAudioDeviceName(i, 0); - char* p = nullptr; - if (!n) { - linked_list_clear(list, tfree); - goto end; - } - if (cstr_util_copy_str(&p, n)) { - linked_list_clear(list, tfree); - goto end; - } - if (!linked_list_append(list, &p)) { - free(p); - linked_list_clear(list, tfree); - goto end; - } - } -end: - SDL_QuitSubSystem(SDL_INIT_AUDIO); - return (DeviceNameList*)list; -} diff --git a/ffmpeg_core/src/core.h b/ffmpeg_core/src/core.h deleted file mode 100644 index 4cddac0f1..000000000 --- a/ffmpeg_core/src/core.h +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef _MUSICPLAYER2_CORE_H -#define _MUSICPLAYER2_CORE_H -#if __cplusplus -#include -extern "C" { -#endif -#include "../ffmpeg_core.h" -#include "libavformat/avformat.h" -#include "libavcodec/avcodec.h" -#include "libavdevice/avdevice.h" -#include "libavutil/avutil.h" -#include "libavutil/audio_fifo.h" -#include "libavutil/opt.h" -#include "libavutil/rational.h" -#include "libavfilter/avfilter.h" -#include "libavfilter/buffersink.h" -#include "libavfilter/buffersrc.h" -#include "libswresample/swresample.h" -#include "SDL2/SDL.h" -#include -#include "c_linked_list.h" -#include "urlparse.h" - -#ifndef __cplusplus -#ifndef min -#define min(x,y) (((x) < (y)) ? (x) : (y)) -#endif -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif -#endif - -#define FFT_SAMPLE 1024 - -typedef struct CDAData { -/// version of the CD format. In May 2006, always equal to 1. -uint16_t cd_format_version; -/// number of the range. The first track has the number 1. -uint16_t no; -/// range offset, in number of frames. -uint32_t range_offset; -/// duration of the track, total number of frames -uint32_t duration; -} CDAData; -typedef struct EqualizerChannel { -/// The filter’s central frequency in Hz. -int channel; -/// The required gain or attenuation in dB. -int gain; -} EqualizerChannel; -typedef struct EqualizerChannels { -EqualizerChannel d; -struct EqualizerChannels* prev; -struct EqualizerChannels* next; -} EqualizerChannels; -typedef struct MusicHandle { -/// Demux 用 -AVFormatContext* fmt; -/// 要解码的流 -AVStream* is; -/// 解码器类型 -const AVCodec* codec; -/// 解码器 -AVCodecContext* decoder; -/// 用于转换音频格式 -struct SwrContext* swrac; -/// 指定的SDL输出格式 -SDL_AudioSpec sdl_spec; -/// 事件处理线程 -HANDLE thread; -/// 事件处理线程线程ID -DWORD thread_id; -/// 维护filters处理后缓冲区线程 -HANDLE filter_thread; -/// 维护filters处理后缓冲区线程线程ID -DWORD filter_thread_id; -/// 音频缓冲区 -AVAudioFifo* buffer; -/// 经过filters处理后的缓冲区 -AVAudioFifo* filters_buffer; -/// 输出格式 -enum AVSampleFormat target_format; -/// 每样本的字节数 -int target_format_pbytes; -/// SDL音频设备ID -SDL_AudioDeviceID device_id; -/// 错误信息(ffmpeg错误或Core错误 -int err; -/// Mutex对象,作为线程锁(用于保护缓冲区和时间) -HANDLE mutex; -/// 用来确保filter graph对象可用 -HANDLE mutex2; -/// 缓冲区开始时间 -int64_t pts; -/// 缓冲区结束时间 -int64_t end_pts; -/// 第一个sample的pts -int64_t first_pts; -int64_t seek_pos; -/// 要播放的部分的开始时间(相对于first_pts的偏移量) -int64_t part_start_pts; -/// 要播放的部分的结束时间(相对于first_pts的偏移量) -int64_t part_end_pts; -/// 设置 -FfmpegCoreSettings* s; -/// 用于设置filter -AVFilterGraph* graph; -/// filter 输入口 -AVFilterContext* filter_inp; -/// filter 输出口 -AVFilterContext* filter_out; -/// filter 链 -c_linked_list* filters; -/// CDA 文件信息 -CDAData* cda; -/// 输出时的声道布局 -uint64_t output_channel_layout; -/// 播放地址 -char* url; -/// 解析后的播放地址 -UrlParseResult* parsed_url; -/// 当前重新打开次数 -int retry_count; -/// 最近一个包的时间 -int64_t last_pkt_pts; -/// 当去filters链从buffer读入的数据量(仅复杂的filters链) -int filters_buffer_offset; -/// SDL是否被初始化 -unsigned char sdl_initialized : 1; -/// 让事件处理线程退出标志位 -unsigned char stoping : 1; -/// 是否已读到文件尾部/读取位置达到要播放的部分的结束时间 -unsigned char is_eof : 1; -/// 是否有错误 -unsigned char have_err : 1; -/// 是否需要Seek -unsigned char is_seek : 1; -/// 是否需要设置新的缓冲区时间 -unsigned char set_new_pts : 1; -unsigned char is_playing : 1; -/// 设置是内部分配 -unsigned char settings_is_alloc : 1; -/// 需要设置新的filters链 -unsigned char need_reinit_filters : 1; -/// 是否正在播放CDA文件 -unsigned char is_cda : 1; -/// 是否仅播放一部分内容 -unsigned char only_part : 1; -/// 是否为本地文件 -unsigned char is_file : 1; -/// 是否正在重新打开文件 -unsigned char is_reopen : 1; -/// 是否是简单的filters链 -unsigned char is_easy_filters : 1; -/// 刚初始化完复杂的filters,等待filters填充数据 -unsigned char is_wait_filters : 1; -} MusicHandle; -typedef struct MusicInfoHandle { -AVFormatContext* fmt; -AVStream* is; -CDAData* cda; -} MusicInfoHandle; -typedef struct FfmpegCoreSettings { -/// 音量 -int volume; -/// 速度 -float speed; -/// 缓存长度(单位s) -int cache_length; -/// 最大重试次数 -int max_retry_count; -/// 非本地文件重试间隔时间(单位s) -int url_retry_interval; -/// 均衡器 -EqualizerChannels* equalizer_channels; -} FfmpegCoreSettings; -#if __cplusplus -} -std::wstring get_metadata_str(AVDictionary* dict, const char* key, int flags); -#endif -#endif diff --git a/ffmpeg_core/src/decode.c b/ffmpeg_core/src/decode.c deleted file mode 100644 index 340d15cc8..000000000 --- a/ffmpeg_core/src/decode.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "decode.h" - -#include "libavutil/timestamp.h" - -int open_decoder(MusicHandle* handle) { - if (!handle || !handle->fmt || !handle->is) return FFMPEG_CORE_ERR_NULLPTR; - handle->codec = avcodec_find_decoder(handle->is->codecpar->codec_id); - if (!handle->codec) return FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER; - handle->decoder = avcodec_alloc_context3(handle->codec); - if (!handle->decoder) return FFMPEG_CORE_ERR_OOM; - int re = 0; - // 从输入流复制参数 - if ((re = avcodec_parameters_to_context(handle->decoder, handle->is->codecpar)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to copy parameters from input stream: %s (%i)\n", av_err2str(re), re); - return re; - } - if (handle->decoder->channel_layout == 0) { - // 如果未设置,设置为默认值 - handle->decoder->channel_layout = av_get_default_channel_layout(handle->decoder->channels); - } - // 打开解码器 - if ((re = avcodec_open2(handle->decoder, handle->codec, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to open decoder \"%s\": %s (%i)\n", handle->codec->name, av_err2str(re), re); - return re; - } - return FFMPEG_CORE_ERR_OK; -} - -int reopen_decoder(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - if (handle->decoder) { - avcodec_free_context(&handle->decoder); - } - handle->decoder = avcodec_alloc_context3(handle->codec); - if (!handle->decoder) return FFMPEG_CORE_ERR_OOM; - int re = 0; - // 从输入流复制参数 - if ((re = avcodec_parameters_to_context(handle->decoder, handle->is->codecpar)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to copy parameters from input stream: %s (%i)\n", av_err2str(re), re); - return re; - } - if (handle->decoder->channel_layout == 0) { - // 如果未设置,设置为默认值 - handle->decoder->channel_layout = av_get_default_channel_layout(handle->decoder->channels); - } - // 打开解码器 - if ((re = avcodec_open2(handle->decoder, handle->codec, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to open decoder \"%s\": %s (%i)\n", handle->codec->name, av_err2str(re), re); - return re; - } - return FFMPEG_CORE_ERR_OK; -} - -int decode_audio_internal(MusicHandle* handle, char* writed, AVFrame* frame) { - if (!handle || !writed || !frame) return FFMPEG_CORE_ERR_NULLPTR; - int re = 0; - re = avcodec_receive_frame(handle->decoder, frame); - if (re >= 0) { - if (handle->first_pts == INT64_MIN) { - handle->first_pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - av_log(NULL, AV_LOG_VERBOSE, "first_pts: %s\n", av_ts2timestr(handle->first_pts, &AV_TIME_BASE_Q)); - if (handle->only_part) { - // 定位到开始位置 - if (handle->part_start_pts > 0) { - handle->is_seek = 1; - handle->seek_pos = handle->part_start_pts; - } - } - } - if (handle->set_new_pts && frame->pts != AV_NOPTS_VALUE) { - av_log(NULL, AV_LOG_VERBOSE, "pts: %s\n", av_ts2timestr(frame->pts, &handle->is->time_base)); - handle->pts = av_rescale_q_rnd(frame->pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX) - handle->first_pts; - handle->end_pts = handle->pts; - handle->set_new_pts = 0; - } else if (handle->set_new_pts) { - av_log(NULL, AV_LOG_VERBOSE, "skip NOPTS frame.\n"); - // 跳过NOPTS的frame - goto end; - } - // 整段数据在结束位置之后,跳过 - if (handle->only_part && (frame->pts - handle->first_pts) >= handle->part_end_pts) { - handle->is_eof = 1; - goto end; - } - re = convert_samples_and_add_to_fifo(handle, frame, writed); - goto end; - } else if (re == AVERROR(EAGAIN)) { - // 数据不够,继续读取 - re = FFMPEG_CORE_ERR_OK; - goto end; - } else if (re == AVERROR_EOF) { - handle->is_eof = 1; - re = FFMPEG_CORE_ERR_OK; - goto end; - } -end: - return re; -} - -int decode_audio(MusicHandle* handle, char* writed) { - if (!handle || !writed) return FFMPEG_CORE_ERR_NULLPTR; - AVPacket pkt; - AVFrame* frame = av_frame_alloc(); - *writed = 0; - if (!frame) { - return FFMPEG_CORE_ERR_OOM; - } - int re = FFMPEG_CORE_ERR_OK; - while (1) { - if ((re = decode_audio_internal(handle, writed, frame))) { - goto end; - } - if (*writed || handle->is_eof) { - goto end; - } - if ((re = av_read_frame(handle->fmt, &pkt)) < 0) { - if (re == AVERROR_EOF) { - handle->is_eof = 1; - re = FFMPEG_CORE_ERR_OK; - goto end; - } - goto end; - } - if (pkt.stream_index != handle->is->index) { - // 其他流,跳过并释放引用 - av_packet_unref(&pkt); - continue; - } - handle->last_pkt_pts = av_rescale_q_rnd(pkt.pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - if ((re = avcodec_send_packet(handle->decoder, &pkt)) < 0) { - if (re == AVERROR(EAGAIN)) { - re = 0; - av_packet_unref(&pkt); - continue; - } - av_packet_unref(&pkt); - goto end; - } - av_packet_unref(&pkt); - if ((re = decode_audio_internal(handle, writed, frame))) { - goto end; - } - if (*writed || handle->is_eof) break; - } -end: - if (frame) av_frame_free(&frame); - return re; -} - -int convert_samples_and_add_to_fifo(MusicHandle* handle, AVFrame* frame, char* writed) { - if (!handle || !frame || !writed) return FFMPEG_CORE_ERR_NULLPTR; - uint8_t** converted_input_samples = NULL; - int re = FFMPEG_CORE_ERR_OK; - AVRational base = { 1, handle->decoder->sample_rate }, target = { 1, handle->sdl_spec.freq }; - int samples = frame->nb_samples; - if (handle->only_part) { - int tmp = av_rescale_q_rnd(handle->part_end_pts - handle->end_pts + handle->first_pts, AV_TIME_BASE_Q, base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - if (samples >= tmp) { - samples = tmp; - handle->is_eof = 1; - } - } - /// 输出的样本数 - int64_t frames = av_rescale_q_rnd(samples, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); - /// 实际输出样本数 - int converted_samples = 0; - DWORD res = 0; - if (!(converted_input_samples = malloc(sizeof(void*) * handle->sdl_spec.channels))) { - re = FFMPEG_CORE_ERR_OOM; - goto end; - } - memset(converted_input_samples, 0, sizeof(void*) * handle->sdl_spec.channels); - if ((re = av_samples_alloc(converted_input_samples, NULL, handle->sdl_spec.channels, frames, handle->target_format, 0)) < 0) { - re = FFMPEG_CORE_ERR_OOM; - goto end; - } - re = 0; - if ((converted_samples = swr_convert(handle->swrac, converted_input_samples, frames, (const uint8_t**)frame->extended_data, samples)) < 0) { - re = converted_samples; - goto end; - } - res = WaitForSingleObject(handle->mutex, INFINITE); - if (res != WAIT_OBJECT_0) { - re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - goto end; - } - if ((converted_samples = av_audio_fifo_write(handle->buffer, (void**)converted_input_samples, converted_samples)) < 0) { - ReleaseMutex(handle->mutex); - re = converted_samples; - goto end; - } - handle->end_pts += av_rescale_q_rnd(converted_samples, target, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - *writed = 1; - ReleaseMutex(handle->mutex); -end: - if (converted_input_samples) { - av_freep(&converted_input_samples[0]); - free(converted_input_samples); - } - return re; -} diff --git a/ffmpeg_core/src/decode.h b/ffmpeg_core/src/decode.h deleted file mode 100644 index 9ea9df360..000000000 --- a/ffmpeg_core/src/decode.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _MUSICPLAYER2_DECODE_H -#define _MUSICPLAYER2_DECODE_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -int open_decoder(MusicHandle* handle); -int reopen_decoder(MusicHandle* handle); -/** - * @brief 解码 - * @param handle Handle - * @param writed 是否成功往缓冲区添加数据 - * @return 非0如果发生错误 -*/ -int decode_audio(MusicHandle* handle, char* writed); -int convert_samples_and_add_to_fifo(MusicHandle* handle, AVFrame* frame, char* writed); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/equalizer.c b/ffmpeg_core/src/equalizer.c deleted file mode 100644 index b8c8315d7..000000000 --- a/ffmpeg_core/src/equalizer.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "equalizer.h" - -int get_equalizer_precision(enum AVSampleFormat f) { - switch (f) { - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_U8P: - case AV_SAMPLE_FMT_S16P: - return 0; - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S32P: - return 1; - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_DBLP: - return 3; - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: - default: - return 2; - } -} - -int create_equalizer_filter(AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int channel, int gain, enum AVSampleFormat f) { - if (!graph || !src || !list) return FFMPEG_CORE_ERR_NULLPTR; - char args[128]; - char name[32]; - const AVFilter* eq = avfilter_get_by_name("equalizer"); - snprintf(args, sizeof(args), "f=%d:g=%d:r=%d", channel, gain, get_equalizer_precision(f)); - snprintf(name, sizeof(name), "equalizer%d", channel); - int re = 0; - AVFilterContext* context = NULL; - if ((re = avfilter_graph_create_filter(&context, eq, name, args, NULL, graph)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to create equalizer filter \"%s\": %s (%i)\n", name, av_err2str(re), re); - return re; - } - if (!c_linked_list_append(list, (void*)context)) { - av_log(NULL, AV_LOG_FATAL, "Failed to append filter \"%s\" to list.\n", context->name); - return FFMPEG_CORE_ERR_OOM; - } - if (c_linked_list_count(*list) > 1) { - AVFilterContext* last = c_linked_list_tail(*list)->prev->d; - if ((re = avfilter_link(last, 0, context, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, context->name, 0, av_err2str(re), re); - return re; - } - } else { - if ((re = avfilter_link(src, 0, context, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", src->name, 0, context->name, 0, av_err2str(re), re); - return re; - } - } - return FFMPEG_CORE_ERR_OK; -} diff --git a/ffmpeg_core/src/equalizer.h b/ffmpeg_core/src/equalizer.h deleted file mode 100644 index e1db8dcf5..000000000 --- a/ffmpeg_core/src/equalizer.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _MUSICPLAYER2_EQUALIZER_H -#define _MUSICPLAYER2_EQUALIZER_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -int get_equalizer_precision(enum AVSampleFormat f); -/** - * @brief 创建 equalizer filter - * @param index Filter 序号 - * @param graph Graph - * @param src Graph - * @param list Filters 列表 - * @param channel 中心频率(hz) - * @param gain - * @return -*/ -int create_equalizer_filter(AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int channel, int gain, enum AVSampleFormat f); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/equalizer_settings.cpp b/ffmpeg_core/src/equalizer_settings.cpp deleted file mode 100644 index 331b5f787..000000000 --- a/ffmpeg_core/src/equalizer_settings.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "equalizer_settings.h" - -#include "dict.h" - -#define INTYPE Dict* -#define OUTTYPE EqualizerChannels* - -void free_equalizer_channels(EqualizerChannels** channels) { - auto c = (INTYPE*)channels; - if (c) { - auto c2 = *c; - dict_free(c2); - *channels = (OUTTYPE)c2; - } -} - -int set_equalizer_channel(EqualizerChannels** channels, int channel, int gain) { - if (!channels) return FFMPEG_CORE_ERR_NULLPTR; - auto c = (INTYPE)(*channels); - if (gain == 0) { - dict_delete(c, channel); - } else { - if (!dict_set(c, channel, gain)) { - *channels = (OUTTYPE)c; - return FFMPEG_CORE_ERR_OOM; - } - } - *channels = (OUTTYPE)c; - return FFMPEG_CORE_ERR_OK; -} - -size_t equalizer_channel_count(EqualizerChannels* channels) { - return dict_count((INTYPE)channels); -} diff --git a/ffmpeg_core/src/equalizer_settings.h b/ffmpeg_core/src/equalizer_settings.h deleted file mode 100644 index e68afc3ba..000000000 --- a/ffmpeg_core/src/equalizer_settings.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _MUSICPLAYER2_EQUALIZER_SETTINGS_H -#define _MUSICPLAYER2_EQUALIZER_SETTINGS_H -#if __cplusplus -#include "core.h" -extern "C" { -#endif -#if !__cplusplus -#include "core.h" -#endif -void free_equalizer_channels(EqualizerChannels** channels); -int set_equalizer_channel(EqualizerChannels** channels, int channel, int gain); -size_t equalizer_channel_count(EqualizerChannels* channels); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/fft_data.c b/ffmpeg_core/src/fft_data.c deleted file mode 100644 index 27bb0802e..000000000 --- a/ffmpeg_core/src/fft_data.c +++ /dev/null @@ -1,116 +0,0 @@ -#include "fft_data.h" - -#include -#include "libavcodec/avfft.h" - -int ffmpeg_core_get_fft_data(MusicHandle* handle, float* fft_data, int len) { - if (!handle || !fft_data) return FFMPEG_CORE_ERR_NULLPTR; - if (len > FFT_SAMPLE / 2) return FFMPEG_CORE_ERR_TOO_BIG_FFT_DATA_LEN; - int cal_samples = FFT_SAMPLE; - AVFrame* f = av_frame_alloc(), * f2 = NULL; - RDFTContext* context = NULL; - SwrContext* swr = NULL; - DWORD re = 0; - int r = FFMPEG_CORE_ERR_OK; - int nbits = log2(cal_samples); - int total_samples = FFT_SAMPLE * 10; - float* datas = NULL; - int inv = cal_samples / len; - if (!f) { - return FFMPEG_CORE_ERR_OOM; - } - f->format = handle->target_format; - f->nb_samples = total_samples; - f->channel_layout = handle->output_channel_layout; - f->channels = handle->sdl_spec.channels; - if ((r = av_frame_get_buffer(f, 0)) < 0) { - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - if ((r = av_frame_make_writable(f)) < 0) { - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - re = WaitForSingleObject(handle->mutex, INFINITE); - if (re != WAIT_OBJECT_0) { - r = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - if (av_audio_fifo_size(handle->buffer) < total_samples) { - ReleaseMutex(handle->mutex); - r = FFMPEG_CORE_ERR_OK; - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - if ((r = av_audio_fifo_peek(handle->buffer, (void**)f->data, total_samples)) < 0) { - ReleaseMutex(handle->mutex); - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - ReleaseMutex(handle->mutex); - r = 0; - if (!(context = av_rdft_init(nbits, DFT_R2C))) { - r = FFMPEG_CORE_ERR_OOM; - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - if (f->format == AV_SAMPLE_FMT_FLT && f->channels == 1) { - for (int j = 0; j < 10; j++) { - av_rdft_calc(context, (FFTSample*)f->data[0] + (size_t)FFT_SAMPLE * j); - } - } else { - swr = swr_alloc_set_opts(NULL, av_get_default_channel_layout(1), AV_SAMPLE_FMT_FLT, handle->sdl_spec.freq, handle->output_channel_layout, handle->target_format, handle->sdl_spec.freq, 0, NULL); - if (!swr) { - r = FFMPEG_CORE_ERR_OOM; - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - if ((r = swr_init(swr)) < 0) { - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - f2 = av_frame_alloc(); - if (!f2) { - r = FFMPEG_CORE_ERR_OOM; - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - f2->format = AV_SAMPLE_FMT_FLT; - f2->channels = 1; - f2->channel_layout = av_get_default_channel_layout(1); - f2->nb_samples = total_samples; - if ((r = av_frame_get_buffer(f2, 0)) < 0) { - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - if ((r = av_frame_make_writable(f2)) < 0) { - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - if ((r = swr_convert(swr, f2->data, f2->nb_samples, (const uint8_t**)f->data, f->nb_samples)) < 0) { - memset(fft_data, 0, sizeof(float) * len); - goto end; - } - r = 0; - for (int j = 0; j < 10; j++) - av_rdft_calc(context, (FFTSample*)f2->data[0] + (size_t)j * FFT_SAMPLE); - } - datas = f2 ? (float*)f2->data[0] : (float*)f->data[0]; - memset(fft_data, 0, sizeof(float) * len); - for (int j = 0; j < 10; j++) { - for (int i = 0; i < len; i++) { - if (i == 0) - fft_data[i] += datas[i + FFT_SAMPLE * j] / FFT_SAMPLE / 10; - else - fft_data[i] += datas[i + FFT_SAMPLE * j] / FFT_SAMPLE / 5; - } - } - r = 0; -end: - if (f) av_frame_free(&f); - if (context) av_rdft_end(context); - if (swr) swr_free(&swr); - if (f2) av_frame_free(&f2); - return r; -} diff --git a/ffmpeg_core/src/fft_data.h b/ffmpeg_core/src/fft_data.h deleted file mode 100644 index 8bf18050e..000000000 --- a/ffmpeg_core/src/fft_data.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _MUSICPLAYER2_FFT_DATA_H -#define _MUSICPLAYER2_FFT_DATA_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/file.cpp b/ffmpeg_core/src/file.cpp deleted file mode 100644 index ea8729d91..000000000 --- a/ffmpeg_core/src/file.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "file.h" - -#include -#include "fileop.h" - -#define chkstr(s) (s ? s : "") - -int is_file(UrlParseResult* url) { - if (!url) return 0; - std::string scheme(chkstr(url->scheme)); - if (scheme.empty() || scheme == "file") return 1; - if (scheme.length() == 1 && isalpha(scheme[0])) return 1; - return 0; -} - -int is_file_exists(MusicHandle* handle) { - if (!handle || !handle->is_file) return 0; - std::string scheme(chkstr(handle->parsed_url->scheme)); - if (scheme != "file") { - return fileop::exists(handle->url); - } else { - std::string fn(chkstr(handle->parsed_url->netloc)); - fn += chkstr(handle->parsed_url->path); - std::string query(chkstr(handle->parsed_url->query)); - if (!query.empty()) { - fn += "?"; - fn += query; - } - std::string fragment(chkstr(handle->parsed_url->fragment)); - if (!fragment.empty()) { - fn += "#"; - fn += fragment; - } - return fileop::exists(fn); - } -} diff --git a/ffmpeg_core/src/file.h b/ffmpeg_core/src/file.h deleted file mode 100644 index bb2d4c305..000000000 --- a/ffmpeg_core/src/file.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _MUSICPLAYER2_FILE_H -#define _MUSICPLAYER2_FILE_H -#include "core.h" -#if __cplusplus -extern "C" { -#endif -int is_file(UrlParseResult* url); -int is_file_exists(MusicHandle* handle); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/filter.c b/ffmpeg_core/src/filter.c deleted file mode 100644 index d9ea721a1..000000000 --- a/ffmpeg_core/src/filter.c +++ /dev/null @@ -1,379 +0,0 @@ -#include "filter.h" - -#include "output.h" -#include "volume.h" -#include "speed.h" -#include "equalizer.h" - -int need_filters(FfmpegCoreSettings* s) { - if (!s) return 0; - if (!avfilter_get_by_name("abuffersink") || !avfilter_get_by_name("abuffer")) { - return 0; - } - if (s->volume != 100 && avfilter_get_by_name("volume")) { - return 1; - } - if (get_speed(s->speed) != 1000 && avfilter_get_by_name("atempo")) { - return 1; - } - if (s->equalizer_channels && avfilter_get_by_name("equalizer")) { - return 1; - } - return 0; -} - -int init_filters(MusicHandle* handle) { - if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; - if (!need_filters(handle->s)) return FFMPEG_CORE_ERR_OK; - int re = FFMPEG_CORE_ERR_OK; - int is_easy_filters = 1; - int speed = get_speed(handle->s->speed); - if ((re = create_src_and_sink(&handle->graph, &handle->filter_inp, &handle->filter_out, handle))) { - return re; - } - if (handle->s->volume != 100 && avfilter_get_by_name("volume")) { - if ((re = create_volume_filter(0, handle->graph, handle->filter_inp, &handle->filters, handle->s->volume, handle->target_format))) { - return re; - } - } - if (speed != 1000 && avfilter_get_by_name("atempo")) { - int index = 0; - while (speed != 1000) { - if ((re = create_speed_filter(index, handle->graph, handle->filter_inp, &handle->filters, &speed))) { - return re; - } - index++; - } - is_easy_filters = 0; - } - if (handle->s->equalizer_channels && avfilter_get_by_name("equalizer")) { - EqualizerChannels* now = handle->s->equalizer_channels; - if ((re = create_equalizer_filter(handle->graph, handle->filter_inp, &handle->filters, now->d.channel, now->d.gain, handle->target_format))) { - return re; - } - while (now->next) { - now = now->next; - if ((re = create_equalizer_filter(handle->graph, handle->filter_inp, &handle->filters, now->d.channel, now->d.gain, handle->target_format))) { - return re; - } - } - is_easy_filters = 0; - } - if (c_linked_list_count(handle->filters) == 0) { - avfilter_graph_free(&handle->graph); - handle->graph = NULL; - handle->filter_inp = NULL; - handle->filter_out = NULL; - return FFMPEG_CORE_ERR_OK; - } - AVFilterContext* last = c_linked_list_tail(handle->filters)->d; - if ((re = avfilter_link(last, 0, handle->filter_out, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, handle->filter_out->name, 0, av_err2str(re), re); - return re; - } - if ((re = avfilter_graph_config(handle->graph, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to check config of filters: %s (%i)\n", av_err2str(re), re); - return re; - } - handle->is_easy_filters = is_easy_filters; - if (!handle->is_easy_filters) { - handle->is_wait_filters = 1; - } - return FFMPEG_CORE_ERR_OK; -} - -int reinit_filters(MusicHandle* handle) { - if (!handle || !handle->s) return FFMPEG_CORE_ERR_NULLPTR; - if (!need_filters(handle->s)) { - if (!handle->graph) return FFMPEG_CORE_ERR_OK; - DWORD re = WaitForSingleObject(handle->mutex2, INFINITE); - if (re != WAIT_OBJECT_0) { - return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - } - re = WaitForSingleObject(handle->mutex, INFINITE); - if (re != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex2); - return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - } - avfilter_graph_free(&handle->graph); - handle->graph = NULL; - handle->filter_inp = NULL; - handle->filter_out = NULL; - c_linked_list_clear(&handle->filters, NULL); - av_audio_fifo_reset(handle->filters_buffer); - handle->filters_buffer_offset = 0; - ReleaseMutex(handle->mutex); - ReleaseMutex(handle->mutex2); - return FFMPEG_CORE_ERR_OK; - } - int re = FFMPEG_CORE_ERR_OK; - AVFilterGraph* graph = NULL; - AVFilterContext* inc = NULL, * outc = NULL; - c_linked_list* list = NULL; - int is_easy_filters = 1; - int speed = get_speed(handle->s->speed); - if ((re = create_src_and_sink(&graph, &inc, &outc, handle)) < 0) { - goto end; - } - if (handle->s->volume != 100 && avfilter_get_by_name("volume")) { - if ((re = create_volume_filter(0, graph, inc, &list, handle->s->volume, handle->target_format))) { - goto end; - } - } - if (speed != 1000 && avfilter_get_by_name("atempo")) { - int index = 0; - while (speed != 1000) { - if ((re = create_speed_filter(index, graph, inc, &list, &speed))) { - goto end; - } - index++; - } - is_easy_filters = 0; - } - if (handle->s->equalizer_channels && avfilter_get_by_name("equalizer")) { - EqualizerChannels* now = handle->s->equalizer_channels; - if ((re = create_equalizer_filter(graph, inc, &list, now->d.channel, now->d.gain, handle->target_format))) { - goto end; - } - while (now->next) { - now = now->next; - if ((re = create_equalizer_filter(graph, inc, &list, now->d.channel, now->d.gain, handle->target_format))) { - goto end; - } - } - is_easy_filters = 0; - } - if (c_linked_list_count(list) == 0) { - if (handle->graph) { - DWORD r = WaitForSingleObject(handle->mutex2, INFINITE); - if (r != WAIT_OBJECT_0) { - re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - goto end; - } - r = WaitForSingleObject(handle->mutex, INFINITE); - if (r != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex2); - re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - goto end; - } - avfilter_graph_free(&handle->graph); - handle->graph = NULL; - handle->filter_inp = NULL; - handle->filter_out = NULL; - c_linked_list_clear(&handle->filters, NULL); - av_audio_fifo_reset(handle->filters_buffer); - handle->filters_buffer_offset = 0; - ReleaseMutex(handle->mutex); - ReleaseMutex(handle->mutex2); - } - re = FFMPEG_CORE_ERR_OK; - goto end; - } - AVFilterContext* last = c_linked_list_tail(list)->d; - if ((re = avfilter_link(last, 0, outc, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, outc->name, 0, av_err2str(re), re); - goto end; - } - if ((re = avfilter_graph_config(graph, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to check config of filters: %s (%i)\n", av_err2str(re), re); - goto end; - } - DWORD r = WaitForSingleObject(handle->mutex2, INFINITE); - if (r != WAIT_OBJECT_0) { - re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - goto end; - } - r = WaitForSingleObject(handle->mutex, INFINITE); - if (r != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex2); - re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - goto end; - } - if (handle->graph) { - avfilter_graph_free(&handle->graph); - handle->graph = NULL; - handle->filter_inp = NULL; - handle->filter_out = NULL; - av_audio_fifo_reset(handle->filters_buffer); - handle->filters_buffer_offset = 0; - c_linked_list_clear(&handle->filters, NULL); - } - handle->graph = graph; - handle->filter_inp = inc; - handle->filter_out = outc; - handle->filters = list; - handle->is_easy_filters = is_easy_filters; - if (!handle->is_easy_filters) { - handle->is_wait_filters = 1; - } - ReleaseMutex(handle->mutex); - ReleaseMutex(handle->mutex2); - return FFMPEG_CORE_ERR_OK; -end: - if (graph) { - avfilter_graph_free(&graph); - c_linked_list_clear(&list, NULL); - } - return re; -} - -int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterContext** sink, MusicHandle* handle) { - if (!graph || !src || !sink) return FFMPEG_CORE_ERR_NULLPTR; - const AVFilter* buffersink = avfilter_get_by_name("abuffersink"), * buffer = avfilter_get_by_name("abuffer"); - if (!(*graph = avfilter_graph_alloc())) { - av_log(NULL, AV_LOG_FATAL, "Failed to allocate filter graph.\n"); - return FFMPEG_CORE_ERR_OOM; - } - int re = 0; - char args[1024]; - char channel_layout[512]; - // 输入的设置:描述见 ffmpeg -h filter=abuffer - uint64_t layout = handle->output_channel_layout; - av_get_channel_layout_string(channel_layout, sizeof(channel_layout), handle->sdl_spec.channels, layout); - snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%d", handle->is->time_base.num, handle->is->time_base.den, handle->sdl_spec.freq, av_get_sample_fmt_name(handle->target_format), channel_layout, handle->sdl_spec.channels); - if ((re = avfilter_graph_create_filter(src, buffer, "in", args, NULL, *graph)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to create input filter: %s (%i)\n", av_err2str(re), re); - return re; - } - if ((re = avfilter_graph_create_filter(sink, buffersink, "out", NULL, NULL, *graph)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to create output filter: %s (%i)\n", av_err2str(re), re); - return re; - } - // 输出设置 - // 描述见 ffmpeg -h filter=abuffersink - // 具体类型参考 ffmpeg 源代码 libavfilter/buffersink.c 里的 abuffersink_options - enum AVSampleFormat sample_fmts[2] = { handle->target_format , AV_SAMPLE_FMT_NONE }; - if ((re = av_opt_set_int_list(*sink, "sample_fmts", sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to set sample_fmts to output filter: %s (%i)\n", av_err2str(re), re); - return re; - } - int sample_rates[2] = { handle->sdl_spec.freq , 0 }; - if ((re = av_opt_set_int_list(*sink, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to set sample_rates to output filter: %s (%i)\n", av_err2str(re), re); - return re; - } - int64_t channel_layouts[2] = { layout , 0 }; - if ((re = av_opt_set_int_list(*sink, "channel_layouts", channel_layouts, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to set channel_layouts to output filter: %s (%i)\n", av_err2str(re), re); - return re; - } - int channel_counts[2] = { handle->sdl_spec.channels, 0 }; - if ((re = av_opt_set_int_list(*sink, "channel_counts", channel_counts, 0, AV_OPT_SEARCH_CHILDREN)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to set channel_counts to output filter: %s (%i)\n", av_err2str(re), re); - return re; - } - return FFMPEG_CORE_ERR_OK; -} - -int add_data_to_filters_buffer(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - DWORD re = WaitForSingleObject(handle->mutex2, INFINITE); - int r = FFMPEG_CORE_ERR_OK; - AVFrame* in = NULL, * out = NULL; - int samples_need = 1000; - int samples_need_in = 0; - /// 音频缓冲区buffer要peek的起始位置 - int input_samples_offset = 0; - int buffer_size = 0; - int writed = 0; - unsigned char have_mutex = 0; - AVRational base = { 1000, 1000 }, target = { 1, 1 }; - if (re == WAIT_TIMEOUT) return FFMPEG_CORE_ERR_OK; - else if (re != WAIT_OBJECT_0) return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - re = WaitForSingleObject(handle->mutex, 10); - if (re == WAIT_TIMEOUT) { - ReleaseMutex(handle->mutex2); - return FFMPEG_CORE_ERR_OK; - } else if (re != WAIT_OBJECT_0) { - ReleaseMutex(handle->mutex2); - return FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - } - have_mutex = 1; - if (!handle->graph || handle->is_easy_filters) { - ReleaseMutex(handle->mutex); - ReleaseMutex(handle->mutex2); - return FFMPEG_CORE_ERR_OK; - } - buffer_size = av_audio_fifo_size(handle->filters_buffer); - if (buffer_size > handle->sdl_spec.freq) { - r = FFMPEG_CORE_ERR_OK; - goto end; - } - base.num = get_speed(handle->s->speed); - input_samples_offset = handle->filters_buffer_offset; - samples_need_in = av_rescale_q_rnd(samples_need, base, target, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); - if (av_audio_fifo_size(handle->buffer) <= input_samples_offset) { - ReleaseMutex(handle->mutex); - have_mutex = 0; - if (handle->is_eof) { - if ((r = av_buffersrc_add_frame(handle->filter_inp, NULL)) < 0) { - goto end; - } - out = av_frame_alloc(); - if (!out) { - r = FFMPEG_CORE_ERR_OOM; - goto end; - } - goto outp; - } - r = FFMPEG_CORE_ERR_OK; - goto end; - } - in = av_frame_alloc(); - out = av_frame_alloc(); - if (!in || !out) { - r = FFMPEG_CORE_ERR_OOM; - goto end; - } - in->channels = handle->sdl_spec.channels; - in->channel_layout = handle->output_channel_layout; - in->format = handle->target_format; - in->sample_rate = handle->sdl_spec.freq; - samples_need_in = min(samples_need_in, av_audio_fifo_size(handle->buffer) - input_samples_offset); - in->nb_samples = samples_need_in; - if ((r = av_frame_get_buffer(in, 0)) < 0) { - goto end; - } - writed = av_audio_fifo_peek_at(handle->buffer, (void**)in->data, samples_need_in, input_samples_offset); - if (writed < 0) { - r = writed; - goto end; - } - handle->filters_buffer_offset += writed; - in->nb_samples = writed; - ReleaseMutex(handle->mutex); - have_mutex = 0; - if ((r = av_buffersrc_add_frame(handle->filter_inp, in)) < 0) { - goto end; - } -outp: - if ((r = av_buffersink_get_frame(handle->filter_out, out)) < 0) { - if (r == AVERROR(EAGAIN)) r = FFMPEG_CORE_ERR_OK; - goto end; - } - re = WaitForSingleObject(handle->mutex, INFINITE); - if (re != WAIT_OBJECT_0) { - r = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - goto end; - } - have_mutex = 1; - if ((r = av_audio_fifo_write(handle->filters_buffer, (void*)out->data, out->nb_samples)) < 0) { - goto end; - } - r = FFMPEG_CORE_ERR_OK; -end: - if (in) av_frame_free(&in); - if (out) av_frame_free(&out); - if (have_mutex) ReleaseMutex(handle->mutex); - ReleaseMutex(handle->mutex2); - return r; -} - -void reset_filters_buffer(MusicHandle* handle) { - if (!handle) return; - av_audio_fifo_reset(handle->filters_buffer); - handle->filters_buffer_offset = 0; - if (!handle->is_easy_filters) { - handle->is_wait_filters = 1; - } -} diff --git a/ffmpeg_core/src/filter.h b/ffmpeg_core/src/filter.h deleted file mode 100644 index 99fb6f1e4..000000000 --- a/ffmpeg_core/src/filter.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _MUSICPLAYER2_FILTER_H -#define _MUSICPLAYER2_FILTER_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -int need_filters(FfmpegCoreSettings* s); -int init_filters(MusicHandle* handle); -int reinit_filters(MusicHandle* handle); -/** - * @brief 新建一个新的FilterGraph,并且分配好输出和输入 - * @param graph FilterGraph - * @param src 输入节点 - * @param sink 输出节点 - * @param handle 读取必要的数据用 - * @return -*/ -int create_src_and_sink(AVFilterGraph** graph, AVFilterContext** src, AVFilterContext** sink, MusicHandle* handle); -/// 往filters_buffer塞数据 -int add_data_to_filters_buffer(MusicHandle* handle); -void reset_filters_buffer(MusicHandle* handle); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/loop.c b/ffmpeg_core/src/loop.c deleted file mode 100644 index 36c27b181..000000000 --- a/ffmpeg_core/src/loop.c +++ /dev/null @@ -1,255 +0,0 @@ -#include "loop.h" - -#include -#include "libavutil/timestamp.h" -#include "decode.h" -#include "filter.h" -#include "file.h" -#include "cda.h" -#include "open.h" - -#define ft2ts(t) (((size_t)t.dwHighDateTime << 32) | (size_t)t.dwLowDateTime) - -int seek_to_pos(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - int re = FFMPEG_CORE_ERR_OK; - DWORD r = WaitForSingleObject(handle->mutex, INFINITE); - AVRational base = { 1, handle->sdl_spec.freq }; - if (r != WAIT_OBJECT_0) { - re = FFMPEG_CORE_ERR_WAIT_MUTEX_FAILED; - goto end; - } - if (handle->seek_pos >= handle->pts && handle->seek_pos <= handle->end_pts) { - // 已经在缓冲区,直接从缓冲区移除不需要的数据 - int64_t samples = min(av_rescale_q_rnd(handle->seek_pos - handle->pts, AV_TIME_BASE_Q, base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX), av_audio_fifo_size(handle->buffer)); - if ((re = av_audio_fifo_drain(handle->buffer, samples)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to drain %" PRIi64 " samples in buffer: %s (%i)\n", samples, av_err2str(re), re); - ReleaseMutex(handle->mutex); - goto end; - } - // 增大当前时间 - handle->pts += av_rescale_q_rnd(samples, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - } else { - // 不在缓冲区,调用av_seek_frame并清空缓冲区 - int flags = 0; - // 修复flac文件解码完之后,继续调用解码器会导致报错的BUG - // 同时解决decoder内多余buffer的问题 - av_log(NULL, AV_LOG_VERBOSE, "Try to reopen decoder \"%s\".\n", handle->codec->name ? handle->codec->name : "(null)"); - if ((re = reopen_decoder(handle))) { - ReleaseMutex(handle->mutex); - goto end; - } - // 指的是定位到指定位置之前的关键帧,而不是从后往前定位 - flags |= AVSEEK_FLAG_BACKWARD; - if ((re = av_seek_frame(handle->fmt, -1, handle->seek_pos + handle->first_pts, flags)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to seek frame %" PRIi64 ": %s (%i)\n", handle->seek_pos + handle->first_pts, av_err2str(re), re); - ReleaseMutex(handle->mutex); - goto end; - } - re = 0; - av_audio_fifo_reset(handle->buffer); - handle->set_new_pts = 1; - handle->is_eof = 0; - } - reset_filters_buffer(handle); - ReleaseMutex(handle->mutex); -end: - handle->is_seek = 0; - return re; -} - -int reopen_file(MusicHandle* handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - int re = FFMPEG_CORE_ERR_OK; - if (handle->is_file) { - int doing = 0; - while (1) { - doing = 0; - if (handle->stoping) return FFMPEG_CORE_ERR_OK; - if (basic_event_handle(handle)) { - doing = 1; - } - if (is_file_exists(handle)) break; - if (!doing) Sleep(10); - } - } else { - int doing = 0; - FILETIME st; - GetSystemTimePreciseAsFileTime(&st); - FILETIME now; - memcpy(&now, &st, sizeof(FILETIME)); - while ((ft2ts(now) - ft2ts(st)) < ((size_t)10000000 * handle->s->url_retry_interval)) { - doing = 0; - if (handle->stoping) return FFMPEG_CORE_ERR_OK; - if (basic_event_handle(handle)) { - doing = 1; - } - if (!doing) Sleep(10); - GetSystemTimePreciseAsFileTime(&now); - } - } - if (handle->fmt) avformat_close_input(&handle->fmt); - if (handle->decoder) avcodec_free_context(&handle->decoder); - if (handle->is_cda) { - if ((re = open_cd_device(handle, handle->url))) { - return re; - } - } else { - if ((re = open_input(handle, handle->url))) { - return re; - } - } - if ((re = find_audio_stream(handle))) { - return re; - } - if ((re = open_decoder(handle))) { - return re; - } - av_log(NULL, AV_LOG_VERBOSE, "The target pts: %s\n", av_ts2timestr(handle->last_pkt_pts, &AV_TIME_BASE_Q)); - if ((re = av_seek_frame(handle->fmt, -1, handle->last_pkt_pts, AVSEEK_FLAG_ANY)) < 0) { - return re; - } - AVPacket pkt; - while (1) { - int64_t tmppts = 0; - if ((re = av_read_frame(handle->fmt, &pkt)) < 0) { - return re; - } - if (pkt.stream_index != handle->is->index) { - av_packet_unref(&pkt); - continue; - } - tmppts = av_rescale_q_rnd(pkt.pts, handle->is->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - if (tmppts < handle->last_pkt_pts) { - continue; - } - break; - } - av_log(NULL, AV_LOG_VERBOSE, "The packet pts after seek: %s\n", av_ts2timestr(pkt.pts, &handle->is->time_base)); - av_packet_unref(&pkt); - handle->is_reopen = 0; - return FFMPEG_CORE_ERR_OK; -} - -int basic_event_handle(MusicHandle* h) { - if (!h) return 0; - if (h->need_reinit_filters) { - int re = reinit_filters(h); - if (re) { - h->have_err = 1; - h->err = re; - av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling reinit_filters: %s (%i).\n", __FILE__, __LINE__, av_err2str(re), re); - } - h->need_reinit_filters = 0; - return 1; - } - return 0; -} - -DWORD WINAPI event_loop(LPVOID handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - MusicHandle* h = (MusicHandle*)handle; - int samples = h->decoder->sample_rate * 15; - /// 本次循环是否有做事 - char doing = 0; - /// 是否往缓冲区加了数据 - char writed = 0; - int buffered_size = h->sdl_spec.freq * h->s->cache_length; - while (1) { - doing = 0; - if (h->stoping) break; - if (basic_event_handle(h)) { - doing = 1; - goto end; - } - if (h->is_reopen) { - /// 禁用了重试或重试次数达上限 - if (!h->s->max_retry_count || (h->s->max_retry_count > 0 && h->retry_count < h->s->max_retry_count)) { - goto end; - } - h->retry_count += 1; - av_log(NULL, AV_LOG_VERBOSE, "Try to reopen file \"%s\" %i times.\n", h->url, h->retry_count); - int re = reopen_file(h); - if (re) { - av_log(NULL, AV_LOG_VERBOSE, "Reopen failed: %i.\n", re); - h->err = re; - } else { - h->have_err = 0; - h->err = 0; - h->retry_count = 0; - } - doing = 1; - goto end; - } - if (h->is_seek && h->first_pts != INT64_MIN) { - int re = seek_to_pos(h); - if (re) { - h->have_err = 1; - h->err = re; - av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling seek_to_pos: %i.\n", __FILE__, __LINE__, re); - } - doing = 1; - goto end; - } - if (!h->is_eof) { - buffered_size = h->sdl_spec.freq * h->s->cache_length; - if (av_audio_fifo_size(h->buffer) < buffered_size) { - int re = decode_audio(handle, &writed); - if (re) { - av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling decode_audio: %s (%i).\n", __FILE__, __LINE__, av_err2str(re), re); - h->have_err = 1; - h->err = re; - av_log(NULL, AV_LOG_VERBOSE, "Try to reopen file \"%s\".\n", h->url); - h->is_reopen = 1; - if (h->s->max_retry_count) { - h->retry_count += 1; - re = reopen_file(h); - if (re) { - av_log(NULL, AV_LOG_VERBOSE, "Reopen failed: %i.\n", re); - h->err = re; - } else { - h->have_err = 0; - h->err = 0; - h->retry_count = 0; - } - } - } - doing = 1; - } - } else { - // 播放完毕,自动停止播放 - if (av_audio_fifo_size(h->buffer) == 0) { - SDL_PauseAudioDevice(h->device_id, 1); - h->is_playing = 0; - } - } -end: - if (!doing) { - Sleep(10); - } - } - return FFMPEG_CORE_ERR_OK; -} - -DWORD WINAPI filter_loop(LPVOID handle) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - MusicHandle* h = (MusicHandle*)handle; - char doing = 0; - while (1) { - doing = 0; - if (h->stoping) break; - if (h->graph && !h->is_easy_filters) { - int re = add_data_to_filters_buffer(h); - if (re) { - h->have_err = 1; - h->err = re; - av_log(NULL, AV_LOG_WARNING, "%s %i: Error when calling add_data_to_filters_buffer: %s (%i).\n", __FILE__, __LINE__, av_err2str(re), re); - } - doing = 1; - } - if (!doing) { - Sleep(10); - } - } - return FFMPEG_CORE_ERR_OK; -} diff --git a/ffmpeg_core/src/loop.h b/ffmpeg_core/src/loop.h deleted file mode 100644 index dbff38440..000000000 --- a/ffmpeg_core/src/loop.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _MUSICPLAYER2_LOOP_H -#define _MUSICPLAYER2_LOOP_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -int seek_to_pos(MusicHandle* handle); -int reopen_file(MusicHandle* handle); -/// 基础事件处理,如果处理过返回1反之0 -int basic_event_handle(MusicHandle* handle); -DWORD WINAPI event_loop(LPVOID handle); -DWORD WINAPI filter_loop(LPVOID handle); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/open.c b/ffmpeg_core/src/open.c deleted file mode 100644 index 28f6aafac..000000000 --- a/ffmpeg_core/src/open.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "open.h" - -int open_input(MusicHandle* handle, const char* url) { - if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; - int re = 0; - if ((re = avformat_open_input(&handle->fmt, url, NULL, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", url, av_err2str(re), re); - return re; - } - if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", url, av_err2str(re), re); - return re; - } - // handle->fmt->flags |= AVFMT_FLAG_FAST_SEEK; // 允许快速定位 - return FFMPEG_CORE_ERR_OK; -} - -int open_input2(MusicInfoHandle* handle, const char* url) { - if (!handle || !url) return FFMPEG_CORE_ERR_NULLPTR; - int re = 0; - if ((re = avformat_open_input(&handle->fmt, url, NULL, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to open \"%s\": %s (%i)\n", url, av_err2str(re), re); - return re; - } - if ((re = avformat_find_stream_info(handle->fmt, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to find streams in \"%s\": %s (%i)\n", url, av_err2str(re), re); - return re; - } - return FFMPEG_CORE_ERR_OK; -} - -int find_audio_stream(MusicHandle* handle) { - if (!handle || !handle->fmt) return FFMPEG_CORE_ERR_NULLPTR; - for (unsigned int i = 0; i < handle->fmt->nb_streams; i++) { - AVStream* is = handle->fmt->streams[i]; - if (is->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - // 确保有对应的解码器 - if (!avcodec_find_decoder(is->codecpar->codec_id)) { - continue; - } - handle->is = is; - return FFMPEG_CORE_ERR_OK; - } - } - return FFMPEG_CORE_ERR_NO_AUDIO_OR_DECODER; -} - -int find_audio_stream2(MusicInfoHandle* handle) { - if (!handle || !handle->fmt) return FFMPEG_CORE_ERR_NULLPTR; - for (unsigned int i = 0; i < handle->fmt->nb_streams; i++) { - AVStream* is = handle->fmt->streams[i]; - if (is->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - handle->is = is; - return FFMPEG_CORE_ERR_OK; - } - } - return FFMPEG_CORE_ERR_NO_AUDIO; -} diff --git a/ffmpeg_core/src/open.h b/ffmpeg_core/src/open.h deleted file mode 100644 index 95e33868c..000000000 --- a/ffmpeg_core/src/open.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _MUSICPLAYER2_OPEN_H -#define _MUSICPLAYER2_OPEN_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -/// 打开文件 -int open_input(MusicHandle* handle, const char* url); -int find_audio_stream(MusicHandle* handle); -int open_input2(MusicInfoHandle* handle, const char* url); -int find_audio_stream2(MusicInfoHandle* handle); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/output.c b/ffmpeg_core/src/output.c deleted file mode 100644 index b8dca1df3..000000000 --- a/ffmpeg_core/src/output.c +++ /dev/null @@ -1,206 +0,0 @@ -#include "output.h" - -#include "speed.h" - -int init_output(MusicHandle* handle, const char* device) { - if (!handle) return FFMPEG_CORE_ERR_NULLPTR; - if (!handle->sdl_initialized) { - if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { - return FFMPEG_CORE_ERR_SDL; - } - handle->sdl_initialized = 1; - } - SDL_AudioSpec sdl_spec; - sdl_spec.freq = handle->decoder->sample_rate; - sdl_spec.format = convert_to_sdl_format(handle->decoder->sample_fmt); - if (!sdl_spec.format) { - const char* tmp = av_get_sample_fmt_name(handle->decoder->sample_fmt); - av_log(NULL, AV_LOG_FATAL, "Unknown sample format: %s (%i)\n", tmp ? tmp : "", handle->decoder->sample_fmt); - return FFMPEG_CORE_ERR_UNKNOWN_SAMPLE_FMT; - } - sdl_spec.channels = handle->decoder->channels; - sdl_spec.samples = handle->decoder->sample_rate / 100; - sdl_spec.callback = SDL_callback; - sdl_spec.userdata = handle; - memcpy(&handle->sdl_spec, &sdl_spec, sizeof(SDL_AudioSpec)); - handle->device_id = SDL_OpenAudioDevice(device, 0, &sdl_spec, &handle->sdl_spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); - if (!handle->device_id) { - av_log(NULL, AV_LOG_FATAL, "Failed to open audio device \"%s\": %s\n", "default", SDL_GetError()); - return FFMPEG_CORE_ERR_SDL; - } - enum AVSampleFormat target_format = convert_to_sdl_supported_format(handle->decoder->sample_fmt); - handle->output_channel_layout = get_sdl_channel_layout(handle->decoder->channels); - handle->swrac = swr_alloc_set_opts(NULL, handle->output_channel_layout, target_format, handle->sdl_spec.freq, handle->decoder->channel_layout, handle->decoder->sample_fmt, handle->decoder->sample_rate, 0, NULL); - if (!handle->swrac) { - av_log(NULL, AV_LOG_FATAL, "Failed to allocate resample context.\n"); - return FFMPEG_CORE_ERR_OOM; - } - int re = 0; - if ((re = swr_init(handle->swrac)) < 0) { - return re; - } - if (!(handle->buffer = av_audio_fifo_alloc(target_format, handle->decoder->channels, 1))) { - av_log(NULL, AV_LOG_FATAL, "Failed to allocate buffer.\n"); - return FFMPEG_CORE_ERR_OOM; - } - handle->target_format = target_format; - handle->target_format_pbytes = av_get_bytes_per_sample(target_format); - return FFMPEG_CORE_ERR_OK; -} - -enum AVSampleFormat convert_to_sdl_supported_format(enum AVSampleFormat fmt) { - switch (fmt) { - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_FLTP: - case AV_SAMPLE_FMT_DBLP: - return AV_SAMPLE_FMT_FLT; - case AV_SAMPLE_FMT_U8P: - return AV_SAMPLE_FMT_U8; - case AV_SAMPLE_FMT_S16P: - return AV_SAMPLE_FMT_S16; - case AV_SAMPLE_FMT_S32P: - case AV_SAMPLE_FMT_S64: - case AV_SAMPLE_FMT_S64P: - return AV_SAMPLE_FMT_S32; - default: - return fmt; - } -} - -SDL_AudioFormat convert_to_sdl_format(enum AVSampleFormat fmt) { - fmt = convert_to_sdl_supported_format(fmt); - switch (fmt) { - case AV_SAMPLE_FMT_U8: - return AUDIO_U8; - case AV_SAMPLE_FMT_S16: - return AUDIO_S16SYS; - case AV_SAMPLE_FMT_S32: - return AUDIO_S32SYS; - case AV_SAMPLE_FMT_FLT: - return AUDIO_F32SYS; - default: - return 0; - } -} - -void SDL_callback(void* userdata, uint8_t* stream, int len) { - MusicHandle* handle = (MusicHandle*)userdata; - DWORD re = WaitForSingleObject(handle->mutex, 10); - if (re != WAIT_OBJECT_0) { - // 无法获取Mutex所有权,填充空白数据 - memset(stream, 0, len); - return; - } - int samples_need = len / handle->target_format_pbytes / handle->sdl_spec.channels; - int buffer_size = handle->sdl_spec.freq / 5; - if (av_audio_fifo_size(handle->buffer) == 0) { - // 缓冲区没有数据,填充空白数据 - memset(stream, 0, len); - } else if (!handle->graph) { - int writed = av_audio_fifo_read(handle->buffer, (void**)&stream, samples_need); - if (writed > 0) { - // 增大缓冲区开始时间 - AVRational base = { 1, handle->sdl_spec.freq }; - handle->pts += av_rescale_q_rnd(writed, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - } - if (writed < 0) { - // 读取发生错误,填充空白数据 - memset(stream, 0, len); - } else if (writed < samples_need) { - // 不足的区域用空白数据填充 - memset(stream + (size_t)writed * handle->target_format_pbytes, 0, (((size_t)samples_need - writed) * handle->target_format_pbytes)); - } - } else if (handle->is_easy_filters) { - AVFrame* in = av_frame_alloc(), * out = av_frame_alloc(); - int writed = 0; - int samples_need_in = 0; - if (!in || !out) { - memset(stream, 0, len); - goto end; - } - samples_need_in = samples_need * get_speed(handle->s->speed) / 1000; - in->channels = handle->sdl_spec.channels; - in->channel_layout = handle->output_channel_layout; - in->format = handle->target_format; - in->sample_rate = handle->sdl_spec.freq; - in->nb_samples = samples_need_in; - if (av_frame_get_buffer(in, 0) < 0) { - memset(stream, 0, len); - goto end; - } - // 从缓冲区读取数据 - writed = av_audio_fifo_read(handle->buffer, (void**)in->data, samples_need_in); - if (writed > 0) { - // 增大缓冲区开始时间 - AVRational base = { 1, handle->sdl_spec.freq }; - handle->pts += av_rescale_q_rnd(writed, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - } - if (writed < 0) { - memset(stream, 0, len); - goto end; - } - in->nb_samples = writed; - // 喂给 filters 数据 - if (av_buffersrc_add_frame(handle->filter_inp, in) < 0) { - memset(stream, 0, len); - goto end; - } - // 从 filters 拿回数据 - if (av_buffersink_get_frame(handle->filter_out, out) < 0) { - memset(stream, 0, len); - goto end; - } - if (out->nb_samples >= samples_need) { - memcpy(stream, out->data[0], len); - } else { - size_t le = (size_t)out->nb_samples * handle->target_format_pbytes * handle->sdl_spec.channels; - memcpy(stream, out->data[0], le); - memset(stream, 0, len - le); - } -end: - if (in) av_frame_free(&in); - if (out) av_frame_free(&out); - } else if (!handle->is_wait_filters || av_audio_fifo_size(handle->filters_buffer) > buffer_size) { - handle->is_wait_filters = 0; - int writed = av_audio_fifo_read(handle->filters_buffer, (void**)&stream, samples_need); - if (writed > 0) { - // 增大缓冲区开始时间 - AVRational base = { 1, handle->sdl_spec.freq }, base2 = { get_speed(handle->s->speed), 1000 }, tar = { 1, 1 }; - int samples_in = av_rescale_q_rnd(writed, base2, tar, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - handle->filters_buffer_offset -= samples_in; - handle->pts += av_rescale_q_rnd(samples_in, base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - av_audio_fifo_drain(handle->buffer, samples_in); - } - if (writed < 0) { - // 读取发生错误,填充空白数据 - memset(stream, 0, len); - } else if (writed < samples_need) { - // 不足的区域用空白数据填充 - memset(stream + (size_t)writed * handle->target_format_pbytes, 0, (((size_t)samples_need - writed) * handle->target_format_pbytes)); - } - } else { - memset(stream, 0, len); - } - ReleaseMutex(handle->mutex); -} - -uint64_t get_sdl_channel_layout(int channels) { - switch (channels) { - case 2: - return av_get_channel_layout("FL+FR"); - case 3: - return av_get_channel_layout("FL+FR+LFE"); - case 4: - return av_get_channel_layout("FL+FR+BL+BR"); - case 5: - return av_get_channel_layout("FL+FR+FC+BL+BR"); - case 6: - return av_get_channel_layout("FL+FR+FC+LFE+SL+SR"); - case 7: - return av_get_channel_layout("FL+FR+FC+LFE+BC+SL+SR"); - case 8: - return av_get_channel_layout("FL+FR+FC+LFE+BL+BR+SL+SR"); - default: - return av_get_default_channel_layout(channels); - } -} diff --git a/ffmpeg_core/src/output.h b/ffmpeg_core/src/output.h deleted file mode 100644 index e0240bc02..000000000 --- a/ffmpeg_core/src/output.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _MUSICPLAYER2_OUTPUT_H -#define _MUSICPLAYER2_OUTPUT_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -int init_output(MusicHandle* handle, const char* device); -enum AVSampleFormat convert_to_sdl_supported_format(enum AVSampleFormat fmt); -void SDL_callback(void* userdata, uint8_t* stream, int len); -SDL_AudioFormat convert_to_sdl_format(enum AVSampleFormat fmt); -/** - * @brief 获取与SDL输出匹配的输出布局 - * @param channels 声道数 - * @return 布局 -*/ -uint64_t get_sdl_channel_layout(int channels); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/speed.c b/ffmpeg_core/src/speed.c deleted file mode 100644 index 4a1b9b4f7..000000000 --- a/ffmpeg_core/src/speed.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "speed.h" - -#include - -int get_speed(float speed) { - return roundf(speed * 1000); -} - -int create_speed_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int* speed) { - if (!graph || !speed || !src || !list) return FFMPEG_CORE_ERR_NULLPTR; - char args[64]; - char name[32]; - const AVFilter* atempo = avfilter_get_by_name("atempo"); - int speed_now = min(max(*speed, 500), 2000); - *speed = 1000 * (*speed) / speed_now; - snprintf(args, sizeof(args), "tempo=%.3f", speed_now / 1000.0); - snprintf(name, sizeof(name), "atempo%d", index); - int re = 0; - AVFilterContext* context = NULL; - if ((re = avfilter_graph_create_filter(&context, atempo, name, args, NULL, graph)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to create speed filter \"%s\": %s (%i)\n", name, av_err2str(re), re); - return re; - } - if (!c_linked_list_append(list, (void*)context)) { - av_log(NULL, AV_LOG_FATAL, "Failed to append filter \"%s\" to list.\n", context->name); - return FFMPEG_CORE_ERR_OOM; - } - if (c_linked_list_count(*list) > 1) { - AVFilterContext* last = c_linked_list_tail(*list)->prev->d; - if ((re = avfilter_link(last, 0, context, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, context->name, 0, av_err2str(re), re); - return re; - } - } else { - if ((re = avfilter_link(src, 0, context, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", src->name, 0, context->name, 0, av_err2str(re), re); - return re; - } - } - return FFMPEG_CORE_ERR_OK; -} diff --git a/ffmpeg_core/src/speed.h b/ffmpeg_core/src/speed.h deleted file mode 100644 index fbe554a85..000000000 --- a/ffmpeg_core/src/speed.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _MUSICPLAYER2_SPEED_H -#define _MUSICPLAYER2_SPEED_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -/// 将speed转为1000为1.0倍的数字,例如1.5倍转为1500 -int get_speed(float speed); -/** - * @brief 创建 atempo Filter - * @param index Filter 序号 - * @param graph Graph - * @param src 输入 - * @param list Filters列表 - * @param speed 指向目标速度的指针,会返回还需要设置的速度 - * @return -*/ -int create_speed_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int* speed); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/src/volume.c b/ffmpeg_core/src/volume.c deleted file mode 100644 index 9e67c8498..000000000 --- a/ffmpeg_core/src/volume.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "volume.h" - -int get_volume_precision(enum AVSampleFormat f) { - switch (f) { - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_U8P: - case AV_SAMPLE_FMT_S16P: - case AV_SAMPLE_FMT_S32P: - return 0; - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_DBLP: - return 2; - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: - default: - return 1; - } -} - -int create_volume_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int volume, enum AVSampleFormat f) { - if (!graph || !src || !list) return FFMPEG_CORE_ERR_NULLPTR; - char args[128]; - char name[32]; - const AVFilter* vol = avfilter_get_by_name("volume"); - snprintf(args, sizeof(args), "volume=%.2f:precision=%d", volume / 100.0, get_volume_precision(f)); - snprintf(name, sizeof(name), "volume%d", index); - int re = 0; - AVFilterContext* context = NULL; - if ((re = avfilter_graph_create_filter(&context, vol, name, args, NULL, graph)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to create volume filter \"%s\": %s (%i)\n", name, av_err2str(re), re); - return re; - } - if (!c_linked_list_append(list, (void*)context)) { - av_log(NULL, AV_LOG_FATAL, "Failed to append filter \"%s\" to list.\n", context->name); - return FFMPEG_CORE_ERR_OOM; - } - if (c_linked_list_count(*list) > 1) { - AVFilterContext* last = c_linked_list_tail(*list)->prev->d; - if ((re = avfilter_link(last, 0, context, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", last->name, 0, context->name, 0, av_err2str(re), re); - return re; - } - } else { - if ((re = avfilter_link(src, 0, context, 0)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Failed to link %s:%i -> %s:%i: %s (%i)\n", src->name, 0, context->name, 0, av_err2str(re), re); - return re; - } - } - return FFMPEG_CORE_ERR_OK; -} diff --git a/ffmpeg_core/src/volume.h b/ffmpeg_core/src/volume.h deleted file mode 100644 index f4f0efe26..000000000 --- a/ffmpeg_core/src/volume.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _MUSICPLAYER2_VOLUME_H -#define _MUSICPLAYER2_VOLUME_H -#if __cplusplus -extern "C" { -#endif -#include "core.h" -int get_volume_precision(enum AVSampleFormat f); -/** - * @brief 创建 volume Filter - * @param index Filter 序号 - * @param graph Graph - * @param src 输入 - * @param list Filters列表 - * @param volume 声音大小百分比 - * @param f 输入的格式 - * @return -*/ -int create_volume_filter(int index, AVFilterGraph* graph, AVFilterContext* src, c_linked_list** list, int volume, enum AVSampleFormat f); -#if __cplusplus -} -#endif -#endif diff --git a/ffmpeg_core/utils b/ffmpeg_core/utils deleted file mode 160000 index 4bdef58fe..000000000 --- a/ffmpeg_core/utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4bdef58fe5965a511b2682f393499e9a34f4bd13