MXVK Vulkan Framework 0.33.1
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 139 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:242
void close()
Close the writer and flush pending packets.
Definition mxwrite.cpp:2425

Member Function Documentation

◆ close()

void Writer::close ( )

Close the writer and flush pending packets.

Definition at line 2425 of file mxwrite.cpp.

2425 {
2426 std::lock_guard<std::mutex> lock(writer_mutex);
2427 if (!opened) {
2428 return;
2429 }
2430
2431 stopEncoderThread();
2432
2433 if (stream && stream->duration > 0) {
2434 last_duration = static_cast<double>(stream->duration) * av_q2d(stream->time_base);
2435 } else if (fps_num > 0 && fps_den > 0) {
2436 last_duration = static_cast<double>(frame_count) * static_cast<double>(fps_den) / static_cast<double>(fps_num);
2437 }
2438
2439 av_write_trailer(format_ctx);
2440 updateBytesWritten();
2441
2442 if (!(format_ctx->oformat->flags & AVFMT_NOFILE)) {
2443 avio_closep(&format_ctx->pb);
2444 }
2445
2446 av_frame_free(&frameRGBA);
2447 av_frame_free(&frameYUV);
2448 av_frame_free(&frame10);
2449 sws_freeContext(sws_ctx);
2450 av_frame_free(&upload_sw_frame);
2451#ifdef MXWRITE_HAS_CUDA_COPY
2452 // Destroy stream before tearing down FFmpeg CUDA device/frames contexts.
2453 if (cuda_upload_stream) {
2454 cudaStreamSynchronize(cuda_upload_stream);
2455 cudaStreamDestroy(cuda_upload_stream);
2456 cuda_upload_stream = nullptr;
2457 }
2458#endif
2459 avcodec_free_context(&codec_ctx);
2460 av_buffer_unref(&hw_frames_ctx);
2461 av_buffer_unref(&hw_device_ctx);
2462 avformat_free_context(format_ctx);
2463
2464 while (!encode_queue.empty()) {
2465 releaseFrame(encode_queue.front());
2466 encode_queue.pop();
2467 }
2468 opened = false;
2469 format_ctx = nullptr;
2470 codec_ctx = nullptr;
2471 sws_ctx = nullptr;
2472 frameRGBA = nullptr;
2473 frameYUV = nullptr;
2474 frame10 = nullptr;
2475 upload_sw_frame = nullptr;
2476 use_hw_encode = false;
2477 active_encoder_hardware = false;
2478 direct_cuda_upload = false;
2479 hdr_output = false;
2480 stop_requested = false;
2481}

◆ 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 250 of file mxwrite.hpp.

