11#include <libavcodec/avcodec.h>
12#include <libavdevice/avdevice.h>
13#include <libavformat/avformat.h>
14#include <libavutil/dict.h>
15#include <libavutil/frame.h>
16#include <libavutil/imgutils.h>
17#include <libswscale/swscale.h>
35 .get_service<Registry::Service::IOService>()) {
66 ? std::string(CAMERA_FORMAT)
69 AVDictionary* opts =
nullptr;
71 std::string fps_str = std::to_string(
static_cast<int>(config.
target_fps));
72 av_dict_set(&opts,
"framerate", fps_str.c_str(), 0);
74 std::string size_str = std::to_string(config.
target_width)
78 av_dict_set(&opts,
"video_size", size_str.c_str(), 0);
104 "CameraReader: opened '{}' via {} — {}x{} @{:.1f}fps (scaler deferred)",
130std::shared_ptr<Kakshya::CameraContainer>
134 if (!
m_video->is_codec_valid()) {
135 m_last_error =
"Cannot create container: reader not open";
141 m_last_error =
"Requested pixel format has no ImageFormat equivalent";
145 return std::make_shared<Kakshya::CameraContainer>(
153 const std::shared_ptr<Kakshya::CameraContainer>& container)
159 if (!
m_video->is_codec_valid())
162 uint8_t* dest = container->mutable_frame_ptr();
166 AVPacket* pkt = av_packet_alloc();
170 AVFrame* frame = av_frame_alloc();
172 av_packet_free(&pkt);
176 bool got_frame =
false;
179 int ret = av_read_frame(
m_demux->format_context, pkt);
183 if (pkt->stream_index !=
m_video->stream_index) {
184 av_packet_unref(pkt);
188 ret = avcodec_send_packet(
m_video->codec_context, pkt);
189 av_packet_unref(pkt);
191 if (ret < 0 && ret != AVERROR(EAGAIN))
194 ret = avcodec_receive_frame(
m_video->codec_context, frame);
195 if (ret == AVERROR(EAGAIN))
201 if (!
m_video->rebuild_scaler_from_frame(
203 static_cast<uint32_t
>(frame->width),
204 static_cast<uint32_t
>(frame->height))) {
206 "CameraReader: scaler init failed: {}",
210 const size_t buf_bytes =
static_cast<size_t>(
m_video->out_linesize) *
m_video->out_height;
215 uint8_t* sws_dst[1] = {
m_sws_buf.data() };
216 int sws_stride[1] = {
m_video->out_linesize };
218 sws_scale(
m_video->sws_context,
219 frame->data, frame->linesize,
220 0,
static_cast<int>(
m_video->height),
221 sws_dst, sws_stride);
223 const int packed_stride =
static_cast<int>(
m_video->out_width *
m_video->out_bytes_per_pixel);
225 if (
m_video->out_linesize == packed_stride) {
227 static_cast<size_t>(packed_stride) *
m_video->out_height);
229 for (uint32_t row = 0; row <
m_video->out_height; ++row) {
231 dest +
static_cast<size_t>(row) * packed_stride,
233 static_cast<size_t>(packed_stride));
237 container->mark_ready_for_processing(
true);
241 av_frame_free(&frame);
242 av_packet_free(&pkt);
266 const std::shared_ptr<Kakshya::CameraContainer>& container)
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
std::atomic< bool > m_decode_stop
uint32_t height() const
Negotiated output height in pixels.
std::atomic< bool > m_decode_active
void setup_io_service(uint64_t reader_id)
Setup an IOService for this reader with the given reader_id.
uint64_t m_standalone_reader_id
int m_requested_pixel_format
Pixel format requested at open() time, as an AVPixelFormat int.
void start_decode_thread()
void set_container(const std::shared_ptr< Kakshya::CameraContainer > &container)
Store a weak reference to the container for IOService dispatch.
std::atomic< bool > m_frame_requested
const std::string & last_error() const
Last error string, empty if no error.
std::shared_ptr< Registry::Service::IOService > m_standalone_service
std::thread m_decode_thread
std::vector< uint8_t > m_sws_buf
std::shared_mutex m_ctx_mutex
void close()
Release codec, demux, and scratch buffer resources.
bool is_open() const
True if the device is open and codec is ready.
std::weak_ptr< Kakshya::CameraContainer > m_container_ref
std::shared_ptr< VideoStreamContext > m_video
bool open(const CameraConfig &config)
Open a camera device using the supplied config.
std::condition_variable m_decode_cv
std::shared_ptr< FFmpegDemuxContext > m_demux
void pull_frame_all()
Signal the background decode thread to pull one frame.
void decode_thread_func()
bool pull_frame(const std::shared_ptr< Kakshya::CameraContainer > &container)
Decode one frame from the device into the container's m_data[0].
std::mutex m_decode_mutex
void stop_decode_thread()
double frame_rate() const
Negotiated frame rate in fps.
std::shared_ptr< Kakshya::CameraContainer > create_container() const
Create a CameraContainer sized to the negotiated device resolution.
uint32_t width() const
Negotiated output width in pixels.
RAII owner of a single AVFormatContext and associated demux state.
RAII owner of one video stream's codec and pixel-format scaler state.
void register_service(ServiceFactory factory)
Register a backend service capability.
static BackendRegistry & instance()
Get the global registry instance.
void unregister_service()
Unregister a service.
std::optional< Portal::Graphics::ImageFormat > to_image_format(int av_pixel_format)
Map an AVPixelFormat to the Portal ImageFormat backing it.
@ FileIO
Filesystem I/O operations.
@ Runtime
General runtime operations (default fallback)
@ IO
Networking, file handling, streaming.
int pixel_format
Target AVPixelFormat as int; negative selects AV_PIX_FMT_RGBA.
std::string device_name
Platform device string.
std::string format_override
Leave empty to use CAMERA_FORMAT for current platform.
uint32_t target_width
Requested width in pixels.
uint32_t target_height
Requested height in pixels.
double target_fps
Hint only; device may ignore.
Platform-specific FFmpeg input format string for camera devices.
Backend IO streaming service interface.