//----------------------------------------------------------------------------
//
// TSDuck - The MPEG Transport Stream Toolkit
// Copyright (c) 2005-2025, Thierry Lelegard
// BSD-2-Clause license, see LICENSE.txt file or https://tsduck.io/license
//
//----------------------------------------------------------------------------

[#hwsupport]
=== Hardware support

This section describes some specificities of the various types of hardware devices
which are supported by TSDuck and their impact on the code.

The TSDuck User's Guide has a complete chapter on each of these classes of devices.
It describes the devices, their naming, usage, supported platforms, required drivers, etc.
In the Developer's Guide, we focus on the structure of the code, for TSDuck maintainers.

==== Standard tuner receiver devices (DVB, ATSC, ISDB)

The code for tuners is located in `src/libtsduck/dtv/broadcast`.

The base class for all tuners is `TunerBase`.
It exposes the common interface of all tuners.
It is normally never directly used by an application.

A tuner is accessed by an application using the subclass `Tuner`.

When opening a `Tuner` instance, depending on the provided name, the tuner is either
a physical device or a software emulator.
The `Tuner` instance creates either an internal `TunerDevice` object or an internal
`TunerEmulator` object. These two classes are also subclasses of `TunerBase`.
Once a `Tuner` is opened, all services are redirected to the internal `TunerDevice`
or `TunerEmulator` object.

The subclass `TunerEmulator` is a pure software implementation which is available
on all systems. The name of such a device in a XML file which describes all the
capabilities of the "tuner" and the available frequencies and corresponding
modulation parameters. The TSDuck User's Guide describes the tuner emulator in details.

The `TunerDevice` class is highly dependent on the operating system.
The source files are different and placed in system-specific directories.
The subdirectories `linux` and `windows` contain actual implementations
for these operating systems. The subdirectories `mac` and `bsd` contain
stub versions which report errors when used.

[.usage]
Linux

The Linux implementation is relatively straightforward using the Linux TV API.
This API evolves on a regular basis. New modulations are added.

It is sometimes useful to review the evolutions of the Linux TV API to detect
new modulations and new parameters. In the `broadcast/linux` subdirectory, there is a
Bash script named `get-linux-tv-api-history.sh` which extracts from the Linux
kernel repository a snapshot of each new version of the Linux TV, with a
`diff` file of each version.

[.usage]
Windows

The Windows implementation is more problematic. The tuner devices are managed by DirectShow,
a Microsoft framework for multimedia. The specific subsystem of DirectShow for TV receiver
devices is BDA (Broadcast Device Architecture). Most of the time, the hardware vendors
provide BDA drivers for their receivers. Windows does not include any predefined BDA driver.

The DirectShow framework is complex. It is based on the concept of "graphs of filters".
Each DirectShow filter is a COM object which exposes standard interfaces. Media processing
starts with building a graph with filters, and connecting these filters together using
the standard interfaces they expose.

Unlike Linux, there is no standard interface to the various BDA drivers. Each vendor
defines its own interface. A vendor also provides its own DirectShow filters, typically
a "Tuner Filter" and an optional "Receiver / Capture Filter". The proprietary tuner
filter communicates with the proprietary BDA driver. An application can only use the
standard interface of the DirectShow filters.

However, a filter cannot be used alone. It must be part of a DirectShow graph. This graph
must be complete and consistent, with no dangling interface. This is the condition to
"start the graph". Starting the graph is required to read incoming packets from the
tuner.

To make things worse, there is no single way to build a tuner reception graph. It depends
on the proprietary filters. Do they include a receiver / capture filter for instance?
Some combinations of filters work, some others don't. Depending on the tuner device,
various types of graphs shall be used, or different filters of the same types.

When building a graph, the application "connects" filters together, by pairs.
A filter can accept or reject the connection. Therefore, in the absence of deterministic
method, TSDuck uses a flexible approach. Several "standard" types of graphs are tested.
At each node of the graph, we search all available filters of the required type for
that node. Normally, only one combination of filters can be built, with all connections
being accepted by all filters. This is the method which is used by TSDuck, testing all
possible graphs for the tuner until one is accepted.

This looks complicated (and it is) but the standard use case for a tuner on Windows
is simpler. When you purchase a tuner, it comes with all software: BDA driver,
DirectShow filters, and TV watching application. Since the application is provided
by the tuner vendor, for that tuner, there is only one way to build a graph for that
tuner and it is well known by the tuner vendor. With TSDuck, we have a less common
use case. The application is the same for all tuners and expects to uniformly read the
transport stream from all of them. Simple on Linux, awfully complex on Windows.

Another annoying property of DirectShow is the absence of data output filter.
A DirectShow graph is designed to process media. Typically, it reads media from
somewhere (a tuner in our case), processes them, filters them, and outputs the
result on screen. In short, DirectShow is designed to watch TV...
There is no way to do such a simple thing as reading TS packets from the tuner
device. Therefore, we were obliged to develop our own DirectShow filter. This
filter is inserted in the graph and collects the TS packets using the DirectShow
standard interfaces. This custom DirectShow filter is implemented in the class
`SinkFilter`, with its source code in the `broadcast/windows` subdirectory.

Because everything is annoying in DirectShow, it uses a "push" model. This means
that, once the graph is started, packets are pushed from the tuner all along the
graph. The applications which needs packets, on the contrary, have a "pull" model.
When packets are needed, it "reads" packets, like reading packets on the network,
reading data from a pipe, etc. Therefore, the passing mechanism is asynchronous.
When incoming TS packets are pushed up to our `SinkFilter`, the filter passes them
through a message queue. The application, on the other hand, consumes packets in
pull mode from the message queue. This is performed in the Windows version of the
`TunerDevice` class.

Because everything is _really_ annoying in DirectShow, there are other constraints.
For instance, a tuner reception graph cannot work without a demux filter. In our
case, we don't demux anything because TSDuck wants the complete transport stream.
Anyway, DirectShow needs it or you won't get any packet. Additionally, even though
it would be logical to insert the demux in the middle of the data flow, DirectShow
wants it in a dead-end branch of the graph. Don't try to understand... To paraphrase
a well-known cryptographer, it seems that DirectShow has been "designed by the
E-commerce Department of the Ministry of Silly Walks".

The following diagram represents a typical DirectShow reception graph for a tuner.
It helps understanding the code. Given the complexity of this code, it is not
recommended to modify it, unless completely and absolutely necessary.

.Usage of Windows DirectShow interfaces in TSDuck
image::tsduck-directshow.png[align="center",alt="Usage of Windows DirectShow",width=500]

As a Grande Finale, let's note that DirectShow received no update, no improvement,
no new features, for years. It looks like abandonware, without any new alternative.
Recent modulations are not supported on Windows and will probably   never be.

In short, if you want to receive broadcast transport streams from a tuner, use Linux...

[.usage]
ISDB-T 204-byte packets

The ISDB-T and ISDB-Tb standards have a specific form of 204-byte packets.
In other modulations, the standard 188-byte TS packet is followed by a 16-byte Reed-Solomon
outer forward error correction code. These 16 bytes are not interesting and dropped by the
demodulator. In ISDB-T and ISDB-Tb, the 16-byte trailer is split in two equal parts. The
Reed-Solomon FEC is reduced to 8 bytes, at the end of the trailer. The first 8 bytes of the
trailer contain ISDB information such as modulation layer, frame counters, system identification.
This information can be analyzed by TSDuck, when available. However, neither the Linux TV
nor the Windows DirectShow API's are able to return 204-byte packets. Therefore, it is
not possible to analyze ISDB-T information from the `dvb` plugin.

==== Dektec devices

Using Dektec devices requires kernel-mode device drivers which must be separately installed.
Building TSDuck code does not depend on the device drivers. They are only required to use Dektec devices.
The TSDuck User's Guide describes how to install the Dektec drivers.

Most of the TSDuck code for Dektec support is located in `src/libtsduck/dtv/dektec`.
The `dektec` plugin is implemented in classes `DektecInputPlugin` and `DektecOutputPlugin`
and is completely embedded inside the TSDuck library.
The main code of the `tsdektec` utility is in `src/tstools` for technical reasons
but this is a simple wrapper around the class `DektecControl` which is inside the TSDuck library as well.

TSDuck does not call the device drivers directly. Dektec provides a user-mode API for {cpp} called "DTAPI".
The DTAPI is well documented and well designed. Its interface is the same over all operating systems.
The DTAPI is closed-source and is available in binary format only, for Windows and Linux on Intel
architectures.

On Windows, the DTAPI shall be installed first. This is automated in the PowerShell script
`install-dektec.ps1`, which is also automatically invoked by `install-prerequisites.ps1`.
The "Dektec Windows SDK" installs the device drivers and the DTAPI system-wise.

On Linux, the DTAPI is not "installed". It is automatically downloaded from https://dektec.com
during the build process of the TSDuck library. The DTAPI is locally installed in the `bin`
build area of the TSDuck project. It is therefore deleted as part of `make clean`.

The DTAPI binary code is delivered as a static library on Windows and an object file on Linux.
Several versions are installed, depending on release vs. debug mode and, sometimes, the
version of the compilers. The TSDuck build process automatically selects the appropriate version.

A TSDuck application process typically contains an executable, several plugin shared libraries,
the TSCore and TSDuck shared libraries. The DTAPI object code has to be somewhere, typically
in a shared library. This is the way to ensure that only one copy of the DTAPI binary is
present in each application process.

On Windows, because of some silly import/export rules, the code in a DLL can be called
from outside that DLL only if it was compiled with the right export attributes
(see xref:windllcrap[xrefstyle=short] for more details).
Because Dektec did not compile the DTAPI with there export attributes, the DTAPI can be called
only from the same shared library it is in. This is the reason why the DTAPI and all {cpp}
classes which directly reference it are in the same TSDuck library. Tools such as `tsdektec`
never reference the DTAPI, they reference TSDuck classes. The DTAPI remains hidden inside the
TSDuck library. For consistency, the same code structure is used on Linux, even though there
is no technical reason which prevents the DTAPI from being called outside the `libtsduck`
shared library.

==== HiDes devices

Using HiDes devices requires kernel-mode device drivers which must be separately installed.
The drivers are provided by ITE, the vendor of the main chip in HiDes modulators.
Building TSDuck code does not depend on the device drivers. They are only required to use HiDes devices.
The TSDuck User's Guide describes how to install the HiDes drivers.

The ITE drivers for Linux and Windows are very different, there is no common structure,
no common API or structure. There is also no stable common userland API above the drivers.
TSDuck directly interfaces the ITE drivers, with different implementations on Linux
and Windows. TSDuck recreates a stable intermediate layer which provides a system-independent
API to access HiDes devices. This code is located in `src/libtsduck/dtv/hides`.
The {cpp} class `HiDesDevice` provides a uniform access to HiDes devices. The subdirectories
`linux`, `windows`, `mac`, `bsd` contain the system-specific variants of this class.
Note that the macOS and BSD implementations simply return errors and error messages.

The code for the `hides` output plugin and the `tshides` command is located in the standard
locations `src/tsplugins` and `src/tstools` respectively. To access the HiDes devices, they
call the classes `HiDesDevice` and `HiDesDeviceInfo` which are part of the TSDuck library.

==== VATek-based modulators

VATek-based USB devices do not need a dedicated device driver. They are accessed through the
portable `libusb` library which is available on all operating systems.
At application level, VATek devices are accessed through an open-source user-mode library
which is provided by Vision Advance Technology Inc. (VATek).

Most of the TSDuck code for VATek support is located in `src/libtsduck/dtv/vatek`.
The `vatek` plugin is implemented in the class `VatekOutputPlugin` and is completely embedded
inside the TSDuck library. The main code of the `tsvatek` utility is in `src/tstools` but this is
a simple wrapper around the class `VatekControl` which is inside the TSDuck library as well.

All code in `src/libtsduck/dtv/vatek` is a free contrbution from Vision Advance Technology Inc. (VATek).
In case of problem, before trying to modify the code, it is recommended to contact the author,
Richie Chang from VATek, `a83912a` on GitHub.
