MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
Writer Class Reference

FFmpeg-backed RGBA video writer. More...

#include <MXWrite/mxwrite.hpp>

Public Member Functions

void close ()
 Close the writer and flush pending packets.
bool get_block_when_full () const
 Check whether the encoder queue blocks instead of dropping frames.
std::uint64_t get_bytes_written () const
 Return the current logical byte position of the output muxer.
double get_duration () const
 Return the encoded duration in seconds.
int64_t get_frame_count () const
 Return the output timeline length in nominal frame ticks.
bool is_hardware_encode () const
 True when FFmpeg identifies the active encoder as hardware or hybrid.
bool is_open () const
 Check whether the writer is currently open.
bool open (const std::string &filename, int width, int height, float fps, const char *crf)
 Open an output file using the legacy CRF string interface.
bool open (const std::string &filename, int width, int height, float fps, const EncodeOptions &opts)
 Open an output file using explicit encoder options.
bool open_ts (const std::string &filename, int width, int height, float fps, const char *crf)
 Open a timestamp-based output stream using the legacy CRF string interface.
bool open_ts (const std::string &filename, int width, int height, float fps, const EncodeOptions &opts)
 Open a timestamp-based output stream using explicit encoder options.
void set_block_when_full (bool value)
 If true, keep one pending frame and pace producer threads to the encoder instead of dropping frames. Intended for headless/batch transcoding where every input frame must reach the output. Default: false.
void write (void *rgba_buffer)
 Queue a host RGBA frame for immediate-mode encoding.
void write_at_pts (void *rgba_buffer, int64_t pts)
 Queue a host RGBA frame with an explicit presentation timestamp.
bool write_cuda_rgba (void *cuda_rgba_buffer, int src_stride, bool bottom_up=false)
 Queue a CUDA RGBA frame for encoding.
bool write_cuda_rgba_at_pts (void *cuda_rgba_buffer, int src_stride, int64_t pts, bool bottom_up=false)
 Queue a CUDA RGBA frame with an explicit presentation timestamp.
void write_hdr_rgba16 (void *rgba16_buffer)
 Write a 16-bit RGBA frame that is already PQ- or HLG-encoded in BT.2020 primaries (8 bytes/pixel: R16,G16,B16,A16, little-endian unsigned normalised).
void write_hdr_rgba16_at_pts (void *rgba16_buffer, int64_t pts)
 Queue a 16-bit HDR RGBA frame with an explicit presentation timestamp.
void write_ts (void *rgba_buffer)
 Queue a host RGBA frame using capture timestamps.
 Writer ()=default
 Construct a closed writer.
 ~Writer ()
 Close the writer on destruction if it is still open.

Detailed Description

FFmpeg-backed RGBA video writer.

The writer accepts host RGBA buffers, optional CUDA device buffers, and 16-bit HDR RGBA buffers. It can encode either frame-by-frame or from timestamped frames, depending on the open mode.

Definition at line 142 of file mxwrite.hpp.

Constructor & Destructor Documentation

◆ Writer()

Writer::Writer ( )
default

Construct a closed writer.

◆ ~Writer()

Writer::~Writer ( )
inline

Close the writer on destruction if it is still open.

Definition at line 266 of file mxwrite.hpp.

266 {
267 if (is_open()) {
268 close();
269 opened = false;
270 }
271 }
bool is_open() const
Check whether the writer is currently open.
Definition mxwrite.hpp:244
void close()
Close the writer and flush pending packets.
Definition mxwrite.cpp:2239

Member Function Documentation

◆ close()

void Writer::close ( )

Close the writer and flush pending packets.

Definition at line 2239 of file mxwrite.cpp.

