GUAC-236: Maintain aspect ratio by adding letterboxes / pillarboxes as necessary.
This commit is contained in:
parent
2798536a7a
commit
8ed0cd5f16
@ -255,18 +255,39 @@ int guacenc_video_advance_timeline(guacenc_video* video,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given Guacamole video encoder buffer to a frame in the format
|
* Converts the given Guacamole video encoder buffer to a frame in the format
|
||||||
* required by libavcodec / libswscale. No scaling is performed; the image data
|
* required by libavcodec / libswscale. Black margins of the specified sizes
|
||||||
* is copied verbatim.
|
* will be added. No scaling is performed; the image data is copied verbatim.
|
||||||
*
|
*
|
||||||
* @param buffer
|
* @param buffer
|
||||||
* The guacenc_buffer to copy as a new AVFrame.
|
* The guacenc_buffer to copy as a new AVFrame.
|
||||||
*
|
*
|
||||||
|
* @param lsize
|
||||||
|
* The size of the letterboxes to add, in pixels. Letterboxes are the
|
||||||
|
* horizontal black boxes added to images which are scaled down to fit the
|
||||||
|
* destination because they are too wide (the width is scaled to exactly
|
||||||
|
* fit the destination, resulting in extra space at the top and bottom).
|
||||||
|
*
|
||||||
|
* @param psize
|
||||||
|
* The size of the pillarboxes to add, in pixels. Pillarboxes are the
|
||||||
|
* vertical black boxes added to images which are scaled down to fit the
|
||||||
|
* destination because they are too tall (the height is scaled to exactly
|
||||||
|
* fit the destination, resulting in extra space on the sides).
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* A pointer to a newly-allocated AVFrame containing exactly the same image
|
* A pointer to a newly-allocated AVFrame containing exactly the same image
|
||||||
* data as the given buffer. The image data within the frame and the frame
|
* data as the given buffer. The image data within the frame and the frame
|
||||||
* itself must be manually freed later.
|
* itself must be manually freed later.
|
||||||
*/
|
*/
|
||||||
static AVFrame* guacenc_video_frame_convert(guacenc_buffer* buffer) {
|
static AVFrame* guacenc_video_frame_convert(guacenc_buffer* buffer, int lsize,
|
||||||
|
int psize) {
|
||||||
|
|
||||||
|
/* Init size of left/right pillarboxes */
|
||||||
|
int left = psize;
|
||||||
|
int right = psize;
|
||||||
|
|
||||||
|
/* Init size of top/bottom letterboxes */
|
||||||
|
int top = lsize;
|
||||||
|
int bottom = lsize;
|
||||||
|
|
||||||
/* Prepare source frame for buffer */
|
/* Prepare source frame for buffer */
|
||||||
AVFrame* frame = av_frame_alloc();
|
AVFrame* frame = av_frame_alloc();
|
||||||
@ -275,8 +296,8 @@ static AVFrame* guacenc_video_frame_convert(guacenc_buffer* buffer) {
|
|||||||
|
|
||||||
/* Copy buffer properties to frame */
|
/* Copy buffer properties to frame */
|
||||||
frame->format = AV_PIX_FMT_RGB32;
|
frame->format = AV_PIX_FMT_RGB32;
|
||||||
frame->width = buffer->width;
|
frame->width = buffer->width + left + right;
|
||||||
frame->height = buffer->height;
|
frame->height = buffer->height + top + bottom;
|
||||||
|
|
||||||
/* Allocate actual backing data for frame */
|
/* Allocate actual backing data for frame */
|
||||||
if (av_image_alloc(frame->data, frame->linesize, frame->width,
|
if (av_image_alloc(frame->data, frame->linesize, frame->width,
|
||||||
@ -300,16 +321,46 @@ static AVFrame* guacenc_video_frame_convert(guacenc_buffer* buffer) {
|
|||||||
int width = buffer->width;
|
int width = buffer->width;
|
||||||
int height = buffer->height;
|
int height = buffer->height;
|
||||||
|
|
||||||
/* Source buffer and destination frame dimensions are identical */
|
/* Source buffer is guaranteed to fit within destination buffer */
|
||||||
assert(width == frame->width);
|
assert(width <= frame->width);
|
||||||
assert(height == frame->height);
|
assert(height <= frame->height);
|
||||||
|
|
||||||
|
/* Add top margin */
|
||||||
|
while (top > 0) {
|
||||||
|
memset(dst_data, 0, frame->width * 4);
|
||||||
|
dst_data += dst_stride;
|
||||||
|
top--;
|
||||||
|
}
|
||||||
|
|
||||||
/* Copy all data from source buffer to destination frame */
|
/* Copy all data from source buffer to destination frame */
|
||||||
while (height > 0) {
|
while (height > 0) {
|
||||||
memcpy(dst_data, src_data, width * 4);
|
|
||||||
|
/* Calculate size of margin and data regions */
|
||||||
|
int left_size = left * 4;
|
||||||
|
int data_size = width * 4;
|
||||||
|
int right_size = right * 4;
|
||||||
|
|
||||||
|
/* Add left margin */
|
||||||
|
memset(dst_data, 0, left_size);
|
||||||
|
|
||||||
|
/* Copy data */
|
||||||
|
memcpy(dst_data + left_size, src_data, data_size);
|
||||||
|
|
||||||
|
/* Add right margin */
|
||||||
|
memset(dst_data + left_size + data_size, 0, right_size);
|
||||||
|
|
||||||
dst_data += dst_stride;
|
dst_data += dst_stride;
|
||||||
src_data += src_stride;
|
src_data += src_stride;
|
||||||
|
|
||||||
height--;
|
height--;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add bottom margin */
|
||||||
|
while (bottom > 0) {
|
||||||
|
memset(dst_data, 0, frame->width * 4);
|
||||||
|
dst_data += dst_stride;
|
||||||
|
bottom--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Frame converted */
|
/* Frame converted */
|
||||||
@ -319,21 +370,45 @@ static AVFrame* guacenc_video_frame_convert(guacenc_buffer* buffer) {
|
|||||||
|
|
||||||
void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer) {
|
void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer) {
|
||||||
|
|
||||||
|
int lsize;
|
||||||
|
int psize;
|
||||||
|
|
||||||
/* Ignore NULL buffers */
|
/* Ignore NULL buffers */
|
||||||
if (buffer == NULL || buffer->surface == NULL)
|
if (buffer == NULL || buffer->surface == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Obtain destination frame */
|
||||||
|
AVFrame* dst = video->next_frame;
|
||||||
|
|
||||||
|
/* Determine width of image if height is scaled to match destination */
|
||||||
|
int scaled_width = buffer->width * dst->height / buffer->height;
|
||||||
|
|
||||||
|
/* Determine height of image if width is scaled to match destination */
|
||||||
|
int scaled_height = buffer->height * dst->width / buffer->width;
|
||||||
|
|
||||||
|
/* If height-based scaling results in a fit width, add pillarboxes */
|
||||||
|
if (scaled_width <= dst->width) {
|
||||||
|
lsize = 0;
|
||||||
|
psize = (dst->width - scaled_width)
|
||||||
|
* buffer->height / dst->height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If width-based scaling results in a fit width, add letterboxes */
|
||||||
|
else {
|
||||||
|
assert(scaled_height <= dst->height);
|
||||||
|
psize = 0;
|
||||||
|
lsize = (dst->height - scaled_height)
|
||||||
|
* buffer->width / dst->width / 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare source frame for buffer */
|
/* Prepare source frame for buffer */
|
||||||
AVFrame* src = guacenc_video_frame_convert(buffer);
|
AVFrame* src = guacenc_video_frame_convert(buffer, lsize, psize);
|
||||||
if (src == NULL) {
|
if (src == NULL) {
|
||||||
guacenc_log(GUAC_LOG_WARNING, "Failed to allocate source frame. "
|
guacenc_log(GUAC_LOG_WARNING, "Failed to allocate source frame. "
|
||||||
"Frame dropped.");
|
"Frame dropped.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Obtain destination frame */
|
|
||||||
AVFrame* dst = video->next_frame;
|
|
||||||
|
|
||||||
/* Prepare scaling context */
|
/* Prepare scaling context */
|
||||||
struct SwsContext* sws = sws_getContext(src->width, src->height,
|
struct SwsContext* sws = sws_getContext(src->width, src->height,
|
||||||
PIX_FMT_RGB32, dst->width, dst->height, PIX_FMT_YUV420P,
|
PIX_FMT_RGB32, dst->width, dst->height, PIX_FMT_YUV420P,
|
||||||
|
Loading…
Reference in New Issue
Block a user