I've attended recently C++Now 2021, which has been held online for the last two years. Some of the sponsors of this event used a channel in the Discord server to post different job offers and one of them, Unified Streaming, suggested to write the program below before applying for a position. Although I'm not looking for a job at the moment, I thought it would be a good way of doing some programming using modern C++.
Could you write a program that takes a TS file and writes the audio and video as two separate files to disk (raw AAC and raw AVC1)?
The code should use:
- standard C++17.
- using only .hpp/.cpp and a Makefile.
- no platform specifics (like MFC/COM/ATL).
- the raw streams should be viewable/playable with for instance FFplay, as verification/test.
- example input: http://test.unified-streaming.com/elephants.ts
- We also would like to mention to not use ffmpeg/avconv or something other from say Github: we would like to see your work.
- We would like to suggest to do this as a Github project and write up your design/docs in the README.md Github provides.
- Command line parsing errors print the usage in standard output and exit.
- Runtime errors such as parsing errors print the error message in standard output and exit.
mainparses the command line, creates aFileReaderto read the TS file, and proceeds to read it.FileReaderopens the TS file and, for each TS packet:- reads it into a
PacketBuffer, - asks
PacketParserto parse it, - asks
PacketProcessorto do some processing on the parsed packet, and, finally, - performs some post processing: e.g. collect stats or write some streams out to file.
- reads it into a
PacketParserparses the TS packet header and, if present, the adaptation field and payload data.
In order to do that, it reads byte chunks fromPacketBuffer.
It also does some error checking: stuffing bytes, reserved and unused bits, CRC32...PacketBufferlets byte chunks to be read in big-endian, which is needed for all the TS headers.PacketProcessorbasically:- builds the PSI tables (PAT and PMT) for packets containing PSI information, and
- saves the PES data for packets containing PES payloads.
Packet,Header,AdaptationField,AdaptationExtensionand so on are structs.- Field masks and byte chunks are
boost::dynamic_bitsets. This way we can easily perform binary operations.
- CMake 3.20.4.
- Boost libraries 1.75.0.
- Boost program_options static multithreaded library (libboost_program_options-vcXXX-mt-[gd-][x32|x64]-1_75.lib)
- Open
ts_reader.slnwith Visual Studio. - Right click on the
ts_readerproject, and go toProperties.- Update the Boost root path in
Configuration Properties > C/C++ > General > Additional Include Directoriesto point to your Boost root path. - Update the Boost library folder in
Configuration Properties > Linker > General > Additional Library Directories. - Save these changes and exit the project properties.
- Update the Boost root path in
- Right click on the
ts_readersolution, andBuild Solution.
- Open
CMakeLists.txtand editBOOST_ROOTandBOOST_LIBRARY_DIRvariables to point to your Boost root and Boost library folders, respectively. - From a shell, go to the project folder, and execute:
~/projects/ts_reader> cmake -S . -B build
~/projects/ts_reader> cmake --build build
ts_reader <TS FILE PATH> [-e|--extract <STREAM TYPE LIST>] [-s|--stats], where:
<TS FILE PATH>is the path to the TS file. It can be absolute or relative to thets_reader.exelocation, and<STREAM TYPE LIST>is a comma separated list of stream types.
It supports hexadecimal, decimal or octal notation (e.g. 0xf, 15 or 017 for audio).
As an example, you can try with the provided sample:
~/projects/ts_reader/build> ./ts_reader ../samples/elephants.ts -e 0xf,0x1b
~/projects/ts_reader/build> ./ts_reader ../samples/elephants.ts --stats