summaryrefslogtreecommitdiffstats
path: root/video/out/wayland/buffer.c
blob: aae590eba270bf2e1dae3360d573abe2473e7230 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * This file is part of mpv video player.
 * Copyright © 2014 Alexander Preisinger <alexander.preisinger@gmail.com>
 *
 * mpv is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * mpv is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "buffer.h"
#include "memfile.h"

#include <unistd.h>
#include <sys/mman.h>

int8_t format_get_bytes(const format_t *fmt)
{
    return mp_imgfmt_get_desc(fmt->mp_format).bytes[0];
}

shm_buffer_t* shm_buffer_create(uint32_t width,
                                uint32_t height,
                                format_t fmt,
                                struct wl_shm *shm,
                                const struct wl_buffer_listener *listener)
{
    int8_t bytes = format_get_bytes(&fmt);
    uint32_t stride = SHM_BUFFER_STRIDE(width, bytes);
    uint32_t size = stride * height;

    shm_buffer_t *buffer = calloc(1, sizeof(shm_buffer_t));
    int fd = memfile_create(size);

    if (fd < 0)
        return NULL;

    buffer->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    if (buffer->data == MAP_FAILED) {
        close(fd);
        return NULL;
    }

    buffer->shm_pool = wl_shm_create_pool(shm, fd, size);
    buffer->buffer = wl_shm_pool_create_buffer(buffer->shm_pool,
                                               0, width, height, stride,
                                               fmt.wl_format);

    wl_buffer_add_listener(buffer->buffer, listener, buffer);

    buffer->fd = fd;
    buffer->height = height;
    buffer->stride = stride;
    buffer->format = fmt;
    buffer->bytes = bytes;
    buffer->pool_size = size;
    buffer->pending_height = 0;
    buffer->pending_width = 0;

    return buffer;
}

int shm_buffer_resize(shm_buffer_t *buffer, uint32_t width, uint32_t height)
{
    uint32_t new_stride = SHM_BUFFER_STRIDE(width, buffer->bytes);
    uint32_t new_size = new_stride * height;

    if (SHM_BUFFER_IS_BUSY(buffer)) {
        SHM_BUFFER_SET_PNDNG_RSZ(buffer);
        buffer->pending_width = width;
        buffer->pending_height = height;
        return SHM_BUFFER_BUSY;
    }

    SHM_BUFFER_CLEAR_PNDNG_RSZ(buffer);

    if (new_size > buffer->pool_size) {
        munmap(buffer->data, buffer->pool_size);
        ftruncate(buffer->fd, new_size);

        buffer->data = mmap(NULL, new_size, PROT_READ | PROT_WRITE,
                            MAP_SHARED, buffer->fd, 0);

        // TODO: the buffer should be destroyed when -1 is return
        if (buffer->data == MAP_FAILED)
            return -1;

        wl_shm_pool_resize(buffer->shm_pool, new_size);
        buffer->pool_size = new_size;
    }

    const void *listener = wl_proxy_get_listener((struct wl_proxy*)buffer->buffer);

    wl_buffer_destroy(buffer->buffer);
    buffer->buffer = wl_shm_pool_create_buffer(buffer->shm_pool,
                                               0, width, height, new_stride,
                                               buffer->format.wl_format);

    wl_buffer_add_listener(buffer->buffer, listener, buffer);

    buffer->height = height;
    buffer->stride = new_stride;

    return 0;
}

int shm_buffer_pending_resize(shm_buffer_t *buffer)
{
    if (SHM_BUFFER_PENDING_RESIZE(buffer)) {
        SHM_BUFFER_CLEAR_PNDNG_RSZ(buffer);
        return shm_buffer_resize(buffer, buffer->pending_width, buffer->pending_height);
    }
    else {
        return 0;
    }
}

void shm_buffer_destroy(shm_buffer_t *buffer)
{
    if (!buffer)
        return;

    wl_buffer_destroy(buffer->buffer);
    wl_shm_pool_destroy(buffer->shm_pool);
    munmap(buffer->data, buffer->pool_size);
    close(buffer->fd);
    free(buffer);
}