Skip to content

Commit c3ab50b

Browse files
Merge pull request #50 from OpenHD/codex/fix-h264-support-and-1080p-modes
Support Cedar H.264 scaling on high-res displays
2 parents 8a6ab73 + deedadb commit c3ab50b

3 files changed

Lines changed: 125 additions & 34 deletions

File tree

allwinnerv4l2display.cpp

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ AllwinnerV4L2Display::AllwinnerV4L2Display(int udp_port,
2929
uint32_t mode_vrefresh)
3030
: m_port(udp_port),
3131
m_h265(h265),
32-
m_mode_width(mode_width ? mode_width : 1280),
33-
m_mode_height(mode_height ? mode_height : 720),
34-
m_mode_vrefresh(mode_vrefresh ? mode_vrefresh : 60) {
32+
m_stream_width(mode_width ? mode_width : 1280),
33+
m_stream_height(mode_height ? mode_height : 720),
34+
m_stream_vrefresh(mode_vrefresh ? mode_vrefresh : 60),
35+
m_display_width(mode_width),
36+
m_display_height(mode_height),
37+
m_display_vrefresh(mode_vrefresh) {
3538
if (m_port < 0) {
3639
m_input_mode = InputMode::StdIn;
3740
m_input_fd = STDIN_FILENO;
@@ -68,6 +71,7 @@ bool AllwinnerV4L2Display::start() {
6871
stop();
6972
return false;
7073
}
74+
m_modeset_initialized = false;
7175
m_running = true;
7276
m_thread = std::thread(&AllwinnerV4L2Display::decode_loop, this);
7377
return true;
@@ -79,6 +83,7 @@ void AllwinnerV4L2Display::stop() {
7983
if (m_thread.joinable())
8084
m_thread.join();
8185
}
86+
m_modeset_initialized = false;
8287

8388
if (m_sock >= 0 && m_input_mode == InputMode::UDP) {
8489
close(m_sock);
@@ -94,6 +99,10 @@ void AllwinnerV4L2Display::stop() {
9499
}
95100
if (m_drm_fd >= 0) {
96101
if (m_drm_prepared) {
102+
if (m_output.video_request) {
103+
drmModeAtomicFree(m_output.video_request);
104+
m_output.video_request = nullptr;
105+
}
97106
modeset_cleanup(m_drm_fd, &m_output);
98107
m_drm_prepared = false;
99108
}
@@ -138,9 +147,9 @@ bool AllwinnerV4L2Display::setup_drm() {
138147
}
139148
if (modeset_prepare(m_drm_fd,
140149
&m_output,
141-
m_mode_width,
142-
m_mode_height,
143-
m_mode_vrefresh,
150+
m_display_width,
151+
m_display_height,
152+
m_display_vrefresh,
144153
DRM_FORMAT_NV12,
145154
MODESET_PLANE_TYPE_PRIMARY) < 0) {
146155
return false;
@@ -159,8 +168,8 @@ bool AllwinnerV4L2Display::setup_v4l2() {
159168
struct v4l2_format fmt;
160169
memset(&fmt, 0, sizeof(fmt));
161170
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
162-
fmt.fmt.pix_mp.width = m_mode_width;
163-
fmt.fmt.pix_mp.height = m_mode_height;
171+
fmt.fmt.pix_mp.width = m_stream_width;
172+
fmt.fmt.pix_mp.height = m_stream_height;
164173
fmt.fmt.pix_mp.pixelformat =
165174
m_h265 ? V4L2_PIX_FMT_HEVC_SLICE : V4L2_PIX_FMT_H264_SLICE;
166175
fmt.fmt.pix_mp.num_planes = 1;
@@ -171,14 +180,16 @@ bool AllwinnerV4L2Display::setup_v4l2() {
171180

172181
memset(&fmt, 0, sizeof(fmt));
173182
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
174-
fmt.fmt.pix_mp.width = m_mode_width;
175-
fmt.fmt.pix_mp.height = m_mode_height;
183+
fmt.fmt.pix_mp.width = m_stream_width;
184+
fmt.fmt.pix_mp.height = m_stream_height;
176185
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
177186
fmt.fmt.pix_mp.num_planes = 2;
178187
if (ioctl(m_v4l2_fd, VIDIOC_S_FMT, &fmt) < 0) {
179188
perror("S_FMT capture");
180189
return false;
181190
}
191+
m_stream_width = fmt.fmt.pix_mp.width;
192+
m_stream_height = fmt.fmt.pix_mp.height;
182193

183194
struct v4l2_requestbuffers req;
184195
memset(&req, 0, sizeof(req));
@@ -267,7 +278,7 @@ bool AllwinnerV4L2Display::setup_v4l2() {
267278
fmt.fmt.pix_mp.plane_fmt[1].bytesperline, 0, 0};
268279
uint32_t offsets[4] = {planes[0].data_offset, planes[1].data_offset, 0, 0};
269280
uint32_t fb_id = 0;
270-
if (drmModeAddFB2(m_drm_fd, m_mode_width, m_mode_height, DRM_FORMAT_NV12,
281+
if (drmModeAddFB2(m_drm_fd, m_stream_width, m_stream_height, DRM_FORMAT_NV12,
271282
handles, pitches, offsets, &fb_id, 0) != 0) {
272283
perror("AddFB2");
273284
close(prime_fd);
@@ -343,7 +354,26 @@ void AllwinnerV4L2Display::decode_loop() {
343354
cbuf.m.planes = cplanes;
344355
if (ioctl(m_v4l2_fd, VIDIOC_DQBUF, &cbuf) == 0) {
345356
int fb_id = m_capture_fbs[cbuf.index];
346-
extra_modeset_set_fb(m_drm_fd, &m_output, &m_output.video_plane, fb_id);
357+
bool submit_via_set_fb = true;
358+
if (!m_modeset_initialized && m_output.video_request) {
359+
int ret = modeset_perform_modeset(m_drm_fd,
360+
&m_output,
361+
m_output.video_request,
362+
&m_output.video_plane,
363+
fb_id,
364+
static_cast<int>(m_stream_width),
365+
static_cast<int>(m_stream_height),
366+
0);
367+
if (ret == 0) {
368+
m_modeset_initialized = true;
369+
submit_via_set_fb = false;
370+
} else {
371+
std::cerr << "Failed to perform initial modeset for Cedar display path." << std::endl;
372+
}
373+
}
374+
if (submit_via_set_fb) {
375+
extra_modeset_set_fb(m_drm_fd, &m_output, &m_output.video_plane, fb_id);
376+
}
347377
ioctl(m_v4l2_fd, VIDIOC_QBUF, &cbuf);
348378
}
349379

allwinnerv4l2display.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ class AllwinnerV4L2Display {
5656
InputMode m_input_mode{InputMode::UDP};
5757
bool m_drm_prepared{false};
5858

59-
uint32_t m_mode_width{1280};
60-
uint32_t m_mode_height{720};
61-
uint32_t m_mode_vrefresh{60};
59+
uint32_t m_stream_width{1280};
60+
uint32_t m_stream_height{720};
61+
uint32_t m_stream_vrefresh{60};
62+
uint32_t m_display_width{0};
63+
uint32_t m_display_height{0};
64+
uint32_t m_display_vrefresh{0};
65+
bool m_modeset_initialized{false};
6266

6367
struct modeset_output m_output{};
6468

drm.c

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -488,25 +488,82 @@ struct modeset_output *modeset_output_create(int fd, drmModeRes *res, drmModeCon
488488
goto out_error;
489489
}
490490

491-
int fc = 0;
492-
if (mode_width>0 && mode_height>0 && mode_vrefresh>0) {
493-
fc = -1;
494-
printf( "Available modes:\n");
495-
for (int i = 0; i < conn->count_modes; i++ ) {
496-
printf( "%d : %dx%d@%d\n",i, conn->modes[i].hdisplay, conn->modes[i].vdisplay , conn->modes[i].vrefresh );
497-
if (conn->modes[i].hdisplay == mode_width &&
498-
conn->modes[i].vdisplay == mode_height &&
499-
conn->modes[i].vrefresh == mode_vrefresh
500-
) {
501-
fc = i;
502-
}
503-
}
504-
if (fc < 0) {
505-
fprintf(stderr, "couldn't find a matching mode for %dx%d@%d\n", mode_width , mode_height , mode_vrefresh);
506-
goto out_error;
507-
}
508-
printf( "Using screen mode %dx%d@%d\n",conn->modes[fc].hdisplay, conn->modes[fc].vdisplay , conn->modes[fc].vrefresh );
509-
}
491+
int fc = 0;
492+
if (mode_width > 0 && mode_height > 0 && mode_vrefresh > 0) {
493+
fc = -1;
494+
int upscale_candidate = -1;
495+
int same_refresh_candidate = -1;
496+
int max_candidate = 0;
497+
uint64_t requested_area = (uint64_t)mode_width * (uint64_t)mode_height;
498+
uint64_t best_upscale_overhead = UINT64_MAX;
499+
uint64_t best_same_refresh_area = 0;
500+
uint64_t best_max_area = 0;
501+
printf("Available modes:\n");
502+
for (int i = 0; i < conn->count_modes; i++) {
503+
const drmModeModeInfo *candidate = &conn->modes[i];
504+
uint64_t area = (uint64_t)candidate->hdisplay * (uint64_t)candidate->vdisplay;
505+
printf("%d : %dx%d@%d\n", i, candidate->hdisplay, candidate->vdisplay, candidate->vrefresh);
506+
if (candidate->hdisplay == mode_width &&
507+
candidate->vdisplay == mode_height &&
508+
candidate->vrefresh == mode_vrefresh) {
509+
fc = i;
510+
}
511+
if (area > best_max_area) {
512+
best_max_area = area;
513+
max_candidate = i;
514+
}
515+
if (candidate->vrefresh != mode_vrefresh)
516+
continue;
517+
if (area >= requested_area) {
518+
uint64_t overhead = area - requested_area;
519+
if (overhead < best_upscale_overhead ||
520+
(overhead == best_upscale_overhead && upscale_candidate >= 0 &&
521+
area < (uint64_t)conn->modes[upscale_candidate].hdisplay *
522+
(uint64_t)conn->modes[upscale_candidate].vdisplay)) {
523+
best_upscale_overhead = overhead;
524+
upscale_candidate = i;
525+
} else if (overhead == best_upscale_overhead && upscale_candidate < 0) {
526+
best_upscale_overhead = overhead;
527+
upscale_candidate = i;
528+
}
529+
}
530+
if (area > best_same_refresh_area) {
531+
best_same_refresh_area = area;
532+
same_refresh_candidate = i;
533+
}
534+
}
535+
if (fc < 0) {
536+
if (upscale_candidate >= 0) {
537+
fc = upscale_candidate;
538+
fprintf(stderr,
539+
"couldn't find a matching mode for %dx%d@%d, using %dx%d@%d for upscaling\n",
540+
mode_width, mode_height, mode_vrefresh,
541+
conn->modes[fc].hdisplay,
542+
conn->modes[fc].vdisplay,
543+
conn->modes[fc].vrefresh);
544+
} else if (same_refresh_candidate >= 0) {
545+
fc = same_refresh_candidate;
546+
fprintf(stderr,
547+
"couldn't find a matching mode for %dx%d@%d, using %dx%d@%d with matching refresh\n",
548+
mode_width, mode_height, mode_vrefresh,
549+
conn->modes[fc].hdisplay,
550+
conn->modes[fc].vdisplay,
551+
conn->modes[fc].vrefresh);
552+
} else {
553+
fc = max_candidate;
554+
fprintf(stderr,
555+
"couldn't find a matching mode for %dx%d@%d, using %dx%d@%d\n",
556+
mode_width, mode_height, mode_vrefresh,
557+
conn->modes[fc].hdisplay,
558+
conn->modes[fc].vdisplay,
559+
conn->modes[fc].vrefresh);
560+
}
561+
}
562+
printf("Using screen mode %dx%d@%d\n",
563+
conn->modes[fc].hdisplay,
564+
conn->modes[fc].vdisplay,
565+
conn->modes[fc].vrefresh);
566+
}
510567
memcpy(&out->mode, &conn->modes[fc], sizeof(out->mode));
511568
if (drmModeCreatePropertyBlob(fd, &out->mode, sizeof(out->mode), &out->mode_blob_id) != 0) {
512569
fprintf(stderr, "couldn't create a blob property\n");

0 commit comments

Comments
 (0)