Getting screen info with xcb and randr - opengl
I was trying to write some simple graphics with Xlib, XF86VidMode, and OpenGL. I had two issues:
Xlib doesn't seem to have the equivalent of WM_TIMER, so I wrote a SIGALRM handler that sent messages to unblock the message loop, but since such usage is totally thread-unsafe, the program would hang after a little while. Thus I tried recoding in xcb.
XF86VidMode was awkward to use and I didn't like the results so I switched to RandR.
Having done the above, it turned out that xcb had the same hang, so I couldn't code a blocking message loop. Instead I polled every once in a while and the program didn't hang, but there were skipped frames that were annoying.
While I could switch the video mode with RandR, I wanted to use the xcb version, which didn't work. I plagiarized an example of #datenwolf for the xcb, but somehow xcb_randr_get_screen_info_reply didn't work. It's supposed to return a pointer to a structure that's followed by an array of screen dimensions (the dimensions in mm are wrong) and then the refresh rate data. The refresh rate data is garbage and mostly zeros. What am I doing wrong?
/*
gcc rrxcb.c -lxcb-randr -lxcb -lX11 -lX11-xcb -lGL -orrxcb
*/
#include <xcb/randr.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h>
#include <xcb/xcb.h>
#include <GL/glx.h>
#include <unistd.h>
void screen_from_Xlib_Display(
Display * const display,
xcb_connection_t *connection,
int * const out_screen_num,
xcb_screen_t ** const out_screen)
{
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
int screen_num = DefaultScreen(display);
while( screen_iter.rem && screen_num > 0 ) {
xcb_screen_next(&screen_iter);
--screen_num;
}
*out_screen_num = screen_num;
*out_screen = screen_iter.data;
}
int main()
{
Display *display;
xcb_connection_t *connection;
xcb_window_t win;
const int GLX_TRUE = True;
int attrib_list[] = {GLX_X_RENDERABLE,GLX_TRUE,
GLX_DRAWABLE_TYPE,GLX_WINDOW_BIT,
GLX_RENDER_TYPE,GLX_RGBA_BIT,
GLX_CONFIG_CAVEAT,GLX_NONE,
GLX_DOUBLEBUFFER,GLX_TRUE,
GLX_BUFFER_SIZE,32,
GLX_DEPTH_SIZE,24,
GLX_STENCIL_SIZE,8,
0};
GLXFBConfig *FBConfigs;
int nelements;
GLXFBConfig fb_config;
XVisualInfo *visual;
int visualID;
GLXContext context;
xcb_colormap_t colormap;
xcb_void_cookie_t create_color;
xcb_void_cookie_t create_win;
const uint32_t eventmask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS;
const uint32_t valuemask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
uint32_t valuelist[] = {eventmask,colormap};
xcb_randr_get_screen_info_cookie_t screen_info;
xcb_randr_get_screen_info_reply_t *reply;
int screen_num;
xcb_screen_t *screen;
xcb_generic_error_t *error;
xcb_randr_screen_size_t *sizes;
int sizes_length;
xcb_randr_refresh_rates_iterator_t rates_iter;
uint16_t *rates;
int rates_length;
int i;
/* Open Xlib Display */
display = XOpenDisplay(NULL);
printf("display = %p\n",display);
connection = XGetXCBConnection(display);
printf("connection = %p\n",connection);
XSetEventQueueOwner(display,XCBOwnsEventQueue);
win = xcb_generate_id(connection);
printf("win = %d\n",win);
screen_from_Xlib_Display(display,connection,&screen_num,&screen);
printf("screen_num = %d\n",screen_num);
printf("screen->root = %d\n",screen->root);
FBConfigs = glXChooseFBConfig(display,screen_num,attrib_list,
&nelements);
printf("FBConfig = %p\n",FBConfigs);
printf("nelements = %d\n",nelements);
fb_config = FBConfigs[0];
visual = glXGetVisualFromFBConfig(display,fb_config);
printf("visual = %p\n",visual);
visualID = visual->visualid;
printf("visualID = %d\n",visualID);
context = glXCreateNewContext(display,fb_config,GLX_RGBA_TYPE,
0,True);
printf("context = %p\n",context);
colormap = xcb_generate_id(connection);
printf("colormap = %d\n",colormap);
create_color = xcb_create_colormap_checked(connection,
XCB_COLORMAP_ALLOC_NONE,colormap,screen->root,visualID);
printf("create_color.sequence = %d\n",create_color.sequence);
error = xcb_request_check(connection,create_color);
printf("error = %p\n",error);
create_win = xcb_create_window_checked(connection,
XCB_COPY_FROM_PARENT,win, screen->root,0,0,640,480,2,
XCB_WINDOW_CLASS_INPUT_OUTPUT,visualID,valuemask,valuelist);
printf("create_win.sequence = %d\n",create_win.sequence);
error = xcb_request_check(connection,create_win);
printf("error = %p\n",error);
screen_info = xcb_randr_get_screen_info_unchecked(connection, screen->root);
printf("screen_info.sequence = %d\n",screen_info.sequence);
reply = xcb_randr_get_screen_info_reply(connection,screen_info,
NULL);
printf("reply = %p\n",reply);
printf("reply->response_type = %d\n",reply->response_type);
printf("reply->rotations = %d\n",reply->rotations);
printf("reply->sequence = %d\n",reply->sequence);
printf("reply->length = %d\n",reply->length);
printf("reply->nSizes = %d\n",reply->nSizes);
printf("reply->sizeID = %d\n",reply->sizeID);
printf("reply->rotation = %d\n",reply->rotation);
printf("reply->rate = %d\n",reply->rate);
printf("reply->nInfo = %d\n",reply->nInfo);
printf("reply+1 = %p\n",reply+1);
sizes = xcb_randr_get_screen_info_sizes(reply);
printf("sizes = %p\n",sizes);
sizes_length = xcb_randr_get_screen_info_sizes_length(reply);
printf("sizes_length = %d\n",sizes_length);
rates_iter = xcb_randr_get_screen_info_rates_iterator(reply);
printf("rates_iter.data = %p\n",rates_iter.data);
printf("rates_iter.rem = %d\n",rates_iter.rem);
printf("rates_iter.index = %d\n",rates_iter.index);
for( ; rates_iter.rem; xcb_randr_refresh_rates_next(&rates_iter))
{
rates = xcb_randr_refresh_rates_rates(rates_iter.data);
printf("rates = %p\n",rates);
rates_length =
xcb_randr_refresh_rates_rates_length(rates_iter.data);
printf("rates_length = %d\n",rates_length);
printf("rates[0] = %d\n",rates[0]);
/*
for(i = 0; i < rates_length; i++)
{
printf("%d%c",rates[i],(i==rates_length-1)?'\n':' ');
}
*/
}
for(i = 0; i < sizes_length; i++)
{
printf("%d %d %d %d %d\n",i,sizes[i].width,sizes[i].height,sizes[i].mwidth,sizes[i].mheight);
}
return 0;
}
So my output is
display = 0x563687942010
connection = 0x563687943410
win = 54525954
screen_num = 0
screen->root = 241
FBConfig = 0x563687951b20
nelements = 8
visual = 0x563687951d30
visualID = 33
context = 0x563687951680
colormap = 54525956
create_color.sequence = 26
error = (nil)
create_win.sequence = 28
error = (nil)
screen_info.sequence = 31
reply = 0x563687abde30
reply->response_type = 1
reply->rotations = 63
reply->sequence = 31
reply->length = 36
reply->nSizes = 18
reply->sizeID = 1
reply->rotation = 1
reply->rate = 30
reply->nInfo = 53
reply+1 = 0x563687abde50
sizes = 0x563687abde50
sizes_length = 18
rates_iter.data = 0x563687abdee0
rates_iter.rem = 35
rates_iter.index = 176
rates = 0x563687abdee2
rates_length = 0
rates[0] = 0
rates = 0x563687abdee4
rates_length = 0
rates[0] = 0
rates = 0x563687abdee6
rates_length = 0
...
rates = 0x563687add7a8
rates_length = 0
rates[0] = 0
0 4096 2160 1872 1053
1 3840 2160 1872 1053
2 1920 1080 1872 1053
3 1680 1050 1872 1053
4 1600 900 1872 1053
5 1280 1024 1872 1053
6 1440 900 1872 1053
7 1366 768 1872 1053
8 1280 800 1872 1053
9 1152 864 1872 1053
10 1280 720 1872 1053
11 1024 768 1872 1053
12 832 624 1872 1053
13 800 600 1872 1053
14 720 576 1872 1053
15 720 480 1872 1053
16 640 480 1872 1053
17 720 400 1872 1053
Can anyone spot what I am doing wrong?
OK, after a day off from Linux and then a day banging my head against this particular brick wall, I have answered most of my question. The reason xcb_randr_get_screen_info_reply didn't fully work was that before you call xcb_randr_get_screen_info_unchecked, you have to first invoke xcb_randr_query_version with both major_version and minor_version set to at least 1 or xcb thinks you have a lower version than 1.1 or something and you don't get the refresh rates data structures set up correctly.
Then when I got that far, I found something horrible in my installation's xcb/randr.h:
/**
* Get the next element of the iterator
* #param i Pointer to a xcb_randr_refresh_rates_iterator_t
*
* Get the next element in the iterator. The member rem is
* decreased by one. The member data points to the next
* element. The member index is increased by sizeof(xcb_randr_refresh_rates_t)
*/
void
xcb_randr_refresh_rates_next (xcb_randr_refresh_rates_iterator_t *i /**< */);
See that? Member rem gets decreased by one, not by data->nRates, so rem is invalidated by invocation of this function. Is that just a bug that was there in the first specification of xcb and is now frozen in, or is there some reason for this? Thus even when the first problem is fixed, depending on rem to count down to zero was not going to work.
I even discovered why RandR gets the wrong physical dimensions for my screen: the physical dimensions are available in cm in EDID bytes 21:22, and are correct for my TV. However they are also given in mm in descriptor 1 and descriptor 2 in EDID bytes 66:68 and bytes 84:86 and those numbers correspond to an 84 in TV! Of course Samsung should fix this problem by sending me a TV that matches their EDID data :)
There is another issue in that the EDID says the preferred video mode is 3840x2150 # 60 Hz, even though both by computer and TV only have HDMI 1.4, so the pixel clock rate specified is impossibly fast. I guess that's why Windows 8.1 has a hard time booting up with this TV and why Linux and OSX both skip the native mode and boot to 4096x2160 # 24 Hz, so when the Unity shell isn't active 128 pixels get chopped off of either side. Windows 10 has no problems with the TV, however.
I hope that answering my own question was the right thing to do; I've never tried that before in this forum.
Related
can we set a bigger block size to the motion vector in ffmpeg
In the FFmpeg v5.0 function add_mb at line 1562 in mpegvideo.c file static int add_mb(AVMotionVector *mb, uint32_t mb_type, int dst_x, int dst_y, int motion_x, int motion_y, int motion_scale, int direction) { mb->w = IS_8X8(mb_type) || IS_8X16(mb_type) ? 8 : 16; mb->h = IS_8X8(mb_type) || IS_16X8(mb_type) ? 8 : 16; mb->motion_x = motion_x; mb->motion_y = motion_y; mb->motion_scale = motion_scale; mb->dst_x = dst_x; mb->dst_y = dst_y; mb->src_x = dst_x + motion_x / motion_scale; mb->src_y = dst_y + motion_y / motion_scale; mb->source = direction ? 1 : -1; mb->flags = 0; // XXX: does mb_type contain extra information that could be exported here? return 1; } Depending on the MB_TYPE the block could take 8 or 16 pixels at one time so can we set the width and the height of the motion block to a bigger range in order to reduce the motion vector total length? that could hurt the quality of the image but I'm ok with that.
What is the correct way to stream custom packets using ffmpeg?
I want to encode frames from camera using NvPipe and stream them via RTP using FFmpeg. My code produces the following error when I want to decode the stream: [h264 # 0x7f3c6c007e80] decode_slice_header error [h264 # 0x7f3c6c007e80] non-existing PPS 0 referenced [h264 # 0x7f3c6c007e80] decode_slice_header error [h264 # 0x7f3c6c007e80] non-existing PPS 0 referenced [h264 # 0x7f3c6c007e80] decode_slice_header error [h264 # 0x7f3c6c007e80] non-existing PPS 0 referenced [h264 # 0x7f3c6c007e80] decode_slice_header error [h264 # 0x7f3c6c007e80] no frame! [h264 # 0x7f3c6c007e80] non-existing PPS 0 referenced 0B f=0/0 Last message repeated 1 times On another PC, it is even not able to stream, and fails with an segmentation fault on av_interleaved_write_frame(..). How to initialize the AVPacket and its timebase correctly to successfully send and receive the stream using ffplay/VLC/ other software? My code: avformat_network_init(); // init encoder AVPacket *pkt = new AVPacket(); int targetBitrate = 1000000; int targetFPS = 30; const uint32_t width = 640; const uint32_t height = 480; NvPipe* encoder = NvPipe_CreateEncoder(NVPIPE_BGRA32, NVPIPE_H264, NVPIPE_LOSSY, targetBitrate, targetFPS); // init stream output std::string str = "rtp://127.0.0.1:49990"; AVStream* stream = nullptr; AVOutputFormat *output_format = av_guess_format("rtp", nullptr, nullptr);; AVFormatContext *output_format_ctx = avformat_alloc_context(); avformat_alloc_output_context2(&output_format_ctx, output_format, output_format->name, str.c_str()); // open output url if (!(output_format->flags & AVFMT_NOFILE)){ ret = avio_open(&output_format_ctx->pb, str.c_str(), AVIO_FLAG_WRITE); } output_format_ctx->oformat = output_format; output_format->video_codec = AV_CODEC_ID_H264; stream = avformat_new_stream(output_format_ctx,nullptr); stream->id = 0; stream->codecpar->codec_id = AV_CODEC_ID_H264; stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; stream->codecpar->width = width; stream->codecpar->height = height; stream->time_base.den = 1; stream->time_base.num = targetFPS; // 30fps /* Write the header */ avformat_write_header(output_format_ctx, nullptr); // this seems to destroy the timebase of the stream std::vector<uint8_t> rgba(width * height * 4); std::vector<uint8_t> compressed(rgba.size()); int frameCnt = 0; // encoding and streaming while (true) { frameCnt++; // Encoding // Construct dummy frame for (uint32_t y = 0; y < height; ++y) for (uint32_t x = 0; x < width; ++x) rgba[4 * (y * width + x) + 1] = (255.0f * x* y) / (width * height) * (y % 100 < 50); uint64_t size = NvPipe_Encode(encoder, rgba.data(), width * 4, compressed.data(), compressed.size(), width, height, false); // last parameter needs to be true for keyframes av_init_packet(pkt); pkt->data = compressed.data(); pkt->size = size; pkt->pts = frameCnt; if(!memcmp(compressed.data(), "\x00\x00\x00\x01\x67", 5)) { pkt->flags |= AV_PKT_FLAG_KEY; } //stream fflush(stdout); // Write the compressed frame into the output pkt->pts = av_rescale_q(frameCnt, AVRational {1, targetFPS}, stream->time_base); pkt->dts = pkt->pts; pkt->stream_index = stream->index; /* Write the data on the packet to the output format */ av_interleaved_write_frame(output_format_ctx, pkt); /* Reset the packet */ av_packet_unref(pkt); } The .sdp file to open the stream with ffplay looks like this: v=0 o=- 0 0 IN IP4 127.0.0.1 s=No Name c=IN IP4 127.0.0.1 t=0 0 a=tool:libavformat 58.18.101 m=video 49990 RTP/AVP 96 a=rtpmap:96 H264/90000 a=fmtp:96 packetization-mode=1
The code above sends no keyframes (or I-frames). The (obvious) solution is to send keyframes by turning the last parameter of NvPipe_Encode() to true. To achieve a certain GOP size gop_size, do something like NvPipe_Encode(encoder, rgba.data(), width * 4, compressed.data(), compressed.size(), width, height, framecnt % gop_size == 0 ? true : false);
Why codecs x264/x265 ignores pts and dts of input frame?
I'm trying to encode images from a webcam with libx265 (libx264 tried earlier) ...The webcam can not shoot with stable FPS because of the different amount of light entering the matrix and, as a result, different delays. Therefore, I count the fps and dts of the incoming frame and set these values for the corresponding parameters of the x265_image object, and init the encoder fpsNum with 1000 and fpsDenom with 1 (for millisecond timebase). The problem is that the encoder ignores pts and dts of input image and encodes at 1000 fps! The same trick with timebase produces smooth record with libvpx. Why it does not work with x264/x265 codecs? Here is parameters initialization: ... error = (x265_param_default_preset(param, "fast", "zerolatency") != 0); if(!error){ param->sourceWidth = width; param->sourceHeight = height; param->frameNumThreads = 1; param->fpsNum = 1000; param->fpsDenom = 1; // Intra refres: param->keyframeMax = 15; param->intraRefine = 1; // Rate control: param->rc.rateControlMode = X265_RC_CQP; param->rc.rfConstant = 12; param->rc.rfConstantMax = 48; // For streaming: param->bRepeatHeaders = 1; param->bAnnexB = 1; encoder = x265_encoder_open(param); ... } ... Here is frame adding function: bool hevc::Push(unsigned char *data){ if(!error){ std::lock_guard<std::mutex> lock(m_framestack); if( timer > 0){ framestack.back()->dts = clock() - timer; timer+= framestack.back()->dts; } else{timer = clock();} x265_picture *picture = x265_picture_alloc(); if( picture){ x265_picture_init(param, picture); picture->height = param->sourceHeight; picture->stride[0] = param->sourceWidth; picture->stride[1] = picture->stride[2] = picture->stride[0] / 2; picture->planes[0] = new char[ luma_size]; picture->planes[1] = new char[chroma_size]; picture->planes[2] = new char[chroma_size]; colorspaces::BGRtoI420(param->sourceWidth, param->sourceHeight, data, (byte*)picture->planes[0], (byte*)picture->planes[1], (byte*)picture->planes[2]); picture->pts = picture->dts = 0; framestack.emplace_back(picture); } else{error = true;} } return !error; } Global PTS is increasing right after x265_encoder_encode call: pts+= pic_in->dts; and sets as a pts of new image from framestack queue when it comes to encoder. Can the x265/x264 codecs encode at variable fps? How to configure it if yes?
I don't know about x265 but in x264 to encode variable frame rate (VFR) video you should enable x264_param_t.b_vfr_input option which was disabled by your zerolatency tuning (VFR encode need 1 frame latency). Also at least in x264 timebase should be in i_timebase_num/i_timebase_den and i_fps_num/i_fps_den to be average fps (or keep default 25/1 if you don't know fps) or you will broke ratecontrol.
What is the correct formula to position my scroll bar slider?
This slider is used in my rpg to get rid of X amount of items so at pos 1 it will junk 1 item at max pos it will junk all the items. slider image My current formula is not correct int pos_int = std::ceil(106.00 / (double)amount); int base_pos = 318 - (amount - this->dj_slider_pos) * pos_int; 122 pixels is the total width of the draw area but after considering the width of the slider is 16 pixels the last draw position is at 106 pixels. Amount is the total number of items you have and dj_slider_pos tracks the position of the slider. I have tried a few different variations but figured I might as well try and get help here instead of continuing to spin my wheels. Here is a little more code. int amount = 1; std::string itname = ""; UTIL_PTR_VECTOR_FOREACH(this->character->inventory_items,Character_Item,it) { if(it->id == this->character_inventory->selected_id) { itname = util::ucfirst(this->world->eif->Get(it->id)->name); amount = it->amount; break; } } int pos_int = std::ceil(106.00 / (double)amount); int base_pos = 318 - (amount - this->dj_slider_pos) * pos_int; if(base_pos < 212) base_pos = 212; else if(base_pos > 318) base_pos = 318; this->DrawSplitBmp(base_pos,165, this->mouse_sliders,this->screen, &drop_junktabs[7]); Problem solved! I initialized dj_slider_pos to 0 instead of 1 and changed the following lines. double pos_int = 106.00 / (double)(amount-1); int base_pos = 212 + (this->dj_slider_pos * pos_int);
How To Change Retarget Difficulty For An Altcoin
I am helping a community of an altcoin that needs to change it's retarget difficulty. So far I have written some code for the new wallet. This is what I have done to the main.cpp file I want to change the retarget the difficulty from 960 blocks (1 day) to 40 blocks (1 hours) of a coin. The block that I want the change to happen is 28000. from: static const int64 nTargetTimespan = 1 * 24 * 60 * 60; // UFO: 1 days static const int64 nTargetSpacing = 90; // UFO: 1.5 minute blocks static const int64 nInterval = nTargetTimespan / nTargetSpacing; static const int64 nReTargetHistoryFact = 4; // look at 4 times the retarget interval into block history to: static int64 nTargetTimespan = 1 * 24 * 60 * 60; // 1 day static int64 nTargetSpacing = 90; // 1.5 minute blocks static int64 nInterval = nTargetTimespan / nTargetSpacing; static int64 nReTargetHistoryFact = 4; // look at 4 times the retarget interval into block history Then in the GetNextWorkRequired function this: from: // Genesis block if (pindexLast == NULL) return nProofOfWorkLimit; // Only change once per interval if ((pindexLast->nHeight+1) % nInterval != 0) to: // Genesis block if (pindexLast == NULL) return nProofOfWorkLimit; // From block 28000 reassess the difficulty every 40 blocks // Reduce Retarget factor to 2 if(pindexLast->nHeight >= 28000) { nTargetTimespan = 60 * 60; // 1 hours nTargetSpacing = 1.5 * 60; // 1.5 minutes nInterval = nTargetTimespan / nTargetSpacing; nReTargetHistoryFact = 2; } else { nTargetTimespan = 1 * 24 * 60 * 60; // 1 day nTargetSpacing = 1.5 * 60; // 1.5 minutes nInterval = nTargetTimespan / nTargetSpacing; nReTargetHistoryFact = 4; } // Only change once per interval if ((pindexLast->nHeight+1) % nInterval != 0) Is this code correct or is something else to be done? Thanks I appreciate any help.
your code looks good. I was wondering the same, so i changed it as you did , but i got this error GetNextWorkRequired RETARGET nTargetTimespan = 60 nActualTimespan = 79 Before: 1d0d7333 0000000d73330000000000000000000000000000000000000000000000000000 After: 1d11b58b 00000011b58baeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ERROR: AcceptBlock() : incorrect proof of work ERROR: ProcessBlock() : AcceptBlock FAILED I don't know if it could work if i release the new wallet