Last active
October 17, 2018 17:22
-
-
Save lxfly2000/118679df4b0a4c098b1613f9cdf53374 to your computer and use it in GitHub Desktop.
用MF转换音频文件到MP3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//用Windows Media Foundation将任何Windows支持的音频文件转换成MP3格式文件,仅限Windows 8(NT6.2)及以上操作系统。 | |
#include<new> | |
#include<iostream> | |
#include<string> | |
#include<locale.h> | |
#include<Windows.h> | |
#include<mfapi.h> | |
#include<mfidl.h> | |
#include<Shlwapi.h> | |
#pragma comment(lib,"mfplat.lib") | |
#pragma comment(lib,"mf.lib") | |
#pragma comment(lib,"mfuuid.lib") | |
#pragma comment(lib,"shlwapi.lib") | |
template<typename IType>void SafeRelease(IType **p) | |
{ | |
if (*p) | |
{ | |
(*p)->Release(); | |
(*p) = nullptr; | |
} | |
} | |
//https://msdn.microsoft.com/en-us/library/windows/desktop/ff819476(v=vs.85).aspx | |
class CSession : public IMFAsyncCallback | |
{ | |
public: | |
static HRESULT Create(CSession **ppSession); | |
// IUnknown methods | |
STDMETHODIMP QueryInterface(REFIID riid, void** ppv); | |
STDMETHODIMP_(ULONG) AddRef(); | |
STDMETHODIMP_(ULONG) Release(); | |
// IMFAsyncCallback methods | |
STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue) | |
{ | |
// Implementation of this method is optional. | |
return E_NOTIMPL; | |
} | |
STDMETHODIMP Invoke(IMFAsyncResult *pResult); | |
// Other methods | |
HRESULT StartEncodingSession(IMFTopology *pTopology); | |
HRESULT GetEncodingPosition(MFTIME *pTime); | |
HRESULT Wait(DWORD dwMsec); | |
private: | |
CSession() : m_cRef(1), m_pSession(NULL), m_pClock(NULL), m_hrStatus(S_OK), m_hWaitEvent(NULL) | |
{ | |
} | |
virtual ~CSession() | |
{ | |
if (m_pSession) | |
{ | |
m_pSession->Shutdown(); | |
} | |
SafeRelease(&m_pClock); | |
SafeRelease(&m_pSession); | |
CloseHandle(m_hWaitEvent); | |
} | |
HRESULT Initialize(); | |
private: | |
IMFMediaSession * m_pSession; | |
IMFPresentationClock *m_pClock; | |
HRESULT m_hrStatus; | |
HANDLE m_hWaitEvent; | |
long m_cRef; | |
}; | |
HRESULT CSession::Create(CSession **ppSession) | |
{ | |
*ppSession = NULL; | |
CSession *pSession = new (std::nothrow) CSession(); | |
if (pSession == NULL) | |
{ | |
return E_OUTOFMEMORY; | |
} | |
HRESULT hr = pSession->Initialize(); | |
if (FAILED(hr)) | |
{ | |
pSession->Release(); | |
return hr; | |
} | |
*ppSession = pSession; | |
return S_OK; | |
} | |
STDMETHODIMP CSession::QueryInterface(REFIID riid, void** ppv) | |
{ | |
static const QITAB qit[] = | |
{ | |
QITABENT(CSession, IMFAsyncCallback), | |
{ 0 } | |
}; | |
return QISearch(this, qit, riid, ppv); | |
} | |
STDMETHODIMP_(ULONG) CSession::AddRef() | |
{ | |
return InterlockedIncrement(&m_cRef); | |
} | |
STDMETHODIMP_(ULONG) CSession::Release() | |
{ | |
long cRef = InterlockedDecrement(&m_cRef); | |
if (cRef == 0) | |
{ | |
delete this; | |
} | |
return cRef; | |
} | |
HRESULT CSession::Initialize() | |
{ | |
IMFClock *pClock = NULL; | |
HRESULT hr = MFCreateMediaSession(NULL, &m_pSession); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = m_pSession->GetClock(&pClock); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = pClock->QueryInterface(IID_PPV_ARGS(&m_pClock)); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = m_pSession->BeginGetEvent(this, NULL); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
m_hWaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | |
if (m_hWaitEvent == NULL) | |
{ | |
hr = HRESULT_FROM_WIN32(GetLastError()); | |
} | |
done: | |
SafeRelease(&pClock); | |
return hr; | |
} | |
// Implements IMFAsyncCallback::Invoke | |
STDMETHODIMP CSession::Invoke(IMFAsyncResult *pResult) | |
{ | |
IMFMediaEvent* pEvent = NULL; | |
MediaEventType meType = MEUnknown; | |
HRESULT hrStatus = S_OK; | |
HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = pEvent->GetType(&meType); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = pEvent->GetStatus(&hrStatus); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
if (FAILED(hrStatus)) | |
{ | |
hr = hrStatus; | |
goto done; | |
} | |
switch (meType) | |
{ | |
case MESessionEnded: | |
hr = m_pSession->Close(); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
break; | |
case MESessionClosed: | |
SetEvent(m_hWaitEvent); | |
break; | |
} | |
if (meType != MESessionClosed) | |
{ | |
hr = m_pSession->BeginGetEvent(this, NULL); | |
} | |
done: | |
if (FAILED(hr)) | |
{ | |
m_hrStatus = hr; | |
m_pSession->Close(); | |
} | |
SafeRelease(&pEvent); | |
return hr; | |
} | |
HRESULT CSession::StartEncodingSession(IMFTopology *pTopology) | |
{ | |
HRESULT hr = m_pSession->SetTopology(0, pTopology); | |
if (SUCCEEDED(hr)) | |
{ | |
PROPVARIANT varStart; | |
PropVariantClear(&varStart); | |
hr = m_pSession->Start(&GUID_NULL, &varStart); | |
} | |
return hr; | |
} | |
HRESULT CSession::GetEncodingPosition(MFTIME *pTime) | |
{ | |
return m_pClock->GetTime(pTime); | |
} | |
HRESULT CSession::Wait(DWORD dwMsec) | |
{ | |
HRESULT hr = S_OK; | |
DWORD dwTimeoutStatus = WaitForSingleObject(m_hWaitEvent, dwMsec); | |
if (dwTimeoutStatus != WAIT_OBJECT_0) | |
{ | |
hr = E_PENDING; | |
} | |
else | |
{ | |
hr = m_hrStatus; | |
} | |
return hr; | |
} | |
HRESULT RunEncodingSession(CSession *pSession, MFTIME duration) | |
{ | |
const DWORD WAIT_PERIOD = 500; | |
const int UPDATE_INCR = 5; | |
HRESULT hr = S_OK; | |
//MFTIME pos; | |
LONGLONG prev = 0; | |
while (1) | |
{ | |
hr = pSession->Wait(WAIT_PERIOD); | |
if (hr == E_PENDING) | |
{ | |
/*hr = pSession->GetEncodingPosition(&pos); | |
LONGLONG percent = (100 * pos) / duration; | |
if (percent >= prev + UPDATE_INCR) | |
{ | |
std::cout << percent << "% .. "; | |
prev = percent; | |
}*/ | |
} | |
else | |
{ | |
//std::cout << std::endl; | |
break; | |
} | |
} | |
return hr; | |
} | |
HRESULT GetSourceDuration(IMFMediaSource *pSource, MFTIME *pDuration) | |
{ | |
*pDuration = 0; | |
IMFPresentationDescriptor *pPD = NULL; | |
HRESULT hr = pSource->CreatePresentationDescriptor(&pPD); | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pPD->GetUINT64(MF_PD_DURATION, (UINT64*)pDuration); | |
pPD->Release(); | |
} | |
return hr; | |
} | |
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa371340(v=vs.85).aspx | |
// Create a media source from a URL. | |
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource) | |
{ | |
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID; | |
IMFSourceResolver* pSourceResolver = NULL; | |
IUnknown* pSource = NULL; | |
// Create the source resolver. | |
HRESULT hr = MFCreateSourceResolver(&pSourceResolver); | |
if (FAILED(hr)) | |
{ | |
return hr; | |
} | |
// Use the source resolver to create the media source. | |
// Note: For simplicity this sample uses the synchronous method to create | |
// the media source. However, creating a media source can take a noticeable | |
// amount of time, especially for a network source. For a more responsive | |
// UI, use the asynchronous BeginCreateObjectFromURL method. | |
hr = pSourceResolver->CreateObjectFromURL( | |
sURL, // URL of the source. | |
MF_RESOLUTION_MEDIASOURCE, // Create a source object. | |
NULL, // Optional property store. | |
&ObjectType, // Receives the created object type. | |
&pSource // Receives a pointer to the media source. | |
); | |
if (FAILED(hr)) | |
{ | |
return hr; | |
} | |
// Get the IMFMediaSource interface from the media source. | |
hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource)); | |
pSourceResolver->Release(); | |
pSource->Release(); | |
return hr; | |
} | |
void EnumAllMP3MediaTypes() | |
{ | |
DWORD dwFlags = | |
(MFT_ENUM_FLAG_ALL & (~MFT_ENUM_FLAG_FIELDOFUSE)) | | |
MFT_ENUM_FLAG_SORTANDFILTER; | |
IMFCollection *pAvailableTypes = NULL; // List of audio media types. | |
if (FAILED(MFTranscodeGetAudioOutputAvailableTypes(MFAudioFormat_MP3, dwFlags, NULL, &pAvailableTypes))) | |
return; | |
DWORD count; | |
pAvailableTypes->GetElementCount(&count); | |
for (DWORD i = 0; i < count; i++) | |
{ | |
IUnknown *elem; | |
pAvailableTypes->GetElement(i, &elem); | |
IMFMediaType *mt; | |
elem->QueryInterface(IID_PPV_ARGS(&mt)); | |
GUID guid; | |
UINT32 uv; | |
mt->GetGUID(MF_MT_MAJOR_TYPE, &guid); | |
if (memcmp(&guid, &MFMediaType_Audio, sizeof(GUID)) == 0) | |
printf("MAJOR:AUDIO "); | |
else printf("MAJOR:ELSE "); | |
mt->GetGUID(MF_MT_SUBTYPE, &guid); | |
if (memcmp(&guid, &MFAudioFormat_MP3, sizeof(GUID)) == 0) | |
printf("SUB:MP3 "); | |
else printf("SUB:ELSE "); | |
mt->GetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &uv); | |
printf("Avg:%d ", uv*8); | |
mt->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &uv); | |
printf("Ch:%d ", uv); | |
mt->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &uv); | |
printf("Freq:%d\n", uv); | |
mt->Release(); | |
elem->Release(); | |
} | |
SafeRelease(&pAvailableTypes); | |
} | |
UINT32 /*bitsPerSample = 16,*/ samplesPerSec = 44100, numChannels = 2, avg = 0; | |
//https://msdn.microsoft.com/en-us/library/windows/desktop/hh162907(v=vs.85).aspx | |
HRESULT CreateMP3Profile_44100Stereo(IMFAttributes **ppAttributes) | |
{ | |
IMFAttributes *pAttributes = NULL; | |
HRESULT hr = MFCreateAttributes(&pAttributes, 7); | |
hr = pAttributes->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pAttributes->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_MP3); | |
} | |
/*if (SUCCEEDED(hr)) | |
{ | |
hr = pAttributes->SetUINT32( | |
MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample); | |
}*/ | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pAttributes->SetUINT32( | |
MF_MT_AUDIO_SAMPLES_PER_SECOND, samplesPerSec); | |
} | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pAttributes->SetUINT32( | |
MF_MT_AUDIO_NUM_CHANNELS, numChannels); | |
} | |
if (SUCCEEDED(hr)&&avg) | |
{ | |
hr = pAttributes->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, avg/8); | |
} | |
/*if (SUCCEEDED(hr)) | |
{ | |
hr = pAttributes->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1); | |
}*/ | |
if (SUCCEEDED(hr)) | |
{ | |
*ppAttributes = pAttributes; | |
(*ppAttributes)->AddRef(); | |
} | |
SafeRelease(&pAttributes); | |
return hr; | |
} | |
HRESULT CreateTranscodeProfile(IMFTranscodeProfile **ppProfile) | |
{ | |
IMFTranscodeProfile *pProfile = NULL; | |
IMFAttributes *pAudio = NULL; | |
IMFAttributes *pContainer = NULL; | |
HRESULT hr = MFCreateTranscodeProfile(&pProfile); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
// Audio attributes. | |
hr = CreateMP3Profile_44100Stereo(&pAudio); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = pProfile->SetAudioAttributes(pAudio); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
// Container attributes. | |
hr = MFCreateAttributes(&pContainer, 1); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = pContainer->SetGUID(MF_TRANSCODE_CONTAINERTYPE, MFTranscodeContainerType_MP3); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = pProfile->SetContainerAttributes(pContainer); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
*ppProfile = pProfile; | |
(*ppProfile)->AddRef(); | |
done: | |
SafeRelease(&pProfile); | |
SafeRelease(&pAudio); | |
SafeRelease(&pContainer); | |
return hr; | |
} | |
HRESULT EncodeFile(PCWSTR pszInput, PCWSTR pszOutput) | |
{ | |
IMFTranscodeProfile *pProfile = NULL; | |
IMFMediaSource *pSource = NULL; | |
IMFTopology *pTopology = NULL; | |
CSession *pSession = NULL; | |
MFTIME duration = 0; | |
HRESULT hr = CreateMediaSource(pszInput, &pSource); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = GetSourceDuration(pSource, &duration); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = CreateTranscodeProfile(&pProfile); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = MFCreateTranscodeTopology(pSource, pszOutput, pProfile, &pTopology); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = CSession::Create(&pSession); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = pSession->StartEncodingSession(pTopology); | |
if (FAILED(hr)) | |
{ | |
goto done; | |
} | |
hr = RunEncodingSession(pSession, duration); | |
done: | |
if (pSource) | |
{ | |
pSource->Shutdown(); | |
} | |
SafeRelease(&pSession); | |
SafeRelease(&pProfile); | |
SafeRelease(&pSource); | |
SafeRelease(&pTopology); | |
return hr; | |
} | |
HRESULT C(HRESULT sth) | |
{ | |
if (sth) | |
throw sth; | |
return sth; | |
} | |
int wmain(int argc, wchar_t *argv[]) | |
{ | |
setlocale(LC_ALL, ""); | |
TCHAR pathin[MAX_PATH], pathout[MAX_PATH]; | |
switch (argc) | |
{ | |
case 6:swscanf_s(argv[5], L"%u", &numChannels); | |
case 5:swscanf_s(argv[4], L"%u", &avg); | |
case 4:swscanf_s(argv[3], L"%u", &samplesPerSec); | |
case 3:lstrcpy(pathout, argv[2]); | |
case 2: | |
C(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); | |
C(MFStartup(MF_VERSION)); | |
if (lstrcmp(argv[1], L"-?") == 0) | |
{ | |
EnumAllMP3MediaTypes(); | |
} | |
else | |
{ | |
lstrcpy(pathin, argv[1]); | |
if (argc == 2) | |
wsprintf(pathout, L"%s.mp3", pathin); | |
HRESULT hr = EncodeFile(pathin, pathout); | |
if (FAILED(hr)) | |
wprintf(L"转换失败:%#x\n", hr); | |
} | |
MFShutdown(); | |
CoUninitialize(); | |
break; | |
case 1: | |
_putws(L"命令行:mfencode <输入文件> [输出MP3文件] [频率(Freq)] [码率(Avg)] [通道(Ch)]\n" | |
"* 输入文件可以是任何Windows系统支持的类型。\nmfencode -? : 列出所有支持的配置。"); | |
return 0; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment