Skip to content

Instantly share code, notes, and snippets.

@lxfly2000
Last active October 17, 2018 17:22
Show Gist options
  • Save lxfly2000/118679df4b0a4c098b1613f9cdf53374 to your computer and use it in GitHub Desktop.
Save lxfly2000/118679df4b0a4c098b1613f9cdf53374 to your computer and use it in GitHub Desktop.
用MF转换音频文件到MP3
//用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