Skip to content

Commit

Permalink
Fix HD-PVR Rocket support
Browse files Browse the repository at this point in the history
Figured out how to get the rocket working properly, finally.  It's
somewhat of a pain.

Basically, the rocket defaults to this weird 'standby' mode.  While in
this mode, the rocket will not stream any data, regardless of filter
connections.  I figured out how to go in to 'capture' mode via
monitoring the DirectShow API when it's being used by other
applications.  I discovered one key call that enabled it:
calling IPropertyKeySet::Set on the encoder with some specific
parameters.  I managed to duplicate the functionality myself by calling
the same function with the same parameters, and presto, the device can
now be activated/deactivated.
  • Loading branch information
jp9000 committed Sep 23, 2014
1 parent ffd5d44 commit 6b7e8e0
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 1 deletion.
2 changes: 1 addition & 1 deletion dshowcapture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

#define DSHOWCAPTURE_VERSION_MAJOR 0
#define DSHOWCAPTURE_VERSION_MINOR 3
#define DSHOWCAPTURE_VERSION_PATCH 2
#define DSHOWCAPTURE_VERSION_PATCH 3

#define MAKE_DSHOWCAPTURE_VERSION(major, minor, patch) \
( (major << 24) | \
Expand Down
40 changes: 40 additions & 0 deletions source/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

namespace DShow {

bool SetRocketEnabled(IBaseFilter *encoder, bool enable);

HDevice::HDevice()
: initialized (false),
active (false)
Expand All @@ -36,6 +38,21 @@ HDevice::~HDevice()
{
if (active)
Stop();

DisconnectFilters();

/*
* the sleeps for the rocket are required. It seems that you cannot
* simply start/stop the stream right away after/before you enable or
* disable the rocket. If you start it too fast after enabling, it
* won't return any data. If you try to turn off the rocket too
* quickly after stopping, then it'll be perpetually stuck on, and then
* you'll have to unplug/replug the device to get it working again.
*/
if (!!rocketEncoder) {
Sleep(3000);
SetRocketEnabled(rocketEncoder, false);
}
}

bool HDevice::EnsureInitialized(const wchar_t *func)
Expand Down Expand Up @@ -643,6 +660,26 @@ bool HDevice::ConnectFilters()
return success;
}

void HDevice::DisconnectFilters()
{
CComPtr<IEnumFilters> filterEnum;
HRESULT hr;

if (!graph)
return;

hr = graph->EnumFilters(&filterEnum);
if (FAILED(hr))
return;

CComPtr<IBaseFilter> filter;
while (filterEnum->Next(1, &filter, nullptr) == S_OK) {
graph->RemoveFilter(filter);
filterEnum->Reset();
filter.Release();
}
}

Result HDevice::Start()
{
HRESULT hr;
Expand All @@ -651,6 +688,9 @@ Result HDevice::Start()
!EnsureInactive(L"Start"))
return Result::Error;

if (!!rocketEncoder)
Sleep(3000);

hr = control->Run();

if (FAILED(hr)) {
Expand Down
2 changes: 2 additions & 0 deletions source/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct HDevice {
CComPtr<IBaseFilter> audioFilter;
CComPtr<CaptureFilter> videoCapture;
CComPtr<CaptureFilter> audioCapture;
CComPtr<IBaseFilter> rocketEncoder;
MediaType videoMediaType;
MediaType audioMediaType;
VideoConfig videoConfig;
Expand Down Expand Up @@ -105,6 +106,7 @@ struct HDevice {
bool RenderFilters(const GUID &category, const GUID &type,
IBaseFilter *filter, CaptureFilter *capture);
bool ConnectFilters();
void DisconnectFilters();
Result Start();
void Stop();
};
Expand Down
56 changes: 56 additions & 0 deletions source/dshow-encoded-device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,55 @@ static inline bool MapPacketIDs(IBaseFilter *demuxer, ULONG video, ULONG audio)
return true;
}

/*
* rocket-specific workaround code. I have no idea what any of these numbers
* are except the GUID which was obvious. All I know is calling
* IKsPropertySet::Set turns on/off some sort of 'mode' on the device.
* I discovered this merely by chance while monitoring API usage in other
* programs because I could not figure out how the hell to get this thing
* to turn on.
*/
static const GUID RocketEncoderGUID =
{0x99100000, 0xa330, 0x11e1, {0xa3, 0x80, 0x99, 0x10, 0x68, 0x64, 0x00, 0x00}};

struct RocketPropStruct {
DWORD dwSize;
DWORD unknown1;
DWORD unknown2;
DWORD unknown3;
DWORD code;
DWORD unknown4;
BOOL enabled;
};

struct RocketInstStruct {
DWORD code;
DWORD unknown1;
};

bool SetRocketEnabled(IBaseFilter *encoder, bool enable)
{
static const ULONG rocketEnableId = 0x9910E001;
static const DWORD rocketEnableCode = 0x38384001;
RocketInstStruct rocketInstance = {};
RocketPropStruct rocketProperty = {};

CComQIPtr<IKsPropertySet> propertySet(encoder);
if (!propertySet)
return false;

rocketProperty.dwSize = sizeof(rocketProperty);
rocketProperty.code = rocketEnableCode;
rocketProperty.enabled = enable;
rocketInstance.code = rocketEnableCode;

HRESULT hr = propertySet->Set(RocketEncoderGUID, rocketEnableId,
&rocketInstance, sizeof(rocketInstance),
&rocketProperty, sizeof(rocketProperty));

return SUCCEEDED(hr);
}

bool HDevice::SetupEncodedVideoCapture(IBaseFilter *filter,
VideoConfig &config,
const EncodedDevice &info)
Expand Down Expand Up @@ -176,6 +225,13 @@ bool HDevice::SetupEncodedVideoCapture(IBaseFilter *filter,
videoCapture = new CaptureFilter(pci);
videoFilter = demuxer;

if (!!encoder && config.name.find(L"IT9910") != std::string::npos) {
rocketEncoder = encoder;

if (!SetRocketEnabled(rocketEncoder, true))
return false;
}

graph->AddFilter(crossbar, L"Crossbar");
graph->AddFilter(filter, L"Device");
graph->AddFilter(demuxer, L"Demuxer");
Expand Down

0 comments on commit 6b7e8e0

Please sign in to comment.