2239 {
2240 std::lock_guard<std::mutex> lock(writer_mutex);
2241 if (!opened) {
2242 return;
2243 }
2244
2245 stopEncoderThread();
2246
2247 if (stream && codec_ctx && frame_count > 0) {
2248 stream->duration = av_rescale_q(frame_count, codec_ctx->time_base, stream->time_base);
2249 }
2250 if (fps_num > 0 && fps_den > 0) {
2251 last_duration = static_cast<double>(frame_count) * static_cast<double>(fps_den) / static_cast<double>(fps_num);
2252 } else if (stream && stream->duration > 0) {
2253 last_duration = static_cast<double>(stream->duration) * av_q2d(stream->time_base);
2254 }
2255
2256 av_write_trailer(format_ctx);
2257 updateBytesWritten();
2258
2259 if (!(format_ctx->oformat->flags & AVFMT_NOFILE)) {
2260 avio_closep(&format_ctx->pb);
2261 }
2262
2263 av_frame_free(&frameRGBA);
2264 av_frame_free(&frameYUV);
2265 av_frame_free(&frame10);
2266 sws_freeContext(sws_ctx);
2267 av_frame_free(&upload_sw_frame);
2268#ifdef MXWRITE_HAS_CUDA_COPY
2269 // Destroy stream before tearing down FFmpeg CUDA device/frames contexts.
2270 if (cuda_upload_stream) {
2271 cudaStreamSynchronize(cuda_upload_stream);
2272 cudaStreamDestroy(cuda_upload_stream);
2273 cuda_upload_stream = nullptr;
2274 }
2275#endif
2276 avcodec_free_context(&codec_ctx);
2277 av_buffer_unref(&hw_frames_ctx);
2278 av_buffer_unref(&hw_device_ctx);
2279 avformat_free_context(format_ctx);
2280
2281 while (!encode_queue.empty()) {
2282 releaseFrame(encode_queue.front());
2283 encode_queue.pop();
2284 }
2285 opened = false;
2286 format_ctx = nullptr;
2287 codec_ctx = nullptr;
2288 sws_ctx = nullptr;
2289 frameRGBA = nullptr;
2290 frameYUV = nullptr;
2291 frame10 = nullptr;
2292 upload_sw_frame = nullptr;
2293 use_hw_encode = false;
2294 active_encoder_hardware = false;
2295 direct_cuda_upload = false;
2296 hdr_output = false;
2297 stop_requested = false;
2298}

◆ get_block_when_full()

bool Writer::get_block_when_full ( ) const
inline

Check whether the encoder queue blocks instead of dropping frames.

Definition at line 252 of file mxwrite.hpp.

252{ return block_when_full; }

◆ get_bytes_written()

std::uint64_t Writer::get_bytes_written ( ) const
inline

Return the current logical byte position of the output muxer.

Definition at line 262 of file mxwrite.hpp.

262{ return bytes_written.load(std::memory_order_relaxed); }

◆ get_duration()

double Writer::get_duration ( ) const

Return the encoded duration in seconds.

Definition at line 2300 of file mxwrite.cpp.

2300 {
2301 if (!opened && last_duration > 0.0) {
2302 return last_duration;
2303 }
2304 if (stream && stream->duration > 0) {
2305 return static_cast<double>(stream->duration) * av_q2d(stream->time_base);
2306 }
2307 if (fps_num > 0 && fps_den > 0) {
2308 return static_cast<double>(frame_count) * static_cast<double>(fps_den) / static_cast<double>(fps_num);
2309 }
2310 return 0.0;
2311}

◆ get_frame_count()

int64_t Writer::get_frame_count ( ) const
inline

Return the output timeline length in nominal frame ticks.

For sequential writes this equals the submitted frame count. Explicit PTS writes may leave gaps, in which case it is the highest accepted PTS plus one.

Definition at line 260 of file mxwrite.hpp.

260{ return frame_count; }

◆ is_hardware_encode()

bool Writer::is_hardware_encode ( ) const
inline

True when FFmpeg identifies the active encoder as hardware or hybrid.

Definition at line 246 of file mxwrite.hpp.

246{ return active_encoder_hardware; }

◆ is_open()

bool Writer::is_open ( ) const
inline

Check whether the writer is currently open.

Definition at line 244 of file mxwrite.hpp.

244{ return opened; }

◆ open() [1/2]

bool Writer::open ( const std::string & filename,
int width,
int height,
float fps,
const char * crf )

Open an output file using the legacy CRF string interface.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsOutput frame rate.
crfConstant Rate Factor as a string.
Returns
true on success.

Definition at line 977 of file mxwrite.cpp.