250{ 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 260 of file mxwrite.hpp.

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

◆ get_duration()

double Writer::get_duration ( ) const

Return the encoded duration in seconds.

Definition at line 2483 of file mxwrite.cpp.

2483 {
2484 if (!opened && last_duration > 0.0) {
2485 return last_duration;
2486 }
2487 if (stream && stream->duration > 0) {
2488 return static_cast<double>(stream->duration) * av_q2d(stream->time_base);
2489 }
2490 if (fps_num > 0 && fps_den > 0) {
2491 return static_cast<double>(frame_count) * static_cast<double>(fps_den) / static_cast<double>(fps_num);
2492 }
2493 return 0.0;
2494}

◆ 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 258 of file mxwrite.hpp.

258{ 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 244 of file mxwrite.hpp.

244{ return active_encoder_hardware; }

◆ is_open()

bool Writer::is_open ( ) const
inline

Check whether the writer is currently open.

Definition at line 242 of file mxwrite.hpp.

242{ 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 1039 of file mxwrite.cpp.

1039 {
1040 std::lock_guard<std::mutex> lock(writer_mutex);
1041 EncodeOptions opts;
1042 if (crf && *crf) {
1043 try {
1044 opts.crf = std::stoi(crf);
1045 } catch (...) {
1046 }
1047 }
1048 // Preserve legacy low-latency behaviour for old callers.
1049 opts.preset = "ultrafast";
1050 opts.tune = "zerolatency";
1051 opts.realtime = true;
1052 return openInternal(filename, w, h, fps, opts, false);
1053}
std::string tune
Optional tuning mode.
Definition mxwrite.hpp:94
bool realtime
Enable low-latency settings.
Definition mxwrite.hpp:98
int crf
Constant Rate Factor.
Definition mxwrite.hpp:95
std::string preset
Encoder preset name.
Definition mxwrite.hpp:93

◆ 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 1055 of file mxwrite.cpp.

1055 {
1056 std::lock_guard<std::mutex> lock(writer_mutex);
1057 return openInternal(filename, w, h, fps, opts, false);
1058}

◆ 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 1060 of file mxwrite.cpp.

1060 {
1061 std::lock_guard<std::mutex> lock(writer_mutex);
1062 EncodeOptions opts;
1063 if (crf && *crf) {
1064 try {
1065 opts.crf = std::stoi(crf);
1066 } catch (...) {
1067 }
1068 }
1069 opts.preset = "ultrafast";
1070 opts.tune = "zerolatency";
1071 opts.realtime = true;
1072 return openInternal(filename, w, h, fps, opts, true);
1073}

◆ 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 1075 of file mxwrite.cpp.

1075 {
1076 std::lock_guard<std::mutex> lock(writer_mutex);
1077 return openInternal(filename, w, h, fps, opts, true);
1078}

◆ 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 248 of file mxwrite.hpp.

248{ 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 1859 of file mxwrite.cpp.

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

◆ 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 1863 of file mxwrite.cpp.

1863 {
1864 if (!rgba_buffer) {
1865 return;
1866 }
1867
1868 {
1869 std::lock_guard<std::mutex> lock(writer_mutex);
1870 if (!opened) {
1871 return;
1872 }
1873 }
1874
1875 AVFrame *queued_frame = av_frame_alloc();
1876 if (!queued_frame) {
1877 std::cerr << "Writer: failed to allocate queued frame\n";
1878 return;
1879 }
1880
1881 if (use_hw_encode && hdr_output) {
1882 queued_frame->format = codec_ctx->pix_fmt;
1883 queued_frame->width = width;
1884 queued_frame->height = height;
1885 if (av_hwframe_get_buffer(hw_frames_ctx, queued_frame, 0) < 0) {
1886 std::cerr << "Writer: failed to allocate frame from hardware pool\n";
1887 releaseFrame(queued_frame);
1888 return;
1889 }
1890
1891 if (av_frame_make_writable(upload_sw_frame) < 0) {
1892 std::cerr << "Writer: software upload frame not writable\n";
1893 releaseFrame(queued_frame);
1894 return;
1895 }
1896
1897 const auto *src = static_cast<const uint8_t *>(rgba_buffer);
1898 if (upload_sw_frame->format == AV_PIX_FMT_RGBA) {
1899 for (int y = 0; y < height; ++y) {
1900 std::memcpy(upload_sw_frame->data[0] +
1901 static_cast<size_t>(y) * upload_sw_frame->linesize[0],
1902 src + static_cast<size_t>(y) * static_cast<size_t>(width) * 4,
1903 static_cast<size_t>(width) * 4);
1904 }
1905 } else {
1906 const uint8_t *source_data[1] = {src};
1907 const int source_linesize[1] = {width * 4};
1908 sws_scale(sws_ctx, source_data, source_linesize, 0, height,
1909 upload_sw_frame->data, upload_sw_frame->linesize);
1910 }
1911
1912 if (av_hwframe_transfer_data(queued_frame, upload_sw_frame, 0) < 0) {
1913 std::cerr << "Writer: failed to transfer system frame to hardware frame\n";
1914 releaseFrame(queued_frame);
1915 return;
1916 }
1917 } else {
1918 queued_frame->format = AV_PIX_FMT_RGBA;
1919 queued_frame->width = width;
1920 queued_frame->height = height;
1921 if (av_frame_get_buffer(queued_frame, 32) < 0) {
1922 std::cerr << "Writer: failed to allocate queued RGBA frame buffer\n";
1923 releaseFrame(queued_frame);
1924 return;
1925 }
1926 if (av_frame_make_writable(queued_frame) < 0) {
1927 std::cerr << "Writer: queued RGBA frame not writable\n";
1928 releaseFrame(queued_frame);
1929 return;
1930 }
1931
1932 const auto *src = static_cast<const uint8_t *>(rgba_buffer);
1933 for (int y = 0; y < height; ++y) {
1934 std::memcpy(queued_frame->data[0] + static_cast<size_t>(y) * queued_frame->linesize[0],
1935 src + static_cast<size_t>(y) * static_cast<size_t>(width) * 4,
1936 static_cast<size_t>(width) * 4);
1937 }
1938 }
1939
1940 {
1941 std::unique_lock<std::mutex> lock(queue_mutex);
1942 if (block_when_full.load(std::memory_order_relaxed)) {
1943 if (encode_queue.size() >= NO_DROP_QUEUE_SIZE) {
1944 // Keep file processing paced with the encoder. Wake whenever
1945 // one queue slot becomes available rather than waiting for a
1946 // large queue to fill and then drain completely.
1947 queue_cv.wait(lock, [this] {
1948 return stop_requested || encode_queue.size() < NO_DROP_QUEUE_SIZE;
1949 });
1950 }
1951 if (stop_requested) {
1952 releaseFrame(queued_frame);
1953 return;
1954 }
1955 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
1956 static int drop_counter = 0;
1957 if (++drop_counter % 30 == 0) {
1958 std::cerr << "Writer: dropped " << drop_counter << " SDR frames (encoder queue full)\n";
1959 }
1960 releaseFrame(queued_frame);
1961 return;
1962 }
1963 if (pts != AV_NOPTS_VALUE && pts < frame_count) {
1964 // A timestamped live frame landed in a nominal frame slot that
1965 // was already filled. Drop it instead of extending the video
1966 // timeline and allowing it to drift behind the capture clock.
1967 releaseFrame(queued_frame);
1968 return;
1969 }
1970 queued_frame->pts = pts == AV_NOPTS_VALUE ? frame_count : pts;
1971 frame_count = queued_frame->pts + 1;
1972 encode_queue.push(queued_frame);
1973 }
1974
1975 queue_cv.notify_one();
1976}

◆ 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 2072 of file mxwrite.cpp.

2072 {
2073 return write_cuda_rgba_at_pts(cuda_rgba_buffer, src_stride, AV_NOPTS_VALUE,
2074 bottom_up);
2075}
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:2077

◆ 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 2077 of file mxwrite.cpp.

2079 {
2080 if (!cuda_rgba_buffer || src_stride <= 0) {
2081 return false;
2082 }
2083
2084 {
2085 std::lock_guard<std::mutex> lock(writer_mutex);
2086 if (!opened || !use_hw_encode || !direct_cuda_upload) {
2087 return false;
2088 }
2089 }
2090
2091 AVFrame *queued_frame = av_frame_alloc();
2092 if (!queued_frame) {
2093 std::cerr << "Writer: failed to allocate queued CUDA frame\n";
2094 return false;
2095 }
2096
2097 queued_frame->format = AV_PIX_FMT_CUDA;
2098 queued_frame->width = width;
2099 queued_frame->height = height;
2100
2101 if (av_hwframe_get_buffer(hw_frames_ctx, queued_frame, 0) < 0) {
2102 std::cerr << "Writer: failed to allocate CUDA frame from hardware pool\n";
2103 releaseFrame(queued_frame);
2104 return false;
2105 }
2106
2107#ifdef MXWRITE_HAS_CUDA_COPY
2108 cudaStream_t stream = cuda_upload_stream;
2109 const bool use_async_stream = (stream != nullptr);
2110 if (!bottom_up) {
2111 const auto copy_err = use_async_stream
2112 ? cudaMemcpy2DAsync(
2113 queued_frame->data[0],
2114 static_cast<size_t>(queued_frame->linesize[0]),
2115 cuda_rgba_buffer,
2116 static_cast<size_t>(src_stride),
2117 static_cast<size_t>(width) * 4,
2118 static_cast<size_t>(height),
2119 cudaMemcpyDeviceToDevice,
2120 stream)
2121 : cudaMemcpy2D(
2122 queued_frame->data[0],
2123 static_cast<size_t>(queued_frame->linesize[0]),
2124 cuda_rgba_buffer,
2125 static_cast<size_t>(src_stride),
2126 static_cast<size_t>(width) * 4,
2127 static_cast<size_t>(height),
2128 cudaMemcpyDeviceToDevice);
2129
2130 if (copy_err != cudaSuccess) {
2131 std::cerr << "Writer: cudaMemcpy2D device upload failed: " << cudaGetErrorString(copy_err) << "\n";
2132 releaseFrame(queued_frame);
2133 return false;
2134 }
2135 } else {
2136 // Flip vertically by issuing one async row copy per destination row
2137 // onto a single stream — kept asynchronous so launch overhead overlaps
2138 // and the producer thread only blocks once at cudaStreamSynchronize.
2139 auto *src_base = static_cast<unsigned char *>(cuda_rgba_buffer);
2140 auto *dst_base = queued_frame->data[0];
2141 const size_t row_bytes = static_cast<size_t>(width) * 4;
2142
2143 for (int y = 0; y < height; ++y) {
2144 auto *src_row = src_base + static_cast<size_t>(height - 1 - y) * static_cast<size_t>(src_stride);
2145 auto *dst_row = dst_base + static_cast<size_t>(y) * static_cast<size_t>(queued_frame->linesize[0]);
2146 const auto row_copy_err = use_async_stream
2147 ? cudaMemcpyAsync(dst_row, src_row, row_bytes, cudaMemcpyDeviceToDevice, stream)
2148 : cudaMemcpy(dst_row, src_row, row_bytes, cudaMemcpyDeviceToDevice);
2149 if (row_copy_err != cudaSuccess) {
2150 std::cerr << "Writer: cudaMemcpy row upload failed: " << cudaGetErrorString(row_copy_err) << "\n";
2151 releaseFrame(queued_frame);
2152 return false;
2153 }
2154 }
2155 }
2156 // Single synchronisation point — NVENC requires the data to be ready when
2157 // avcodec_send_frame() reads it, and the encoder thread is decoupled by
2158 // the queue so this sync only serialises this one producer call.
2159 if (use_async_stream) {
2160 const auto sync_err = cudaStreamSynchronize(stream);
2161 if (sync_err != cudaSuccess) {
2162 std::cerr << "Writer: cudaStreamSynchronize failed: " << cudaGetErrorString(sync_err) << "\n";
2163 releaseFrame(queued_frame);
2164 return false;
2165 }
2166 }
2167#else
2168 std::cerr << "Writer: CUDA copy support disabled at build time\n";
2169 releaseFrame(queued_frame);
2170 return false;
2171#endif
2172
2173 {
2174 std::unique_lock<std::mutex> lock(queue_mutex);
2175 if (block_when_full.load(std::memory_order_relaxed)) {
2176 if (encode_queue.size() >= NO_DROP_QUEUE_SIZE) {
2177 queue_cv.wait(lock, [this] {
2178 return stop_requested || encode_queue.size() < NO_DROP_QUEUE_SIZE;
2179 });
2180 }
2181 if (stop_requested) {
2182 releaseFrame(queued_frame);
2183 return false;
2184 }
2185 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
2186 static int drop_counter = 0;
2187 if (++drop_counter % 30 == 0) {
2188 std::cerr << "Writer: dropped " << drop_counter << " frames (encoder queue full)\n";
2189 }
2190 releaseFrame(queued_frame);
2191 // Return TRUE so the producer does NOT fall back to the slow CPU
2192 // write() path — we already "handled" the frame (by dropping it).
2193 // Falling back would double-process every frame and double the drops.
2194 queue_cv.notify_one();
2195 return true;
2196 }
2197 if (pts != AV_NOPTS_VALUE && pts < frame_count) {
2198 releaseFrame(queued_frame);
2199 queue_cv.notify_one();
2200 return true;
2201 }
2202 queued_frame->pts = pts == AV_NOPTS_VALUE ? frame_count : pts;
2203 frame_count = queued_frame->pts + 1;
2204 encode_queue.push(queued_frame);
2205 }
2206
2207 queue_cv.notify_one();
2208 return true;
2209}

◆ 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 1978 of file mxwrite.cpp.

1978 {
1979 write_hdr_rgba16_at_pts(rgba16_buffer, AV_NOPTS_VALUE);
1980}
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:1982

◆ 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 1982 of file mxwrite.cpp.

1982 {
1983 if (!rgba16_buffer) {
1984 return;
1985 }
1986
1987 {
1988 std::lock_guard<std::mutex> lock(writer_mutex);
1989 if (!opened) {
1990 return;
1991 }
1992 if (!hdr_output) {
1993 std::cerr << "Writer: write_hdr_rgba16 called but writer not in HDR mode\n";
1994 return;
1995 }
1996 }
1997
1998 AVFrame *queued_frame = av_frame_alloc();
1999 if (!queued_frame) {
2000 std::cerr << "Writer: failed to allocate queued HDR frame\n";
2001 return;
2002 }
2003 queued_frame->format = AV_PIX_FMT_YUV420P10LE;
2004 queued_frame->width = width;
2005 queued_frame->height = height;
2006 if (av_frame_get_buffer(queued_frame, 32) < 0) {
2007 std::cerr << "Writer: failed to allocate YUV420P10 buffer\n";
2008 releaseFrame(queued_frame);
2009 return;
2010 }
2011 if (av_frame_make_writable(queued_frame) < 0) {
2012 std::cerr << "Writer: queued HDR frame not writable\n";
2013 releaseFrame(queued_frame);
2014 return;
2015 }
2016
2017 // Convert the already-PQ/HLG-encoded 16-bit BT.2020 RGBA into the
2018 // 10-bit limited-range YUV420 plane that libx265 Main10 expects.
2020 reinterpret_cast<const uint16_t *>(rgba16_buffer),
2021 width * 4,
2022 reinterpret_cast<uint16_t *>(queued_frame->data[0]),
2023 queued_frame->linesize[0] / 2,
2024 reinterpret_cast<uint16_t *>(queued_frame->data[1]),
2025 queued_frame->linesize[1] / 2,
2026 reinterpret_cast<uint16_t *>(queued_frame->data[2]),
2027 queued_frame->linesize[2] / 2,
2028 width,
2029 height);
2030
2031 queued_frame->color_primaries = static_cast<AVColorPrimaries>(
2032 hdr_info.color_primaries ? hdr_info.color_primaries : AVCOL_PRI_BT2020);
2033 queued_frame->color_trc = static_cast<AVColorTransferCharacteristic>(
2034 hdr_info.color_trc ? hdr_info.color_trc : AVCOL_TRC_SMPTE2084);
2035 queued_frame->colorspace = static_cast<AVColorSpace>(
2036 hdr_info.color_space ? hdr_info.color_space : AVCOL_SPC_BT2020_NCL);
2037 queued_frame->color_range = static_cast<AVColorRange>(
2038 hdr_info.color_range ? hdr_info.color_range : AVCOL_RANGE_MPEG);
2039
2040 {
2041 std::unique_lock<std::mutex> lock(queue_mutex);
2042 if (block_when_full.load(std::memory_order_relaxed)) {
2043 if (encode_queue.size() >= NO_DROP_QUEUE_SIZE) {
2044 queue_cv.wait(lock, [this] {
2045 return stop_requested || encode_queue.size() < NO_DROP_QUEUE_SIZE;
2046 });
2047 }
2048 if (stop_requested) {
2049 releaseFrame(queued_frame);
2050 return;
2051 }
2052 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
2053 static int drop_counter = 0;
2054 if (++drop_counter % 30 == 0) {
2055 std::cerr << "Writer: dropped " << drop_counter << " HDR frames (encoder queue full)\n";
2056 }
2057 releaseFrame(queued_frame);
2058 return;
2059 }
2060 if (pts != AV_NOPTS_VALUE && pts < frame_count) {
2061 releaseFrame(queued_frame);
2062 return;
2063 }
2064 queued_frame->pts = pts == AV_NOPTS_VALUE ? frame_count : pts;
2065 frame_count = queued_frame->pts + 1;
2066 encode_queue.push(queued_frame);
2067 }
2068
2069 queue_cv.notify_one();
2070}
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:146

◆ 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 2211 of file mxwrite.cpp.

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

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