Skip to content

Commit

Permalink
Add RTP AC3 streaming support
Browse files Browse the repository at this point in the history
  • Loading branch information
mincequi committed May 3, 2020
1 parent eb71341 commit 72c1f31
Show file tree
Hide file tree
Showing 9 changed files with 434 additions and 52 deletions.
5 changes: 4 additions & 1 deletion Scream/Scream.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<Configuration>Win8.1 Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
<RootNamespace>Scream</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8.1 Debug|Win32'" Label="Configuration">
Expand Down Expand Up @@ -226,6 +226,7 @@
<FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ac3encoder.cpp" />
<ClCompile Include="adapter.cpp" />
<ClCompile Include="common.cpp" />
<ClCompile Include="hw.cpp" />
Expand All @@ -237,6 +238,7 @@
<ClCompile Include="savedata.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ac3encoder.h" />
<ClInclude Include="common.h" />
<ClInclude Include="hw.h" />
<ClInclude Include="ivshmemsavedata.h" />
Expand All @@ -246,6 +248,7 @@
<ClInclude Include="minwave.h" />
<ClInclude Include="netiodef.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="rtpheader.h" />
<ClInclude Include="savedata.h" />
<ClInclude Include="scream.h" />
<ClInclude Include="toptable.h" />
Expand Down
9 changes: 9 additions & 0 deletions Scream/Scream.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@
<ClCompile Include="ivshmemsavedata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ac3encoder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h">
Expand Down Expand Up @@ -92,6 +95,12 @@
<ClInclude Include="ivshmemsavedata.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ac3encoder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="rtpheader.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="scream.rc">
Expand Down
211 changes: 211 additions & 0 deletions Scream/ac3encoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#include "ac3encoder.h"

#include <mfapi.h>
#include <mferror.h>

template<class T>
void SafeRelease(T **ppT)
{
if (*ppT) {
(*ppT)->Release();
*ppT = NULL;
}
}

Ac3Encoder::Ac3Encoder(UINT32 sampleRate,
UINT32 numChannels) :
m_sampleRate(sampleRate),
m_numChannels(numChannels)
{
Initialize(m_sampleRate, m_numChannels);
}

Ac3Encoder::~Ac3Encoder()
{
SafeRelease(&m_transform);
SafeRelease(&m_inputType);
SafeRelease(&m_outputType);
}

HRESULT Ac3Encoder::Initialize(UINT32 sampleRate, UINT32 numChannels)
{
m_sampleRate = sampleRate;
m_numChannels = numChannels;

SafeRelease(&m_transform);
SafeRelease(&m_inputType);
SafeRelease(&m_outputType);

// Look for a encoder.
MFT_REGISTER_TYPE_INFO outInfo;
outInfo.guidMajorType = MFMediaType_Audio;
outInfo.guidSubtype = MFAudioFormat_Dolby_AC3;
CLSID *clsids = nullptr; // Pointer to an array of CLISDs.
UINT32 clsidsSize = 0; // Size of the array.
auto hr = MFTEnum(MFT_CATEGORY_AUDIO_ENCODER,
0, // Flags
NULL, // Input type to match.
&outInfo, // Output type to match.
NULL, // Attributes to match. (None.)
&clsids, // Receives a pointer to an array of CLSIDs.
&clsidsSize // Receives the size of the array.
);

if (SUCCEEDED(hr) && clsidsSize == 0) {
hr = MF_E_TOPO_CODEC_NOT_FOUND;
}

// Create the MFT decoder
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(clsids[0], NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_transform));
}

// Create and set output type
MFCreateMediaType(&m_outputType);
m_outputType->SetGUID(MF_MT_MAJOR_TYPE, outInfo.guidMajorType);
m_outputType->SetGUID(MF_MT_SUBTYPE, outInfo.guidSubtype);
m_outputType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate);
m_outputType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, numChannels);
m_outputType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, m_bitrateKbps*1000/8);
hr = m_transform->SetOutputType(0, m_outputType , 0);