977 {
978 std::lock_guard<std::mutex> lock(writer_mutex);
979 EncodeOptions opts;
980 if (crf && *crf) {
981 try {
982 opts.crf = std::stoi(crf);
983 } catch (...) {
984 }
985 }
986 // Preserve legacy low-latency behaviour for old callers.
987 opts.preset = "ultrafast";
988 opts.tune = "zerolatency";
989 opts.realtime = true;
990 return openInternal(filename, w, h, fps, opts, false);
991}
std::string tune
Optional tuning mode.
Definition mxwrite.hpp:96
bool realtime
Enable low-latency settings.
Definition mxwrite.hpp:101
int crf
Constant Rate Factor.
Definition mxwrite.hpp:97
std::string preset
Encoder preset name.
Definition mxwrite.hpp:95

◆ open() [2/2]

bool Writer::open ( const std::string & filename,
int width,
int height,
float fps,
const EncodeOptions & opts )

Open an output file using explicit encoder options.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsOutput frame rate.
optsEncoder configuration.
Returns
true on success.

Definition at line 993 of file mxwrite.cpp.

993 {
994 std::lock_guard<std::mutex> lock(writer_mutex);
995 return openInternal(filename, w, h, fps, opts, false);
996}

◆ open_ts() [1/2]

bool Writer::open_ts ( const std::string & filename,
int width,
int height,
float fps,
const char * crf )

Open a timestamp-based output stream using the legacy CRF string interface.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsNominal input frame rate.
crfConstant Rate Factor as a string.
Returns
true on success.

Definition at line 998 of file mxwrite.cpp.

998 {
999 std::lock_guard<std::mutex> lock(writer_mutex);
1000 EncodeOptions opts;
1001 if (crf && *crf) {
1002 try {
1003 opts.crf = std::stoi(crf);
1004 } catch (...) {
1005 }
1006 }
1007 opts.preset = "ultrafast";
1008 opts.tune = "zerolatency";
1009 opts.realtime = true;
1010 return openInternal(filename, w, h, fps, opts, true);
1011}

◆ open_ts() [2/2]

bool Writer::open_ts ( const std::string & filename,
int width,
int height,
float fps,
const EncodeOptions & opts )

Open a timestamp-based output stream using explicit encoder options.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsNominal input frame rate.
optsEncoder configuration.
Returns
true on success.

Definition at line 1013 of file mxwrite.cpp.

1013 {
1014 std::lock_guard<std::mutex> lock(writer_mutex);
1015 return openInternal(filename, w, h, fps, opts, true);
1016}

◆ set_block_when_full()

void Writer::set_block_when_full ( bool value)
inline

If true, keep one pending frame and pace producer threads to the encoder instead of dropping frames. Intended for headless/batch transcoding where every input frame must reach the output. Default: false.

Definition at line 250 of file mxwrite.hpp.

250{ block_when_full = value; }

◆ write()

void Writer::write ( void * rgba_buffer)

Queue a host RGBA frame for immediate-mode encoding.

Parameters
rgba_bufferPointer to tightly packed RGBA8 pixels.

Definition at line 1751 of file mxwrite.cpp.

1751{ write_at_pts(rgba_buffer, AV_NOPTS_VALUE); }
void write_at_pts(void *rgba_buffer, int64_t pts)
Queue a host RGBA frame with an explicit presentation timestamp.
Definition mxwrite.cpp:1753

◆ write_at_pts()

void Writer::write_at_pts ( void * rgba_buffer,
int64_t pts )

Queue a host RGBA frame with an explicit presentation timestamp.

Parameters
rgba_bufferPointer to tightly packed RGBA8 pixels.
ptsPresentation timestamp in units of the configured frame time base.

Definition at line 1753 of file mxwrite.cpp.

1753 {
1754 if (!rgba_buffer) {
1755 return;
1756 }
1757
1758 {
1759 std::lock_guard<std::mutex> lock(writer_mutex);
1760 if (!opened) {
1761 return;
1762 }
1763 }
1764
1765 AVFrame *queued_frame = av_frame_alloc();
1766 if (!queued_frame) {
1767 std::cerr << "Writer: failed to allocate queued frame\n";
1768 return;
1769 }
1770
1771 if (use_hw_encode && hdr_output) {
1772 queued_frame->format = codec_ctx->pix_fmt;
1773 queued_frame->width = width;
1774 queued_frame->height = height;
1775 if (av_hwframe_get_buffer(hw_frames_ctx, queued_frame, 0) < 0) {
1776 std::cerr << "Writer: failed to allocate frame from hardware pool\n";
1777 releaseFrame(queued_frame);
1778 return;
1779 }
1780
1781 if (av_frame_make_writable(upload_sw_frame) < 0) {
1782 std::cerr << "Writer: software upload frame not writable\n";
1783 releaseFrame(queued_frame);
1784 return;
1785 }
1786
1787 const auto *src = static_cast<const uint8_t *>(rgba_buffer);
1788 if (upload_sw_frame->format == AV_PIX_FMT_RGBA) {
1789 for (int y = 0; y < height; ++y) {
1790 std::memcpy(upload_sw_frame->data[0] + static_cast<size_t>(y) * upload_sw_frame->linesize[0], src + static_cast<size_t>(y) * static_cast<size_t>(width) * 4, static_cast<size_t>(width) * 4);
1791 }
1792 } else {
1793 const uint8_t *source_data[1] = {src};
1794 const int source_linesize[1] = {width * 4};
1795 sws_scale(sws_ctx, source_data, source_linesize, 0, height, upload_sw_frame->data, upload_sw_frame->linesize);
1796 }
1797
1798 if (av_hwframe_transfer_data(queued_frame, upload_sw_frame, 0) < 0) {
1799 std::cerr << "Writer: failed to transfer system frame to hardware frame\n";
1800 releaseFrame(queued_frame);
1801 return;
1802 }
1803 } else {
1804 queued_frame->format = AV_PIX_FMT_RGBA;
1805 queued_frame->width = width;
1806 queued_frame->height = height;
1807 if (av_frame_get_buffer(queued_frame, 32) < 0) {
1808 std::cerr << "Writer: failed to allocate queued RGBA frame buffer\n";
1809 releaseFrame(queued_frame);
1810 return;
1811 }
1812 if (av_frame_make_writable(queued_frame) < 0) {
1813 std::cerr << "Writer: queued RGBA frame not writable\n";
1814 releaseFrame(queued_frame);
1815 return;
1816 }
1817
1818 const auto *src = static_cast<const uint8_t *>(rgba_buffer);
1819 for (int y = 0; y < height; ++y) {
1820 std::memcpy(queued_frame->data[0] + static_cast<size_t>(y) * queued_frame->linesize[0], src + static_cast<size_t>(y) * static_cast<size_t>(width) * 4, static_cast<size_t>(width) * 4);
1821 }
1822 }
1823
1824 {
1825 std::unique_lock<std::mutex> lock(queue_mutex);
1826 if (block_when_full.load(std::memory_order_relaxed)) {
1827 if (encode_queue.size() >= NO_DROP_QUEUE_SIZE) {
1828 // Keep file processing paced with the encoder. Wake whenever
1829 // one queue slot becomes available rather than waiting for a
1830 // large queue to fill and then drain completely.
1831 queue_cv.wait(lock, [this] { return stop_requested || encode_queue.size() < NO_DROP_QUEUE_SIZE; });
1832 }
1833 if (stop_requested) {
1834 releaseFrame(queued_frame);
1835 return;
1836 }
1837 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
1838 static int drop_counter = 0;
1839 if (++drop_counter % 30 == 0) {
1840 std::cerr << "Writer: dropped " << drop_counter << " SDR frames (encoder queue full)\n";
1841 }
1842 releaseFrame(queued_frame);
1843 return;
1844 }
1845 if (pts != AV_NOPTS_VALUE && pts < frame_count) {
1846 // A timestamped live frame landed in a nominal frame slot that
1847 // was already filled. Drop it instead of extending the video
1848 // timeline and allowing it to drift behind the capture clock.
1849 releaseFrame(queued_frame);
1850 return;
1851 }
1852 queued_frame->pts = pts == AV_NOPTS_VALUE ? frame_count : pts;
1853 frame_count = queued_frame->pts + 1;
1854 encode_queue.push(queued_frame);
1855 }
1856
1857 queue_cv.notify_one();
1858}

◆ write_cuda_rgba()

bool Writer::write_cuda_rgba ( void * cuda_rgba_buffer,
int src_stride,
bool bottom_up = false )

Queue a CUDA RGBA frame for encoding.

Parameters
cuda_rgba_bufferCUDA device pointer.
src_strideSource row pitch in bytes.
bottom_upWhether the source is stored bottom-up.
Returns
true if the frame was accepted.

Definition at line 1936 of file mxwrite.cpp.

