This tutorial shows how to use the LEADTOOLS Multimedia SDK to create a Windows C++ application that uses the ltmmConvert
control and other LEADTOOLS objects to combine video and audio from separate files into one output file.
Overview | |
---|---|
Summary | This tutorial shows how to use the LEADTOOLS Multimedia SDK to combine audio and video into one file. |
Completion Time | 30 minutes |
Visual Studio Project | Download tutorial project (19 KB) |
Platform | Windows API C++ Application |
IDE | Visual Studio 2019 |
Development License | Download LEADTOOLS |
Try it in another language |
|
Before working on the Combine Video and Audio from Separate Sources - Windows C++ tutorial, complete the Add References and Set a License tutorial.
Start with a copy of the 64-bit Windows API project created in the Add References and Set a License tutorial. If the project is not available, create it by following the steps in that tutorial.
In order to use the ltmmConvert
Object, LEADTOOLS requires additional references. Add the required Multimedia library reference by opening the pre-compiled header file, either pch.h
or stdafx.h
depending on the Visual Studio version used, and add the following lines:
// Add LEADTOOLS Multimedia reference
#include "C:\LEADTOOLS23\Include\ltmm.h"
#include "C:\LEADTOOLS23\Include\ltmm_Errors.h"
//x64 libs
#pragma comment (lib, "C:\\LEADTOOLS23\\Lib\\CDLL\\x64\\Ltmmx.lib")
#pragma comment (lib, "C:\\LEADTOOLS23\\Lib\\CDLL\\x64\\ltmmuuidx.lib")
With the project created, the references added, and the license set, coding can begin.
In the main CPP file of the project, locate the InitInstance()
function and add the following line of code at the beginning, to initialize the Windows COM library.
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
CoInitialize(NULL);
// Keep the rest of the function unchanged
The steps below are for Visual Studio 2019; they could be different for other versions of Visual Studio.
Go to the Solution Explorer and double-click the resources file (.rc). Expand the menu tab in the resources tree and double-click the menu resource to open it in the designer interface. In the empty item below the Exit item, click and type &Merge Audio and Video. Drag the new item above Exit. Ensure the item's ID is ID_FILE_MERGEAUDIOANDVIDEO
.
Go to the main CPP file of the project, which contains the WndProc()
function for the main window. Navigate to the switch (wmId)
statement that is below the WM_COMMAND
case and add the new case shown below.
// In WndProc(), under "case WM_COMMAND:"
switch (wmId)
{
case ID_FILE_MERGEAUDIOANDVIDEO:
if(FAILED(CombineAudioandVideo()))
MessageBox(hWnd, TEXT("Combining failed"), TEXT("LEADTOOLS Demo"), MB_ICONERROR);
else
MessageBox(NULL, TEXT("Combining successful"), TEXT("LEADTOOLS Demo"), MB_ICONINFORMATION);
break;
// Keep rest of the code as is
Below is the code for the SelectCompressor()
, CombineFiles()
and CombineAudioandVideo()
functions used in the code.
To test use these sample files:
void SelectCompressor(IltmmCompressors *pCompressors, const TCHAR *pszCompressorName)
{
long index;
BSTR bstrCompressorName = SysAllocString(pszCompressorName);
pCompressors->Find(bstrCompressorName, &index);
pCompressors->put_Selection(index);
SysFreeString(bstrCompressorName);
}
HRESULT CombineFiles(IltmmSampleTarget* pVideoTarget, IltmmSampleTarget* pAudioTarget, const TCHAR* _targetFile)
{
IltmmMultiStreamSource* pMSSource = NULL;
HRESULT hr = CoCreateInstance(CLSID_ltmmMultiStreamSource, NULL, CLSCTX_INPROC_SERVER, IID_IltmmMultiStreamSource, (void**)&pMSSource);
if (FAILED(hr))
return hr;
pMSSource->put_StreamCount(2);
IltmmMediaTypeDisp* pmt = NULL;
hr = pVideoTarget->GetConnectedMediaType(&pmt);
if (FAILED(hr))
return hr;
hr = pMSSource->SetMediaType(0, pmt);
if (FAILED(hr))
return hr;
pmt->Release();
pmt = NULL;
hr = pAudioTarget->GetConnectedMediaType(&pmt);
if (FAILED(hr))
return hr;
hr = pMSSource->SetMediaType(1, pmt);
if (FAILED(hr))
return hr;
pmt->Release();
pmt = NULL;
IltmmConvert* pCombineConvert = NULL;
hr = CoCreateInstance(CLSID_ltmmConvert, NULL, CLSCTX_INPROC_SERVER, IID_IltmmConvert, (void**)&pCombineConvert);
if (FAILED(hr))
return hr;
pCombineConvert->put_SourceObject(pMSSource);
BSTR bstrTargetFile = SysAllocString(_targetFile);
pCombineConvert->put_TargetFile(bstrTargetFile);
SysFreeString(bstrTargetFile);
//Select the desired compressor based on the name
#define LEAD_H264_ENCODER L"@device:sw:{33D9A760-90C8-11D0-BD43-00A0C911CE86}\\LEAD H264 Encoder (4.0)"
#define LEAD_AAC_AUDIO_ENCODER L"@device:sw:{33D9A761-90C8-11D0-BD43-00A0C911CE86}\\{E2B7DD70-38C5-11D5-91F6-00104BDB8FF9}"
//Set the Video Compressor
IltmmCompressors* pCompressors;
pCombineConvert->get_VideoCompressors(&pCompressors);
SelectCompressor(pCompressors, LEAD_H264_ENCODER);
pCompressors->Release();
//Set the Audio Compressor
pCombineConvert->get_AudioCompressors(&pCompressors);
SelectCompressor(pCompressors, LEAD_AAC_AUDIO_ENCODER);
pCompressors->Release();
pCombineConvert->put_TargetFormat(ltmmConvert_TargetFormat_MPEG2_TRANSPORT);
IltmmMediaSampleDisp* pmsSrc = NULL;
IltmmMediaSampleDisp* pmsDst = NULL;
long lStartTimeHi;
long lStartTimeLo;
long lStopTimeHi;
long lStopTimeLo;
VARIANT vBuffer;
VARIANT_BOOL vBool;
long lActualDataLength;
//Begin the running the Combine ConvertCtrl
pCombineConvert->StartConvert();
//Video Write
while (true)
{
pVideoTarget->GetSample(10000, &pmsSrc);
if (!pmsSrc)
break;
// get a source sample
hr = pMSSource->GetSampleBuffer(0, 10000, &pmsDst);
if (FAILED(hr))
break;
// copy the data to the source sample
hr = pmsSrc->get_Buffer(&vBuffer);
if (FAILED(hr))
break;
hr = pmsSrc->get_ActualDataLength(&lActualDataLength);
if (FAILED(hr))
break;
hr = pmsDst->SetData(lActualDataLength, vBuffer);
if (FAILED(hr))
break;
// copy the sample time
hr = pmsSrc->GetTime(&lStartTimeHi, &lStartTimeLo, &lStopTimeHi, &lStopTimeLo);
if (FAILED(hr))
pmsDst->ResetTime();
else
{
hr = pmsDst->SetTime(lStartTimeHi, lStartTimeLo, lStopTimeHi, lStopTimeLo);
if (FAILED(hr))
break;
}
// copy the other flags
hr = pmsSrc->get_Discontinuity(&vBool);
if (FAILED(hr))
break;
hr = pmsDst->put_Discontinuity(vBool);
if (FAILED(hr))
break;
hr = pmsSrc->get_Preroll(&vBool);
if (FAILED(hr))
break;
hr = pmsDst->put_Preroll(vBool);
if (FAILED(hr))
break;
hr = pmsSrc->get_SyncPoint(&vBool);
if (FAILED(hr))
break;
hr = pmsDst->put_SyncPoint(vBool);
if (FAILED(hr))
break;
//release the source sample
pmsSrc->Release();
pmsSrc = NULL;
// deliver the source sample
hr = pMSSource->DeliverSample(0, 2000, pmsDst);
if (FAILED(hr))
break;
// release the source sample
pmsDst->Release();
pmsDst = NULL;
}
//Audio Write
while (true)
{
pAudioTarget->GetSample(10000, &pmsSrc);
if (!pmsSrc)
break;
// get a source sample
hr = pMSSource->GetSampleBuffer(1, 10000, &pmsDst);
if (FAILED(hr))
break;
// copy the data to the source sample
hr = pmsSrc->get_Buffer(&vBuffer);
if (FAILED(hr))
break;
hr = pmsSrc->get_ActualDataLength(&lActualDataLength);
if (FAILED(hr))
break;
hr = pmsDst->SetData(lActualDataLength, vBuffer);
if (FAILED(hr))
break;
// copy the sample time
hr = pmsSrc->GetTime(&lStartTimeHi, &lStartTimeLo, &lStopTimeHi, &lStopTimeLo);
if (FAILED(hr))
pmsDst->ResetTime();
else
{
hr = pmsDst->SetTime(lStartTimeHi, lStartTimeLo, lStopTimeHi, lStopTimeLo);
if (FAILED(hr))
break;
}
// copy the other flags
hr = pmsSrc->get_Discontinuity(&vBool);
if (FAILED(hr))
break;
hr = pmsDst->put_Discontinuity(vBool);
if (FAILED(hr))
break;
hr = pmsSrc->get_Preroll(&vBool);
if (FAILED(hr))
break;
hr = pmsDst->put_Preroll(vBool);
if (FAILED(hr))
break;
hr = pmsSrc->get_SyncPoint(&vBool);
if (FAILED(hr))
break;
hr = pmsDst->put_SyncPoint(vBool);
if (FAILED(hr))
break;
//release the source sample
pmsSrc->Release();
pmsSrc = NULL;
// deliver the source sample
hr = pMSSource->DeliverSample(1, 2000, pmsDst);
if (FAILED(hr))
break;
// release the source sample
pmsDst->Release();
pmsDst = NULL;
}
pCombineConvert->StopConvert();
pMSSource->DeliverEndOfStream(0, 1000);
pMSSource->DeliverEndOfStream(1, 1000);
pCombineConvert->ResetSource();
pMSSource->Release();
pCombineConvert->Release();
return hr;
}
int CombineAudioandVideo()
{
// Initialize the convert control
IltmmConvert *pVidConvert = NULL, *pAudConvert = NULL;
HRESULT hr = CoCreateInstance(CLSID_ltmmConvert, NULL, CLSCTX_INPROC_SERVER, IID_IltmmConvert, (void**)&pVidConvert);
if (FAILED(hr))
return hr;
hr = CoCreateInstance(CLSID_ltmmConvert, NULL, CLSCTX_INPROC_SERVER, IID_IltmmConvert, (void**)&pAudConvert);
if (FAILED(hr))
return hr;
//Init the SampleTargets. The Video and Audio data from our files will write to these.
IltmmSampleTarget *pVidTarget = NULL, *pAudTarget = NULL;
hr = CoCreateInstance(CLSID_ltmmSampleTarget, NULL, CLSCTX_INPROC_SERVER, IID_IltmmSampleTarget, (void**)&pVidTarget);
if (FAILED(hr))
return hr;
hr = CoCreateInstance(CLSID_ltmmSampleTarget, NULL, CLSCTX_INPROC_SERVER, IID_IltmmSampleTarget, (void**)&pAudTarget);
if (FAILED(hr))
return hr;
IltmmMediaTypeDisp* pmt = NULL;
hr = CoCreateInstance(CLSID_ltmmMediaType, NULL, CLSCTX_INPROC_SERVER, IID_IltmmMediaTypeDisp, (void**)&pmt);
if (FAILED(hr))
return hr;
pmt->put_Type((BSTR)ltmmMEDIATYPE_Video);
// make the SampleTarget object accept connections of this media type
hr = pVidTarget->SetAcceptedMediaType(pmt);
if (FAILED(hr))
return hr;
pVidConvert->put_TargetObject(pVidTarget);
pmt->Release();
pmt = NULL;
hr = CoCreateInstance(CLSID_ltmmMediaType, NULL, CLSCTX_INPROC_SERVER, IID_IltmmMediaTypeDisp, (void**)&pmt);
if (FAILED(hr))
return hr;
pmt->put_Type((BSTR)ltmmMEDIATYPE_Audio);
// make the SampleTarget object accept connections of this media type
hr = pAudTarget->SetAcceptedMediaType(pmt);
if (FAILED(hr))
return hr;
pAudConvert->put_TargetObject(pAudTarget);
pmt->Release();
pmt = NULL;
// Ensure this is the correct path
BSTR bstrSourceFileVid = SysAllocString(TEXT("Video-Source.mp4"));
hr = pVidConvert->put_SourceFile(bstrSourceFileVid);
if (FAILED(hr))
return hr;
SysFreeString(bstrSourceFileVid);
// Ensure this is the correct path
BSTR bstrSourceFileAud = SysAllocString(TEXT("Audio-Source.mpg"));
hr = pAudConvert->put_SourceFile(bstrSourceFileAud);
if (FAILED(hr))
return hr;
SysFreeString(bstrSourceFileAud);
pVidConvert->StartConvert();
pAudConvert->StartConvert();
// Ensure this is the correct path
hr = CombineFiles(pVidTarget, pAudTarget, TEXT("Combined-File.mpg"));
long lState = 0;
pVidConvert->get_State(&lState);
if (lState == ltmmConvert_State::ltmmConvert_State_Running)
pVidConvert->StopConvert();
lState = 0;
pAudConvert->get_State(&lState);
if (lState == ltmmConvert_State::ltmmConvert_State_Running)
pAudConvert->StopConvert();
pVidTarget->Release();
pVidTarget = NULL;
pAudTarget->Release();
pAudTarget = NULL;
pAudConvert->Release();
pVidConvert->Release();
return hr;
}
Run the project by pressing F5, or by selecting Debug -> Start Debugging.
If the steps are followed correctly, the application runs and converts the audio and video source files to one media file. If the sample files above were used, this would be the expected output media file.
This tutorial showed how to combine two media files, one video and one audio, to one file using the ltmmConvert
Object.