Add 'projects/rocdecode/' from commit 'b0bab079403eda171f9056409fa96b0908f61073'

git-subtree-dir: projects/rocdecode
git-subtree-mainline: 5d609c1e57
git-subtree-split: b0bab07940
This commit is contained in:
Ameya Keshava Mallya
2026-01-30 20:33:26 +00:00
208 changed files with 43533 additions and 0 deletions
@@ -0,0 +1,76 @@
.. meta::
:description: Using the rocDecode bitstream reader API
:keywords: rocDecode, AMD, ROCm, bitstream decoder
********************************************************************
Using the rocDecode bitstream reader APIs
********************************************************************
The rocDecode bitstream reader APIs are a simplified set of APIs that provide a way to use and test the decoder without relying on FFMpeg. The bitstream reader APIs can be used to extract and parse coded picture data from an elementary video stream for the decoder to consume.
.. note::
The bitstream reader APIs can only be used with elementary video streams and IVF container files.
The |videodecoderaw|_ sample demonstrates how to use the bitstream reader APIs, including how to create a bitstream reader and use it to extract picture data and pass it to the decoder:
.. code:: C++
RocdecBitstreamReader bs_reader = nullptr;
rocDecVideoCodec rocdec_codec_id;
int bit_depth;
if (rocDecCreateBitstreamReader(&bs_reader, input_file_path.c_str()) != ROCDEC_SUCCESS) {
std::cerr << "Failed to create the bitstream reader." << std::endl;
return 1;
}
[...]
# Decode loop:
do {
if (rocDecGetBitstreamPicData(bs_reader, &pvideo, &n_video_bytes, &pts) != ROCDEC_SUCCESS) {
std::cerr << "Failed to get picture data." << std::endl;
return 1;
}
[...]
n_frame_returned = viddec.DecodeFrame(pvideo, n_video_bytes, pkg_flags, pts, &decoded_pics);
}
The ``videodecoderaw.cpp`` example also demonstrates how to use the bitstream reader APIs to obtain the bit depth and codec of a stream:
.. code:: C++
if (rocDecGetBitstreamCodecType(bs_reader, &rocdec_codec_id) != ROCDEC_SUCCESS) {
std::cerr << "Failed to get stream codec type." << std::endl;
return 1;
}
[...]
if (rocDecGetBitstreamBitDepth(bs_reader, &bit_depth) != ROCDEC_SUCCESS) {
std::cerr << "Failed to get stream bit depth." << std::endl;
return 1;
}
.. note::
``rocDecDestroyBitstreamReader`` must always be called to destroy the bitstream reader once processing is complete.
.. |videodecode| replace:: ``videodecode.cpp``
.. _videodecode: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecode/videodecode.cpp
.. |videodecoderaw| replace:: ``videodecoderaw.cpp``
.. _videodecoderaw: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecodeRaw
.. |common| replace:: ``common.h``
.. _common: https://github.com/ROCm/rocDecode/blob/develop/samples/common.h
.. |apifolder| replace:: ``api`` folder
.. _apifolder: https://github.com/ROCm/rocDecode/tree/develop/api
.. |utilsfolder| replace:: ``utils`` folder
.. _utilsfolder: https://github.com/ROCm/rocDecode/tree/develop/utils
.. |reconfig_struct| replace:: ``ReconfigParams_t``
.. _reconfig_struct: https://rocm.docs.amd.com/projects/rocDecode/en/latest/doxygen/html/structReconfigParams__t.html
@@ -0,0 +1,96 @@
.. meta::
:description: Using rocDecode with the FFMpeg demultiplexer
:keywords: parse video, parse, rocDecode, AMD, ROCm, FFmpeg demuxer
********************************************************************
Using the rocDecode FFmpeg demultiplexer
********************************************************************
The rocDecode FFmpeg demultiplexer (demuxer) extracts coded picture data from digital media files.
To use the rocDecode FFmpeg demuxer , import the ``video_demuxer.h`` header file.
.. code:: C++
#include "video_demuxer.h"
Instantiate a ``VideoDemuxer`` with the path to the video file. The ``GetCodecId`` and ``GetBitDepth`` functions can be used to obtain the video stream's codec ID and bit depth. The ``AVCodec2RocDecVideoCodec`` utility function converts the codec ID returned from the demuxer to its corresponding ``rocDecVideoCodec_enum`` value.
.. code:: C++
VideoDemuxer *demuxer;
demuxer = new VideoDemuxer(input_file_path.c_str());
rocdec_codec_id = AVCodec2RocDecVideoCodec(demuxer->GetCodecID());
bit_depth = demuxer->GetBitDepth();
Call ``Demux`` to extract frame data from the stream:
.. code:: C++
demuxer->Demux(&pvideo, &n_video_bytes, &pts);
The demuxer will demultiplex frames sequentially starting at the beginning of the stream. To start the demultiplexing and decoding process from a different frame, create a seek context that specifies a seek criteria and a seek mode.
The seek criteria describes whether the demuxer needs to seek to a specific frame or seek to a specific timestamp. The seek mode indicates whether the demuxer should seek to the exact frame or to the previous keyframe.
The seek criteria is defined by the ``SeekCriteriaEnum`` enum and the seek mode is defined by the ``SeekModeEnum`` enum. Both the ``SeekCriteriaEnum`` and the ``SeekModeEnum`` are defined in ``video_demuxer.h``.
Set the seek criteria to ``SEEK_CRITERIA_FRAME_NUM`` to seek to a frame or to ``SEEK_CRITERIA_TIME_STAMP`` to seek to a timestamp. Set the seek mode to ``SEEK_MODE_EXACT_FRAME`` to seek to the exact frame or to ``SEEK_MODE_PREV_KEY_FRAME`` to seek to the previous keyframe.
From |videodecode|_:
.. code:: C++
VideoSeekContext video_seek_ctx;
[...]
do {
[...]
if (seek_criteria == 1 && first_frame) {
// use VideoSeekContext class to seek to given frame number
video_seek_ctx.seek_frame_ = seek_to_frame;
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_FRAME_NUM;
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
demuxer->Seek(video_seek_ctx, &pvideo, &n_video_bytes);
pts = video_seek_ctx.out_frame_pts_;
std::cout << "info: Number of frames that were decoded during seek - " << video_seek_ctx.num_frames_decoded_ << std::endl;
first_frame = false;
} else if (seek_criteria == 2 && first_frame) {
// use VideoSeekContext class to seek to given timestamp
video_seek_ctx.seek_frame_ = seek_to_frame;
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_TIME_STAMP;
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
demuxer->Seek(video_seek_ctx, &pvideo, &n_video_bytes);
pts = video_seek_ctx.out_frame_pts_;
std::cout << "info: Duration of frame found after seek - " << video_seek_ctx.out_frame_duration_ << " ms" << std::endl;
first_frame = false;
} else {
demuxer->Demux(&pvideo, &n_video_bytes, &pts);
}
[...]
} while (n_video_bytes);
Delete the demuxer once demultiplexing is complete.
.. code:: C++
delete demuxer;
.. |videodecode| replace:: ``videodecode.cpp``
.. _videodecode: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecode/videodecode.cpp
.. |videodecoderaw| replace:: ``videodecoderaw.cpp``
.. _videodecoderaw: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecodeRaw
.. |common| replace:: ``common.h``
.. _common: https://github.com/ROCm/rocDecode/blob/develop/samples/common.h
.. |apifolder| replace:: ``api`` folder
.. _apifolder: https://github.com/ROCm/rocDecode/tree/develop/api
.. |utilsfolder| replace:: ``utils`` folder
.. _utilsfolder: https://github.com/ROCm/rocDecode/tree/develop/utils
.. |reconfig_struct| replace:: ``ReconfigParams_t``
.. _reconfig_struct: https://rocm.docs.amd.com/projects/rocDecode/en/latest/doxygen/html/structReconfigParams__t.html
@@ -0,0 +1,245 @@
.. meta::
:description: Using the rocDecode core API
:keywords: rocDecode, AMD, ROCm, core API
********************************************************************
Using the rocdecdecode example
********************************************************************
rocDecode provides four core APIs exposed in the header files in the |apifolder|_ directory:
| The rocDecode parser API, exposed in ``rocparser.h``.
| The hardware decoder API, exposed in ``rocdecode.h``.
| The software decoder API, exposed in ``rocdecode_host.h``.
| The bitstream reader API, exposed in ``roc_bitstream_reader.h``.
The |rocdecdecode|_ sample demonstrates how to use the rocDecode core APIs in an application. It shows how to use the parser and both the hardware and software decoders. For information on how to use the bitstream reader API, see :doc:`Using the rocDecode bitstream reader API <./using-rocDecode-bitstream>`.
The sample decodes raw elementary video frame files as input and produces individually decoded frames in YUV format as output. The input can be one individual frame file or multiple frames from one or more video files. The individual frame files must be numbered in ascending order of frames.
``rocdecdecode.cpp`` takes the following arguments:
.. list-table::
:widths: 10 60 30
:header-rows: 1
* - Argument
- Description
- Note
* - ``-i``
- Path to the input video frame file or to frame folder.
- Required.
* - ``-o``
- Output path. Saves the decoded YUV frames to this folder.
- Optional. Decoded frames aren't saved by default.
* - ``-d``
- GPU device ID. Set it to 0 for the first device, 1 for the second device, 2 for the third device, and so on for each subsequent device.
- Optional. Set to 0 by default.
* - ``-b``
- Backend. Set it to 0 to use the hardware decoder on the GPU or to 1 to use the software decoder on the CPU.
- Optional. Set to 0 by default.
* - ``-c``
- Codec. Set to 0 for HEVC, 1 for H264, 2 for AV1, 4 for VP9, 5 for VP8, or 6 for MJPEG.
- Optional. Set to 0 by default.
* - ``-n``
- Number of iterations for performance evaluation.
- Optional. Set to 1 by default.
* - ``-m``
- The output surface memory type. The memory type where the surface data, such as the decoded frames, resides. Set this to 0 for intermediate GPU memory, to 1 for GPU memory, and to 2 for CPU memory. See :doc:`Surface data memory locations <../conceptual/rocDecode-memory-types>` for more information.
- Optional. Set to 0 by default.
The ``DecoderInfo`` struct defined in the sample is used to store user-supplied parameters as well as the decoder and parser handles.
The memory type and the type of decoder is set by the specified backend. If the GPU (device) backend is selected, both a parser and a hardware decoder are created. If the CPU (host) backend is selected, only a software decoder is created:
.. code:: cpp
DecoderInfo dec_info;
[...]
int main(int argc, char** argv) {
[...]
dec_info.rocdec_codec_id = CodecTypeToRocDecVideoCodec(codec_type);
dec_info.dec_device_id = device_id;
dec_info.mem_type = (!backend) ? OUT_SURFACE_MEM_DEV_INTERNAL : OUT_SURFACE_MEM_HOST;
init();
if (backend == DECODER_BACKEND_DEVICE) {
create_parser(dec_info);
create_decoder(dec_info);
} else {
create_decoder_host(dec_info);
}
[...]
}
All applications need to register the ``pfn_sequence_callback`` and ``pfn_display_picture`` callbacks. Applications that use the parser must also register the ``pfn_decode_picture`` callback.
When the GPU backend is selected, these callbacks are registered in the ``create_parser()`` function. ``create_parser`` also creates the parser using ``rocDecCreateVideoParser()``:
.. code:: cpp
void create_parser(DecoderInfo& dec_info) {
RocdecParserParams params = {};
params.codec_type = dec_info.rocdec_codec_id;
params.max_num_decode_surfaces = 6;
params.max_display_delay = 1;
params.user_data = &dec_info;
params.pfn_sequence_callback = handle_video_sequence;
params.pfn_decode_picture = handle_picture_decode;
params.pfn_display_picture = handle_picture_display;
CHECK(rocDecCreateVideoParser(&dec_info.parser, &params));
}
The ``create_decoder()`` function sets the decoder parameters and passes them to ``rocDecCreateDecoder()`` to create the hardware decoder:
.. code:: cpp
void create_decoder(DecoderInfo& dec_info) {
RocDecoderCreateInfo create_info = {};
create_info.codec_type = dec_info.rocdec_codec_id; // user specified codec_type for raw files
[...]
CHECK(rocDecCreateDecoder(&dec_info.decoder, &create_info));
}
The ``create_decoder_host()`` function performs the same actions as ``create_decoder()``, but uses ``rocDecCreateDecoderHost()`` to create a software decoder. Because the parser isn't used with the software decoder, and because the software decoder uses different function calls, the callbacks for the software decoder are registered in ``create_decoder_host()``:
.. code:: cpp
void create_decoder_host(DecoderInfo& dec_info) {
RocDecoderHostCreateInfo create_info = {};
create_info.codec_type = dec_info.rocdec_codec_id;
[...]
create_info.pfn_sequence_callback = handle_video_sequence_host;
create_info.pfn_display_picture = handle_picture_display_host;
CHECK(rocDecCreateDecoderHost(&dec_info.decoder, &create_info));
dec_info.backend = DECODER_BACKEND_HOST;
}
After the decoder and parser have been created, ``decode_frames`` is called.
.. code:: cpp
int main(int argc, char** argv) {
[...]
dec_info.dump_decoded_frames = dump_output_frames;
auto input_frames = read_frames(input_file_names);
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < num_iterations; i++) {
decode_frames(dec_info, input_frames);
}
[...]
}
``decode_frames`` calls ``rocDecParseVideoData()`` or ``rocDecDecodeFrameHost()``, depending on the backend, to parse and decode the frames:
.. code:: cpp
void decode_frames(DecoderInfo& dec_info, const std::vector<std::vector<uint8_t>>& frames) {
// gpu backend using VCN
if (dec_info.backend == DECODER_BACKEND_DEVICE) {
for (int i=0; i < static_cast<int>(frames.size()); ++i) {
RocdecSourceDataPacket packet = {};
packet.payload_size = frames[i].size();
packet.payload = frames[i].data();
if (i == static_cast<int>(frames.size() - 1)) {
packet.flags = ROCDEC_PKT_ENDOFPICTURE; // mark end_of_picture flag for last frame
}
CHECK(rocDecParseVideoData(dec_info.parser, &packet));
}
} else if (dec_info.backend == DECODER_BACKEND_HOST) {
for (int i=0; i < static_cast<int>(frames.size()); ++i) {
RocdecPicParamsHost pic_params = {};
pic_params.bitstream_data_len = frames[i].size();
pic_params.bitstream_data = frames[i].data();
if (i == static_cast<int>(frames.size() - 1)) {
pic_params.flags = ROCDEC_PKT_ENDOFPICTURE; // mark end_of_picture flag for last frame
}
CHECK(rocDecDecodeFrameHost(dec_info.decoder, &pic_params));
}
}
}
The registered callbacks are triggered during the calls to ``rocDecParseVideoData()`` and ``rocDecDecodeFrameHost()``.
``pfn_decode_picture`` is triggered when a new frame is ready to be decoded, ``pfn_sequence_callback`` is triggered when a new sequence header is encountered, and ``pfn_display_picture`` is triggered when a frame has finished being decoded.
``pfn_decode_picture`` needs to call ``rocDecDecodeFrame()`` or ``rocDecodeFrameHost()``, depending on the specified backend, to decode a frame.
In ``rocdecdecode.cpp``, ``pfn_decode_picture`` calls ``handle_picture_decode()`` or ``handle_picture_decode_host()``, depending on the specified backend:
.. code:: cpp
int ROCDECAPI handle_picture_decode(void* user_data, RocdecPicParams* params) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
CHECK(rocDecDecodeFrame(p_dec_info->decoder, params));
return 1;
}
``pfn_sequence_callback`` is triggered when a format change occurs or when a new sequence header is encountered. When this happens, the decoder is reconfigured to handle the new sequence or format.
``pfn_sequence_callback`` needs to call ``rocDecReconfigureDecoder()`` or ``rocDecReconfigureDecoderHost()`` depending on the backend, to reconfigure the decoder.
In the ``rocdecdecode.cpp`` sample, ``pfn_sequence_callback`` calls ``handle_video_sequence()`` or ``handle_video_sequence_host()``, depending on the specified backend:
.. code:: cpp
int ROCDECAPI handle_video_sequence(void* user_data, RocdecVideoFormat* format) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
[...]
RocdecReconfigureDecoderInfo reconfig_params = {};
reconfig_params.width = format->coded_width;
reconfig_params.height = format->coded_height;
reconfig_params.bit_depth_minus_8 = bitdepth_minus_8;
reconfig_params.num_decode_surfaces = format->min_num_decode_surfaces;
reconfig_params.target_width = target_width;
reconfig_params.target_height = target_height;
reconfig_params.display_rect.left = format->display_area.left;
reconfig_params.display_rect.right = format->display_area.right;
reconfig_params.display_rect.top = format->display_area.top;
reconfig_params.display_rect.bottom = format->display_area.bottom;
CHECK(rocDecReconfigureDecoder(p_dec_info->decoder, &reconfig_params));
[...]
return 1;
}
``pfn_display_picture`` is triggered when a frame has been decoded. It needs to call ``rocDecGetVideoFrame()`` or ``rocDecGetVideoFrameHost()``, depending on the specified backend.
``rocDecGetVideoFrame()`` and ``rocDecGetVideoFrameHost()`` map the video ID of the decoded frame to HIP. Calls to both these functions block until the frame is decoded and the memory mapping is complete. They return the HIP device pointer or the host memory pointer, depending on the backend specified, as well as information about the :doc:`output surface <../conceptual/rocDecode-memory-types>`.
``pfn_display_picture`` calls ``handle_picture_display()`` or ``handle_handle_picture_display_host()``, depending on the specified backend, and saves the frames to file if the ``rocdecdecode`` was run with the ``-o`` option:
From the ``rocdecdecode.cpp`` sample:
.. code:: cpp
int ROCDECAPI handle_picture_display(void* user_data, RocdecParserDispInfo* disp_info) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
RocdecProcParams params = {};
params.progressive_frame = disp_info->progressive_frame;
params.top_field_first = disp_info->top_field_first;
void* dev_mem_ptr[3] = { 0 };
uint32_t pitch[3] = { 0 };
CHECK(rocDecGetVideoFrame(p_dec_info->decoder, disp_info->picture_index, dev_mem_ptr, pitch, &params));
if (p_dec_info->dump_decoded_frames) {
save_frame_to_file(p_dec_info, dev_mem_ptr, pitch);
}
return 1;
}
Once decoding is complete, ``rocDecDestroyVideoParser()`` needs to be called to destroy the parser, and either ``rocDecDestroyDecoderHost()`` or ``rocDecDestroyDecoder()`` needs to be called to destroy the decoder.
.. |rocdecdecode| replace:: ``rocdecdecode``
.. _rocdecdecode: https://github.com/ROCm/rocDecode/tree/develop/samples/rocdecDecode/README.md
.. |apifolder| replace:: ``api/rocdecode/``
.. _apifolder: https://github.com/ROCm/rocDecode/tree/develop/api/rocdecode
.. |utilsfolder| replace:: ``utils`` folder
.. _utilsfolder: https://github.com/ROCm/rocDecode/tree/develop/utils
@@ -0,0 +1,146 @@
.. meta::
:description: UUsing the rocDecode RocVideoDecoder
:keywords: parse video, parse, decode, video decoder, video decoding, rocDecode, AMD, ROCm, RocVideoDecoder
********************************************************************
Using the rocDecode RocVideoDecoder
********************************************************************
rocDecode provides two methods fpr decoding a video stream: using the rocDecode RocVideoDecoder on the GPU or using the FFmpeg decoder on the CPU.
This topic covers how to decode a video stream using the RocVideoDecoder class in |roc_video_dec|_. The RocVideoDecode class provides high-level calls to the core APIs in the |apifolder|_ of the rocDecode GitHub repository. For information about the core APIs, see :doc:`Using the rocDecode core APIs <../reference/rocDecode-core-APIs>`.
The RocVideoDecoder takes a demultiplexed coded picture as input. The picture can be demultiplexed from a video stream using the :doc:`FFmpeg demultiplexer <./using-rocDecode-ffmpeg>`.
To use the rocDecode video decoder, import the ``roc_video_dec.h`` header file and instantiate ``RocVideoDecoder``.
The ``RocVideoDecoder`` constructor takes the following parameters:
.. list-table::
:widths: 15 70 15
:header-rows: 1
* - Parameter
- Description
- Default
* - ``device_id``
- ``int`` |br| |br| The GPU device ID. |br| |br| Set it to 0 for the first device, 1 for the second device, 2 for the third device, and so on for each subsequent device.
- 0
* - ``out_mem_type``
- |OutputSurfaceMemoryType|_ |br| |br| The memory type where the surface data, such as the decoded frames, resides. |br| |br| 0: ``OUT_SURFACE_MEM_DEV_INTERNAL``. The surface data is stored internally on memory shared by the GPU and CPU. |br| |br| 1: ``OUT_SURFACE_MEM_DEV_COPIED``. The surface data resides on the GPU. |br| |br| 2: ``OUT_SURFACE_MEM_HOST_COPIED``. The surface data resides on the CPU. |br| |br| See :doc:`Surface data memory locations <../conceptual/rocDecode-memory-types>` for more information.
- 0, OUT_SURFACE_MEM_DEV_INTERNAL
* - ``codec``
- |rocDecVideoCodec|_ |br| |br| The video file's codec ID converted to ``rocDecVideoCodec`` using ``AVCodec2RocDecVideoCodec``.
- No default, a value must be provided
* - ``force_zero_latency``
- ``bool`` |br| |br| Set to ``true`` to flush decoded frames for immediate display.
- ``false``
* - ``p_crop_rect``
- ``const Rect *`` |br| |br| The rectangle to use for cropping.
- No cropping
* - ``extract_user_SEI_Message``
- ``bool`` |br| |br| Set to ``true`` to extract Supplemental Enhancement Information (SEI) from the video stream.
- ``false``, no SEI will be extracted
* - ``disp_delay``
- ``uint32_t`` |br| |br| Delay the display by this number of frames.
- 0, no delay in displaying the frames
* - ``max_width``
- ``int`` |br| |br| Max width.
- 0
* - ``max_height``
- ``int`` |br| |br| Max height.
- 0
* - ``clk_rate``
- ``uint32_t`` |br| |br| Clock rate.
- 1000
.. |br| raw:: html
</br>
For example, from |videodecode|_:
.. code:: C++
RocVideoDecoder viddec(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay);
``RocVideoDecoder`` will create a parser and a decoder, and initialize HIP on the device.
The same decoder instance is reused when there's a change to the video resolution without a change in the codec.
The decoder maintains a pool of frame buffers for decoded images that haven't yet been displayed or processed. When the video stream resolution changes, the existing frame buffers in the buffer pool are deleted. The decoder is then reconfigured for the new resolution and new buffers are created.
To prevent the remaining frames in the buffers from being deleted along with the buffers, a callback function can be defined to consume the remaining frames.
The |reconfig_struct|_ struct stores information on how to handle the reconfiguration. A callback, a user-defined flush mode, and a user-defined struct are passed to ``ReconfigParams_t``. The reconfiguration parameters are then passed to the decoder using ``SetReconfigParams``.
The reconfiguration parameters need to be defined prior to entering the decoding loop. For example, the reconfiguration structs are defined in |common|_ in the rocDecode samples and then used in ``videodecode.cpp``
.. code:: cpp
typedef enum ReconfigFlushMode_enum {
RECONFIG_FLUSH_MODE_NONE = 0x0, /**< Just flush to get the frame count */
RECONFIG_FLUSH_MODE_DUMP_TO_FILE = 0x1, /**< The remaining frames will be dumped to file in this mode */
RECONFIG_FLUSH_MODE_CALCULATE_MD5 = (0x1 << 1), /**< Calculate the MD5 of the flushed frames */
} ReconfigFlushMode;
typedef struct ReconfigDumpFileStruct_t {
bool b_dump_frames_to_file;
std::string output_file_name;
void *md5_generator_handle;
} ReconfigDumpFileStruct;
reconfig_params.p_fn_reconfigure_flush = ReconfigureFlushCallback;
reconfig_user_struct.b_dump_frames_to_file = dump_output_frames;
reconfig_user_struct.output_file_name = output_file_path;
reconfig_params.reconfig_flush_mode = RECONFIG_FLUSH_MODE_NONE;
if (dump_output_frames) {
reconfig_params.reconfig_flush_mode |= RECONFIG_FLUSH_MODE_DUMP_TO_FILE;
}
if (b_generate_md5) {
reconfig_params.reconfig_flush_mode |= RECONFIG_FLUSH_MODE_CALCULATE_MD5;
}
reconfig_params.p_reconfig_user_struct = &reconfig_user_struct;
viddec.SetReconfigParams(&reconfig_params);
In the decode loop, the demultiplexed coded picture is passed to ``DecodeFrame``. Once the frame is decoded and processed, it is released with ``ReleaseFrame``.
.. |videodecode| replace:: ``videodecode.cpp``
.. _videodecode: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecode/videodecode.cpp
.. |videodecoderaw| replace:: ``videodecoderaw.cpp``
.. _videodecoderaw: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecodeRaw
.. |common| replace:: ``common.h``
.. _common: https://github.com/ROCm/rocDecode/blob/develop/samples/common.h
.. |apifolder| replace:: ``api`` folder
.. _apifolder: https://github.com/ROCm/rocDecode/tree/develop/api
.. |utilsfolder| replace:: ``utils`` folder
.. _utilsfolder: https://github.com/ROCm/rocDecode/tree/develop/utils
.. |roc_video_dec| replace:: ``roc_video_dec.h``
.. _roc_video_dec: https://github.com/ROCm/rocDecode/tree/develop/utils/rocvideodecode/roc_video_dec.h
.. |reconfig_struct| replace:: ``ReconfigParams_t``
.. _reconfig_struct: https://rocm.docs.amd.com/projects/rocDecode/en/latest/doxygen/html/structReconfigParams__t.html
.. |OutputSurfaceMemoryType| replace:: ``OutputSurfaceMemoryType``
.. _OutputSurfaceMemoryType: https://rocm.docs.amd.com/projects/rocDecode/en/latest/doxygen/html/roc__video__dec_8h.html
.. |rocDecVideoCodec| replace:: ``rocDecVideoCodec``
.. _rocDecVideoCodec: https://rocm.docs.amd.com/projects/rocDecode/en/latest/doxygen/html/rocdecode_8h.html
@@ -0,0 +1,257 @@
.. meta::
:description: Understanding the rocDecode videodecode sample
:keywords: parse video, parse, decode, video decoder, video decoding, rocDecode, AMD, ROCm, sample, walkthrough
********************************************************************
Understanding the rocDecode videodecode sample
********************************************************************
The |videodecode|_ sample in the rocDecode GitHub repository |samplefolder|_ demonstrates how to decode a video stream.
As with the other rocDecode samples, ``videodecode.cpp`` uses the utility classes in the rocDecode repository's |utilsfolder|_.
rocDecode provides two ways to decode a video stream: using the rocDecode RocVideoDecoder on GPU or using the FFMpeg video decoder on CPU.
The ``videodecode.cpp`` sample lets the user choose which method to use through the ``--backend`` argument.
``videodecode.cpp`` takes the following arguments:
.. list-table::
:widths: 10 30 60
:header-rows: 1
* - Argument
- Description
- Note
* - ``-i``
- Input file path
- Required. The path to the input video stream.
* - ``-o``
- Output file path
- Optional. The file to which to write the decoded frames, including those that remain in the decoded frame buffer pool when the RocVideoDecoder is being reconfigured.
* - ``-d``
- GPU device ID
- Optional. Set it to 0 for the first device, 1 for the second device, 2 for the third device, and so on for each subsequent device. Set to 0 by default.
* - ``-backend``
- The backend to use for decoding
- Optional. Set it to 0 to use RocVideoDecode on GPU, 1 to use the FFMpeg decoder on CPU, or 2 to use the FFMpeg decoder with no multithreading on CPU. Uses RocVideoDecode on GPU by default.
* - ``-f``
- Number of frames to decode
- Optional. Decodes the entire stream by default.
* - ``-z``
- Force zero latency
- Optional. When set to ``true`` forces decoded frames to be flushed out for display immediately. ``false`` by default.
* - ``-disp_delay``
- Display delay
- Optional. The number of frames to decode before displaying the results. Set to 1 by default.
* - ``-sei``
- Extract Supplemental Enhancement Information (SEI)
- Optional. Set to ``true`` to extract SEI. ``false`` by default.
* - ``-md5``
- Generate MD5 message digest
- Optional. Set to ``true`` to generate the MD5 message digest for the decoded YUV image sequence. ``false`` by default.
* - ``-md5_check``
- Compare the generated MD5 with a provided MD5 string
- Optional. When a file containing an MD5 string is passed to this argument, the MD5 message is compared to the string in the file.
* - ``-crop``
- Crop rectangle
- Optional. Takes four integers defining the crop rectangle to use with the output. This argument is ignored when using interopped decoded frame. See the documentation for the `Rect struct <https://rocm.docs.amd.com/projects/rocDecode/en/latest/doxygen/html/structRect.html>`_ for more information. There is no cropping by default.
* - ``-m``
- The output surface memory type
- Optional. The memory type where the surface data, such as the decoded frames, resides. Set this to 0 for intermediate GPU memory, to 1 for GPU memory, and to 2 for CPU memory. See :doc:`Surface data memory locations <../conceptual/rocDecode-memory-types>` for more information. Uses intermediate GPU memory by default.
* - ``-seek_criteria``
- Seek criteria and seek starting point
- Optional. Set to 1 and the frame number to start demultiplexing from that specific frame. Set to 2 and the timestamp to start demultiplexing from that specific timestamp. The seek criteria and starting point must be comma-separated (``,``). Demultiplexing begins at the first frame by default.
* - ``-seek_mode``
- Seek mode
- Optional. Set to 0 to seek to the previous keyframe. Set to 1 to seek to the exact frame. Seeks to previous keyframe by default.
* - ``-no_ffmpeg_demux``
- Don't use the FFMpeg demultiplexer
- Optional. Set to ``true`` to use the RocDecode bitstream reader to obtain picture data. The bitstream reader can only be used with an elementary stream. The FFmpeg demultiplexer is used by default.
Because the ``videodecode.cpp`` example can use the RocDecode RocVideoDecoder, the FFMpeg decoder, the FFmpeg demultiplexer (demuxer), or the RocDecode bitstream reader, it imports the ``roc_video_dec.h``, ``video_demuxer.h``, and ``ffmpeg_video_dec.h`` header files. These headers contain the convenience classes and functions for decoding and demultiplexing video.
The FFMpeg demuxer is used to demultiplex the input stream unless the ``-no_ffmpeg_demux`` argument was set to ``true``.
.. code:: C++
VideoDemuxer *demuxer;
demuxer = new VideoDemuxer(input_file_path.c_str());
The ``GetCodecId`` and ``GetBitDepth`` functions are used to obtain the video stream's codec and bit depth. The ``AVCodec2RocDecVideoCodec`` utility function converts the codec returned from the demuxer to its corresponding ``rocDecVideoCodec_enum`` value.
.. code:: C++
rocdec_codec_id = AVCodec2RocDecVideoCodec(demuxer->GetCodecID());
bit_depth = demuxer->GetBitDepth();
The codec ID and bit depth are used to instantiate the video decoder. If the GPU backend was selected, the RocVideoDecoder is instantiated:
.. code:: C++
RocVideoDecoder *viddec;
viddec = new RocVideoDecoder(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay);
For more information about the rocDecode RocVideoDecoder, see :doc:`Using the rocDecode RocVideoDecoder <./using-rocDecode-video-decoder>`.
If the CPU backend was selected, the FFMpeg decoder is instantiated:
.. code:: C++
viddec = new FFMpegVideoDecoder(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay);
The decoder instance is reused when there is a change to the video resolution without a change in the codec. When the video stream resolution changes, the decoder is reconfigured for the new resolution and the pool of frame buffers that the decoder maintains is deleted.
The |reconfig_struct|_ struct is used to store information on how to handle the frames that remain in the buffers at the time of reconfiguration. A callback, a user-defined flush mode, and a user-defined struct are passed to ``ReconfigParams_t``. The reconfiguration parameters are then passed to the decoder using ``SetReconfigParams``.
The reconfiguration structs are defined in |common|_ in the rocDecode samples. Three possibilities for the remaining frames in the decoded frame buffer pool are provided:
* ``RECONFIG_FLUSH_MODE_NONE``: delete the frames along with the buffers.
* ``RECONFIG_FLUSH_MODE_DUMP_TO_FILE``: write the frames to the specified output file before deleting the buffers.
* ``RECONFIG_FLUSH_MODE_CALCULATE_MD5``: calculate the MD5 of the frames before deleting the buffers.
.. code:: C++
typedef enum ReconfigFlushMode_enum {
RECONFIG_FLUSH_MODE_NONE = 0x0, /**< Just flush to get the frame count */
RECONFIG_FLUSH_MODE_DUMP_TO_FILE = 0x1, /**< The remaining frames will be dumped to file in this mode */
RECONFIG_FLUSH_MODE_CALCULATE_MD5 = (0x1 << 1), /**< Calculate the MD5 of the flushed frames */
} ReconfigFlushMode;
typedef struct ReconfigDumpFileStruct_t {
bool b_dump_frames_to_file;
std::string output_file_name;
void *md5_generator_handle;
} ReconfigDumpFileStruct;
If the ``-o`` output file path argument was set, the remaining frames in the decoded frame buffer pool will be written to the output file upon reconfiguration. If the ``-md5`` argument was set to ``true``, the MD5 of the frames in the decoded frame buffer pool will be calculated before they're flushed or written to file. If neither option was selected, the frames in the decoded frame buffer pool will be deleted along with the buffers without being saved or processed.
.. code:: C++
reconfig_params.p_fn_reconfigure_flush = ReconfigureFlushCallback;
reconfig_user_struct.b_dump_frames_to_file = dump_output_frames;
reconfig_user_struct.output_file_name = output_file_path;
reconfig_params.reconfig_flush_mode = RECONFIG_FLUSH_MODE_NONE;
if (dump_output_frames) {
reconfig_params.reconfig_flush_mode |= RECONFIG_FLUSH_MODE_DUMP_TO_FILE;
}
if (b_generate_md5) {
reconfig_params.reconfig_flush_mode |= RECONFIG_FLUSH_MODE_CALCULATE_MD5;
}
reconfig_params.p_reconfig_user_struct = &reconfig_user_struct;
The reconfiguration parameters need to be defined prior to entering the decoding loop.
In the decode loop, the video stream is demultiplexed before being decoded.
The demuxer will demultiplex frames sequentially starting at the beginning of the stream unless ``-seek_criteria`` was set to either 1 or 2.
If the ``-seek_criteria`` argument was set to 1 and ``-seek_mode`` was set to 1, the demuxer will start demultiplexing the video at the frame provided.
If the ``-seek_criteria`` argument was set to 1 and ``-seek_mode`` wasn't set or was set to 0, the demuxer will start demultiplexing the video at the first keyframe before the frame provided.
If the ``-seek_criteria`` argument was set to 2 the demuxer will start demultiplexing the video at the timestamp provided.
The seek criteria is defined by the ``SeekCriteriaEnum`` enum and the seek mode is defined by the ``SeekModeEnum`` enum. Both the ``SeekCriteriaEnum`` and the ``SeekModeEnum`` are defined in ``video_demuxer.h``.
From ``videodecode.cpp``:
.. code:: C++
VideoSeekContext video_seek_ctx;
[...]
do {
[...]
if (seek_criteria == 1 && first_frame) {
// use VideoSeekContext class to seek to given frame number
video_seek_ctx.seek_frame_ = seek_to_frame;
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_FRAME_NUM;
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
demuxer->Seek(video_seek_ctx, &pvideo, &n_video_bytes);
pts = video_seek_ctx.out_frame_pts_;
std::cout << "info: Number of frames that were decoded during seek - " << video_seek_ctx.num_frames_decoded_ << std::endl;
first_frame = false;
} else if (seek_criteria == 2 && first_frame) {
// use VideoSeekContext class to seek to given timestamp
video_seek_ctx.seek_frame_ = seek_to_frame;
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_TIME_STAMP;
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
demuxer->Seek(video_seek_ctx, &pvideo, &n_video_bytes);
pts = video_seek_ctx.out_frame_pts_;
std::cout << "info: Duration of frame found after seek - " << video_seek_ctx.out_frame_duration_ << " ms" << std::endl;
first_frame = false;
} else {
demuxer->Demux(&pvideo, &n_video_bytes, &pts);
}
The video can now be decoded using the ``DecodeFrame`` function.
If the ``-md5`` argument was set to ``true``, MD5 is calculated for the file. If an output file path was provided, the decoded frames will be written to file.
The frame is released with ``ReleaseFrame`` once processing is complete.
.. code:: C++
n_frame_returned = viddec->DecodeFrame(pvideo, n_video_bytes, pkg_flags, pts, &decoded_pics);
[...]
for (int i = 0; i < n_frame_returned; i++) {
pframe = viddec->GetFrame(&pts);
if (b_generate_md5) {
md5_generator->UpdateMd5ForFrame(pframe, surf_info);
}
if (dump_output_frames && mem_type != OUT_SURFACE_MEM_NOT_MAPPED) {
viddec->SaveFrameToFile(output_file_path, pframe, surf_info);
}
viddec->ReleaseFrame(pts);
The demuxer is deleted once decoding is done.
.. code:: C++
delete demuxer;
.. |videodecode| replace:: ``videodecode.cpp``
.. _videodecode: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecode/videodecode.cpp
.. |videodecoderaw| replace:: ``videodecoderaw.cpp``
.. _videodecoderaw: https://github.com/ROCm/rocDecode/tree/develop/samples/videoDecodeRaw
.. |common| replace:: ``common.h``
.. _common: https://github.com/ROCm/rocDecode/blob/develop/samples/common.h
.. |apifolder| replace:: ``api`` folder
.. _apifolder: https://github.com/ROCm/rocDecode/tree/develop/api
.. |utilsfolder| replace:: ``utils`` folder
.. _utilsfolder: https://github.com/ROCm/rocDecode/tree/develop/utils
.. |samplefolder| replace:: ``samples`` folder
.. _samplefolder: https://github.com/ROCm/rocDecode/tree/develop/samples
.. |reconfig_struct| replace:: ``ReconfigParams_t``
.. _reconfig_struct: https://rocm.docs.amd.com/projects/rocDecode/en/latest/doxygen/html/structReconfigParams__t.html
.. |br| raw:: html
</br>