1936{ return write_cuda_rgba_at_pts(cuda_rgba_buffer, src_stride, AV_NOPTS_VALUE, bottom_up); }
bool write_cuda_rgba_at_pts(void *cuda_rgba_buffer, int src_stride, int64_t pts, bool bottom_up=false)
Queue a CUDA RGBA frame with an explicit presentation timestamp.
Definition mxwrite.cpp:1938

◆ write_cuda_rgba_at_pts()

bool Writer::write_cuda_rgba_at_pts ( void * cuda_rgba_buffer,
int src_stride,
int64_t pts,
bool bottom_up = false )

Queue a CUDA RGBA frame with an explicit presentation timestamp.

Parameters
cuda_rgba_bufferCUDA device pointer.
src_strideSource row pitch in bytes.
ptsPresentation timestamp in units of the configured frame time base.
bottom_upWhether the source is stored bottom-up.
Returns
true if the frame was accepted.

Definition at line 1938 of file mxwrite.cpp.

1938 {
1939 if (!cuda_rgba_buffer || src_stride <= 0) {
1940 return false;
1941 }
1942
1943 {
1944 std::lock_guard<std::mutex> lock(writer_mutex);
1945 if (!opened || !use_hw_encode || !direct_cuda_upload) {
1946 return false;
1947 }
1948 }
1949
1950 AVFrame *queued_frame = av_frame_alloc();
1951 if (!queued_frame) {
1952 std::cerr << "Writer: failed to allocate queued CUDA frame\n";
1953 return false;
1954 }
1955
1956 queued_frame->format = AV_PIX_FMT_CUDA;
1957 queued_frame->width = width;
1958 queued_frame->height = height;
1959
1960 if (av_hwframe_get_buffer(hw_frames_ctx, queued_frame, 0) < 0) {
1961 std::cerr << "Writer: failed to allocate CUDA frame from hardware pool\n";
1962 releaseFrame(queued_frame);
1963 return false;
1964 }
1965
1966#ifdef MXWRITE_HAS_CUDA_COPY
1967 cudaStream_t stream = cuda_upload_stream;
1968 const bool use_async_stream = (stream != nullptr);
1969 if (!bottom_up) {
1970 const auto copy_err = use_async_stream ? cudaMemcpy2DAsync(queued_frame->data[0], static_cast<size_t>(queued_frame->linesize[0]), cuda_rgba_buffer, static_cast<size_t>(src_stride), static_cast<size_t>(width) * 4, static_cast<size_t>(height), cudaMemcpyDeviceToDevice, stream) : cudaMemcpy2D(queued_frame->data[0], static_cast<size_t>(queued_frame->linesize[0]), cuda_rgba_buffer, static_cast<size_t>(src_stride), static_cast<size_t>(width) * 4, static_cast<size_t>(height), cudaMemcpyDeviceToDevice);
1971
1972 if (copy_err != cudaSuccess) {
1973 std::cerr << "Writer: cudaMemcpy2D device upload failed: " << cudaGetErrorString(copy_err) << "\n";
1974 releaseFrame(queued_frame);
1975 return false;
1976 }
1977 } else {
1978 // Flip vertically by issuing one async row copy per destination row
1979 // onto a single stream — kept asynchronous so launch overhead overlaps
1980 // and the producer thread only blocks once at cudaStreamSynchronize.
1981 auto *src_base = static_cast<unsigned char *>(cuda_rgba_buffer);
1982 auto *dst_base = queued_frame->data[0];
1983 const size_t row_bytes = static_cast<size_t>(width) * 4;
1984
1985 for (int y = 0; y < height; ++y) {
1986 auto *src_row = src_base + static_cast<size_t>(height - 1 - y) * static_cast<size_t>(src_stride);
1987 auto *dst_row = dst_base + static_cast<size_t>(y) * static_cast<size_t>(queued_frame->linesize[0]);
1988 const auto row_copy_err = use_async_stream ? cudaMemcpyAsync(dst_row, src_row, row_bytes, cudaMemcpyDeviceToDevice, stream) : cudaMemcpy(dst_row, src_row, row_bytes, cudaMemcpyDeviceToDevice);
1989 if (row_copy_err != cudaSuccess) {
1990 std::cerr << "Writer: cudaMemcpy row upload failed: " << cudaGetErrorString(row_copy_err) << "\n";
1991 releaseFrame(queued_frame);
1992 return false;
1993 }
1994 }
1995 }
1996 // Single synchronisation point — NVENC requires the data to be ready when
1997 // avcodec_send_frame() reads it, and the encoder thread is decoupled by
1998 // the queue so this sync only serialises this one producer call.
1999 if (use_async_stream) {
2000 const auto sync_err = cudaStreamSynchronize(stream);
2001 if (sync_err != cudaSuccess) {
2002 std::cerr << "Writer: cudaStreamSynchronize failed: " << cudaGetErrorString(sync_err) << "\n";
2003 releaseFrame(queued_frame);
2004 return false;
2005 }
2006 }
2007#else
2008 std::cerr << "Writer: CUDA copy support disabled at build time\n";
2009 releaseFrame(queued_frame);
2010 return false;
2011#endif
2012
2013 {
2014 std::unique_lock<std::mutex> lock(queue_mutex);
2015 if (block_when_full.load(std::memory_order_relaxed)) {
2016 if (encode_queue.size() >= NO_DROP_QUEUE_SIZE) {
2017 queue_cv.wait(lock, [this] { return stop_requested || encode_queue.size() < NO_DROP_QUEUE_SIZE; });
2018 }
2019 if (stop_requested) {
2020 releaseFrame(queued_frame);
2021 return false;
2022 }
2023 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
2024 static int drop_counter = 0;
2025 if (++drop_counter % 30 == 0) {
2026 std::cerr << "Writer: dropped " << drop_counter << " frames (encoder queue full)\n";
2027 }
2028 releaseFrame(queued_frame);
2029 // Return TRUE so the producer does NOT fall back to the slow CPU
2030 // write() path — we already "handled" the frame (by dropping it).
2031 // Falling back would double-process every frame and double the drops.
2032 queue_cv.notify_one();
2033 return true;
2034 }
2035 if (pts != AV_NOPTS_VALUE && pts < frame_count) {
2036 releaseFrame(queued_frame);
2037 queue_cv.notify_one();
2038 return true;
2039 }
2040 queued_frame->pts = pts == AV_NOPTS_VALUE ? frame_count : pts;
2041 frame_count = queued_frame->pts + 1;
2042 encode_queue.push(queued_frame);
2043 }
2044
2045 queue_cv.notify_one();
2046 return true;
2047}