MFCreateMediaType(&m_inputType);
m_inputType->SetGUID(MF_MT_MAJOR_TYPE, outInfo.guidMajorType);
m_inputType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
m_inputType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
m_inputType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate);
m_inputType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, numChannels);
m_inputType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4);
m_inputType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, sampleRate*4);
hr = m_transform->SetInputType(0, m_inputType , 0);

return hr;
}

void Ac3Encoder::SetBitrate(UINT32 kbps)
{
if (m_bitrateKbps == kbps) {
return;
}

m_bitrateKbps = kbps;
Initialize(m_sampleRate, m_numChannels);
}

IMFSample* Ac3Encoder::Process(void *inBuffer, UINT32 inSize)
{
// Feed inpud data
ProcessInput(inBuffer, inSize);

// Check if we have a complete AC3 frame ready for output.
IMFSample *outSample = ProcessOutput();
// Once we get an outSample, we have to process output again to put the
// transform back in accepting state.
if (outSample) {
while (auto sample = ProcessOutput()) {
ReleaseSample(sample);
}
}

return outSample;
}

void Ac3Encoder::ProcessInput(void *inSamples, UINT32 inSize)
{
// Create buffer
IMFMediaBuffer *pBuffer = NULL;
auto hr = MFCreateMemoryBuffer(inSize, &pBuffer);

// Copy sample data to buffer
BYTE *pMem = NULL;
if (SUCCEEDED(hr)) {
hr = pBuffer->Lock(&pMem, NULL, NULL);
}

if (SUCCEEDED(hr)) {
memcpy(pMem, inSamples, inSize);
}
pBuffer->Unlock();

// Set the data length of the buffer.
if (SUCCEEDED(hr)) {
hr = pBuffer->SetCurrentLength(inSize);
}

// Create media sample and add the buffer to the sample.
IMFSample *pSample = NULL;
if (SUCCEEDED(hr)) {
hr = MFCreateSample(&pSample);
}
if (SUCCEEDED(hr)) {
hr = pSample->AddBuffer(pBuffer);
}

hr = m_transform->ProcessInput(0, pSample, 0);

SafeRelease(&pSample);
SafeRelease(&pBuffer);
}

void Ac3Encoder::ReleaseSample(IMFSample *sample)
{
SafeRelease(&sample);
}

void Ac3Encoder::Drain()
{
m_transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
}

IMFSample* Ac3Encoder::ProcessOutput()
{
IMFSample *ppSample = nullptr;

// Get output info
MFT_OUTPUT_STREAM_INFO mftStreamInfo = { 0 };
HRESULT hr = m_transform->GetOutputStreamInfo(0, &mftStreamInfo);
if (FAILED(hr)) {
goto done;
}

// Create the output sample
IMFSample* pSampleOut = NULL;
hr = MFCreateSample(&pSampleOut);
if (FAILED(hr)) {
goto done;
}
// Create buffer and add to output sample
IMFMediaBuffer* pBufferOut = NULL;
hr = MFCreateMemoryBuffer(mftStreamInfo.cbSize, &pBufferOut);
if (FAILED(hr)) {
goto done;
}
hr = pSampleOut->AddBuffer(pBufferOut);
if (FAILED(hr)) {
goto done;
}

// Set the output sample
MFT_OUTPUT_DATA_BUFFER mftOutputData = { 0 };
mftOutputData.pSample = pSampleOut;
mftOutputData.dwStreamID = 0;

//Generate the output sample
DWORD dwStatus;
hr = m_transform->ProcessOutput(0, 1, &mftOutputData, &dwStatus);
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
goto done;
}
if (FAILED(hr)) {
goto done;
}

ppSample = pSampleOut;
ppSample->AddRef();

done:
SafeRelease(&pBufferOut);
SafeRelease(&pSampleOut);

return ppSample;
}
33 changes: 33 additions & 0 deletions Scream/ac3encoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef AC3ENCODER_H
#define AC3ENCODER_H

#include <mftransform.h>

class Ac3Encoder
{
public:
Ac3Encoder(UINT32 sampleRate = 48000,
UINT32 numChannels = 2);
~Ac3Encoder();

HRESULT Initialize(UINT32 sampleRate, UINT32 numChannels);
void SetBitrate(UINT32 kbps);

IMFSample* Process(void *inSamples, UINT32 inSize);
void ReleaseSample(IMFSample *sample);

void Drain();

private:
void ProcessInput(void *sampleData, UINT32 size);
IMFSample* ProcessOutput();

UINT32 m_sampleRate = 48000;
UINT32 m_numChannels = 2;
UINT32 m_bitrateKbps = 256;
IMFTransform *m_transform = nullptr; // Pointer to the encoder MFT.
IMFMediaType *m_inputType = nullptr; // Input media type of the encoder.
IMFMediaType *m_outputType = nullptr; // Output media type of the encoder.
};

#endif // AC3ENCODER_H
4 changes: 4 additions & 0 deletions Scream/adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ PCHAR g_UnicastIPv4;
DWORD g_UnicastPort;
//0 = false, otherwhise it's value is the size in MiB of the IVSHMEM we want to use
UINT8 g_UseIVSHMEM;
UINT8 g_UseRtpAc3;

//-----------------------------------------------------------------------------
// Referenced forward.
Expand Down Expand Up @@ -78,13 +79,15 @@ Routine Description:
UNICODE_STRING unicastIPv4;
DWORD unicastPort = 0;
DWORD useIVSHMEM = 0;
DWORD useRtpAc3 = 0;

RtlZeroMemory(&unicastIPv4, sizeof(UNICODE_STRING));

RTL_QUERY_REGISTRY_TABLE paramTable[] = {
{ NULL, RTL_QUERY_REGISTRY_DIRECT, L"UnicastIPv4", &unicastIPv4, REG_NONE, NULL, 0 },
{ NULL, RTL_QUERY_REGISTRY_DIRECT, L"UnicastPort", &unicastPort, REG_NONE, NULL, 0 },
{ NULL, RTL_QUERY_REGISTRY_DIRECT, L"UseIVSHMEM", &useIVSHMEM, REG_NONE, NULL, 0 },
{ NULL, RTL_QUERY_REGISTRY_DIRECT, L"UseRtpAc3", &useRtpAc3, REG_NONE, NULL, 0 },
{ NULL, 0, NULL, NULL, 0, NULL, 0 }
};

Expand Down Expand Up @@ -164,6 +167,7 @@ Routine Description:
}

g_UseIVSHMEM = (UINT8)useIVSHMEM;
g_UseRtpAc3 = (UINT8)useRtpAc3;

ExFreePool(parametersPath.Buffer);

Expand Down
25 changes: 25 additions & 0 deletions Scream/rtpheader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef RTPHEADER_H
#define RTPHEADER_H

#pragma pack(push, 1)
struct RtpHeader
{
unsigned char cc : 4; /* CSRC count (always 0) */
unsigned char x : 1; /* header extension flag (always 0) */
unsigned char p : 1; /* padding flag (always 0) */
unsigned char v : 2; /* protocol version (always 2) */

unsigned char pt : 7; /* payload type (always 10, meaning L16 linear PCM, 2 channels) */
unsigned char m : 1; /* marker bit (always 0) */

unsigned short seq : 16; /* sequence number (monotonically incrementing, will just wrap) */

// Bytes 4-7
unsigned int ts : 32; /* timestamp of 1st sample in packet, increase by 1 for each 4 bytes sent */

// Bytes 8-11
unsigned int ssrc : 32; /* synchronization source (always 0) */
};
#pragma pack(pop)

#endif // RTPHEADER_H

0 comments on commit 72c1f31

Please sign in to comment.