◆ write_hdr_rgba16()

void Writer::write_hdr_rgba16 ( void * rgba16_buffer)

Write a 16-bit RGBA frame that is already PQ- or HLG-encoded in BT.2020 primaries (8 bytes/pixel: R16,G16,B16,A16, little-endian unsigned normalised).

The data is expected to originate from the HDR GPU encode pass: it is BT.2020 primaries with a non-linear PQ (or HLG) transfer already applied, so this path skips colour-space conversion and only performs (a) the BT.2020 non-constant-luminance RGB'->YCbCr' matrix, and (b) 16-bit -> 10-bit limited-range scaling, producing AV_PIX_FMT_YUV420P10LE for libx265 Main10. Requires the writer to have been opened with EncodeOptions::HdrInfo::enabled.

Parameters
rgba16_bufferPointer to tightly packed RGBA16 pixels.

Definition at line 1860 of file mxwrite.cpp.

1860{ write_hdr_rgba16_at_pts(rgba16_buffer, AV_NOPTS_VALUE); }
void write_hdr_rgba16_at_pts(void *rgba16_buffer, int64_t pts)
Queue a 16-bit HDR RGBA frame with an explicit presentation timestamp.
Definition mxwrite.cpp:1862

◆ write_hdr_rgba16_at_pts()

void Writer::write_hdr_rgba16_at_pts ( void * rgba16_buffer,
int64_t pts )

Queue a 16-bit HDR RGBA frame with an explicit presentation timestamp.

Parameters
rgba16_bufferPointer to tightly packed RGBA16 pixels.
ptsPresentation timestamp in units of the configured frame time base.

Definition at line 1862 of file mxwrite.cpp.

1862 {
1863 if (!rgba16_buffer) {
1864 return;
1865 }
1866
1867 {
1868 std::lock_guard<std::mutex> lock(writer_mutex);
1869 if (!opened) {
1870 return;
1871 }
1872 if (!hdr_output) {
1873 std::cerr << "Writer: write_hdr_rgba16 called but writer not in HDR mode\n";
1874 return;
1875 }
1876 }
1877
1878 AVFrame *queued_frame = av_frame_alloc();
1879 if (!queued_frame) {
1880 std::cerr << "Writer: failed to allocate queued HDR frame\n";
1881 return;
1882 }
1883 queued_frame->format = AV_PIX_FMT_YUV420P10LE;
1884 queued_frame->width = width;
1885 queued_frame->height = height;
1886 if (av_frame_get_buffer(queued_frame, 32) < 0) {
1887 std::cerr << "Writer: failed to allocate YUV420P10 buffer\n";
1888 releaseFrame(queued_frame);
1889 return;
1890 }
1891 if (av_frame_make_writable(queued_frame) < 0) {
1892 std::cerr << "Writer: queued HDR frame not writable\n";
1893 releaseFrame(queued_frame);
1894 return;
1895 }
1896
1897 // Convert the already-PQ/HLG-encoded 16-bit BT.2020 RGBA into the
1898 // 10-bit limited-range YUV420 plane that libx265 Main10 expects.
1899 convertBt2020Rgba16EncodedToYuv420p10(reinterpret_cast<const uint16_t *>(rgba16_buffer), width * 4, reinterpret_cast<uint16_t *>(queued_frame->data[0]), queued_frame->linesize[0] / 2, reinterpret_cast<uint16_t *>(queued_frame->data[1]), queued_frame->linesize[1] / 2, reinterpret_cast<uint16_t *>(queued_frame->data[2]), queued_frame->linesize[2] / 2, width, height);
1900
1901 queued_frame->color_primaries = static_cast<AVColorPrimaries>(hdr_info.color_primaries ? hdr_info.color_primaries : AVCOL_PRI_BT2020);
1902 queued_frame->color_trc = static_cast<AVColorTransferCharacteristic>(hdr_info.color_trc ? hdr_info.color_trc : AVCOL_TRC_SMPTE2084);
1903 queued_frame->colorspace = static_cast<AVColorSpace>(hdr_info.color_space ? hdr_info.color_space : AVCOL_SPC_BT2020_NCL);
1904 queued_frame->color_range = static_cast<AVColorRange>(hdr_info.color_range ? hdr_info.color_range : AVCOL_RANGE_MPEG);
1905
1906 {
1907 std::unique_lock<std::mutex> lock(queue_mutex);
1908 if (block_when_full.load(std::memory_order_relaxed)) {
1909 if (encode_queue.size() >= NO_DROP_QUEUE_SIZE) {
1910 queue_cv.wait(lock, [this] { return stop_requested || encode_queue.size() < NO_DROP_QUEUE_SIZE; });
1911 }
1912 if (stop_requested) {
1913 releaseFrame(queued_frame);
1914 return;
1915 }
1916 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
1917 static int drop_counter = 0;
1918 if (++drop_counter % 30 == 0) {
1919 std::cerr << "Writer: dropped " << drop_counter << " HDR frames (encoder queue full)\n";
1920 }
1921 releaseFrame(queued_frame);
1922 return;
1923 }
1924 if (pts != AV_NOPTS_VALUE && pts < frame_count) {
1925 releaseFrame(queued_frame);
1926 return;
1927 }
1928 queued_frame->pts = pts == AV_NOPTS_VALUE ? frame_count : pts;
1929 frame_count = queued_frame->pts + 1;
1930 encode_queue.push(queued_frame);
1931 }
1932
1933 queue_cv.notify_one();
1934}
void convertBt2020Rgba16EncodedToYuv420p10(const uint16_t *rgba, int src_stride_shorts, uint16_t *y_plane, int y_stride_shorts, uint16_t *u_plane, int u_stride_shorts, uint16_t *v_plane, int v_stride_shorts, int width, int height)
Definition mxwrite.cpp:139

◆ write_ts()

void Writer::write_ts ( void * rgba_buffer)

Queue a host RGBA frame using capture timestamps.

Parameters
rgba_bufferPointer to tightly packed RGBA8 pixels.

Definition at line 2049 of file mxwrite.cpp.

2049{ write(rgba_buffer); }
void write(void *rgba_buffer)
Queue a host RGBA frame for immediate-mode encoding.
Definition mxwrite.cpp:1751

The documentation for this class was generated from the following files: